From abdbbaeddeac654b9dedcebd89e4f05ac22576e3 Mon Sep 17 00:00:00 2001 From: Dmitry Kovalenko Date: Wed, 6 Jul 2022 09:15:34 +0300 Subject: [PATCH] Opensourcing arkcompiler_ets_runtime Signed-off-by: Dmitry Kovalenko --- AUTHORS | 136 + BUILD.gn | 122 + CMakeLists.txt | 46 + LICENSE-2.0.txt | 191 + README.en.md | 36 - README.md | 39 - assembler/CMakeLists.txt | 33 + assembler/assembler_sources.gn | 14 + assembler/extension/ecmascript_meta.cpp | 22 + assembler/extension/ecmascript_meta.h | 231 + assembler/extension/metadata.yaml | 27 + bytecode_optimizer/CMakeLists.txt | 33 + bytecode_optimizer/bytecodeopt_sources.gn | 12 + .../ecmascript_codegen_intrinsics_gen.inc.erb | 85 + .../ecmascript_codegen_intrinsics.inc | 16 + compiler/CMakeLists.txt | 46 + compiler/codegen_intrinsics_ecmascript.cpp | 307 ++ compiler/compiler_sources.gn | 20 + compiler/ecmascript_extensions/CMakeLists.txt | 18 + .../ecmascript_codegen_extensions.cpp | 83 + .../ecmascript_codegen_extensions.h | 22 + .../ecmascript_compiler_interface.h | 34 + .../ecmascript_environment.h | 122 + .../thread_environment_api.h | 81 + compiler/extensions.h | 22 + compiler/intrinsics_inline_ecmascript.inl | 99 + .../intrinsics_type_resolving_ecmascript.cpp | 504 +++ ...intrinsics_type_resolving_ecmascript.inl.h | 33 + .../code_generator/compiler_base_types.cpp | 390 ++ .../code_generator/compiler_base_types.h | 38 + compiler/optimizer/ir/dyn_datatype.h | 122 + .../ir_builder/ecmascript_inst_builder.cpp | 16 + .../ir_builder/ecmascript_inst_builder.h | 24 + .../ir_builder/ecmascript_inst_templates.yaml | 37 + .../ecmascript_inst_builder_gen.cpp.erb | 303 ++ ecmascript_plugin_options.yaml | 86 + ecmastdlib/CMakeLists.txt | 29 + ecmastdlib/ecmastdlib.pa | 182 + ecmastdlib/ecmastdlib_inline_gen.h.erb | 41 + ecmastdlib/ecmastdlib_inline_gen.rb | 61 + irtoc_scripts/common.irt | 70 + irtoc_scripts/interpreter_handlers.irt | 1578 +++++++ irtoc_scripts/interpreter_main_loop.irt | 248 ++ irtoc_scripts/irtoc_scripts.gn | 17 + isa/CMakeLists.txt | 14 + isa/isa.yaml | 877 ++++ isa/isa_sources.gn | 14 + runtime/CMakeLists.txt | 308 ++ runtime/accessor_data.h | 106 + runtime/arch/aarch64/builtin_bridge_aarch64.S | 110 + runtime/arch/amd64/builtin_bridge_amd64.S | 98 + runtime/arch/arm/builtin_bridge_arm.S | 24 + runtime/asm_defines/asm_defines.def | 45 + runtime/asm_defines/defines.h | 31 + runtime/base/array_helper.cpp | 144 + runtime/base/array_helper.h | 35 + runtime/base/builtins_base.cpp | 32 + runtime/base/builtins_base.h | 85 + runtime/base/config.h | 58 + runtime/base/error_helper.cpp | 221 + runtime/base/error_helper.h | 44 + runtime/base/error_type.h | 33 + runtime/base/gc_ring_buffer.h | 77 + runtime/base/json_parser.cpp | 79 + runtime/base/json_parser.h | 767 ++++ runtime/base/json_stringifier.cpp | 724 +++ runtime/base/json_stringifier.h | 92 + runtime/base/number_helper.cpp | 697 +++ runtime/base/number_helper.h | 103 + runtime/base/object_helper.cpp | 106 + runtime/base/object_helper.h | 20 + runtime/base/string_helper.cpp | 162 + runtime/base/string_helper.h | 204 + runtime/base/typed_array_helper-inl.h | 203 + runtime/base/typed_array_helper.cpp | 557 +++ runtime/base/typed_array_helper.h | 69 + runtime/base/utf_helper.cpp | 229 + runtime/base/utf_helper.h | 87 + .../aarch64/handle_call_ecma_N_v8_aarch64.S | 72 + ...ecma_callirangedyn_pref_imm16_v8_aarch64.S | 75 + ..._callithisrangedyn_pref_imm16_v8_aarch64.S | 67 + .../arch/amd64/handle_call_ecma_N_v8_amd64.S | 89 + ...l_ecma_callirangedyn_pref_imm16_v8_amd64.S | 73 + ...ma_callithisrangedyn_pref_imm16_v8_amd64.S | 69 + .../arch/arm/handle_call_ecma_N_v8_arm.S | 16 + ...all_ecma_callirangedyn_pref_imm16_v8_arm.S | 16 + ...ecma_callithisrangedyn_pref_imm16_v8_arm.S | 16 + runtime/bridge/ecma_bridge_helpers.cpp | 17 + runtime/builtins.cpp | 3054 +++++++++++++ runtime/builtins.h | 231 + runtime/builtins/builtins_ark_tools.cpp | 44 + runtime/builtins/builtins_ark_tools.h | 31 + runtime/builtins/builtins_array.cpp | 2870 ++++++++++++ runtime/builtins/builtins_array.h | 106 + runtime/builtins/builtins_arraybuffer.cpp | 590 +++ runtime/builtins/builtins_arraybuffer.h | 87 + .../builtins_async_from_sync_iterator.cpp | 235 + .../builtins_async_from_sync_iterator.h | 25 + runtime/builtins/builtins_async_function.cpp | 28 + runtime/builtins/builtins_async_function.h | 29 + runtime/builtins/builtins_async_generator.cpp | 83 + runtime/builtins/builtins_async_generator.h | 30 + runtime/builtins/builtins_async_iterator.cpp | 18 + runtime/builtins/builtins_async_iterator.h | 19 + runtime/builtins/builtins_boolean.cpp | 95 + runtime/builtins/builtins_boolean.h | 38 + runtime/builtins/builtins_collator.cpp | 153 + runtime/builtins/builtins_collator.h | 40 + runtime/builtins/builtins_dataview.cpp | 440 ++ runtime/builtins/builtins_dataview.h | 81 + runtime/builtins/builtins_date.cpp | 358 ++ runtime/builtins/builtins_date.h | 180 + .../builtins/builtins_date_time_format.cpp | 324 ++ runtime/builtins/builtins_date_time_format.h | 51 + runtime/builtins/builtins_errors.cpp | 106 + runtime/builtins/builtins_errors.h | 82 + runtime/builtins/builtins_function.cpp | 351 ++ runtime/builtins/builtins_function.h | 47 + runtime/builtins/builtins_generator.cpp | 99 + runtime/builtins/builtins_generator.h | 41 + runtime/builtins/builtins_global.cpp | 559 +++ runtime/builtins/builtins_global.h | 67 + runtime/builtins/builtins_intl.cpp | 38 + runtime/builtins/builtins_intl.h | 28 + runtime/builtins/builtins_iterator.cpp | 52 + runtime/builtins/builtins_iterator.h | 36 + runtime/builtins/builtins_json.cpp | 106 + runtime/builtins/builtins_json.h | 31 + runtime/builtins/builtins_locale.cpp | 357 ++ runtime/builtins/builtins_locale.h | 56 + runtime/builtins/builtins_map.cpp | 256 ++ runtime/builtins/builtins_map.h | 51 + runtime/builtins/builtins_math.cpp | 745 ++++ runtime/builtins/builtins_math.h | 112 + runtime/builtins/builtins_number.cpp | 455 ++ runtime/builtins/builtins_number.h | 61 + runtime/builtins/builtins_number_format.cpp | 206 + runtime/builtins/builtins_number_format.h | 42 + runtime/builtins/builtins_object.cpp | 1179 +++++ runtime/builtins/builtins_object.h | 114 + runtime/builtins/builtins_plural_rules.cpp | 130 + runtime/builtins/builtins_plural_rules.h | 37 + runtime/builtins/builtins_promise.cpp | 601 +++ runtime/builtins/builtins_promise.h | 63 + runtime/builtins/builtins_promise_handler.cpp | 243 + runtime/builtins/builtins_promise_handler.h | 44 + runtime/builtins/builtins_promise_job.cpp | 118 + runtime/builtins/builtins_promise_job.h | 28 + runtime/builtins/builtins_proxy.cpp | 96 + runtime/builtins/builtins_proxy.h | 37 + runtime/builtins/builtins_reflect.cpp | 308 ++ runtime/builtins/builtins_reflect.h | 66 + runtime/builtins/builtins_regexp.cpp | 1834 ++++++++ runtime/builtins/builtins_regexp.h | 213 + .../builtins_relative_time_format.cpp | 174 + .../builtins/builtins_relative_time_format.h | 52 + runtime/builtins/builtins_set.cpp | 262 ++ runtime/builtins/builtins_set.h | 47 + runtime/builtins/builtins_string.cpp | 1883 ++++++++ runtime/builtins/builtins_string.h | 128 + runtime/builtins/builtins_string_iterator.cpp | 95 + runtime/builtins/builtins_string_iterator.h | 28 + runtime/builtins/builtins_symbol.cpp | 262 ++ runtime/builtins/builtins_symbol.h | 50 + runtime/builtins/builtins_typedarray.cpp | 1438 ++++++ runtime/builtins/builtins_typedarray.h | 110 + runtime/builtins/builtins_weak_map.cpp | 220 + runtime/builtins/builtins_weak_map.h | 38 + runtime/builtins/builtins_weak_set.cpp | 174 + runtime/builtins/builtins_weak_set.h | 35 + runtime/class_info_extractor.cpp | 419 ++ runtime/class_info_extractor.h | 125 + .../class_linker/panda_file_translator.cpp | 587 +++ runtime/class_linker/panda_file_translator.h | 130 + runtime/class_linker/program_object-inl.h | 42 + runtime/class_linker/program_object.h | 84 + runtime/common.h | 40 + .../compiler/ecmascript_runtime_interface.cpp | 46 + .../compiler/ecmascript_runtime_interface.h | 47 + runtime/containers/containers_arraylist.cpp | 69 + runtime/containers/containers_arraylist.h | 31 + runtime/containers/containers_private.cpp | 191 + runtime/containers/containers_private.h | 69 + runtime/dump.cpp | 3131 +++++++++++++ runtime/ecma_class_linker_extension.cpp | 149 + runtime/ecma_class_linker_extension.h | 109 + runtime/ecma_exceptions.cpp | 45 + runtime/ecma_exceptions.h | 29 + runtime/ecma_global_storage-inl.h | 211 + runtime/ecma_global_storage.h | 269 ++ runtime/ecma_handle_scope-inl.h | 54 + runtime/ecma_handle_scope.h | 51 + runtime/ecma_language_context-inl.h | 30 + runtime/ecma_language_context.cpp | 131 + runtime/ecma_language_context.h | 308 ++ runtime/ecma_macros.h | 514 +++ runtime/ecma_module.cpp | 251 ++ runtime/ecma_module.h | 102 + runtime/ecma_runtime.yaml | 1851 ++++++++ runtime/ecma_runtime_call_info.h | 128 + runtime/ecma_string-inl.h | 164 + runtime/ecma_string.cpp | 447 ++ runtime/ecma_string.h | 343 ++ runtime/ecma_string_table.cpp | 196 + runtime/ecma_string_table.h | 77 + runtime/ecma_vm.cpp | 1049 +++++ runtime/ecma_vm.h | 592 +++ runtime/frames.h | 266 ++ runtime/free_object.cpp | 24 + runtime/free_object.h | 81 + runtime/generator_helper.cpp | 38 + runtime/generator_helper.h | 40 + runtime/global_dictionary-inl.h | 187 + runtime/global_dictionary.h | 93 + runtime/global_env.cpp | 53 + runtime/global_env.h | 220 + runtime/global_env_constants-inl.h | 65 + runtime/global_env_constants.cpp | 511 +++ runtime/global_env_constants.h | 369 ++ runtime/global_handle_collection.h | 51 + runtime/hprof/heap_profiler.cpp | 209 + runtime/hprof/heap_profiler.h | 76 + runtime/hprof/heap_profiler_interface.cpp | 57 + runtime/hprof/heap_profiler_interface.h | 47 + runtime/hprof/heap_root_visitor.cpp | 33 + runtime/hprof/heap_root_visitor.h | 36 + runtime/hprof/heap_snapshot.cpp | 737 ++++ runtime/hprof/heap_snapshot.h | 378 ++ .../hprof/heap_snapshot_json_serializer.cpp | 206 + runtime/hprof/heap_snapshot_json_serializer.h | 56 + runtime/hprof/heap_tracker.cpp | 44 + runtime/hprof/heap_tracker.h | 92 + runtime/hprof/string_hashmap.cpp | 99 + runtime/hprof/string_hashmap.h | 83 + runtime/ic/ic_binary_op-inl.h | 355 ++ runtime/ic/ic_binary_op.h | 62 + runtime/ic/ic_compare_op.cpp | 470 ++ runtime/ic/ic_compare_op.h | 78 + runtime/ic/ic_handler-inl.h | 144 + runtime/ic/ic_handler.h | 155 + runtime/ic/ic_runtime.cpp | 229 + runtime/ic/ic_runtime.h | 100 + runtime/ic/ic_runtime_stub-inl.h | 422 ++ runtime/ic/ic_runtime_stub.cpp | 91 + runtime/ic/ic_runtime_stub.h | 78 + runtime/ic/invoke_cache.h | 51 + runtime/ic/profile_type_info.cpp | 287 ++ runtime/ic/profile_type_info.h | 155 + runtime/ic/properties_cache-inl.h | 56 + runtime/ic/properties_cache.h | 66 + runtime/ic/property_box.cpp | 25 + runtime/ic/property_box.h | 50 + runtime/ic/proto_change_details.cpp | 75 + runtime/ic/proto_change_details.h | 75 + .../include/tooling/pt_ecmascript_extension.h | 33 + runtime/internal_call_params.cpp | 132 + runtime/internal_call_params.h | 232 + runtime/interpreter/ecma-interpreter-inl.h | 2215 ++++++++++ runtime/interpreter/ecma-interpreter.h | 29 + runtime/interpreter/fast_runtime_stub-inl.h | 1364 ++++++ runtime/interpreter/fast_runtime_stub.h | 122 + runtime/interpreter/interpreter-inl.h | 210 + runtime/interpreter/interpreter.h | 230 + runtime/interpreter/js_decode_call_instr.h | 132 + runtime/interpreter/js_frame-inl.h | 73 + runtime/interpreter/js_frame.h | 41 + runtime/interpreter/slow_runtime_helper.cpp | 270 ++ runtime/interpreter/slow_runtime_helper.h | 40 + runtime/interpreter/slow_runtime_stub.cpp | 2033 +++++++++ runtime/interpreter/slow_runtime_stub.h | 171 + .../debugger_instruction_dispatch.inl | 272 ++ .../debugger_instruction_handler.inl | 780 ++++ .../templates/instruction_dispatch.inl | 273 ++ runtime/intrinsics-inl.h | 1947 ++++++++ runtime/intrinsics.cpp | 7 + runtime/jobs/micro_job_queue.cpp | 79 + runtime/jobs/micro_job_queue.h | 56 + runtime/jobs/pending_job.h | 57 + runtime/js_arguments.cpp | 190 + runtime/js_arguments.h | 69 + runtime/js_array.cpp | 379 ++ runtime/js_array.h | 101 + runtime/js_array_iterator.cpp | 96 + runtime/js_array_iterator.h | 40 + runtime/js_arraybuffer.cpp | 64 + runtime/js_arraybuffer.h | 48 + runtime/js_arraylist.cpp | 121 + runtime/js_arraylist.h | 64 + .../js_async_from_sync_iterator_object.cpp | 98 + runtime/js_async_from_sync_iterator_object.h | 49 + runtime/js_async_function.cpp | 121 + runtime/js_async_function.h | 60 + runtime/js_async_generator_object.cpp | 364 ++ runtime/js_async_generator_object.h | 62 + runtime/js_collator.cpp | 441 ++ runtime/js_collator.h | 100 + runtime/js_dataview.cpp | 45 + runtime/js_dataview.h | 41 + runtime/js_date.cpp | 1000 +++++ runtime/js_date.h | 192 + runtime/js_date_time_format.cpp | 1471 ++++++ runtime/js_date_time_format.h | 203 + runtime/js_for_in_iterator.cpp | 313 ++ runtime/js_for_in_iterator.h | 58 + runtime/js_function.cpp | 664 +++ runtime/js_function.h | 434 ++ runtime/js_function_extra_info.h | 43 + runtime/js_function_kind.h | 64 + runtime/js_generator_object.cpp | 146 + runtime/js_generator_object.h | 101 + runtime/js_global_object.h | 37 + runtime/js_handle.h | 201 + runtime/js_hclass-inl.h | 186 + runtime/js_hclass.cpp | 505 +++ runtime/js_hclass.h | 1124 +++++ runtime/js_intl.h | 39 + runtime/js_invoker.cpp | 54 + runtime/js_invoker.h | 66 + runtime/js_iterator.cpp | 247 ++ runtime/js_iterator.h | 55 + runtime/js_locale.cpp | 1394 ++++++ runtime/js_locale.h | 701 +++ runtime/js_map.cpp | 81 + runtime/js_map.h | 53 + runtime/js_map_iterator.cpp | 117 + runtime/js_map_iterator.h | 47 + runtime/js_method.cpp | 69 + runtime/js_method.h | 127 + runtime/js_native_pointer.h | 66 + runtime/js_number_format.cpp | 1004 +++++ runtime/js_number_format.h | 160 + runtime/js_object-inl.h | 309 ++ runtime/js_object.cpp | 1973 +++++++++ runtime/js_object.h | 632 +++ runtime/js_plural_rules.cpp | 370 ++ runtime/js_plural_rules.h | 81 + runtime/js_primitive_ref.cpp | 72 + runtime/js_primitive_ref.h | 84 + runtime/js_promise.cpp | 240 + runtime/js_promise.h | 142 + runtime/js_proxy.cpp | 945 ++++ runtime/js_proxy.h | 103 + runtime/js_realm.h | 38 + runtime/js_regexp.h | 42 + runtime/js_relative_time_format.cpp | 523 +++ runtime/js_relative_time_format.h | 95 + runtime/js_runtime_options.h | 180 + runtime/js_serializer.cpp | 1464 ++++++ runtime/js_serializer.h | 255 ++ runtime/js_set.cpp | 69 + runtime/js_set.h | 48 + runtime/js_set_iterator.cpp | 111 + runtime/js_set_iterator.h | 45 + runtime/js_stable_array.cpp | 267 ++ runtime/js_stable_array.h | 35 + runtime/js_string_iterator.cpp | 41 + runtime/js_string_iterator.h | 41 + runtime/js_symbol.h | 162 + runtime/js_tagged_number.h | 174 + runtime/js_tagged_value-inl.h | 1030 +++++ runtime/js_tagged_value.cpp | 828 ++++ runtime/js_tagged_value.h | 354 ++ runtime/js_thread.cpp | 244 + runtime/js_thread.h | 382 ++ runtime/js_typed_array.cpp | 488 ++ runtime/js_typed_array.h | 98 + runtime/js_weak_container.cpp | 99 + runtime/js_weak_container.h | 73 + runtime/layout_info-inl.h | 174 + runtime/layout_info.cpp | 120 + runtime/layout_info.h | 92 + runtime/lexical_env.h | 61 + runtime/linked_hash_table-inl.h | 271 ++ runtime/linked_hash_table.cpp | 273 ++ runtime/linked_hash_table.h | 210 + runtime/literal_data_extractor.cpp | 190 + runtime/literal_data_extractor.h | 42 + runtime/mem/allocator-inl.h | 199 + runtime/mem/allocator.h | 140 + runtime/mem/area.h | 76 + runtime/mem/assert_scope-inl.h | 67 + runtime/mem/assert_scope.h | 89 + runtime/mem/barriers-inl.h | 74 + runtime/mem/barriers.h | 37 + runtime/mem/c_containers.h | 63 + runtime/mem/c_string.cpp | 146 + runtime/mem/c_string.h | 87 + runtime/mem/caddress_allocator.h | 163 + runtime/mem/chunk.cpp | 80 + runtime/mem/chunk.h | 99 + runtime/mem/chunk_allocator.h | 151 + runtime/mem/chunk_containers.h | 93 + runtime/mem/clock_scope.h | 56 + runtime/mem/compress_collector.cpp | 160 + runtime/mem/compress_collector.h | 59 + runtime/mem/concurrent_marker.cpp | 163 + runtime/mem/concurrent_marker.h | 95 + runtime/mem/concurrent_sweeper.cpp | 258 ++ runtime/mem/concurrent_sweeper.h | 99 + runtime/mem/ecma_list.h | 155 + runtime/mem/ecma_reference_processor.cpp | 137 + runtime/mem/ecma_reference_processor.h | 79 + runtime/mem/evacuation_allocator-inl.h | 46 + runtime/mem/evacuation_allocator.cpp | 157 + runtime/mem/evacuation_allocator.h | 84 + runtime/mem/free_object_kind.cpp | 71 + runtime/mem/free_object_kind.h | 69 + runtime/mem/free_object_list-inl.h | 57 + runtime/mem/free_object_list.cpp | 230 + runtime/mem/free_object_list.h | 87 + runtime/mem/gc_stats.cpp | 152 + runtime/mem/gc_stats.h | 95 + runtime/mem/heap-inl.h | 272 ++ runtime/mem/heap.cpp | 604 +++ runtime/mem/heap.h | 426 ++ runtime/mem/machine_code.h | 81 + runtime/mem/mark_stack-inl.h | 64 + runtime/mem/mark_stack.h | 147 + runtime/mem/mark_word.h | 76 + runtime/mem/mem.h | 122 + runtime/mem/mem_controller.cpp | 236 + runtime/mem/mem_controller.h | 171 + runtime/mem/mem_manager-inl.h | 158 + runtime/mem/mem_manager.cpp | 31 + runtime/mem/mem_manager.h | 90 + runtime/mem/mix_space_collector.cpp | 141 + runtime/mem/mix_space_collector.h | 70 + runtime/mem/object_helpers.h | 16 + runtime/mem/object_xray-inl.h | 333 ++ runtime/mem/object_xray.h | 62 + runtime/mem/parallel_evacuation-inl.h | 129 + runtime/mem/parallel_evacuation.cpp | 521 +++ runtime/mem/parallel_evacuation.h | 196 + runtime/mem/parallel_marker-inl.h | 330 ++ runtime/mem/parallel_marker.cpp | 122 + runtime/mem/parallel_marker.h | 152 + runtime/mem/parallel_work_helper.cpp | 182 + runtime/mem/parallel_work_helper.h | 218 + runtime/mem/region-inl.h | 150 + runtime/mem/region.h | 352 ++ runtime/mem/region_factory.cpp | 171 + runtime/mem/region_factory.h | 181 + runtime/mem/remembered_set.h | 95 + runtime/mem/semi_space_collector-inl.h | 37 + runtime/mem/semi_space_collector.cpp | 148 + runtime/mem/semi_space_collector.h | 80 + runtime/mem/slots.h | 102 + runtime/mem/space-inl.h | 62 + runtime/mem/space.cpp | 632 +++ runtime/mem/space.h | 310 ++ runtime/mem/tagged_object-inl.h | 53 + runtime/mem/tagged_object.h | 51 + runtime/mem/tlab_allocator-inl.h | 157 + runtime/mem/tlab_allocator.h | 60 + runtime/mem/verification.cpp | 90 + runtime/mem/verification.h | 79 + runtime/message_string.cpp | 34 + runtime/message_string.h | 45 + runtime/napi/include/jsnapi.h | 971 ++++ runtime/napi/jsnapi.cpp | 2029 +++++++++ runtime/napi/jsnapi_helper-inl.h | 48 + runtime/napi/jsnapi_helper.h | 82 + runtime/object_factory-inl.h | 84 + runtime/object_factory.cpp | 2271 ++++++++++ runtime/object_factory.h | 512 +++ runtime/object_operator.cpp | 703 +++ runtime/object_operator.h | 307 ++ runtime/object_wrapper.h | 28 + runtime/platform/platform.cpp | 51 + runtime/platform/platform.h | 71 + runtime/platform/runner.cpp | 65 + runtime/platform/runner.h | 72 + runtime/platform/task.h | 47 + runtime/platform/task_queue.cpp | 50 + runtime/platform/task_queue.h | 49 + runtime/property_attributes.h | 312 ++ runtime/record.h | 27 + runtime/regexp/dyn_chunk.cpp | 113 + runtime/regexp/dyn_chunk.h | 150 + runtime/regexp/regexp_executor.cpp | 732 +++ runtime/regexp/regexp_executor.h | 367 ++ runtime/regexp/regexp_opcode.cpp | 666 +++ runtime/regexp/regexp_opcode.h | 453 ++ runtime/regexp/regexp_parser.cpp | 1294 ++++++ runtime/regexp/regexp_parser.h | 233 + runtime/regexp/regexp_parser_cache.cpp | 64 + runtime/regexp/regexp_parser_cache.h | 52 + runtime/runtime_api.h | 35 + runtime/runtime_call_id.h | 603 +++ runtime/runtime_sources.gn | 178 + runtime/snapshot/mem/CMakeLists.txt | 15 + runtime/snapshot/mem/constants.h | 50 + runtime/snapshot/mem/slot_bit.cpp | 56 + runtime/snapshot/mem/slot_bit.h | 134 + runtime/snapshot/mem/snapshot.cpp | 216 + runtime/snapshot/mem/snapshot.h | 55 + runtime/snapshot/mem/snapshot_serialize.cpp | 1268 ++++++ runtime/snapshot/mem/snapshot_serialize.h | 119 + runtime/symbol_table-inl.h | 73 + runtime/symbol_table.h | 66 + runtime/tagged_array-inl.h | 164 + runtime/tagged_array.h | 99 + runtime/tagged_dictionary.cpp | 250 ++ runtime/tagged_dictionary.h | 133 + runtime/tagged_hash_table-inl.h | 469 ++ runtime/tagged_hash_table.h | 164 + runtime/tagged_queue-inl.h | 48 + runtime/tagged_queue.h | 181 + runtime/template_map.h | 67 + runtime/template_string.cpp | 62 + runtime/template_string.h | 27 + runtime/templates/intrinsics_gen.cpp.erb | 49 + runtime/tooling/pt_ecmascript_extension.cpp | 34 + runtime/tooling/pt_js_extractor.h | 86 + runtime/transitions_dictionary.h | 138 + runtime/vmstat/caller_stat.cpp | 71 + runtime/vmstat/caller_stat.h | 120 + runtime/vmstat/runtime_stat.cpp | 96 + runtime/vmstat/runtime_stat.h | 80 + runtime/weak_vector-inl.h | 61 + runtime/weak_vector.cpp | 72 + runtime/weak_vector.h | 64 + runtime_options.yaml | 187 + subproject_sources.gn | 36 + tests/CMakeLists.txt | 273 ++ tests/assembler/CMakeLists.txt | 29 + tests/assembler/emitter_test_ecmascript.cpp | 106 + tests/assembler/parser_test_ecmascript.cpp | 43 + tests/bytecode_optimizer/CMakeLists.txt | 39 + .../codegen_test_ecmascript.cpp | 149 + .../reg_encoder_test_ecmascript.cpp | 134 + tests/checked/CMakeLists.txt | 119 + tests/checked/type_resolving.js | 592 +++ tests/compiler/CMakeLists.txt | 73 + .../compiler/branch_elimination_ecma_test.cpp | 533 +++ .../compiler/checks_elimination_ecma_test.cpp | 399 ++ tests/compiler/codegen_ecma_test.cpp | 393 ++ tests/compiler/ir_builder_ecma_test.cpp | 97 + tests/compiler/lowering_ecma_test.cpp | 103 + tests/compiler/peepholes_ecma_test.cpp | 382 ++ tests/compiler/types_resolving_ecma_tests.cpp | 476 ++ tests/compiler/unit_ecma_test.cpp | 175 + tests/compiler/unit_ecma_test.h | 24 + tests/compiler/vn_test_ecma.cpp | 210 + tests/disassembler/CMakeLists.txt | 69 + .../disassembler/disasm_test_lit_ecma.cpp.in | 37 + tests/disassembler/sources/lit.js | 16 + .../ecmascript-tests/js-bitops-bitwise-and.js | 41 + .../ecmascript-tests/js-bitops-bitwise-and.pa | 154 + tests/runtime/CMakeLists.txt | 668 +++ .../runtime/builtins/builtins_array_test.cpp | 1724 ++++++++ .../builtins/builtins_arraybuffer_test.cpp | 127 + .../builtins/builtins_boolean_test.cpp | 191 + .../builtins/builtins_dataview_test.cpp | 466 ++ tests/runtime/builtins/builtins_date_test.cpp | 960 ++++ .../runtime/builtins/builtins_errors_test.cpp | 1069 +++++ .../builtins/builtins_function_test.cpp | 427 ++ .../builtins/builtins_iterator_test.cpp | 56 + tests/runtime/builtins/builtins_json_test.cpp | 408 ++ tests/runtime/builtins/builtins_map_test.cpp | 359 ++ tests/runtime/builtins/builtins_math_test.cpp | 3924 +++++++++++++++++ .../runtime/builtins/builtins_number_test.cpp | 599 +++ .../runtime/builtins/builtins_object_test.cpp | 1206 +++++ .../builtins/builtins_promise_test.cpp | 727 +++ .../runtime/builtins/builtins_proxy_test.cpp | 138 + .../builtins/builtins_reflect_test.cpp | 511 +++ .../runtime/builtins/builtins_regexp_test.cpp | 657 +++ tests/runtime/builtins/builtins_set_test.cpp | 363 ++ .../runtime/builtins/builtins_string_test.cpp | 1461 ++++++ .../runtime/builtins/builtins_symbol_test.cpp | 331 ++ .../builtins/builtins_typedarray_test.cpp | 367 ++ .../builtins/builtins_weak_map_test.cpp | 211 + .../builtins/builtins_weak_set_test.cpp | 205 + tests/runtime/common/CMakeLists.txt | 26 + tests/runtime/common/assert_scope_test.cpp | 59 + tests/runtime/common/async/CMakeLists.txt | 22 + tests/runtime/common/async/async.js | 10 + tests/runtime/common/async/verify.sh | 17 + tests/runtime/common/big_file/CMakeLists.txt | 26 + tests/runtime/common/big_file/big_file.js | 2097 +++++++++ tests/runtime/common/bitwiseop/CMakeLists.txt | 23 + tests/runtime/common/bitwiseop/bitwiseop.js | 7 + tests/runtime/common/bitwiseop/verify.sh | 21 + tests/runtime/common/builtins_test.cpp | 119 + tests/runtime/common/class/CMakeLists.txt | 23 + tests/runtime/common/class/class.js | 28 + tests/runtime/common/class/verify.sh | 16 + .../common/concurrent_marking_test.cpp | 112 + .../runtime/common/concurrent_sweep_test.cpp | 64 + tests/runtime/common/dump_test.cpp | 672 +++ .../common/dyninstruction/CMakeLists.txt | 22 + .../common/dyninstruction/dyninstruction.js | 7 + tests/runtime/common/dyninstruction/verify.sh | 18 + .../ecma_empty_class_check/CMakeLists.txt | 50 + .../ecma_empty_class_check.cpp | 79 + .../ecma_empty_class_check.js | 20 + .../ecma_empty_class_check_sample.txt | 3 + tests/runtime/common/ecma_module_test.cpp | 467 ++ tests/runtime/common/ecma_string_test.cpp | 1859 ++++++++ tests/runtime/common/fortest/CMakeLists.txt | 23 + tests/runtime/common/fortest/fortest.js | 18 + tests/runtime/common/fortest/verify.sh | 34 + tests/runtime/common/gc_test.cpp | 86 + tests/runtime/common/generator/CMakeLists.txt | 22 + tests/runtime/common/generator/generator.js | 14 + tests/runtime/common/generator/verify.sh | 18 + .../common/getunmappedargs/CMakeLists.txt | 23 + .../common/getunmappedargs/getunmappedargs.js | 7 + .../runtime/common/getunmappedargs/verify.sh | 17 + tests/runtime/common/glue_regs_test.cpp | 100 + .../runtime/common/helloworld/CMakeLists.txt | 23 + tests/runtime/common/helloworld/helloworld.js | 1 + tests/runtime/common/helloworld/verify.sh | 15 + tests/runtime/common/huge_object_test.cpp | 128 + tests/runtime/common/js_arguments_test.cpp | 165 + .../runtime/common/js_array_iterator_test.cpp | 178 + tests/runtime/common/js_array_test.cpp | 189 + tests/runtime/common/js_dataview_test.cpp | 214 + tests/runtime/common/js_date_test.cpp | 164 + .../runtime/common/js_forin_iterator_test.cpp | 86 + tests/runtime/common/js_function_test.cpp | 164 + tests/runtime/common/js_handle_test.cpp | 218 + tests/runtime/common/js_iterator_test.cpp | 130 + tests/runtime/common/js_map_test.cpp | 161 + tests/runtime/common/js_object_test.cpp | 1291 ++++++ .../runtime/common/js_primitive_ref_test.cpp | 69 + tests/runtime/common/js_promise_test.cpp | 128 + tests/runtime/common/js_proxy_test.cpp | 634 +++ tests/runtime/common/js_serializer_test.cpp | 992 +++++ tests/runtime/common/js_set_iterator_test.cpp | 149 + tests/runtime/common/js_set_test.cpp | 151 + tests/runtime/common/js_symbol_test.cpp | 54 + tests/runtime/common/js_tagged_queue_test.cpp | 110 + tests/runtime/common/js_typed_array_test.cpp | 1344 ++++++ tests/runtime/common/js_verification_test.cpp | 107 + tests/runtime/common/large_object_test.cpp | 119 + tests/runtime/common/lexical_env_test.cpp | 47 + .../runtime/common/lexicalenv/CMakeLists.txt | 23 + tests/runtime/common/lexicalenv/lexicalenv.js | 10 + tests/runtime/common/lexicalenv/verify.sh | 15 + .../runtime/common/linked_hash_table_test.cpp | 284 ++ tests/runtime/common/mem_controller_test.cpp | 135 + .../runtime/common/missingargs/CMakeLists.txt | 24 + .../runtime/common/missingargs/missingargs.js | 8 + tests/runtime/common/missingargs/verify.sh | 18 + tests/runtime/common/module/CMakeLists.txt | 28 + tests/runtime/common/module/module_a.js | 30 + tests/runtime/common/module/module_b.js | 16 + tests/runtime/common/module/module_c.js | 15 + tests/runtime/common/module/verify.sh | 15 + tests/runtime/common/multiargs/CMakeLists.txt | 23 + tests/runtime/common/multiargs/multiargs.js | 47 + tests/runtime/common/multiargs/verify.sh | 20 + tests/runtime/common/name_dictionary_test.cpp | 192 + .../CMakeLists.txt | 13 + .../native_methods_api_no_crash.cpp | 116 + tests/runtime/common/native_pointer_test.cpp | 75 + .../common/newobjdynrange/CMakeLists.txt | 23 + .../common/newobjdynrange/newobjdynrange.js | 6 + tests/runtime/common/newobjdynrange/verify.sh | 15 + tests/runtime/common/object_factory_test.cpp | 183 + tests/runtime/common/promise/CMakeLists.txt | 23 + tests/runtime/common/promise/promise.js | 33 + tests/runtime/common/promise/verify.sh | 20 + tests/runtime/common/restargs/CMakeLists.txt | 22 + tests/runtime/common/restargs/restargs.js | 25 + tests/runtime/common/restargs/verify.sh | 18 + .../common/returnundefined/CMakeLists.txt | 22 + .../common/returnundefined/returnundefined.js | 12 + .../runtime/common/returnundefined/verify.sh | 17 + tests/runtime/common/separate_jsvm_test.cpp | 95 + tests/runtime/common/sieve/CMakeLists.txt | 22 + tests/runtime/common/sieve/sieve.js | 47 + tests/runtime/common/sieve/verify.sh | 16 + .../runtime/common/strictequal/CMakeLists.txt | 23 + .../runtime/common/strictequal/strictequal.js | 19 + tests/runtime/common/strictequal/verify.sh | 31 + tests/runtime/common/symbol_table_test.cpp | 270 ++ tests/runtime/common/tagged_value_test.cpp | 1209 +++++ tests/runtime/common/test_helper.cpp | 72 + tests/runtime/common/test_helper.h | 90 + tests/runtime/common/throwdyn/CMakeLists.txt | 22 + tests/runtime/common/throwdyn/throwdyn.js | 13 + tests/runtime/common/throwdyn/verify.sh | 15 + .../common/visitor_compatibility_tests.cpp | 291 ++ tests/runtime/common/weak_ref_gen_gc_test.cpp | 163 + tests/runtime/common/weak_ref_stw_gc_test.cpp | 144 + tests/runtime/common/yieldstar/CMakeLists.txt | 22 + tests/runtime/common/yieldstar/verify.sh | 17 + tests/runtime/common/yieldstar/yieldstar.js | 16 + tests/runtime/hprof/heap_tracker_test.cpp | 109 + tests/runtime/hprof/hprof_test.cpp | 341 ++ tests/runtime/ic/ic_binaryop_test.cpp | 384 ++ tests/runtime/ic/ic_compareop_test.cpp | 463 ++ tests/runtime/ic/ic_invoke_test.cpp | 134 + tests/runtime/irtoc/CMakeLists.txt | 34 + tests/runtime/irtoc/advanced/CMakeLists.txt | 8 + tests/runtime/irtoc/advanced/advanced.js | 189 + tests/runtime/irtoc/basic/CMakeLists.txt | 8 + tests/runtime/irtoc/basic/basic.js | 189 + tests/runtime/mem/CMakeLists.txt | 42 + tests/runtime/mem/g1gc_barrier_test.cpp | 119 + tests/runtime/mem/object_helpers_test.cpp | 174 + tests/runtime/mem/weakContainers.js | 85 + tests/runtime/mem/weak_containers_test.cpp | 118 + tests/runtime/napi/CMakeLists.txt | 28 + tests/runtime/napi/jsnapi_tests.cpp | 869 ++++ tests/runtime/regexp/dyn_buffer_test.cpp | 96 + tests/runtime/regexp/regexp_test.cpp | 2028 +++++++++ tests/runtime/snapshot/snapshot_test.cpp | 52 + tests/runtime/tooling/CMakeLists.txt | 60 + tests/runtime/tooling/api_tests/api_tests.h | 26 + .../tooling/api_tests/js/js_breakpoint_test.h | 73 + .../api_tests/js/js_enumerate_frames_test.h | 73 + .../api_tests/js/js_exception_events_test.h | 90 + .../tooling/api_tests/js/js_frame_pop_test.h | 113 + .../api_tests/js/js_get_current_frame_test.h | 69 + .../api_tests/js/js_get_variable_test.h | 120 + .../api_tests/js/js_method_event_test.h | 63 + .../api_tests/js/js_restart_frame_test.h | 125 + .../api_tests/js/js_set_notification_test.h | 109 + .../api_tests/js/js_set_variable_test.h | 164 + .../api_tests/js/js_single_step_test.h | 98 + .../tooling/api_tests/js/js_vm_event_test.h | 66 + tests/runtime/tooling/js/ExceptionTest.js | 20 + tests/runtime/tooling/js/FramePop.js | 38 + tests/runtime/tooling/js/GetFrame.js | 42 + tests/runtime/tooling/js/GetVariable.js | 27 + tests/runtime/tooling/js/RestartFrame.js | 50 + tests/runtime/tooling/js/Sample.js | 30 + tests/runtime/tooling/js/SetNotification.js | 26 + tests/runtime/tooling/js/SetVariable.js | 46 + tests/runtime/tooling/js_test_api.h | 50 + tests/runtime/tooling/launcher.cpp | 75 + .../tooling/options_test/options_test.cpp | 50 + tests/runtime/tooling/test_list.cpp | 72 + tests/runtime/tooling/test_list.h | 31 + 737 files changed, 168847 insertions(+), 75 deletions(-) create mode 100644 AUTHORS create mode 100644 BUILD.gn create mode 100644 CMakeLists.txt create mode 100644 LICENSE-2.0.txt delete mode 100644 README.en.md delete mode 100644 README.md create mode 100644 assembler/CMakeLists.txt create mode 100644 assembler/assembler_sources.gn create mode 100644 assembler/extension/ecmascript_meta.cpp create mode 100644 assembler/extension/ecmascript_meta.h create mode 100644 assembler/extension/metadata.yaml create mode 100644 bytecode_optimizer/CMakeLists.txt create mode 100644 bytecode_optimizer/bytecodeopt_sources.gn create mode 100644 bytecode_optimizer/templates/ecmascript_codegen_intrinsics_gen.inc.erb create mode 100644 bytecode_optimizer/visitors/ecmascript_codegen_intrinsics.inc create mode 100644 compiler/CMakeLists.txt create mode 100644 compiler/codegen_intrinsics_ecmascript.cpp create mode 100644 compiler/compiler_sources.gn create mode 100644 compiler/ecmascript_extensions/CMakeLists.txt create mode 100644 compiler/ecmascript_extensions/ecmascript_codegen_extensions.cpp create mode 100644 compiler/ecmascript_extensions/ecmascript_codegen_extensions.h create mode 100644 compiler/ecmascript_extensions/ecmascript_compiler_interface.h create mode 100644 compiler/ecmascript_extensions/ecmascript_environment.h create mode 100644 compiler/ecmascript_extensions/thread_environment_api.h create mode 100644 compiler/extensions.h create mode 100644 compiler/intrinsics_inline_ecmascript.inl create mode 100644 compiler/intrinsics_type_resolving_ecmascript.cpp create mode 100644 compiler/intrinsics_type_resolving_ecmascript.inl.h create mode 100644 compiler/optimizer/code_generator/compiler_base_types.cpp create mode 100644 compiler/optimizer/code_generator/compiler_base_types.h create mode 100644 compiler/optimizer/ir/dyn_datatype.h create mode 100644 compiler/optimizer/ir_builder/ecmascript_inst_builder.cpp create mode 100644 compiler/optimizer/ir_builder/ecmascript_inst_builder.h create mode 100644 compiler/optimizer/ir_builder/ecmascript_inst_templates.yaml create mode 100644 compiler/templates/ecmascript_inst_builder_gen.cpp.erb create mode 100644 ecmascript_plugin_options.yaml create mode 100644 ecmastdlib/CMakeLists.txt create mode 100644 ecmastdlib/ecmastdlib.pa create mode 100644 ecmastdlib/ecmastdlib_inline_gen.h.erb create mode 100644 ecmastdlib/ecmastdlib_inline_gen.rb create mode 100644 irtoc_scripts/common.irt create mode 100644 irtoc_scripts/interpreter_handlers.irt create mode 100644 irtoc_scripts/interpreter_main_loop.irt create mode 100644 irtoc_scripts/irtoc_scripts.gn create mode 100644 isa/CMakeLists.txt create mode 100644 isa/isa.yaml create mode 100644 isa/isa_sources.gn create mode 100644 runtime/CMakeLists.txt create mode 100644 runtime/accessor_data.h create mode 100644 runtime/arch/aarch64/builtin_bridge_aarch64.S create mode 100644 runtime/arch/amd64/builtin_bridge_amd64.S create mode 100644 runtime/arch/arm/builtin_bridge_arm.S create mode 100644 runtime/asm_defines/asm_defines.def create mode 100644 runtime/asm_defines/defines.h create mode 100644 runtime/base/array_helper.cpp create mode 100644 runtime/base/array_helper.h create mode 100644 runtime/base/builtins_base.cpp create mode 100644 runtime/base/builtins_base.h create mode 100644 runtime/base/config.h create mode 100644 runtime/base/error_helper.cpp create mode 100644 runtime/base/error_helper.h create mode 100644 runtime/base/error_type.h create mode 100644 runtime/base/gc_ring_buffer.h create mode 100644 runtime/base/json_parser.cpp create mode 100644 runtime/base/json_parser.h create mode 100644 runtime/base/json_stringifier.cpp create mode 100644 runtime/base/json_stringifier.h create mode 100644 runtime/base/number_helper.cpp create mode 100644 runtime/base/number_helper.h create mode 100644 runtime/base/object_helper.cpp create mode 100644 runtime/base/object_helper.h create mode 100644 runtime/base/string_helper.cpp create mode 100644 runtime/base/string_helper.h create mode 100644 runtime/base/typed_array_helper-inl.h create mode 100644 runtime/base/typed_array_helper.cpp create mode 100644 runtime/base/typed_array_helper.h create mode 100644 runtime/base/utf_helper.cpp create mode 100644 runtime/base/utf_helper.h create mode 100644 runtime/bridge/arch/aarch64/handle_call_ecma_N_v8_aarch64.S create mode 100644 runtime/bridge/arch/aarch64/handle_call_ecma_callirangedyn_pref_imm16_v8_aarch64.S create mode 100644 runtime/bridge/arch/aarch64/handle_call_ecma_callithisrangedyn_pref_imm16_v8_aarch64.S create mode 100644 runtime/bridge/arch/amd64/handle_call_ecma_N_v8_amd64.S create mode 100644 runtime/bridge/arch/amd64/handle_call_ecma_callirangedyn_pref_imm16_v8_amd64.S create mode 100644 runtime/bridge/arch/amd64/handle_call_ecma_callithisrangedyn_pref_imm16_v8_amd64.S create mode 100644 runtime/bridge/arch/arm/handle_call_ecma_N_v8_arm.S create mode 100644 runtime/bridge/arch/arm/handle_call_ecma_callirangedyn_pref_imm16_v8_arm.S create mode 100644 runtime/bridge/arch/arm/handle_call_ecma_callithisrangedyn_pref_imm16_v8_arm.S create mode 100644 runtime/bridge/ecma_bridge_helpers.cpp create mode 100644 runtime/builtins.cpp create mode 100644 runtime/builtins.h create mode 100644 runtime/builtins/builtins_ark_tools.cpp create mode 100644 runtime/builtins/builtins_ark_tools.h create mode 100644 runtime/builtins/builtins_array.cpp create mode 100644 runtime/builtins/builtins_array.h create mode 100644 runtime/builtins/builtins_arraybuffer.cpp create mode 100644 runtime/builtins/builtins_arraybuffer.h create mode 100644 runtime/builtins/builtins_async_from_sync_iterator.cpp create mode 100644 runtime/builtins/builtins_async_from_sync_iterator.h create mode 100644 runtime/builtins/builtins_async_function.cpp create mode 100644 runtime/builtins/builtins_async_function.h create mode 100644 runtime/builtins/builtins_async_generator.cpp create mode 100644 runtime/builtins/builtins_async_generator.h create mode 100644 runtime/builtins/builtins_async_iterator.cpp create mode 100644 runtime/builtins/builtins_async_iterator.h create mode 100644 runtime/builtins/builtins_boolean.cpp create mode 100644 runtime/builtins/builtins_boolean.h create mode 100644 runtime/builtins/builtins_collator.cpp create mode 100644 runtime/builtins/builtins_collator.h create mode 100644 runtime/builtins/builtins_dataview.cpp create mode 100644 runtime/builtins/builtins_dataview.h create mode 100644 runtime/builtins/builtins_date.cpp create mode 100644 runtime/builtins/builtins_date.h create mode 100644 runtime/builtins/builtins_date_time_format.cpp create mode 100644 runtime/builtins/builtins_date_time_format.h create mode 100644 runtime/builtins/builtins_errors.cpp create mode 100644 runtime/builtins/builtins_errors.h create mode 100644 runtime/builtins/builtins_function.cpp create mode 100644 runtime/builtins/builtins_function.h create mode 100644 runtime/builtins/builtins_generator.cpp create mode 100644 runtime/builtins/builtins_generator.h create mode 100644 runtime/builtins/builtins_global.cpp create mode 100644 runtime/builtins/builtins_global.h create mode 100644 runtime/builtins/builtins_intl.cpp create mode 100644 runtime/builtins/builtins_intl.h create mode 100644 runtime/builtins/builtins_iterator.cpp create mode 100644 runtime/builtins/builtins_iterator.h create mode 100644 runtime/builtins/builtins_json.cpp create mode 100644 runtime/builtins/builtins_json.h create mode 100644 runtime/builtins/builtins_locale.cpp create mode 100644 runtime/builtins/builtins_locale.h create mode 100644 runtime/builtins/builtins_map.cpp create mode 100644 runtime/builtins/builtins_map.h create mode 100644 runtime/builtins/builtins_math.cpp create mode 100644 runtime/builtins/builtins_math.h create mode 100644 runtime/builtins/builtins_number.cpp create mode 100644 runtime/builtins/builtins_number.h create mode 100644 runtime/builtins/builtins_number_format.cpp create mode 100644 runtime/builtins/builtins_number_format.h create mode 100644 runtime/builtins/builtins_object.cpp create mode 100644 runtime/builtins/builtins_object.h create mode 100644 runtime/builtins/builtins_plural_rules.cpp create mode 100644 runtime/builtins/builtins_plural_rules.h create mode 100644 runtime/builtins/builtins_promise.cpp create mode 100644 runtime/builtins/builtins_promise.h create mode 100644 runtime/builtins/builtins_promise_handler.cpp create mode 100644 runtime/builtins/builtins_promise_handler.h create mode 100644 runtime/builtins/builtins_promise_job.cpp create mode 100644 runtime/builtins/builtins_promise_job.h create mode 100644 runtime/builtins/builtins_proxy.cpp create mode 100644 runtime/builtins/builtins_proxy.h create mode 100644 runtime/builtins/builtins_reflect.cpp create mode 100644 runtime/builtins/builtins_reflect.h create mode 100644 runtime/builtins/builtins_regexp.cpp create mode 100644 runtime/builtins/builtins_regexp.h create mode 100644 runtime/builtins/builtins_relative_time_format.cpp create mode 100644 runtime/builtins/builtins_relative_time_format.h create mode 100644 runtime/builtins/builtins_set.cpp create mode 100644 runtime/builtins/builtins_set.h create mode 100644 runtime/builtins/builtins_string.cpp create mode 100644 runtime/builtins/builtins_string.h create mode 100644 runtime/builtins/builtins_string_iterator.cpp create mode 100644 runtime/builtins/builtins_string_iterator.h create mode 100644 runtime/builtins/builtins_symbol.cpp create mode 100644 runtime/builtins/builtins_symbol.h create mode 100644 runtime/builtins/builtins_typedarray.cpp create mode 100644 runtime/builtins/builtins_typedarray.h create mode 100644 runtime/builtins/builtins_weak_map.cpp create mode 100644 runtime/builtins/builtins_weak_map.h create mode 100644 runtime/builtins/builtins_weak_set.cpp create mode 100644 runtime/builtins/builtins_weak_set.h create mode 100644 runtime/class_info_extractor.cpp create mode 100644 runtime/class_info_extractor.h create mode 100644 runtime/class_linker/panda_file_translator.cpp create mode 100644 runtime/class_linker/panda_file_translator.h create mode 100644 runtime/class_linker/program_object-inl.h create mode 100644 runtime/class_linker/program_object.h create mode 100644 runtime/common.h create mode 100644 runtime/compiler/ecmascript_runtime_interface.cpp create mode 100644 runtime/compiler/ecmascript_runtime_interface.h create mode 100644 runtime/containers/containers_arraylist.cpp create mode 100644 runtime/containers/containers_arraylist.h create mode 100644 runtime/containers/containers_private.cpp create mode 100644 runtime/containers/containers_private.h create mode 100644 runtime/dump.cpp create mode 100644 runtime/ecma_class_linker_extension.cpp create mode 100644 runtime/ecma_class_linker_extension.h create mode 100644 runtime/ecma_exceptions.cpp create mode 100644 runtime/ecma_exceptions.h create mode 100644 runtime/ecma_global_storage-inl.h create mode 100644 runtime/ecma_global_storage.h create mode 100644 runtime/ecma_handle_scope-inl.h create mode 100644 runtime/ecma_handle_scope.h create mode 100644 runtime/ecma_language_context-inl.h create mode 100644 runtime/ecma_language_context.cpp create mode 100644 runtime/ecma_language_context.h create mode 100644 runtime/ecma_macros.h create mode 100644 runtime/ecma_module.cpp create mode 100644 runtime/ecma_module.h create mode 100644 runtime/ecma_runtime.yaml create mode 100644 runtime/ecma_runtime_call_info.h create mode 100644 runtime/ecma_string-inl.h create mode 100644 runtime/ecma_string.cpp create mode 100644 runtime/ecma_string.h create mode 100644 runtime/ecma_string_table.cpp create mode 100644 runtime/ecma_string_table.h create mode 100644 runtime/ecma_vm.cpp create mode 100644 runtime/ecma_vm.h create mode 100644 runtime/frames.h create mode 100644 runtime/free_object.cpp create mode 100644 runtime/free_object.h create mode 100644 runtime/generator_helper.cpp create mode 100644 runtime/generator_helper.h create mode 100644 runtime/global_dictionary-inl.h create mode 100644 runtime/global_dictionary.h create mode 100644 runtime/global_env.cpp create mode 100644 runtime/global_env.h create mode 100644 runtime/global_env_constants-inl.h create mode 100644 runtime/global_env_constants.cpp create mode 100644 runtime/global_env_constants.h create mode 100644 runtime/global_handle_collection.h create mode 100644 runtime/hprof/heap_profiler.cpp create mode 100644 runtime/hprof/heap_profiler.h create mode 100644 runtime/hprof/heap_profiler_interface.cpp create mode 100644 runtime/hprof/heap_profiler_interface.h create mode 100644 runtime/hprof/heap_root_visitor.cpp create mode 100644 runtime/hprof/heap_root_visitor.h create mode 100644 runtime/hprof/heap_snapshot.cpp create mode 100644 runtime/hprof/heap_snapshot.h create mode 100644 runtime/hprof/heap_snapshot_json_serializer.cpp create mode 100644 runtime/hprof/heap_snapshot_json_serializer.h create mode 100644 runtime/hprof/heap_tracker.cpp create mode 100644 runtime/hprof/heap_tracker.h create mode 100644 runtime/hprof/string_hashmap.cpp create mode 100644 runtime/hprof/string_hashmap.h create mode 100644 runtime/ic/ic_binary_op-inl.h create mode 100644 runtime/ic/ic_binary_op.h create mode 100644 runtime/ic/ic_compare_op.cpp create mode 100644 runtime/ic/ic_compare_op.h create mode 100644 runtime/ic/ic_handler-inl.h create mode 100644 runtime/ic/ic_handler.h create mode 100644 runtime/ic/ic_runtime.cpp create mode 100644 runtime/ic/ic_runtime.h create mode 100644 runtime/ic/ic_runtime_stub-inl.h create mode 100644 runtime/ic/ic_runtime_stub.cpp create mode 100644 runtime/ic/ic_runtime_stub.h create mode 100644 runtime/ic/invoke_cache.h create mode 100644 runtime/ic/profile_type_info.cpp create mode 100644 runtime/ic/profile_type_info.h create mode 100644 runtime/ic/properties_cache-inl.h create mode 100644 runtime/ic/properties_cache.h create mode 100644 runtime/ic/property_box.cpp create mode 100644 runtime/ic/property_box.h create mode 100644 runtime/ic/proto_change_details.cpp create mode 100644 runtime/ic/proto_change_details.h create mode 100644 runtime/include/tooling/pt_ecmascript_extension.h create mode 100644 runtime/internal_call_params.cpp create mode 100644 runtime/internal_call_params.h create mode 100644 runtime/interpreter/ecma-interpreter-inl.h create mode 100644 runtime/interpreter/ecma-interpreter.h create mode 100644 runtime/interpreter/fast_runtime_stub-inl.h create mode 100644 runtime/interpreter/fast_runtime_stub.h create mode 100644 runtime/interpreter/interpreter-inl.h create mode 100644 runtime/interpreter/interpreter.h create mode 100644 runtime/interpreter/js_decode_call_instr.h create mode 100644 runtime/interpreter/js_frame-inl.h create mode 100644 runtime/interpreter/js_frame.h create mode 100644 runtime/interpreter/slow_runtime_helper.cpp create mode 100644 runtime/interpreter/slow_runtime_helper.h create mode 100644 runtime/interpreter/slow_runtime_stub.cpp create mode 100644 runtime/interpreter/slow_runtime_stub.h create mode 100644 runtime/interpreter/templates/debugger_instruction_dispatch.inl create mode 100644 runtime/interpreter/templates/debugger_instruction_handler.inl create mode 100644 runtime/interpreter/templates/instruction_dispatch.inl create mode 100644 runtime/intrinsics-inl.h create mode 100644 runtime/intrinsics.cpp create mode 100644 runtime/jobs/micro_job_queue.cpp create mode 100644 runtime/jobs/micro_job_queue.h create mode 100644 runtime/jobs/pending_job.h create mode 100644 runtime/js_arguments.cpp create mode 100644 runtime/js_arguments.h create mode 100644 runtime/js_array.cpp create mode 100644 runtime/js_array.h create mode 100644 runtime/js_array_iterator.cpp create mode 100644 runtime/js_array_iterator.h create mode 100644 runtime/js_arraybuffer.cpp create mode 100644 runtime/js_arraybuffer.h create mode 100644 runtime/js_arraylist.cpp create mode 100644 runtime/js_arraylist.h create mode 100644 runtime/js_async_from_sync_iterator_object.cpp create mode 100644 runtime/js_async_from_sync_iterator_object.h create mode 100644 runtime/js_async_function.cpp create mode 100644 runtime/js_async_function.h create mode 100644 runtime/js_async_generator_object.cpp create mode 100644 runtime/js_async_generator_object.h create mode 100644 runtime/js_collator.cpp create mode 100644 runtime/js_collator.h create mode 100644 runtime/js_dataview.cpp create mode 100644 runtime/js_dataview.h create mode 100644 runtime/js_date.cpp create mode 100644 runtime/js_date.h create mode 100644 runtime/js_date_time_format.cpp create mode 100644 runtime/js_date_time_format.h create mode 100644 runtime/js_for_in_iterator.cpp create mode 100644 runtime/js_for_in_iterator.h create mode 100644 runtime/js_function.cpp create mode 100644 runtime/js_function.h create mode 100644 runtime/js_function_extra_info.h create mode 100644 runtime/js_function_kind.h create mode 100644 runtime/js_generator_object.cpp create mode 100644 runtime/js_generator_object.h create mode 100644 runtime/js_global_object.h create mode 100644 runtime/js_handle.h create mode 100644 runtime/js_hclass-inl.h create mode 100644 runtime/js_hclass.cpp create mode 100644 runtime/js_hclass.h create mode 100644 runtime/js_intl.h create mode 100644 runtime/js_invoker.cpp create mode 100644 runtime/js_invoker.h create mode 100644 runtime/js_iterator.cpp create mode 100644 runtime/js_iterator.h create mode 100644 runtime/js_locale.cpp create mode 100644 runtime/js_locale.h create mode 100644 runtime/js_map.cpp create mode 100644 runtime/js_map.h create mode 100644 runtime/js_map_iterator.cpp create mode 100644 runtime/js_map_iterator.h create mode 100644 runtime/js_method.cpp create mode 100644 runtime/js_method.h create mode 100644 runtime/js_native_pointer.h create mode 100644 runtime/js_number_format.cpp create mode 100644 runtime/js_number_format.h create mode 100644 runtime/js_object-inl.h create mode 100644 runtime/js_object.cpp create mode 100644 runtime/js_object.h create mode 100644 runtime/js_plural_rules.cpp create mode 100644 runtime/js_plural_rules.h create mode 100644 runtime/js_primitive_ref.cpp create mode 100644 runtime/js_primitive_ref.h create mode 100644 runtime/js_promise.cpp create mode 100644 runtime/js_promise.h create mode 100644 runtime/js_proxy.cpp create mode 100644 runtime/js_proxy.h create mode 100644 runtime/js_realm.h create mode 100644 runtime/js_regexp.h create mode 100644 runtime/js_relative_time_format.cpp create mode 100644 runtime/js_relative_time_format.h create mode 100644 runtime/js_runtime_options.h create mode 100644 runtime/js_serializer.cpp create mode 100644 runtime/js_serializer.h create mode 100644 runtime/js_set.cpp create mode 100644 runtime/js_set.h create mode 100644 runtime/js_set_iterator.cpp create mode 100644 runtime/js_set_iterator.h create mode 100644 runtime/js_stable_array.cpp create mode 100644 runtime/js_stable_array.h create mode 100644 runtime/js_string_iterator.cpp create mode 100644 runtime/js_string_iterator.h create mode 100644 runtime/js_symbol.h create mode 100644 runtime/js_tagged_number.h create mode 100644 runtime/js_tagged_value-inl.h create mode 100644 runtime/js_tagged_value.cpp create mode 100644 runtime/js_tagged_value.h create mode 100644 runtime/js_thread.cpp create mode 100644 runtime/js_thread.h create mode 100644 runtime/js_typed_array.cpp create mode 100644 runtime/js_typed_array.h create mode 100644 runtime/js_weak_container.cpp create mode 100644 runtime/js_weak_container.h create mode 100644 runtime/layout_info-inl.h create mode 100644 runtime/layout_info.cpp create mode 100644 runtime/layout_info.h create mode 100644 runtime/lexical_env.h create mode 100644 runtime/linked_hash_table-inl.h create mode 100644 runtime/linked_hash_table.cpp create mode 100644 runtime/linked_hash_table.h create mode 100644 runtime/literal_data_extractor.cpp create mode 100644 runtime/literal_data_extractor.h create mode 100644 runtime/mem/allocator-inl.h create mode 100644 runtime/mem/allocator.h create mode 100644 runtime/mem/area.h create mode 100644 runtime/mem/assert_scope-inl.h create mode 100644 runtime/mem/assert_scope.h create mode 100644 runtime/mem/barriers-inl.h create mode 100644 runtime/mem/barriers.h create mode 100644 runtime/mem/c_containers.h create mode 100644 runtime/mem/c_string.cpp create mode 100644 runtime/mem/c_string.h create mode 100644 runtime/mem/caddress_allocator.h create mode 100644 runtime/mem/chunk.cpp create mode 100644 runtime/mem/chunk.h create mode 100644 runtime/mem/chunk_allocator.h create mode 100644 runtime/mem/chunk_containers.h create mode 100644 runtime/mem/clock_scope.h create mode 100644 runtime/mem/compress_collector.cpp create mode 100644 runtime/mem/compress_collector.h create mode 100644 runtime/mem/concurrent_marker.cpp create mode 100644 runtime/mem/concurrent_marker.h create mode 100644 runtime/mem/concurrent_sweeper.cpp create mode 100644 runtime/mem/concurrent_sweeper.h create mode 100644 runtime/mem/ecma_list.h create mode 100644 runtime/mem/ecma_reference_processor.cpp create mode 100644 runtime/mem/ecma_reference_processor.h create mode 100644 runtime/mem/evacuation_allocator-inl.h create mode 100644 runtime/mem/evacuation_allocator.cpp create mode 100644 runtime/mem/evacuation_allocator.h create mode 100644 runtime/mem/free_object_kind.cpp create mode 100644 runtime/mem/free_object_kind.h create mode 100644 runtime/mem/free_object_list-inl.h create mode 100644 runtime/mem/free_object_list.cpp create mode 100644 runtime/mem/free_object_list.h create mode 100644 runtime/mem/gc_stats.cpp create mode 100644 runtime/mem/gc_stats.h create mode 100644 runtime/mem/heap-inl.h create mode 100644 runtime/mem/heap.cpp create mode 100644 runtime/mem/heap.h create mode 100644 runtime/mem/machine_code.h create mode 100644 runtime/mem/mark_stack-inl.h create mode 100644 runtime/mem/mark_stack.h create mode 100644 runtime/mem/mark_word.h create mode 100644 runtime/mem/mem.h create mode 100644 runtime/mem/mem_controller.cpp create mode 100644 runtime/mem/mem_controller.h create mode 100644 runtime/mem/mem_manager-inl.h create mode 100644 runtime/mem/mem_manager.cpp create mode 100644 runtime/mem/mem_manager.h create mode 100644 runtime/mem/mix_space_collector.cpp create mode 100644 runtime/mem/mix_space_collector.h create mode 100644 runtime/mem/object_helpers.h create mode 100644 runtime/mem/object_xray-inl.h create mode 100644 runtime/mem/object_xray.h create mode 100644 runtime/mem/parallel_evacuation-inl.h create mode 100644 runtime/mem/parallel_evacuation.cpp create mode 100644 runtime/mem/parallel_evacuation.h create mode 100644 runtime/mem/parallel_marker-inl.h create mode 100644 runtime/mem/parallel_marker.cpp create mode 100644 runtime/mem/parallel_marker.h create mode 100644 runtime/mem/parallel_work_helper.cpp create mode 100644 runtime/mem/parallel_work_helper.h create mode 100644 runtime/mem/region-inl.h create mode 100644 runtime/mem/region.h create mode 100644 runtime/mem/region_factory.cpp create mode 100644 runtime/mem/region_factory.h create mode 100644 runtime/mem/remembered_set.h create mode 100644 runtime/mem/semi_space_collector-inl.h create mode 100644 runtime/mem/semi_space_collector.cpp create mode 100644 runtime/mem/semi_space_collector.h create mode 100644 runtime/mem/slots.h create mode 100644 runtime/mem/space-inl.h create mode 100644 runtime/mem/space.cpp create mode 100644 runtime/mem/space.h create mode 100644 runtime/mem/tagged_object-inl.h create mode 100644 runtime/mem/tagged_object.h create mode 100644 runtime/mem/tlab_allocator-inl.h create mode 100644 runtime/mem/tlab_allocator.h create mode 100644 runtime/mem/verification.cpp create mode 100644 runtime/mem/verification.h create mode 100644 runtime/message_string.cpp create mode 100644 runtime/message_string.h create mode 100644 runtime/napi/include/jsnapi.h create mode 100644 runtime/napi/jsnapi.cpp create mode 100644 runtime/napi/jsnapi_helper-inl.h create mode 100644 runtime/napi/jsnapi_helper.h create mode 100644 runtime/object_factory-inl.h create mode 100644 runtime/object_factory.cpp create mode 100644 runtime/object_factory.h create mode 100644 runtime/object_operator.cpp create mode 100644 runtime/object_operator.h create mode 100644 runtime/object_wrapper.h create mode 100644 runtime/platform/platform.cpp create mode 100644 runtime/platform/platform.h create mode 100644 runtime/platform/runner.cpp create mode 100644 runtime/platform/runner.h create mode 100644 runtime/platform/task.h create mode 100644 runtime/platform/task_queue.cpp create mode 100644 runtime/platform/task_queue.h create mode 100644 runtime/property_attributes.h create mode 100644 runtime/record.h create mode 100644 runtime/regexp/dyn_chunk.cpp create mode 100644 runtime/regexp/dyn_chunk.h create mode 100644 runtime/regexp/regexp_executor.cpp create mode 100644 runtime/regexp/regexp_executor.h create mode 100644 runtime/regexp/regexp_opcode.cpp create mode 100644 runtime/regexp/regexp_opcode.h create mode 100644 runtime/regexp/regexp_parser.cpp create mode 100644 runtime/regexp/regexp_parser.h create mode 100644 runtime/regexp/regexp_parser_cache.cpp create mode 100644 runtime/regexp/regexp_parser_cache.h create mode 100644 runtime/runtime_api.h create mode 100644 runtime/runtime_call_id.h create mode 100644 runtime/runtime_sources.gn create mode 100644 runtime/snapshot/mem/CMakeLists.txt create mode 100644 runtime/snapshot/mem/constants.h create mode 100644 runtime/snapshot/mem/slot_bit.cpp create mode 100644 runtime/snapshot/mem/slot_bit.h create mode 100644 runtime/snapshot/mem/snapshot.cpp create mode 100644 runtime/snapshot/mem/snapshot.h create mode 100644 runtime/snapshot/mem/snapshot_serialize.cpp create mode 100644 runtime/snapshot/mem/snapshot_serialize.h create mode 100644 runtime/symbol_table-inl.h create mode 100644 runtime/symbol_table.h create mode 100644 runtime/tagged_array-inl.h create mode 100644 runtime/tagged_array.h create mode 100644 runtime/tagged_dictionary.cpp create mode 100644 runtime/tagged_dictionary.h create mode 100644 runtime/tagged_hash_table-inl.h create mode 100644 runtime/tagged_hash_table.h create mode 100644 runtime/tagged_queue-inl.h create mode 100644 runtime/tagged_queue.h create mode 100644 runtime/template_map.h create mode 100644 runtime/template_string.cpp create mode 100644 runtime/template_string.h create mode 100644 runtime/templates/intrinsics_gen.cpp.erb create mode 100644 runtime/tooling/pt_ecmascript_extension.cpp create mode 100644 runtime/tooling/pt_js_extractor.h create mode 100644 runtime/transitions_dictionary.h create mode 100644 runtime/vmstat/caller_stat.cpp create mode 100644 runtime/vmstat/caller_stat.h create mode 100644 runtime/vmstat/runtime_stat.cpp create mode 100644 runtime/vmstat/runtime_stat.h create mode 100644 runtime/weak_vector-inl.h create mode 100644 runtime/weak_vector.cpp create mode 100644 runtime/weak_vector.h create mode 100644 runtime_options.yaml create mode 100644 subproject_sources.gn create mode 100644 tests/CMakeLists.txt create mode 100644 tests/assembler/CMakeLists.txt create mode 100644 tests/assembler/emitter_test_ecmascript.cpp create mode 100644 tests/assembler/parser_test_ecmascript.cpp create mode 100644 tests/bytecode_optimizer/CMakeLists.txt create mode 100644 tests/bytecode_optimizer/codegen_test_ecmascript.cpp create mode 100644 tests/bytecode_optimizer/reg_encoder_test_ecmascript.cpp create mode 100644 tests/checked/CMakeLists.txt create mode 100644 tests/checked/type_resolving.js create mode 100644 tests/compiler/CMakeLists.txt create mode 100644 tests/compiler/branch_elimination_ecma_test.cpp create mode 100644 tests/compiler/checks_elimination_ecma_test.cpp create mode 100644 tests/compiler/codegen_ecma_test.cpp create mode 100644 tests/compiler/ir_builder_ecma_test.cpp create mode 100644 tests/compiler/lowering_ecma_test.cpp create mode 100644 tests/compiler/peepholes_ecma_test.cpp create mode 100644 tests/compiler/types_resolving_ecma_tests.cpp create mode 100644 tests/compiler/unit_ecma_test.cpp create mode 100644 tests/compiler/unit_ecma_test.h create mode 100644 tests/compiler/vn_test_ecma.cpp create mode 100644 tests/disassembler/CMakeLists.txt create mode 100644 tests/disassembler/disasm_test_lit_ecma.cpp.in create mode 100644 tests/disassembler/sources/lit.js create mode 100644 tests/ecmascript-tests/js-bitops-bitwise-and.js create mode 100644 tests/ecmascript-tests/js-bitops-bitwise-and.pa create mode 100644 tests/runtime/CMakeLists.txt create mode 100644 tests/runtime/builtins/builtins_array_test.cpp create mode 100644 tests/runtime/builtins/builtins_arraybuffer_test.cpp create mode 100644 tests/runtime/builtins/builtins_boolean_test.cpp create mode 100644 tests/runtime/builtins/builtins_dataview_test.cpp create mode 100644 tests/runtime/builtins/builtins_date_test.cpp create mode 100644 tests/runtime/builtins/builtins_errors_test.cpp create mode 100644 tests/runtime/builtins/builtins_function_test.cpp create mode 100644 tests/runtime/builtins/builtins_iterator_test.cpp create mode 100644 tests/runtime/builtins/builtins_json_test.cpp create mode 100644 tests/runtime/builtins/builtins_map_test.cpp create mode 100644 tests/runtime/builtins/builtins_math_test.cpp create mode 100644 tests/runtime/builtins/builtins_number_test.cpp create mode 100644 tests/runtime/builtins/builtins_object_test.cpp create mode 100644 tests/runtime/builtins/builtins_promise_test.cpp create mode 100644 tests/runtime/builtins/builtins_proxy_test.cpp create mode 100644 tests/runtime/builtins/builtins_reflect_test.cpp create mode 100644 tests/runtime/builtins/builtins_regexp_test.cpp create mode 100644 tests/runtime/builtins/builtins_set_test.cpp create mode 100644 tests/runtime/builtins/builtins_string_test.cpp create mode 100644 tests/runtime/builtins/builtins_symbol_test.cpp create mode 100644 tests/runtime/builtins/builtins_typedarray_test.cpp create mode 100644 tests/runtime/builtins/builtins_weak_map_test.cpp create mode 100644 tests/runtime/builtins/builtins_weak_set_test.cpp create mode 100644 tests/runtime/common/CMakeLists.txt create mode 100644 tests/runtime/common/assert_scope_test.cpp create mode 100644 tests/runtime/common/async/CMakeLists.txt create mode 100644 tests/runtime/common/async/async.js create mode 100755 tests/runtime/common/async/verify.sh create mode 100644 tests/runtime/common/big_file/CMakeLists.txt create mode 100644 tests/runtime/common/big_file/big_file.js create mode 100644 tests/runtime/common/bitwiseop/CMakeLists.txt create mode 100644 tests/runtime/common/bitwiseop/bitwiseop.js create mode 100755 tests/runtime/common/bitwiseop/verify.sh create mode 100644 tests/runtime/common/builtins_test.cpp create mode 100644 tests/runtime/common/class/CMakeLists.txt create mode 100644 tests/runtime/common/class/class.js create mode 100755 tests/runtime/common/class/verify.sh create mode 100644 tests/runtime/common/concurrent_marking_test.cpp create mode 100644 tests/runtime/common/concurrent_sweep_test.cpp create mode 100644 tests/runtime/common/dump_test.cpp create mode 100644 tests/runtime/common/dyninstruction/CMakeLists.txt create mode 100644 tests/runtime/common/dyninstruction/dyninstruction.js create mode 100755 tests/runtime/common/dyninstruction/verify.sh create mode 100644 tests/runtime/common/ecma_empty_class_check/CMakeLists.txt create mode 100644 tests/runtime/common/ecma_empty_class_check/ecma_empty_class_check.cpp create mode 100644 tests/runtime/common/ecma_empty_class_check/ecma_empty_class_check.js create mode 100644 tests/runtime/common/ecma_empty_class_check/ecma_empty_class_check_sample.txt create mode 100644 tests/runtime/common/ecma_module_test.cpp create mode 100644 tests/runtime/common/ecma_string_test.cpp create mode 100644 tests/runtime/common/fortest/CMakeLists.txt create mode 100644 tests/runtime/common/fortest/fortest.js create mode 100755 tests/runtime/common/fortest/verify.sh create mode 100644 tests/runtime/common/gc_test.cpp create mode 100644 tests/runtime/common/generator/CMakeLists.txt create mode 100644 tests/runtime/common/generator/generator.js create mode 100755 tests/runtime/common/generator/verify.sh create mode 100644 tests/runtime/common/getunmappedargs/CMakeLists.txt create mode 100644 tests/runtime/common/getunmappedargs/getunmappedargs.js create mode 100755 tests/runtime/common/getunmappedargs/verify.sh create mode 100644 tests/runtime/common/glue_regs_test.cpp create mode 100644 tests/runtime/common/helloworld/CMakeLists.txt create mode 100644 tests/runtime/common/helloworld/helloworld.js create mode 100755 tests/runtime/common/helloworld/verify.sh create mode 100644 tests/runtime/common/huge_object_test.cpp create mode 100644 tests/runtime/common/js_arguments_test.cpp create mode 100644 tests/runtime/common/js_array_iterator_test.cpp create mode 100644 tests/runtime/common/js_array_test.cpp create mode 100644 tests/runtime/common/js_dataview_test.cpp create mode 100644 tests/runtime/common/js_date_test.cpp create mode 100644 tests/runtime/common/js_forin_iterator_test.cpp create mode 100644 tests/runtime/common/js_function_test.cpp create mode 100644 tests/runtime/common/js_handle_test.cpp create mode 100644 tests/runtime/common/js_iterator_test.cpp create mode 100644 tests/runtime/common/js_map_test.cpp create mode 100644 tests/runtime/common/js_object_test.cpp create mode 100644 tests/runtime/common/js_primitive_ref_test.cpp create mode 100644 tests/runtime/common/js_promise_test.cpp create mode 100644 tests/runtime/common/js_proxy_test.cpp create mode 100644 tests/runtime/common/js_serializer_test.cpp create mode 100644 tests/runtime/common/js_set_iterator_test.cpp create mode 100644 tests/runtime/common/js_set_test.cpp create mode 100644 tests/runtime/common/js_symbol_test.cpp create mode 100644 tests/runtime/common/js_tagged_queue_test.cpp create mode 100644 tests/runtime/common/js_typed_array_test.cpp create mode 100644 tests/runtime/common/js_verification_test.cpp create mode 100644 tests/runtime/common/large_object_test.cpp create mode 100644 tests/runtime/common/lexical_env_test.cpp create mode 100644 tests/runtime/common/lexicalenv/CMakeLists.txt create mode 100644 tests/runtime/common/lexicalenv/lexicalenv.js create mode 100755 tests/runtime/common/lexicalenv/verify.sh create mode 100644 tests/runtime/common/linked_hash_table_test.cpp create mode 100644 tests/runtime/common/mem_controller_test.cpp create mode 100644 tests/runtime/common/missingargs/CMakeLists.txt create mode 100644 tests/runtime/common/missingargs/missingargs.js create mode 100755 tests/runtime/common/missingargs/verify.sh create mode 100644 tests/runtime/common/module/CMakeLists.txt create mode 100644 tests/runtime/common/module/module_a.js create mode 100644 tests/runtime/common/module/module_b.js create mode 100644 tests/runtime/common/module/module_c.js create mode 100755 tests/runtime/common/module/verify.sh create mode 100644 tests/runtime/common/multiargs/CMakeLists.txt create mode 100644 tests/runtime/common/multiargs/multiargs.js create mode 100755 tests/runtime/common/multiargs/verify.sh create mode 100644 tests/runtime/common/name_dictionary_test.cpp create mode 100644 tests/runtime/common/native_methods_api_no_crash/CMakeLists.txt create mode 100644 tests/runtime/common/native_methods_api_no_crash/native_methods_api_no_crash.cpp create mode 100644 tests/runtime/common/native_pointer_test.cpp create mode 100644 tests/runtime/common/newobjdynrange/CMakeLists.txt create mode 100644 tests/runtime/common/newobjdynrange/newobjdynrange.js create mode 100755 tests/runtime/common/newobjdynrange/verify.sh create mode 100644 tests/runtime/common/object_factory_test.cpp create mode 100644 tests/runtime/common/promise/CMakeLists.txt create mode 100644 tests/runtime/common/promise/promise.js create mode 100755 tests/runtime/common/promise/verify.sh create mode 100644 tests/runtime/common/restargs/CMakeLists.txt create mode 100644 tests/runtime/common/restargs/restargs.js create mode 100755 tests/runtime/common/restargs/verify.sh create mode 100644 tests/runtime/common/returnundefined/CMakeLists.txt create mode 100644 tests/runtime/common/returnundefined/returnundefined.js create mode 100755 tests/runtime/common/returnundefined/verify.sh create mode 100644 tests/runtime/common/separate_jsvm_test.cpp create mode 100644 tests/runtime/common/sieve/CMakeLists.txt create mode 100644 tests/runtime/common/sieve/sieve.js create mode 100755 tests/runtime/common/sieve/verify.sh create mode 100644 tests/runtime/common/strictequal/CMakeLists.txt create mode 100644 tests/runtime/common/strictequal/strictequal.js create mode 100755 tests/runtime/common/strictequal/verify.sh create mode 100644 tests/runtime/common/symbol_table_test.cpp create mode 100644 tests/runtime/common/tagged_value_test.cpp create mode 100644 tests/runtime/common/test_helper.cpp create mode 100644 tests/runtime/common/test_helper.h create mode 100644 tests/runtime/common/throwdyn/CMakeLists.txt create mode 100644 tests/runtime/common/throwdyn/throwdyn.js create mode 100755 tests/runtime/common/throwdyn/verify.sh create mode 100644 tests/runtime/common/visitor_compatibility_tests.cpp create mode 100644 tests/runtime/common/weak_ref_gen_gc_test.cpp create mode 100644 tests/runtime/common/weak_ref_stw_gc_test.cpp create mode 100644 tests/runtime/common/yieldstar/CMakeLists.txt create mode 100755 tests/runtime/common/yieldstar/verify.sh create mode 100644 tests/runtime/common/yieldstar/yieldstar.js create mode 100644 tests/runtime/hprof/heap_tracker_test.cpp create mode 100644 tests/runtime/hprof/hprof_test.cpp create mode 100644 tests/runtime/ic/ic_binaryop_test.cpp create mode 100644 tests/runtime/ic/ic_compareop_test.cpp create mode 100644 tests/runtime/ic/ic_invoke_test.cpp create mode 100644 tests/runtime/irtoc/CMakeLists.txt create mode 100644 tests/runtime/irtoc/advanced/CMakeLists.txt create mode 100644 tests/runtime/irtoc/advanced/advanced.js create mode 100644 tests/runtime/irtoc/basic/CMakeLists.txt create mode 100644 tests/runtime/irtoc/basic/basic.js create mode 100644 tests/runtime/mem/CMakeLists.txt create mode 100644 tests/runtime/mem/g1gc_barrier_test.cpp create mode 100644 tests/runtime/mem/object_helpers_test.cpp create mode 100644 tests/runtime/mem/weakContainers.js create mode 100644 tests/runtime/mem/weak_containers_test.cpp create mode 100644 tests/runtime/napi/CMakeLists.txt create mode 100644 tests/runtime/napi/jsnapi_tests.cpp create mode 100644 tests/runtime/regexp/dyn_buffer_test.cpp create mode 100644 tests/runtime/regexp/regexp_test.cpp create mode 100644 tests/runtime/snapshot/snapshot_test.cpp create mode 100644 tests/runtime/tooling/CMakeLists.txt create mode 100644 tests/runtime/tooling/api_tests/api_tests.h create mode 100644 tests/runtime/tooling/api_tests/js/js_breakpoint_test.h create mode 100644 tests/runtime/tooling/api_tests/js/js_enumerate_frames_test.h create mode 100644 tests/runtime/tooling/api_tests/js/js_exception_events_test.h create mode 100644 tests/runtime/tooling/api_tests/js/js_frame_pop_test.h create mode 100644 tests/runtime/tooling/api_tests/js/js_get_current_frame_test.h create mode 100644 tests/runtime/tooling/api_tests/js/js_get_variable_test.h create mode 100644 tests/runtime/tooling/api_tests/js/js_method_event_test.h create mode 100644 tests/runtime/tooling/api_tests/js/js_restart_frame_test.h create mode 100644 tests/runtime/tooling/api_tests/js/js_set_notification_test.h create mode 100644 tests/runtime/tooling/api_tests/js/js_set_variable_test.h create mode 100644 tests/runtime/tooling/api_tests/js/js_single_step_test.h create mode 100644 tests/runtime/tooling/api_tests/js/js_vm_event_test.h create mode 100644 tests/runtime/tooling/js/ExceptionTest.js create mode 100644 tests/runtime/tooling/js/FramePop.js create mode 100644 tests/runtime/tooling/js/GetFrame.js create mode 100644 tests/runtime/tooling/js/GetVariable.js create mode 100644 tests/runtime/tooling/js/RestartFrame.js create mode 100644 tests/runtime/tooling/js/Sample.js create mode 100644 tests/runtime/tooling/js/SetNotification.js create mode 100644 tests/runtime/tooling/js/SetVariable.js create mode 100644 tests/runtime/tooling/js_test_api.h create mode 100644 tests/runtime/tooling/launcher.cpp create mode 100644 tests/runtime/tooling/options_test/options_test.cpp create mode 100644 tests/runtime/tooling/test_list.cpp create mode 100644 tests/runtime/tooling/test_list.h diff --git a/AUTHORS b/AUTHORS new file mode 100644 index 000000000..dce934982 --- /dev/null +++ b/AUTHORS @@ -0,0 +1,136 @@ +Aleksandr Emelenko +Aleksandr Latikov +Aleksandr Lovkov +Aleksandr Popov +Aleksandr Semenov +Aleksei Grebenkin +Aleksei Sidorov +Alexey Biryukov +Alexey Romanov +Alina Kropacheva +Anna Antipina +Anton Makarenko +Anton Romanov +Anton Soldatov +Anton Youdkevitch +Artem Udovichenko +Boris Molodenkov +Boris Ulasevich +Chen Mudan +Chen Qiuyao +Chen Tingwei +Csaba Osztrogonac +Cui Guihua +Dai Huina +Daniil Kochergin +Daniil Kofanov +Denis Kononenko +Denis Krylov +Denis Slynko +Denis Tomashev +Denis Zakharov +Ding Ding +Ding Wen +Dmitrii Trubenkov +Dmitry Alexeev +Dmitry Bubnov +Dmitry Buzmakov +Dmitry Chuyko +Dmitry Kovalenko +Dmitry Pochepko +Dong Kaixing +Evgenii Kudriashov +Evgeny Gavrin +Filipp Zhinkin +Gan Lan +Ge Tingke +Gong Junsong +Guo Bingbing +Hao Tuo +Hu Feng +Hu Xiaowei +Huang Feijie +Huang Haitao +Huang Huijin +Huang Yu +Igor Gorban +Igor Petrov +Ilya Trubachev +Ivan Burimskii +Ivan Trubachev +Ji Andong +Jiang Han +Kirill Galitskiy +Konstantin Baladurin +Konstantin Nazarov +Leonid Dyachkov +Li Chenshuai +Li Wentao +Li Yiming +Li Yongbiao +Lin Xiang +Liu Xin +Lu Kai +Luo Chuhao +Maria Filippova +Mark Gonopolskiy +Martin Negyorku +Maxim Bolshov +Maxim Morozov +Maxim Zimnyukov +Mikhail Aksenov +Mikhail Chernov +Mikhail Kaskov +Mikhail Redkin +Mikhail Sherstennikov +Mikita Strizhak +Nikita Kharitonov +Nikita Sizov +Pan Zhengyu +Pang Desong +Pavel Andrianov +Pavel Ishin +Pei Jiajun +Peng Biao +Qiu Yu +Robert Fancsik +Roland Takacs +Roman Zhuykov +Sergei Shadrin +Sergey Chernykh +Sergey Goldenberg +Sergey Nikitin +Stepan Dyatkovskiy +Su Chongwei +Sun Zhe +Vadim Mutilin +Vadim Sofin +Vasil Dyadov +Vasily Isaenko +Vasily Kopyl +Viktor Kutuzov +Vitalii Mordan +Vladimir Kuznetsov +Vladimir Popov +Vsevolod Pukhov +Vyacheslav Cherkashin +Wan Yanglan +Wang Gang +Wang Yaofeng +Wang Zhaoyong +Weng Changcheng +Wu Pengyong +Wu Zhefeng +Xian Yuqiang +Xiong Luo +Xu Cheng +Xu Jie +Yan Churkin +Ye Xiangrun +Ying Guofeng +Yu Jianchao +Zhang Quan +Zhang Rengao +Zhang Yukun +Zhao Gaoyi +Zheng Jiahuan diff --git a/BUILD.gn b/BUILD.gn new file mode 100644 index 000000000..f7f1ff33a --- /dev/null +++ b/BUILD.gn @@ -0,0 +1,122 @@ +# Copyright (c) 2021-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("//ark/runtime_core/ark_config.gni") +import("//build/ohos.gni") + +group("ark_packages") { + deps = [] +} + +group("ark_host_linux_tools_packages") { +} + +group("ark_host_windows_tools_packages") { +} + +group("ark_host_mac_tools_packages") { +} + +config("ark_config") { + defines = [ "PANDA_WITH_ECMASCRIPT" ] +} + +config("assembler") { + include_dirs = [ + "$ark_root/plugins/ecmascript/assembler/extension", + "$target_gen_dir", + ] +} + +group("assembler_deps") { + deps = [ ":ark_asm_ecmascript_meta_gen_h" ] +} + +ark_gen_file("ark_asm_ecmascript_meta_gen_h") { + template_file = "$ark_root/assembler/templates/meta_gen.cpp.erb" + data_file = "$ark_root/plugins/ecmascript/assembler/extension/metadata.yaml" + requires = [ "$ark_root/assembler/asm_metadata.rb" ] + output_file = "$target_gen_dir/ecmascript_meta_gen.h" +} + +config("runtime") { + include_dirs = [ + "$ark_root/plugins/ecmascript/runtime/", + "$target_gen_dir", + "$ark_root/ark-third-party/icu/icu4c/source/common", + "$ark_root/ark-third-party/icu/icu4c/source/i18n", + "$ark_root/ark-third-party/icu/icu4c/source/", + ] +} + +config("runtime_interpreter_impl") { + include_dirs = [ + "$ark_root/ark-third-party/icu/icu4c/source/common", + "$ark_root/ark-third-party/icu/icu4c/source/i18n", + "$ark_root/ark-third-party/icu/icu4c/source/", + ] +} + +# Empty inlined ecmastblib. +action("ecmastblib_inline_h") { + script = "ecmastdlib/ecmastdlib_inline_gen.rb" + outputs = [ "$target_gen_dir/ecmastdlib_inline_gen.h" ] + args = [ + "-t", + rebase_path("ecmastdlib/ecmastdlib_inline_gen.h.erb", root_build_dir), + "-o", + rebase_path("$target_gen_dir/ecmastdlib_inline_gen.h"), + ] +} + +ark_gen_file("ecma_intrinsics_gen_arkruntime") { + template_file = + "$ark_root/plugins/ecmascript/runtime/templates/intrinsics_gen.cpp.erb" + data_file = "$ark_root/plugins/ecmascript/runtime/ecma_runtime.yaml" + requires = [ + "$ark_root/runtime/templates/intrinsics.rb", + "$ark_root/runtime/templates/runtime.rb", + ] + output_file = "$target_gen_dir/intrinsics_gen.cpp" +} + +config("bytecodeopt") { + include_dirs = [ + "$ark_root/plugins/ecmascript/bytecode_optimizer/", + "$target_gen_dir", + ] +} + +ark_isa_gen("isa_gen_ecma_bytecodeopt") { + template_files = [ + "ecmascript_codegen_intrinsics_gen.inc.erb", + ] + sources = "$ark_root/plugins/ecmascript/bytecode_optimizer/templates/" + destination = "$target_gen_dir/" + requires = [ "$ark_root//assembler/asm_isapi.rb" ] +} + +config("compiler") { + include_dirs = [ + "$ark_root/plugins/ecmascript/compiler/", + "$target_gen_dir", + ] +} + +ark_isa_gen("isa_gen_ecma_compiler") { + template_files = [ + "ecmascript_inst_builder_gen.cpp.erb", + ] + sources = "$ark_root/plugins/ecmascript/compiler/templates/" + destination = "$target_gen_dir/" +} diff --git a/CMakeLists.txt b/CMakeLists.txt new file mode 100644 index 000000000..c3f28906d --- /dev/null +++ b/CMakeLists.txt @@ -0,0 +1,46 @@ +# Copyright (c) 2021-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. + +cmake_minimum_required(VERSION 3.5.2 FATAL_ERROR) + +project(plugin_ecmascript) +message(STATUS "ecmascript plugin is found") + +set(ICU_ROOT ${PANDA_THIRD_PARTY_SOURCES_DIR}/icu) +add_subdirectory(${PANDA_THIRD_PARTY_CONFIG_DIR}/icu "${CMAKE_CURRENT_BINARY_DIR}/ark-third-party/icu") + +if(PANDA_WITH_TOOLCHAIN) + add_subdirectory(assembler) + add_subdirectory(isa) + add_subdirectory(es2panda) + add_dependencies(panda_bins es2panda) +endif() + +add_subdirectory(bytecode_optimizer) + +if(PANDA_WITH_COMPILER) + add_subdirectory(compiler) +endif() + +if(PANDA_WITH_RUNTIME) + add_subdirectory(ecmastdlib) + add_subdirectory(runtime) +endif() + +if(PANDA_WITH_TESTS) + add_subdirectory(tests) +endif() + +add_plugin_options(${CMAKE_CURRENT_SOURCE_DIR}/ecmascript_plugin_options.yaml) + +add_runtime_options(${CMAKE_CURRENT_SOURCE_DIR}/runtime_options.yaml) diff --git a/LICENSE-2.0.txt b/LICENSE-2.0.txt new file mode 100644 index 000000000..30e57694c --- /dev/null +++ b/LICENSE-2.0.txt @@ -0,0 +1,191 @@ + + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + Copyright [yyyy] [name of copyright owner] + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT 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/README.en.md b/README.en.md deleted file mode 100644 index 614c25158..000000000 --- a/README.en.md +++ /dev/null @@ -1,36 +0,0 @@ -# arkcompiler_ets_runtime - -#### Description -{**When you're done, you can delete the content in this README and update the file with details for others getting started with your repository**} - -#### Software Architecture -Software architecture description - -#### Installation - -1. xxxx -2. xxxx -3. xxxx - -#### Instructions - -1. xxxx -2. xxxx -3. xxxx - -#### Contribution - -1. Fork the repository -2. Create Feat_xxx branch -3. Commit your code -4. Create Pull Request - - -#### Gitee Feature - -1. You can use Readme\_XXX.md to support different languages, such as Readme\_en.md, Readme\_zh.md -2. Gitee blog [blog.gitee.com](https://blog.gitee.com) -3. Explore open source project [https://gitee.com/explore](https://gitee.com/explore) -4. The most valuable open source project [GVP](https://gitee.com/gvp) -5. The manual of Gitee [https://gitee.com/help](https://gitee.com/help) -6. The most popular members [https://gitee.com/gitee-stars/](https://gitee.com/gitee-stars/) diff --git a/README.md b/README.md deleted file mode 100644 index f3228f879..000000000 --- a/README.md +++ /dev/null @@ -1,39 +0,0 @@ -# arkcompiler_ets_runtime - -#### 介绍 -{**以下是 Gitee 平台说明,您可以替换此简介** -Gitee 是 OSCHINA 推出的基于 Git 的代码托管平台(同时支持 SVN)。专为开发者提供稳定、高效、安全的云端软件开发协作平台 -无论是个人、团队、或是企业,都能够用 Gitee 实现代码托管、项目管理、协作开发。企业项目请看 [https://gitee.com/enterprises](https://gitee.com/enterprises)} - -#### 软件架构 -软件架构说明 - - -#### 安装教程 - -1. xxxx -2. xxxx -3. xxxx - -#### 使用说明 - -1. xxxx -2. xxxx -3. xxxx - -#### 参与贡献 - -1. Fork 本仓库 -2. 新建 Feat_xxx 分支 -3. 提交代码 -4. 新建 Pull Request - - -#### 特技 - -1. 使用 Readme\_XXX.md 来支持不同的语言,例如 Readme\_en.md, Readme\_zh.md -2. Gitee 官方博客 [blog.gitee.com](https://blog.gitee.com) -3. 你可以 [https://gitee.com/explore](https://gitee.com/explore) 这个地址来了解 Gitee 上的优秀开源项目 -4. [GVP](https://gitee.com/gvp) 全称是 Gitee 最有价值开源项目,是综合评定出的优秀开源项目 -5. Gitee 官方提供的使用手册 [https://gitee.com/help](https://gitee.com/help) -6. Gitee 封面人物是一档用来展示 Gitee 会员风采的栏目 [https://gitee.com/gitee-stars/](https://gitee.com/gitee-stars/) diff --git a/assembler/CMakeLists.txt b/assembler/CMakeLists.txt new file mode 100644 index 000000000..f3c6f93bf --- /dev/null +++ b/assembler/CMakeLists.txt @@ -0,0 +1,33 @@ +# Copyright (c) 2021-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. + +set(EXTENSION_DIR ${PANDA_ECMASCRIPT_PLUGIN_SOURCE}/assembler/extension) + +target_sources(arkassembler PRIVATE ${EXTENSION_DIR}/ecmascript_meta.cpp) +target_include_directories(arkassembler PUBLIC ${EXTENSION_DIR}) + +set(ECMASCRIPT_META_GEN_H ${PANDA_BINARY_ROOT}/assembler/ecmascript_meta_gen.h) +panda_gen_file( + DATAFILE ${PANDA_ECMASCRIPT_PLUGIN_SOURCE}/assembler/extension/metadata.yaml + TEMPLATE ${PANDA_ROOT}/assembler/templates/meta_gen.cpp.erb + OUTPUTFILE ${ECMASCRIPT_META_GEN_H} + REQUIRES ${PANDA_ROOT}/assembler/asm_metadata.rb +) +add_custom_target(ecmascript_meta_gen_h DEPENDS ${ECMASCRIPT_META_GEN_H}) +add_dependencies(arkassembler ecmascript_meta_gen_h) + +if (PANDA_ENABLE_AFL) + target_sources(arkassembler_fuzz PRIVATE ${EXTENSION_DIR}/ecmascript_meta.cpp) + target_include_directories(arkassembler_fuzz PUBLIC ${EXTENSION_DIR}) + add_dependencies(arkassembler_fuzz ecmascript_meta_gen_h) +endif() diff --git a/assembler/assembler_sources.gn b/assembler/assembler_sources.gn new file mode 100644 index 000000000..4611d6c3b --- /dev/null +++ b/assembler/assembler_sources.gn @@ -0,0 +1,14 @@ +# Copyright (c) 2021-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. + +srcs = [ "extension/ecmascript_meta.cpp" ] diff --git a/assembler/extension/ecmascript_meta.cpp b/assembler/extension/ecmascript_meta.cpp new file mode 100644 index 000000000..3bfd5cf59 --- /dev/null +++ b/assembler/extension/ecmascript_meta.cpp @@ -0,0 +1,22 @@ +/** + * Copyright (c) 2021-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_meta.h" + +namespace panda::pandasm::extensions::ecmascript { + +#include + +} // namespace panda::pandasm::extensions::ecmascript diff --git a/assembler/extension/ecmascript_meta.h b/assembler/extension/ecmascript_meta.h new file mode 100644 index 000000000..c43410770 --- /dev/null +++ b/assembler/extension/ecmascript_meta.h @@ -0,0 +1,231 @@ +/** + * Copyright (c) 2021-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 PANDA_ASSEMBLER_EXTENSIONS_ECMASCRIPT_META_H_ +#define PANDA_ASSEMBLER_EXTENSIONS_ECMASCRIPT_META_H_ + +#include "meta.h" + +namespace panda::pandasm::extensions::ecmascript { + +class RecordMetadata : public pandasm::RecordMetadata { +public: + std::string GetBase() const override + { + auto base = GetAttributeValue("ecmascript.extends"); + if (base) { + return base.value(); + } + + return ""; + } + + std::vector GetInterfaces() const override + { + return {}; + } + + bool IsAnnotation() const override + { + return (GetAccessFlags() & ACC_ANNOTATION) != 0; + } + + bool IsRuntimeAnnotation() const override + { + return false; + } + +protected: + bool IsAnnotationRecordAttribute([[maybe_unused]] std::string_view attribute) const override + { + return false; + } + + bool IsAnnotationIdAttribute([[maybe_unused]] std::string_view attribute) const override + { + return false; + } + + bool IsAnnotationElementNameAttribute([[maybe_unused]] std::string_view attribute) const override + { + return false; + } + + bool IsAnnotationElementTypeAttribute([[maybe_unused]] std::string_view attribute) const override + { + return false; + } + + bool IsAnnotationElementArrayComponentTypeAttribute([[maybe_unused]] std::string_view attribute) const override + { + return false; + } + + bool IsAnnotationElementValueAttribute([[maybe_unused]] std::string_view attribute) const override + { + return false; + } + + std::optional Validate(std::string_view attribute) const override; + + std::optional Validate(std::string_view attribute, std::string_view value) const override; + + void SetFlags(std::string_view attribute) override; + + void SetFlags(std::string_view attribute, std::string_view value) override; + + void RemoveFlags(std::string_view attribute) override; + + void RemoveFlags(std::string_view attribute, std::string_view value) override; +}; + +class FieldMetadata : public pandasm::FieldMetadata { +protected: + bool IsAnnotationRecordAttribute([[maybe_unused]] std::string_view attribute) const override + { + return false; + } + + bool IsAnnotationIdAttribute([[maybe_unused]] std::string_view attribute) const override + { + return false; + } + + bool IsAnnotationElementNameAttribute([[maybe_unused]] std::string_view attribute) const override + { + return false; + } + + bool IsAnnotationElementTypeAttribute([[maybe_unused]] std::string_view attribute) const override + { + return false; + } + + bool IsAnnotationElementArrayComponentTypeAttribute([[maybe_unused]] std::string_view attribute) const override + { + return false; + } + + bool IsAnnotationElementValueAttribute([[maybe_unused]] std::string_view attribute) const override + { + return false; + } + + std::optional Validate(std::string_view attribute) const override; + + std::optional Validate(std::string_view attribute, std::string_view value) const override; + + void SetFlags(std::string_view attribute) override; + + void SetFlags(std::string_view attribute, std::string_view value) override; + + void RemoveFlags(std::string_view attribute) override; + + void RemoveFlags(std::string_view attribute, std::string_view value) override; +}; + +class FunctionMetadata : public pandasm::FunctionMetadata { +protected: + bool IsAnnotationRecordAttribute([[maybe_unused]] std::string_view attribute) const override + { + return false; + } + + bool IsAnnotationIdAttribute([[maybe_unused]] std::string_view attribute) const override + { + return false; + } + + bool IsAnnotationElementNameAttribute([[maybe_unused]] std::string_view attribute) const override + { + return false; + } + + bool IsAnnotationElementTypeAttribute([[maybe_unused]] std::string_view attribute) const override + { + return false; + } + + bool IsAnnotationElementArrayComponentTypeAttribute([[maybe_unused]] std::string_view attribute) const override + { + return false; + } + + bool IsAnnotationElementValueAttribute([[maybe_unused]] std::string_view attribute) const override + { + return false; + } + + std::optional Validate(std::string_view attribute) const override; + + std::optional Validate(std::string_view attribute, std::string_view value) const override; + + void SetFlags(std::string_view attribute) override; + + void SetFlags(std::string_view attribute, std::string_view value) override; + + void RemoveFlags(std::string_view attribute) override; + + void RemoveFlags(std::string_view attribute, std::string_view value) override; +}; + +class ParamMetadata : public pandasm::ParamMetadata { +protected: + bool IsAnnotationRecordAttribute([[maybe_unused]] std::string_view attribute) const override + { + return false; + } + + bool IsAnnotationIdAttribute([[maybe_unused]] std::string_view attribute) const override + { + return false; + } + + bool IsAnnotationElementNameAttribute([[maybe_unused]] std::string_view attribute) const override + { + return false; + } + + bool IsAnnotationElementTypeAttribute([[maybe_unused]] std::string_view attribute) const override + { + return false; + } + + bool IsAnnotationElementArrayComponentTypeAttribute([[maybe_unused]] std::string_view attribute) const override + { + return false; + } + + bool IsAnnotationElementValueAttribute([[maybe_unused]] std::string_view attribute) const override + { + return false; + } + + std::optional Validate(std::string_view attribute) const override; + + std::optional Validate(std::string_view attribute, std::string_view value) const override; + + void SetFlags(std::string_view attribute) override; + + void SetFlags(std::string_view attribute, std::string_view value) override; + + void RemoveFlags(std::string_view attribute) override; + + void RemoveFlags(std::string_view attribute, std::string_view value) override; +}; + +} // namespace panda::pandasm::extensions::ecmascript + +#endif // PANDA_ECMASCRIPT_META_H diff --git a/assembler/extension/metadata.yaml b/assembler/extension/metadata.yaml new file mode 100644 index 000000000..cd78f1c18 --- /dev/null +++ b/assembler/extension/metadata.yaml @@ -0,0 +1,27 @@ +# Copyright (c) 2021-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. + +language: EcmaScript + +attributes: +- name: annotation + type: bool + flags: [ACC_ANNOTATION] + applicable_to: + - record + +- name: extends + type: record + multiple: false + applicable_to: + - record diff --git a/bytecode_optimizer/CMakeLists.txt b/bytecode_optimizer/CMakeLists.txt new file mode 100644 index 000000000..4ed90cb3e --- /dev/null +++ b/bytecode_optimizer/CMakeLists.txt @@ -0,0 +1,33 @@ +# Copyright (c) 2021-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. + +project(plugin_ecmascript_bytecode_optimizer) + +set(GENERATED_DIR ${CMAKE_CURRENT_BINARY_DIR}/generated) +file(MAKE_DIRECTORY ${GENERATED_DIR}) + +panda_isa_gen( + TEMPLATES + "ecmascript_codegen_intrinsics_gen.inc.erb" + SOURCE ${PANDA_ECMASCRIPT_PLUGIN_SOURCE}/bytecode_optimizer/templates + REQUIRES ${PANDA_ROOT}/assembler/asm_isapi.rb + DESTINATION ${GENERATED_DIR} + ) + +add_dependencies(arkbytecodeopt isa_gen_${PROJECT_NAME}) + +target_include_directories(arkbytecodeopt PUBLIC + ${GENERATED_DIR} +) + +add_check_style(".") diff --git a/bytecode_optimizer/bytecodeopt_sources.gn b/bytecode_optimizer/bytecodeopt_sources.gn new file mode 100644 index 000000000..e0b107945 --- /dev/null +++ b/bytecode_optimizer/bytecodeopt_sources.gn @@ -0,0 +1,12 @@ +# Copyright (c) 2021-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. diff --git a/bytecode_optimizer/templates/ecmascript_codegen_intrinsics_gen.inc.erb b/bytecode_optimizer/templates/ecmascript_codegen_intrinsics_gen.inc.erb new file mode 100644 index 000000000..e699e5f2b --- /dev/null +++ b/bytecode_optimizer/templates/ecmascript_codegen_intrinsics_gen.inc.erb @@ -0,0 +1,85 @@ +/** + * Copyright (c) 2021-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. + */ + +// Autogenerated file -- DO NOT EDIT! + +% instructions = Panda::instructions.select{|b| b.namespace == "ecmascript"} +% if instructions.first && instructions.first.intrinsic_name +% instructions = instructions.group_by(&:intrinsic_name) +% else +% instructions = instructions.group_by(&:opcode) +% end +% instructions.each do |intrinsic_name, group| +% inst = group.first +% next if inst.properties.include?("jump") +% opcode = inst.opcode.upcase +% params_arr = inst.operands + case compiler::RuntimeInterface::IntrinsicId::<%= intrinsic_name %>: { +% if inst.acc.include?("in") + auto acc_src = inst->GetSrcReg(inst->GetInputsCount() - 2); + if (acc_src != compiler::ACC_REG_ID) { + DoLdaDyn(acc_src, enc->result_); + } +% end +% params_str = "" +% vreg_index = 0 +% imm_index = 0 +% id_index = 0 +% input_index = 0 +% params_arr.each do |param| +% if param.reg? + auto v<%= vreg_index %> = inst->GetSrcReg(<%= input_index %>); +% params_str = params_str + "v#{vreg_index}, " +% vreg_index = vreg_index + 1 +% input_index = input_index + 1 +% elsif param.id? +% if inst.properties.include?("method_id") + ASSERT(inst->HasImms() && inst->GetImms().size() > <%= imm_index %>); // NOLINTNEXTLINE(readability-container-size-empty) + auto ir_id<%= id_index %> = static_cast(inst->GetImms()[<%= imm_index %>]); + auto bc_id<%= id_index %> = enc->ir_interface_->GetMethodIdByOffset(ir_id<%= id_index %>); +% params_str = params_str + "bc_id#{id_index}, " +% elsif inst.properties.include?("string_id") + ASSERT(inst->HasImms() && inst->GetImms().size() > <%= imm_index %>); // NOLINTNEXTLINE(readability-container-size-empty) + auto ir_id<%= id_index %> = static_cast(inst->GetImms()[<%= imm_index %>]); + auto bc_id<%= id_index %> = enc->ir_interface_->GetStringIdByOffset(ir_id<%= id_index %>); +% params_str = params_str + "bc_id#{id_index}, " +% end +% id_index = id_index + 1 +% imm_index = imm_index + 1 +% elsif param.imm? + ASSERT(inst->HasImms() && inst->GetImms().size() > <%= imm_index %>); // NOLINTNEXTLINE(readability-container-size-empty) +% if inst.properties.include?("jump") +% params_str += "LabelName(inst->GetBasicBlock()->GetTrueSuccessor()->GetId()), " +% else + auto imm<%= imm_index %> = static_cast(inst->GetImms()[<%= imm_index %>]); +% params_str = params_str + "imm#{imm_index}, " +% imm_index = imm_index + 1 +% end +% end +% end +% if params_arr.length > 0 +% params_str = params_str.chop +% params_str = params_str.chop +% end + enc->result_.emplace_back(pandasm::Create_<%= inst.asm_token %>(<%= params_str %>)); +% if inst.acc.include?("out") + auto acc_dst = inst->GetDstReg(); + if (acc_dst != compiler::ACC_REG_ID) { + DoStaDyn(inst->GetDstReg(), enc->result_); + } +% end + break; + } +% end diff --git a/bytecode_optimizer/visitors/ecmascript_codegen_intrinsics.inc b/bytecode_optimizer/visitors/ecmascript_codegen_intrinsics.inc new file mode 100644 index 000000000..813190b2f --- /dev/null +++ b/bytecode_optimizer/visitors/ecmascript_codegen_intrinsics.inc @@ -0,0 +1,16 @@ +/* + * Copyright (c) 2021-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_codegen_intrinsics_gen.inc" diff --git a/compiler/CMakeLists.txt b/compiler/CMakeLists.txt new file mode 100644 index 000000000..39330c15c --- /dev/null +++ b/compiler/CMakeLists.txt @@ -0,0 +1,46 @@ +# Copyright (c) 2021-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. + +project(plugin_ecmascript_compiler) + +add_subdirectory(ecmascript_extensions) + +set(GENERATED_DIR ${CMAKE_CURRENT_BINARY_DIR}/generated) +file(MAKE_DIRECTORY ${GENERATED_DIR}) + +panda_isa_gen( + TEMPLATES + "ecmascript_inst_builder_gen.cpp.erb" + SOURCE ${CMAKE_CURRENT_SOURCE_DIR}/templates + REQUIRES ${PANDA_ROOT}/assembler/asm_isapi.rb + DESTINATION ${GENERATED_DIR} + ) + +add_dependencies(arkcompiler isa_gen_${PROJECT_NAME}) + +set(COMPILER_SOURCES + ${CMAKE_CURRENT_SOURCE_DIR}/optimizer/code_generator/compiler_base_types.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/intrinsics_type_resolving_ecmascript.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/codegen_intrinsics_ecmascript.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/optimizer/ir_builder/ecmascript_inst_builder.cpp +) + +target_sources(arkcompiler PRIVATE ${COMPILER_SOURCES}) + +target_include_directories(arkcompiler PUBLIC + ${GENERATED_DIR} +) + +add_inst_templates(${CMAKE_CURRENT_SOURCE_DIR}/optimizer/ir_builder/ecmascript_inst_templates.yaml) + +add_check_style(".") diff --git a/compiler/codegen_intrinsics_ecmascript.cpp b/compiler/codegen_intrinsics_ecmascript.cpp new file mode 100644 index 000000000..d426b68db --- /dev/null +++ b/compiler/codegen_intrinsics_ecmascript.cpp @@ -0,0 +1,307 @@ +/** + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "compiler/optimizer/code_generator/codegen.h" +#include "plugins/ecmascript/compiler/ecmascript_extensions/ecmascript_environment.h" +#include "runtime/include/hclass.h" +#include "runtime/include/thread.h" +#include "plugins/ecmascript/runtime/mem/tagged_object.h" + +namespace panda::compiler { +MemRef AccMemRef(Encoder *enc, Reg thread, Reg acc_reg) +{ + enc->EncodeAdd(acc_reg, thread, Imm(ManagedThread::GetFrameOffset())); + auto acc_ptr = MemRef(acc_reg); + enc->EncodeLdr(acc_reg, false, MemRef(acc_ptr)); + return MemRef(acc_reg, cross_values::GetFrameAccOffset(enc->GetArch())); +} + +void Codegen::LdlexenvDyn([[maybe_unused]] IntrinsicInst *inst, [[maybe_unused]] Reg dst, [[maybe_unused]] SRCREGS src, + [[maybe_unused]] RegMask *lvrmask) +{ + auto enc = GetEncoder(); + auto arch = enc->GetArch(); + + ScopedTmpReg tmp1(enc); + Reg tmp1_reg(tmp1.GetReg().GetId(), Codegen::ConvertDataType(DataType::POINTER, arch)); + enc->EncodeLdr(tmp1_reg, false, MemRef(ThreadReg(), GetRuntime()->GetLanguageExtensionsDataOffset(arch))); + + auto lexical_env_ptr = MemRef(tmp1_reg, panda::ecmascript::EcmascriptEnvironment::GetLexicalEnvOffset()); + Reg tmp2_reg(tmp1.GetReg().GetId(), Codegen::ConvertDataType(DataType::ANY, arch)); + enc->EncodeLdr(tmp2_reg, false, lexical_env_ptr); + + if (!GetGraph()->GetMode().IsInterpreter()) { + enc->EncodeMov(dst, tmp2_reg); + } else { + ScopedTmpReg acc(enc); + Reg acc_reg(acc.GetReg().GetId(), Codegen::ConvertDataType(DataType::POINTER, arch)); + enc->EncodeStr(tmp2_reg, AccMemRef(enc, ThreadReg(), acc_reg)); + } +} + +void IsObject(Encoder *enc, const Reg &value, const Reg &tmp1, const Reg &tmp2) +{ + enc->EncodeAnd(tmp1, value, Imm(coretypes::TaggedValue::TAG_MASK)); + enc->EncodeMov(tmp2, Imm(coretypes::TaggedValue::TAG_OBJECT)); + enc->EncodeCompare(tmp1, tmp1, tmp2, Condition::EQ); +} + +void IsSpecial(Encoder *enc, const Reg &value, const Reg &tmp1, const Reg &tmp2, MemRef tmp_stack_slot) +{ + // IsSpecial(value) = (val <= ~TAG_SPECIAL_MASK) & (((val & TAG_SPECIAL_VALUE) >> 1) | (val == 0)) + + // tmp1 = val == 0 + enc->EncodeMov(tmp2, Imm(0U)); + enc->EncodeCompare(tmp1, value, tmp2, Condition::EQ); + + // tmp2 = ((val & TAG_SPECIAL_VALUE) >> 1) | tmp1 + enc->EncodeAnd(tmp2, value, Imm(coretypes::TaggedValue::TAG_SPECIAL_VALUE)); + enc->EncodeShr(tmp2, tmp1, Imm(1)); + enc->EncodeOr(tmp2, tmp2, tmp1); + + // store(tmp2) + enc->EncodeStr(tmp2, tmp_stack_slot); + + // tmp2 = value & ~TAG_SPECIAL_MASK + enc->EncodeMov(tmp2, Imm(~coretypes::TaggedValue::TAG_SPECIAL_MASK)); + enc->EncodeCompare(tmp1, value, tmp2, Condition::LS); + + // load(tmp2) + enc->EncodeLdr(tmp2, false, tmp_stack_slot); + // tmp1 = tmp1 & tmp2 + enc->EncodeAnd(tmp1, tmp1, tmp2); +} + +void IsHeapObject(Encoder *enc, const Reg &value, const Reg &tmp1, const Reg &tmp2, MemRef slot1, MemRef slot2) +{ + // IsHeapObject(value) = IsObject(value) && !IsSpecial(value) + + // e1 = IsObject(value) + IsObject(enc, value, tmp1, tmp2); // tmp1 = e1 + enc->EncodeStr(tmp1, slot1); // stack[slot1] = e1 + // e2 = IsSpecial(value) + IsSpecial(enc, value, tmp1, tmp2, slot2); // tmp1 = e2 + // e3 = e1 && !e2 + enc->EncodeLdr(tmp2, false, slot1); // tmp2 = e1 + enc->EncodeNot(tmp1, tmp1); + enc->EncodeAnd(tmp1, tmp2, tmp1); // tmp1 = e3 +} + +void Codegen::LdLexVarDyn([[maybe_unused]] IntrinsicInst *inst, [[maybe_unused]] Reg dst, SRCREGS src, + [[maybe_unused]] RegMask *lvrmask) +{ + auto enc = GetEncoder(); + auto arch = enc->GetArch(); + auto runtime = GetRuntime(); + + ScopedTmpReg tmp1(enc); + Reg lang_ext(tmp1.GetReg().GetId(), Codegen::ConvertDataType(DataType::POINTER, arch)); + enc->EncodeLdr(lang_ext, false, MemRef(ThreadReg(), GetRuntime()->GetLanguageExtensionsDataOffset(arch))); + + auto lexical_env_ptr = MemRef(lang_ext, panda::ecmascript::EcmascriptEnvironment::GetLexicalEnvOffset()); + Reg lex_env(tmp1.GetReg().GetId(), Codegen::ConvertDataType(DataType::ANY, arch)); + enc->EncodeLdr(lex_env, false, lexical_env_ptr); + + auto begin_label = enc->CreateLabel(); + auto exit_label = enc->CreateLabel(); + + ScopedTmpReg tmp2(enc); + Reg i_reg(tmp2.GetReg().GetId(), Codegen::ConvertDataType(DataType::UINT16, arch)); + enc->EncodeMov(i_reg, Imm(0)); + + // enter the loop body if i is less then level (level = src[0]) + enc->BindLabel(begin_label); + if (!GetGraph()->GetMode().IsInterpreter()) { + enc->EncodeJump(exit_label, i_reg, Imm(inst->GetImms()[0]), Condition::HS); + } else { + enc->EncodeJump(exit_label, i_reg, src[0], Condition::HS); + } + + // the beginning of the loop body + + // get env tagged array length + ScopedTmpReg length(enc); + Reg length_reg(length.GetReg().GetId(), Codegen::ConvertDataType(DataType::UINT32, arch)); + enc->EncodeLdr(length_reg, false, MemRef(lex_env, runtime->GetArrayLengthOffset(arch))); + // check if the length is greater than zero + auto length_slow_path = CreateSlowPath(inst, EntrypointId::NULL_POINTER_EXCEPTION); + enc->EncodeJump(length_slow_path->GetLabel(), length_reg, Condition::LO); + + // get parent env + enc->EncodeLdr(lex_env, false, MemRef(lex_env, runtime->GetArrayDataOffset(arch))); + // check if the env value is defined + Reg lex_env_u64(length.GetReg().GetId(), Codegen::ConvertDataType(DataType::UINT64, arch)); + enc->EncodeMov(lex_env_u64, lex_env); + auto undefined_slow_path = CreateSlowPath(inst, EntrypointId::NULL_POINTER_EXCEPTION); + enc->EncodeJump(undefined_slow_path->GetLabel(), lex_env_u64, Imm(coretypes::TaggedValue::VALUE_UNDEFINED), + Condition::EQ); + + // store i and env to stack + auto stack_slots_count = GetFrameLayout().GetSpillsCount(); + auto stack_slot_size = GetFrameLayout().GetSlotSize(); + auto stask_slot_1 = MemRef(SpReg(), stack_slots_count * stack_slot_size); + enc->EncodeStr(lex_env, stask_slot_1); + auto stask_slot_2 = MemRef(SpReg(), (stack_slots_count + 1) * stack_slot_size); + enc->EncodeStr(i_reg, stask_slot_2); + + // check if the env value is HeapObject + Reg v1(tmp1.GetReg().GetId(), Codegen::ConvertDataType(DataType::UINT64, arch)); + Reg v2(tmp2.GetReg().GetId(), Codegen::ConvertDataType(DataType::UINT64, arch)); + auto stask_slot_3 = MemRef(SpReg(), (stack_slots_count + 2) * stack_slot_size); + auto stask_slot_4 = MemRef(SpReg(), (stack_slots_count + 3) * stack_slot_size); + IsHeapObject(enc, lex_env_u64, v1, v2, stask_slot_3, stask_slot_4); + enc->EncodeStr(v1, stask_slot_3); // stack[stask_slot_3] = IsHeapObject(value) + + // check if it is possible to convert TaggedValue to HeapObject object + + // assert(IsHeapObject(value) && ((value & TAG_WEAK_FILTER) == 0U)) + enc->EncodeAnd(v1, lex_env_u64, Imm(coretypes::TaggedValue::TAG_WEAK_FILTER)); + enc->EncodeMov(v2, Imm(0U)); + enc->EncodeCompare(v1, v1, v2, Condition::EQ); // v1 = ((value & TAG_WEAK_FILTER) == 0U) + enc->EncodeLdr(v2, false, stask_slot_3); // v2 = IsHeapObject(value) + enc->EncodeAnd(v2, v2, v1); + auto can_not_convert_slow_path = CreateSlowPath(inst, EntrypointId::NULL_POINTER_EXCEPTION); + enc->EncodeJump(can_not_convert_slow_path->GetLabel(), v2, Imm(1), Condition::NE); + + // restore i and env + enc->EncodeLdr(i_reg, false, stask_slot_2); + enc->EncodeLdr(lex_env, false, stask_slot_1); + + // increment i + enc->EncodeAdd(i_reg, i_reg, Imm(1)); + // go to the beginning of the loop + enc->EncodeJump(begin_label); + enc->BindLabel(exit_label); + + enc->EncodeStr(lex_env, stask_slot_1); + + // increase slot by RESERVED_ENV_LENGTH ( = 1 ) + Reg tmp_reg(length.GetReg().GetId(), Codegen::ConvertDataType(DataType::UINT32, arch)); + if (!GetGraph()->GetMode().IsInterpreter()) { + enc->EncodeMov(tmp_reg, Imm(inst->GetImms()[1] + 1)); + } else { + enc->EncodeAdd(tmp_reg, src[1], Imm(1)); + } + // get env tagged array length + Reg tmp_reg3(tmp1.GetReg().GetId(), Codegen::ConvertDataType(DataType::UINT32, arch)); + enc->EncodeLdr(tmp_reg3, false, MemRef(lex_env, runtime->GetArrayLengthOffset(arch))); + // check if the slot is less than the length + auto index_out_of_range_slow_path = CreateSlowPath(inst, EntrypointId::NULL_POINTER_EXCEPTION); + enc->EncodeJump(index_out_of_range_slow_path->GetLabel(), tmp_reg3, tmp_reg, Condition::LS); + + // get array data offset + Reg offset(tmp2.GetReg().GetId(), Codegen::ConvertDataType(DataType::UINT64, arch)); + enc->EncodeMov(offset, tmp_reg); + Reg tmp_reg_u64(length.GetReg().GetId(), Codegen::ConvertDataType(DataType::UINT64, arch)); + enc->EncodeMov(tmp_reg_u64, Imm(sizeof(coretypes::TaggedType))); + enc->EncodeMul(offset, offset, tmp_reg_u64); + enc->EncodeAdd(offset, offset, Imm(runtime->GetArrayDataOffset(arch))); + + // get raw data of the array element + Reg lex_env_ptr(tmp1.GetReg().GetId(), Codegen::ConvertDataType(DataType::POINTER, arch)); + enc->EncodeLdr(lex_env, false, stask_slot_1); + enc->EncodeMov(lex_env_ptr, lex_env); + enc->EncodeAdd(lex_env_ptr, lex_env_ptr, offset); + enc->EncodeLdr(offset, false, MemRef(lex_env_ptr)); + + // save to acc + if (!GetGraph()->GetMode().IsInterpreter()) { + enc->EncodeMov(dst, offset); + } else { + Reg acc_reg(tmp1.GetReg().GetId(), Codegen::ConvertDataType(DataType::POINTER, arch)); + enc->EncodeStr(offset, AccMemRef(enc, ThreadReg(), acc_reg)); + } +} + +void Codegen::GetObjectClassTypeIntrinsic([[maybe_unused]] IntrinsicInst *inst, [[maybe_unused]] Reg dst, SRCREGS src, + [[maybe_unused]] RegMask *lvrmask) +{ + ScopedTmpReg tmp_reg(GetEncoder(), ConvertDataType(DataType::UINT64, GetArch())); + GetEncoder()->EncodeLdr(tmp_reg, false, MemRef(src[0], GetRuntime()->GetObjClassOffset(GetArch()))); + GetEncoder()->EncodeLdr(tmp_reg, false, MemRef(tmp_reg, cross_values::GetHclassDataOffset(GetArch()))); + GetEncoder()->EncodeAnd(tmp_reg, tmp_reg, + Imm(static_cast(cross_values::GetJshclassBitfieldTypeMask(GetArch())))); + auto type_start_bit = cross_values::GetJshclassBitfieldTypeStartBit(GetArch()); + if (type_start_bit != 0) { + GetEncoder()->EncodeShr(tmp_reg, tmp_reg, Imm(type_start_bit)); + } + GetEncoder()->EncodeMov(dst, tmp_reg); +} + +void Codegen::GetEcmaConstantPool([[maybe_unused]] IntrinsicInst *inst, [[maybe_unused]] Reg dst, + [[maybe_unused]] SRCREGS src, [[maybe_unused]] RegMask *lvrmask) +{ + auto dst_ext = dst.As(TypeInfo(TypeInfo::TypeId::INT64)); + GetEncoder()->EncodeLdr(dst_ext, false, + MemRef(ThreadReg(), GetRuntime()->GetLanguageExtensionsDataOffset(GetArch()))); + GetEncoder()->EncodeLdr(dst, false, + MemRef(dst_ext, panda::ecmascript::EcmascriptEnvironment::GetConstantPoolOffset())); +} + +void Codegen::GetEcmaThisFunc([[maybe_unused]] IntrinsicInst *inst, [[maybe_unused]] Reg dst, + [[maybe_unused]] SRCREGS src, [[maybe_unused]] RegMask *lvrmask) +{ + auto dst_ext = dst.As(TypeInfo(TypeInfo::TypeId::INT64)); + GetEncoder()->EncodeLdr(dst_ext, false, + MemRef(ThreadReg(), GetRuntime()->GetLanguageExtensionsDataOffset(GetArch()))); + GetEncoder()->EncodeLdr(dst, false, MemRef(dst_ext, panda::ecmascript::EcmascriptEnvironment::GetThisFuncOffset())); +} + +void Codegen::GetWeakReferent([[maybe_unused]] IntrinsicInst *inst, [[maybe_unused]] Reg dst, + [[maybe_unused]] SRCREGS src, [[maybe_unused]] RegMask *lvrmask) +{ + GetEncoder()->EncodeMov(dst, src[0]); + GetEncoder()->EncodeAnd(dst, dst, Imm(~TaggedValue::TAG_WEAK_MASK)); +} + +void Codegen::CreateDynClassIsDictionaryElement([[maybe_unused]] IntrinsicInst *inst, Reg dst, SRCREGS src, + [[maybe_unused]] RegMask *lvrmask) +{ + ScopedTmpReg tmp_reg(GetEncoder(), ConvertDataType(DataType::UINT64, GetArch())); + GetEncoder()->EncodeLdr(tmp_reg, false, MemRef(src[0], cross_values::GetJshclassBitfieldOffset(GetArch()))); + GetEncoder()->EncodeShr(tmp_reg, tmp_reg, Imm(cross_values::GetJshclassBitfieldIsDictionaryStartBit(GetArch()))); + GetEncoder()->EncodeAnd(dst, tmp_reg, Imm(1U)); +} + +void Codegen::CreateDynClassIsExtensible([[maybe_unused]] IntrinsicInst *inst, Reg dst, SRCREGS src, + [[maybe_unused]] RegMask *lvrmask) +{ + ScopedTmpReg tmp_reg(GetEncoder(), ConvertDataType(DataType::UINT64, GetArch())); + GetEncoder()->EncodeLdr(tmp_reg, false, MemRef(src[0], cross_values::GetJshclassBitfieldOffset(GetArch()))); + GetEncoder()->EncodeShr(tmp_reg, tmp_reg, Imm(cross_values::GetJshclassBitfieldExtensibleStartBit(GetArch()))); + GetEncoder()->EncodeAnd(dst, tmp_reg, Imm(1U)); +} + +void Codegen::CreateDynObjectGetClass(IntrinsicInst *inst, Reg dst, SRCREGS src, [[maybe_unused]] RegMask *lvrmask) +{ + LoadClassFromObject(dst, src[0]); + GetEncoder()->EncodeSub(dst, dst, Imm(cross_values::GetJshclassHclassOffset(GetArch()))); + if (inst->GetSaveState() != nullptr) { + CreateStackMap(inst); + } +} + +void Codegen::CreateDynObjectSetClass(IntrinsicInst *inst, [[maybe_unused]] Reg dst, SRCREGS src, + [[maybe_unused]] RegMask *lvrmask) +{ + ScopedTmpReg tmp_reg(GetEncoder(), ConvertDataType(DataType::REFERENCE, GetArch())); + GetEncoder()->EncodeMov(tmp_reg, src[1]); + GetEncoder()->EncodeAdd(tmp_reg, tmp_reg, Imm(cross_values::GetJshclassHclassOffset(GetArch()))); + GetEncoder()->EncodeStr(tmp_reg, MemRef(src[0], GetRuntime()->GetObjClassOffset(GetArch()))); + if (inst->GetSaveState() != nullptr) { + CreateStackMap(inst); + } +} +} // namespace panda::compiler diff --git a/compiler/compiler_sources.gn b/compiler/compiler_sources.gn new file mode 100644 index 000000000..69b6e7e58 --- /dev/null +++ b/compiler/compiler_sources.gn @@ -0,0 +1,20 @@ +# Copyright (c) 2021-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. + +srcs = [ + "optimizer/code_generator/compiler_base_types.cpp", + "ecmascript_extensions/ecmascript_codegen_extensions.cpp", + "intrinsics_type_resolving_ecmascript.cpp", + "codegen_intrinsics_ecmascript.cpp", + "optimizer/ir_builder/ecmascript_inst_builder.cpp", +] diff --git a/compiler/ecmascript_extensions/CMakeLists.txt b/compiler/ecmascript_extensions/CMakeLists.txt new file mode 100644 index 000000000..59c86668a --- /dev/null +++ b/compiler/ecmascript_extensions/CMakeLists.txt @@ -0,0 +1,18 @@ +# Copyright (c) 2021-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. + +set(COMPILER_SOURCES ${PANDA_ECMASCRIPT_PLUGIN_SOURCE}/compiler/ecmascript_extensions/ecmascript_codegen_extensions.cpp) + +target_sources(arkcompiler PRIVATE ${COMPILER_SOURCES}) + +add_check_style(".") diff --git a/compiler/ecmascript_extensions/ecmascript_codegen_extensions.cpp b/compiler/ecmascript_extensions/ecmascript_codegen_extensions.cpp new file mode 100644 index 000000000..868daf01d --- /dev/null +++ b/compiler/ecmascript_extensions/ecmascript_codegen_extensions.cpp @@ -0,0 +1,83 @@ +/* + * Copyright (c) 2021-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 "compiler/optimizer/code_generator/codegen.h" +#include "ecmascript_environment.h" + +#ifdef PANDA_QEMU_AARCH64_GCC_8 +// Build PANDA_QEMU_AARCH64_GCC_8 hangs without this workaround. Please check issue #8094. +#include "plugins/ecmascript/runtime/js_thread.h" +namespace panda::ecmascript { +JSTaggedValue GetGlobalObject(JSThread *thread) +{ + return thread->GetGlobalObject(); +} +} // namespace panda::ecmascript +#endif + +namespace panda::compiler { + +bool Codegen::GenerateEcmascriptEnvInPrologue() +{ + if (GetRuntime()->IsEcmascriptRuntime()) { + auto enc = GetEncoder(); + + auto cp_src_offset = GetGraph()->GetRuntime()->GetConstantPoolOffset(); + auto le_src_offset = GetGraph()->GetRuntime()->GetLexicalEnvOffset(); + + auto ext_offset = GetLanguageExtensionOffsetFromSpInBytes(); + auto cp_dst_offset = ext_offset + panda::ecmascript::EcmascriptEnvironment::GetConstantPoolOffset(); + auto le_dst_offset = ext_offset + panda::ecmascript::EcmascriptEnvironment::GetLexicalEnvOffset(); + auto prev_env_offset = ext_offset + panda::ecmascript::EcmascriptEnvironment::GetPrevEnvironmentOffset(); + auto this_fnc_offset = ext_offset + panda::ecmascript::EcmascriptEnvironment::GetThisFuncOffset(); + + size_t js_func_reg_number = 2; + auto jsfunc_reg = GetTarget().GetParamReg(js_func_reg_number); + + ScopedTmpReg tmp_reg(enc); + enc->EncodeLdr(tmp_reg, false, MemRef(ThreadReg(), GetRuntime()->GetLanguageExtensionsDataOffset(GetArch()))); + + ScopedTmpReg offset_reg(enc); + enc->EncodeAdd(offset_reg, SpReg(), Imm(ext_offset)); + enc->EncodeStr(offset_reg, MemRef(ThreadReg(), GetRuntime()->GetLanguageExtensionsDataOffset(GetArch()))); + enc->EncodeMemCopy(MemRef(jsfunc_reg, cp_src_offset), MemRef(SpReg(), cp_dst_offset), jsfunc_reg.GetSize()); + enc->EncodeMemCopy(MemRef(jsfunc_reg, le_src_offset), MemRef(SpReg(), le_dst_offset), jsfunc_reg.GetSize()); + enc->EncodeStr(jsfunc_reg, MemRef(SpReg(), this_fnc_offset)); + enc->EncodeStr(tmp_reg, MemRef(SpReg(), prev_env_offset)); + return true; + } + return false; +} + +bool Codegen::GenerateEcmascriptEnvInEpilogue() +{ + if (!GetRuntime()->IsEcmascriptRuntime()) { + return false; + } + + ScopedTmpReg js_env_reg(GetEncoder()); + + auto enc = GetEncoder(); + enc->EncodeLdr(js_env_reg, false, MemRef(ThreadReg(), GetRuntime()->GetLanguageExtensionsDataOffset(GetArch()))); + + auto prev_env_offset = panda::ecmascript::EcmascriptEnvironment::GetPrevEnvironmentOffset(); + enc->EncodeMemCopy(MemRef(js_env_reg, prev_env_offset), + MemRef(ThreadReg(), GetRuntime()->GetLanguageExtensionsDataOffset(GetArch())), + js_env_reg.GetReg().GetSize()); + + return true; +} + +} // namespace panda::compiler diff --git a/compiler/ecmascript_extensions/ecmascript_codegen_extensions.h b/compiler/ecmascript_extensions/ecmascript_codegen_extensions.h new file mode 100644 index 000000000..43cacf59b --- /dev/null +++ b/compiler/ecmascript_extensions/ecmascript_codegen_extensions.h @@ -0,0 +1,22 @@ +/** + * Copyright (c) 2021-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 PANDA_COMPILER_ECMASCRIPT_CODEGEN_EXTENSIONS_H +#define PANDA_COMPILER_ECMASCRIPT_CODEGEN_EXTENSIONS_H + +bool GenerateEcmascriptEnvInPrologue(); +bool GenerateEcmascriptEnvInEpilogue(); + +#endif // PANDA_COMPILER_ECMASCRIPT_CODEGEN_EXTENSIONS_H \ No newline at end of file diff --git a/compiler/ecmascript_extensions/ecmascript_compiler_interface.h b/compiler/ecmascript_extensions/ecmascript_compiler_interface.h new file mode 100644 index 000000000..16bdae245 --- /dev/null +++ b/compiler/ecmascript_extensions/ecmascript_compiler_interface.h @@ -0,0 +1,34 @@ +/* + * Copyright (c) 2021-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 PANDA_COMPILER_ECMASCRIPT_COMPILER_INTERFACE_H +#define PANDA_COMPILER_ECMASCRIPT_COMPILER_INTERFACE_H + +virtual size_t GetConstantPoolOffset() +{ + return 0; +} + +virtual size_t GetLexicalEnvOffset() +{ + return 0; +} + +virtual bool IsEcmascriptRuntime() +{ + return false; +} + +#endif // PANDA_COMPILER_ECMASCRIPT_COMPILER_INTERFACE_H \ No newline at end of file diff --git a/compiler/ecmascript_extensions/ecmascript_environment.h b/compiler/ecmascript_extensions/ecmascript_environment.h new file mode 100644 index 000000000..ec2edf36e --- /dev/null +++ b/compiler/ecmascript_extensions/ecmascript_environment.h @@ -0,0 +1,122 @@ +/** + * Copyright (c) 2021-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 PANDA_COMPILER_ECMASCRIPT_ENVIRONMENT_H +#define PANDA_COMPILER_ECMASCRIPT_ENVIRONMENT_H + +#include +#include "runtime/include/object_header.h" +#include "runtime/mem/object_helpers.h" +#include "libpandabase/mem/mem.h" +#include "runtime/include/coretypes/tagged_value.h" + +namespace panda { + +class ObjectHeader; + +namespace ecmascript { + +template +class JSHandle; + +class EcmascriptEnvironment { +public: + EcmascriptEnvironment(ObjectHeader *cp, ObjectHeader *le, ObjectHeader *tf, EcmascriptEnvironment *prev) + : constant_pool_(cp), lexical_env_(le), this_func_(tf), prev_env_(prev) + { + } + + static EcmascriptEnvironment *Cast(void *data) + { + return static_cast(data); + } + + static constexpr size_t GetConstantPoolOffset() + { + return MEMBER_OFFSET(EcmascriptEnvironment, constant_pool_); + } + + static constexpr size_t GetLexicalEnvOffset() + { + return MEMBER_OFFSET(EcmascriptEnvironment, lexical_env_); + } + + static constexpr size_t GetPrevEnvironmentOffset() + { + return MEMBER_OFFSET(EcmascriptEnvironment, prev_env_); + } + + static constexpr size_t GetThisFuncOffset() + { + return MEMBER_OFFSET(EcmascriptEnvironment, this_func_); + } + + ObjectHeader *GetConstantPool() const + { + return constant_pool_.GetHeapObject(); + } + + ObjectHeader *GetLexicalEnv() const + { + return lexical_env_.GetHeapObject(); + } + + void SetLexicalEnv(ObjectHeader *le) + { + lexical_env_ = panda::coretypes::TaggedValue(le); + } + + void SetConstantPool(ObjectHeader *cp) + { + constant_pool_ = panda::coretypes::TaggedValue(cp); + } + + ObjectHeader *GetThisFunc() const + { + return this_func_.GetHeapObject(); + } + + void SetThisFunc(ObjectHeader *this_func) + { + this_func_ = panda::coretypes::TaggedValue(this_func); + } + + EcmascriptEnvironment *GetPrevEnvironment() const + { + return prev_env_; + } + + void SetPrevEnvironment(EcmascriptEnvironment *prev_env) + { + prev_env_ = prev_env; + } + + static size_t GetSize() + { + return AlignUp(sizeof(EcmascriptEnvironment), GetAlignmentInBytes(DEFAULT_FRAME_ALIGNMENT)); + } + +private: + panda::coretypes::TaggedValue constant_pool_; + panda::coretypes::TaggedValue lexical_env_; + panda::coretypes::TaggedValue this_func_; + EcmascriptEnvironment *prev_env_; +}; + +} // namespace ecmascript + +} // namespace panda + +#endif // PANDA_COMPILER_ECMASCRIPT_ENVIRONMENT_H diff --git a/compiler/ecmascript_extensions/thread_environment_api.h b/compiler/ecmascript_extensions/thread_environment_api.h new file mode 100644 index 000000000..84673c1b6 --- /dev/null +++ b/compiler/ecmascript_extensions/thread_environment_api.h @@ -0,0 +1,81 @@ +/** + * Copyright (c) 2021-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 PANDA_COMPILER_THREAD_ENVIRONMENT_API_H +#define PANDA_COMPILER_THREAD_ENVIRONMENT_API_H + +#include "ecmascript_environment.h" +#include "plugins/ecmascript/runtime/class_linker/program_object-inl.h" +#include "plugins/ecmascript/runtime/global_env.h" +#include "plugins/ecmascript/runtime/js_function.h" +#include "plugins/ecmascript/runtime/js_tagged_value-inl.h" +#include "plugins/ecmascript/runtime/js_thread.h" + +namespace panda::ecmascript { + +inline EcmascriptEnvironment *GetEcmascriptEnvironment(JSThread *thread) +{ + auto lang_ext = thread->GetLanguageExtensionsData(); + return EcmascriptEnvironment::Cast(lang_ext); +} + +inline ConstantPool *GetConstantPool(JSThread *thread) +{ + return ConstantPool::Cast(GetEcmascriptEnvironment(thread)->GetConstantPool()); +} + +inline LexicalEnv *GetLexicalEnv(JSThread *thread) +{ + return LexicalEnv::Cast(GetEcmascriptEnvironment(thread)->GetLexicalEnv()); +} + +inline void SetLexicalEnv(JSThread *thread, JSTaggedValue env) +{ + GetEcmascriptEnvironment(thread)->SetLexicalEnv(env.GetHeapObject()); +} + +inline JSTaggedValue GetThisFunc(JSThread *thread) +{ + return JSTaggedValue(GetEcmascriptEnvironment(thread)->GetThisFunc()); +} + +#ifdef PANDA_QEMU_AARCH64_GCC_8 +// Build PANDA_QEMU_AARCH64_GCC_8 hangs without this workaround. Please check issue #8094. +JSTaggedValue GetGlobalObject(JSThread *thread); +#else +inline JSTaggedValue GetGlobalObject(JSThread *thread) +{ + return thread->GetGlobalObject(); +} +#endif + +inline JSHandle GetGlobalEnv(JSThread *thread) +{ + return thread->GetEcmaVM()->GetGlobalEnv(); +} + +inline ObjectFactory *GetFactory(JSThread *thread) +{ + return thread->GetEcmaVM()->GetFactory(); +} + +inline JSThread *GetJSThread() +{ + return JSThread::Cast(ManagedThread::GetCurrent()); +} + +} // namespace panda::ecmascript + +#endif // PANDA_COMPILER_THREAD_ENVIRONMENT_API_H diff --git a/compiler/extensions.h b/compiler/extensions.h new file mode 100644 index 000000000..742da1df8 --- /dev/null +++ b/compiler/extensions.h @@ -0,0 +1,22 @@ +/** + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef PLUGINS_ECMASCRIPT_COMPILER_EXTENSIONS_H +#define PLUGINS_ECMASCRIPT_COMPILER_EXTENSIONS_H + +#include "optimizer/code_generator/compiler_base_types.h" +#include "optimizer/ir/dyn_datatype.h" + +#endif // PLUGINS_ECMASCRIPT_COMPILER_EXTENSIONS_H diff --git a/compiler/intrinsics_inline_ecmascript.inl b/compiler/intrinsics_inline_ecmascript.inl new file mode 100644 index 000000000..e65c094d0 --- /dev/null +++ b/compiler/intrinsics_inline_ecmascript.inl @@ -0,0 +1,99 @@ + /* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +case RuntimeInterface::IntrinsicId::INTRINSIC_ADD2_DYN: { + ASSERT(types_.size() == 2); + return panda::compiler::IrtocInlineAddDyn(intrinsic, types_[0], types_[1]); +} +case RuntimeInterface::IntrinsicId::INTRINSIC_INC_DYN: { + ASSERT(types_.size() == 1); + return panda::compiler::IrtocInlineIncDyn(intrinsic, types_[0]); +} +case RuntimeInterface::IntrinsicId::INTRINSIC_DEC_DYN: { + ASSERT(types_.size() == 1); + return panda::compiler::IrtocInlineDecDyn(intrinsic, types_[0]); +} +case RuntimeInterface::IntrinsicId::INTRINSIC_SUB2_DYN: { + ASSERT(types_.size() == 2); + return panda::compiler::IrtocInlineSubDyn(intrinsic, types_[0], types_[1]); +} +case RuntimeInterface::IntrinsicId::INTRINSIC_EQ_DYN: { + ASSERT(types_.size() == 2); + return panda::compiler::IrtocInlineCompareEqDyn(intrinsic, types_[0], types_[1]); +} +case RuntimeInterface::IntrinsicId::INTRINSIC_NOT_EQ_DYN: { + ASSERT(types_.size() == 2); + return panda::compiler::IrtocInlineCompareNeDyn(intrinsic, types_[0], types_[1]); +} +case RuntimeInterface::IntrinsicId::INTRINSIC_LESS_DYN: { + ASSERT(types_.size() == 2); + return panda::compiler::IrtocInlineCompareLtDyn(intrinsic, types_[0], types_[1]); +} +case RuntimeInterface::IntrinsicId::INTRINSIC_LESS_EQ_DYN: { + ASSERT(types_.size() == 2); + return panda::compiler::IrtocInlineCompareLeDyn(intrinsic, types_[0], types_[1]); +} +case RuntimeInterface::IntrinsicId::INTRINSIC_GREATER_DYN: { + ASSERT(types_.size() == 2); + return panda::compiler::IrtocInlineCompareGtDyn(intrinsic, types_[0], types_[1]); +} +case RuntimeInterface::IntrinsicId::INTRINSIC_GREATER_EQ_DYN: { + ASSERT(types_.size() == 2); + return panda::compiler::IrtocInlineCompareGeDyn(intrinsic, types_[0], types_[1]); +} +case RuntimeInterface::IntrinsicId::INTRINSIC_SHL2_DYN: { + ASSERT(types_.size() == 2); + return panda::compiler::IrtocInlineShlDyn(intrinsic, types_[0], types_[1]); +} +case RuntimeInterface::IntrinsicId::INTRINSIC_SHR2_DYN: { + ASSERT(types_.size() == 2); + return panda::compiler::IrtocInlineShrDyn(intrinsic, types_[0], types_[1]); +} +case RuntimeInterface::IntrinsicId::INTRINSIC_AND2_DYN: { + ASSERT(types_.size() == 2); + return panda::compiler::IrtocInlineAndDyn(intrinsic, types_[0], types_[1]); +} +case RuntimeInterface::IntrinsicId::INTRINSIC_OR2_DYN: { + ASSERT(types_.size() == 2); + return panda::compiler::IrtocInlineOrDyn(intrinsic, types_[0], types_[1]); +} +case RuntimeInterface::IntrinsicId::INTRINSIC_XOR2_DYN: { + ASSERT(types_.size() == 2); + return panda::compiler::IrtocInlineXorDyn(intrinsic, types_[0], types_[1]); +} +case RuntimeInterface::IntrinsicId::INTRINSIC_MUL2_DYN: { + ASSERT(types_.size() == 2); + return panda::compiler::IrtocInlineMulDyn(intrinsic, types_[0], types_[1]); +} +case RuntimeInterface::IntrinsicId::INTRINSIC_DIV2_DYN: { + ASSERT(types_.size() == 2); + return panda::compiler::IrtocInlineDivDyn(intrinsic, types_[0], types_[1]); +} +case RuntimeInterface::IntrinsicId::INTRINSIC_NOT_DYN: { + ASSERT(types_.size() == 1); + return panda::compiler::IrtocInlineNotDyn(intrinsic, types_[0]); +} +case RuntimeInterface::IntrinsicId::INTRINSIC_NEG_DYN: { + ASSERT(types_.size() == 1); + return panda::compiler::IrtocInlineNegDyn(intrinsic, types_[0]); +} +case RuntimeInterface::IntrinsicId::INTRINSIC_TOBOOLEAN: { + ASSERT(types_.size() == 1); + return panda::compiler::IrtocInlineToBooleanDyn(intrinsic, types_[0]); +} +case RuntimeInterface::IntrinsicId::INTRINSIC_TONUMBER: { + ASSERT(types_.size() == 1); + return InlineToNumberDyn(intrinsic, types_[0]); +} diff --git a/compiler/intrinsics_type_resolving_ecmascript.cpp b/compiler/intrinsics_type_resolving_ecmascript.cpp new file mode 100644 index 000000000..ac119fda9 --- /dev/null +++ b/compiler/intrinsics_type_resolving_ecmascript.cpp @@ -0,0 +1,504 @@ +/* + * Copyright (c) 2021-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 "compiler/optimizer/optimizations/types_resolving.h" + +namespace panda::compiler { +template +static Inst *InsertOverflowInst(Inst *input1, Inst *input2, IntrinsicInst *intrinsics) +{ + auto current_block = intrinsics->GetBasicBlock(); + auto pc = intrinsics->GetPc(); + // Split current block + current_block->SplitBlockAfterInstruction(intrinsics, true); + auto graph = current_block->GetGraph(); + + // Create and insert Add/SubOverflow instruction + Inst *inst = nullptr; + // NOLINTNEXTLINE(readability-magic-numbers,readability-braces-around-statements) + if constexpr (opcode == Opcode::AddOverflow) { + inst = graph->CreateInstAddOverflow(DataType::INT32, pc); + inst->CastToAddOverflow()->SetCc(ConditionCode::CC_NE); + inst->CastToAddOverflow()->SetOperandsType(DataType::INT32); + // NOLINTNEXTLINE(readability-magic-numbers,readability-misleading-indentation) + } else { + // NOLINTNEXTLINE(readability-magic-numbers,readability-braces-around-statements) + static_assert(opcode == Opcode::SubOverflow); + inst = graph->CreateInstSubOverflow(DataType::INT32, pc); + inst->CastToSubOverflow()->SetCc(ConditionCode::CC_NE); + inst->CastToSubOverflow()->SetOperandsType(DataType::INT32); + } + inst->SetInput(0, input1); + inst->SetInput(1, input2); + current_block->AppendInst(inst); + + // Create block for deoptimization + auto deopt_block = graph->CreateEmptyBlock(current_block); + current_block->AddSucc(deopt_block); + // Create and insert Deoptimize inst + auto deopt = graph->CreateInstDeoptimize(DataType::NO_TYPE, pc); + deopt->SetDeoptimizeType(DeoptimizeType::ANY_TYPE_CHECK); + deopt->SetInput(0, intrinsics->GetSaveState()); + deopt_block->AppendInst(deopt); + + auto end_block = graph->HasEndBlock() ? graph->GetEndBlock() : graph->CreateEndBlock(); + deopt_block->AddSucc(end_block); + + return inst; +} + +static Inst *InsertAnyCastInst(Inst *input, IntrinsicInst *intrinsics, AnyBaseType type) +{ + auto current_block = intrinsics->GetBasicBlock(); + auto graph = current_block->GetGraph(); + auto cast_to_value_inst = graph->CreateInstCastAnyTypeValue(AnyBaseTypeToDataType(type), intrinsics->GetPc()); + cast_to_value_inst->SetAnyType(type); + cast_to_value_inst->SetInput(0, input); + current_block->InsertBefore(cast_to_value_inst, intrinsics); + return cast_to_value_inst; +} + +bool TypesResolving::InlineIncDyn(IntrinsicInst *intrinsics, AnyBaseType type) +{ + ASSERT(type != AnyBaseType::UNDEFINED_TYPE); + if (type != AnyBaseType::ECMASCRIPT_INT_TYPE && type != AnyBaseType::ECMASCRIPT_DOUBLE_TYPE) { + return false; + } + auto pc = intrinsics->GetPc(); + auto current_block = intrinsics->GetBasicBlock(); + // Create and insert CastAnyTypeValueType inst + auto cast_to_value_inst = InsertAnyCastInst(intrinsics->GetInput(0).GetInst(), intrinsics, type); + + // Create and insert CastValueToAnyType inst + auto *cast_to_any_inst = GetGraph()->CreateInstCastValueToAnyType(DataType::ANY, pc); + cast_to_any_inst->SetAnyType(type); + current_block->InsertAfter(cast_to_any_inst, intrinsics); + Inst *add = nullptr; + if (type == AnyBaseType::ECMASCRIPT_INT_TYPE) { + auto constant = GetGraph()->FindOrCreateConstant(1); + add = InsertOverflowInst(cast_to_value_inst, constant, intrinsics); + } else { + ASSERT(type == AnyBaseType::ECMASCRIPT_DOUBLE_TYPE); + auto constant = GetGraph()->FindOrCreateConstant(1.0); + + // Create and insert Add instruction + add = GetGraph()->CreateInstAdd(DataType::FLOAT64, pc); + add->SetInput(0, cast_to_value_inst); + add->SetInput(1, constant); + current_block->InsertBefore(add, intrinsics); + } + cast_to_any_inst->SetInput(0, add); + intrinsics->ReplaceUsers(cast_to_any_inst); + intrinsics->SetInlined(false); + intrinsics->ClearFlag(compiler::inst_flags::NO_DCE); + return true; +} + +bool TypesResolving::InlineAddDyn(IntrinsicInst *intrinsics, AnyBaseType type1, AnyBaseType type2) +{ + ASSERT(type1 != AnyBaseType::UNDEFINED_TYPE); + ASSERT(type2 != AnyBaseType::UNDEFINED_TYPE); + if (type1 != AnyBaseType::ECMASCRIPT_INT_TYPE && type1 != AnyBaseType::ECMASCRIPT_DOUBLE_TYPE) { + return false; + } + if (type2 != AnyBaseType::ECMASCRIPT_INT_TYPE && type2 != AnyBaseType::ECMASCRIPT_DOUBLE_TYPE) { + return false; + } + auto pc = intrinsics->GetPc(); + auto current_block = intrinsics->GetBasicBlock(); + // Create and insert CastAnyTypeValueType inst + auto cast_to_value_inst1 = InsertAnyCastInst(intrinsics->GetInput(0).GetInst(), intrinsics, type1); + auto cast_to_value_inst2 = InsertAnyCastInst(intrinsics->GetInput(1).GetInst(), intrinsics, type2); + + // Create and insert CastValueToAnyType inst + auto *cast_to_any_inst = GetGraph()->CreateInstCastValueToAnyType(DataType::ANY, pc); + current_block->InsertAfter(cast_to_any_inst, intrinsics); + Inst *add = nullptr; + AnyBaseType type = AnyBaseType::ECMASCRIPT_INT_TYPE; + if (type1 == AnyBaseType::ECMASCRIPT_INT_TYPE && type2 == AnyBaseType::ECMASCRIPT_INT_TYPE) { + add = InsertOverflowInst(cast_to_value_inst1, cast_to_value_inst2, intrinsics); + } else { + Inst *input0 = cast_to_value_inst1; + Inst *input1 = cast_to_value_inst2; + if (type1 == AnyBaseType::ECMASCRIPT_INT_TYPE) { + auto cast = GetGraph()->CreateInstCast(DataType::FLOAT64, pc); + cast->SetInput(0, input0); + input0 = cast; + cast->SetOperandsType(DataType::INT32); + current_block->InsertBefore(cast, intrinsics); + } + if (type2 == AnyBaseType::ECMASCRIPT_INT_TYPE) { + auto cast = GetGraph()->CreateInstCast(DataType::FLOAT64, pc); + cast->SetInput(0, input1); + input1 = cast; + cast->SetOperandsType(DataType::INT32); + current_block->InsertBefore(cast, intrinsics); + } + // Create and insert Add instruction + add = GetGraph()->CreateInstAdd(DataType::FLOAT64, pc); + add->SetInput(0, input0); + add->SetInput(1, input1); + current_block->InsertBefore(add, intrinsics); + type = AnyBaseType::ECMASCRIPT_DOUBLE_TYPE; + } + cast_to_any_inst->SetAnyType(type); + cast_to_any_inst->SetInput(0, add); + intrinsics->ReplaceUsers(cast_to_any_inst); + intrinsics->SetInlined(false); + intrinsics->ClearFlag(compiler::inst_flags::NO_DCE); + return true; +} + +bool TypesResolving::InlineDecDyn(IntrinsicInst *intrinsics, AnyBaseType type) +{ + ASSERT(type != AnyBaseType::UNDEFINED_TYPE); + if (type != AnyBaseType::ECMASCRIPT_INT_TYPE && type != AnyBaseType::ECMASCRIPT_DOUBLE_TYPE) { + return false; + } + auto pc = intrinsics->GetPc(); + auto current_block = intrinsics->GetBasicBlock(); + // Create and insert CastAnyTypeValueType inst + auto cast_to_value_inst = InsertAnyCastInst(intrinsics->GetInput(0).GetInst(), intrinsics, type); + + // Create and insert CastValueToAnyType inst + auto *cast_to_any_inst = GetGraph()->CreateInstCastValueToAnyType(DataType::ANY, pc); + cast_to_any_inst->SetAnyType(type); + current_block->InsertAfter(cast_to_any_inst, intrinsics); + Inst *sub = nullptr; + if (type == AnyBaseType::ECMASCRIPT_INT_TYPE) { + auto constant = GetGraph()->FindOrCreateConstant(1); + sub = InsertOverflowInst(cast_to_value_inst, constant, intrinsics); + } else { + ASSERT(type == AnyBaseType::ECMASCRIPT_DOUBLE_TYPE); + auto constant = GetGraph()->FindOrCreateConstant(1.0); + + // Create and insert Sub instruction + sub = GetGraph()->CreateInstSub(DataType::FLOAT64, pc); + sub->SetInput(0, cast_to_value_inst); + sub->SetInput(1, constant); + current_block->InsertBefore(sub, intrinsics); + } + cast_to_any_inst->SetInput(0, sub); + intrinsics->ReplaceUsers(cast_to_any_inst); + intrinsics->SetInlined(false); + intrinsics->ClearFlag(compiler::inst_flags::NO_DCE); + return true; +} + +bool TypesResolving::InlineSubDyn(IntrinsicInst *intrinsics, AnyBaseType type1, AnyBaseType type2) +{ + ASSERT(type1 != AnyBaseType::UNDEFINED_TYPE); + ASSERT(type2 != AnyBaseType::UNDEFINED_TYPE); + if (type1 != AnyBaseType::ECMASCRIPT_INT_TYPE && type1 != AnyBaseType::ECMASCRIPT_DOUBLE_TYPE) { + return false; + } + if (type2 != AnyBaseType::ECMASCRIPT_INT_TYPE && type2 != AnyBaseType::ECMASCRIPT_DOUBLE_TYPE) { + return false; + } + auto pc = intrinsics->GetPc(); + auto current_block = intrinsics->GetBasicBlock(); + // Create and insert CastAnyTypeValueType inst + auto cast_to_value_inst1 = InsertAnyCastInst(intrinsics->GetInput(0).GetInst(), intrinsics, type1); + auto cast_to_value_inst2 = InsertAnyCastInst(intrinsics->GetInput(1).GetInst(), intrinsics, type2); + + // Create and insert CastValueToAnyType inst + auto *cast_to_any_inst = GetGraph()->CreateInstCastValueToAnyType(DataType::ANY, pc); + current_block->InsertAfter(cast_to_any_inst, intrinsics); + Inst *sub = nullptr; + AnyBaseType type = AnyBaseType::ECMASCRIPT_INT_TYPE; + if (type1 == AnyBaseType::ECMASCRIPT_INT_TYPE && type2 == AnyBaseType::ECMASCRIPT_INT_TYPE) { + sub = InsertOverflowInst(cast_to_value_inst1, cast_to_value_inst2, intrinsics); + } else { + Inst *input0 = cast_to_value_inst1; + Inst *input1 = cast_to_value_inst2; + if (type1 == AnyBaseType::ECMASCRIPT_INT_TYPE) { + auto cast = GetGraph()->CreateInstCast(DataType::FLOAT64, pc); + cast->SetInput(0, input0); + input0 = cast; + cast->SetOperandsType(DataType::INT32); + current_block->InsertBefore(cast, intrinsics); + } + if (type2 == AnyBaseType::ECMASCRIPT_INT_TYPE) { + auto cast = GetGraph()->CreateInstCast(DataType::FLOAT64, pc); + cast->SetInput(0, input1); + input1 = cast; + cast->SetOperandsType(DataType::INT32); + current_block->InsertBefore(cast, intrinsics); + } + // Create and insert Sub instruction + sub = GetGraph()->CreateInstSub(DataType::FLOAT64, pc); + sub->SetInput(0, input0); + sub->SetInput(1, input1); + current_block->InsertBefore(sub, intrinsics); + type = AnyBaseType::ECMASCRIPT_DOUBLE_TYPE; + } + cast_to_any_inst->SetAnyType(type); + cast_to_any_inst->SetInput(0, sub); + intrinsics->ReplaceUsers(cast_to_any_inst); + intrinsics->SetInlined(false); + intrinsics->ClearFlag(compiler::inst_flags::NO_DCE); + return true; +} + +bool TypesResolving::InlineMulDyn(IntrinsicInst *intrinsics, AnyBaseType type1, AnyBaseType type2) +{ + ASSERT(type1 != AnyBaseType::UNDEFINED_TYPE); + ASSERT(type2 != AnyBaseType::UNDEFINED_TYPE); + if (type1 != AnyBaseType::ECMASCRIPT_DOUBLE_TYPE || type2 != AnyBaseType::ECMASCRIPT_DOUBLE_TYPE) { + return false; + } + auto pc = intrinsics->GetPc(); + auto current_block = intrinsics->GetBasicBlock(); + // Create and insert CastAnyTypeValueType inst + auto cast_to_value_inst1 = InsertAnyCastInst(intrinsics->GetInput(0).GetInst(), intrinsics, type1); + auto cast_to_value_inst2 = InsertAnyCastInst(intrinsics->GetInput(1).GetInst(), intrinsics, type2); + + // Create and insert CastValueToAnyType inst + auto *cast_to_any_inst = GetGraph()->CreateInstCastValueToAnyType(DataType::ANY, pc); + current_block->InsertAfter(cast_to_any_inst, intrinsics); + // Create and insert Mul instruction + Inst *add = GetGraph()->CreateInstMul(DataType::FLOAT64, pc); + add->SetInput(0, cast_to_value_inst1); + add->SetInput(1, cast_to_value_inst2); + current_block->InsertBefore(add, intrinsics); + cast_to_any_inst->SetAnyType(AnyBaseType::ECMASCRIPT_DOUBLE_TYPE); + cast_to_any_inst->SetInput(0, add); + intrinsics->ReplaceUsers(cast_to_any_inst); + intrinsics->SetInlined(false); + intrinsics->ClearFlag(compiler::inst_flags::NO_DCE); + return true; +} + +bool TypesResolving::InlineAndDyn(IntrinsicInst *intrinsics, AnyBaseType type1, AnyBaseType type2) +{ + ASSERT(type1 != AnyBaseType::UNDEFINED_TYPE); + ASSERT(type2 != AnyBaseType::UNDEFINED_TYPE); + if (type1 != AnyBaseType::ECMASCRIPT_INT_TYPE || type2 != AnyBaseType::ECMASCRIPT_INT_TYPE) { + return false; + } + auto pc = intrinsics->GetPc(); + auto current_block = intrinsics->GetBasicBlock(); + // Create and insert CastAnyTypeValueType inst + auto cast_to_value_inst1 = InsertAnyCastInst(intrinsics->GetInput(0).GetInst(), intrinsics, type1); + auto cast_to_value_inst2 = InsertAnyCastInst(intrinsics->GetInput(1).GetInst(), intrinsics, type2); + + // Create and insert CastValueToAnyType inst + auto *cast_to_any_inst = GetGraph()->CreateInstCastValueToAnyType(DataType::ANY, pc); + current_block->InsertAfter(cast_to_any_inst, intrinsics); + auto and_inst = GetGraph()->CreateInstAnd(DataType::INT32, pc); + and_inst->SetInput(0, cast_to_value_inst1); + and_inst->SetInput(1, cast_to_value_inst2); + current_block->InsertBefore(and_inst, intrinsics); + + cast_to_any_inst->SetAnyType(AnyBaseType::ECMASCRIPT_INT_TYPE); + cast_to_any_inst->SetInput(0, and_inst); + intrinsics->ReplaceUsers(cast_to_any_inst); + intrinsics->SetInlined(false); + intrinsics->ClearFlag(compiler::inst_flags::NO_DCE); + return true; +} + +bool TypesResolving::InlineShlDyn(IntrinsicInst *intrinsics, AnyBaseType type1, AnyBaseType type2) +{ + ASSERT(type1 != AnyBaseType::UNDEFINED_TYPE); + ASSERT(type2 != AnyBaseType::UNDEFINED_TYPE); + if (type1 != AnyBaseType::ECMASCRIPT_INT_TYPE || type2 != AnyBaseType::ECMASCRIPT_INT_TYPE) { + return false; + } + auto pc = intrinsics->GetPc(); + auto current_block = intrinsics->GetBasicBlock(); + // Create and insert CastAnyTypeValueType inst + auto cast_to_value_inst1 = InsertAnyCastInst(intrinsics->GetInput(0).GetInst(), intrinsics, type1); + auto cast_to_value_inst2 = InsertAnyCastInst(intrinsics->GetInput(1).GetInst(), intrinsics, type2); + + // Create and insert CastValueToAnyType inst + auto *cast_to_any_inst = GetGraph()->CreateInstCastValueToAnyType(DataType::ANY, pc); + current_block->InsertAfter(cast_to_any_inst, intrinsics); + auto shl_inst = GetGraph()->CreateInstShl(DataType::INT32, pc); + shl_inst->SetInput(0, cast_to_value_inst1); + shl_inst->SetInput(1, cast_to_value_inst2); + current_block->InsertBefore(shl_inst, intrinsics); + + cast_to_any_inst->SetAnyType(AnyBaseType::ECMASCRIPT_INT_TYPE); + cast_to_any_inst->SetInput(0, shl_inst); + intrinsics->ReplaceUsers(cast_to_any_inst); + intrinsics->SetInlined(false); + intrinsics->ClearFlag(compiler::inst_flags::NO_DCE); + return true; +} + +bool TypesResolving::InlineShrDyn(IntrinsicInst *intrinsics, AnyBaseType type1, AnyBaseType type2) +{ + ASSERT(type1 != AnyBaseType::UNDEFINED_TYPE); + ASSERT(type2 != AnyBaseType::UNDEFINED_TYPE); + if (type1 != AnyBaseType::ECMASCRIPT_INT_TYPE || type2 != AnyBaseType::ECMASCRIPT_INT_TYPE) { + return false; + } + auto pc = intrinsics->GetPc(); + auto current_block = intrinsics->GetBasicBlock(); + // Create and insert CastAnyTypeValueType inst + auto cast_to_value_inst1 = InsertAnyCastInst(intrinsics->GetInput(0).GetInst(), intrinsics, type1); + auto cast_to_value_inst2 = InsertAnyCastInst(intrinsics->GetInput(1).GetInst(), intrinsics, type2); + + // Create and insert CastValueToAnyType inst + auto *cast_to_any_inst = GetGraph()->CreateInstCastValueToAnyType(DataType::ANY, pc); + current_block->InsertAfter(cast_to_any_inst, intrinsics); + auto shr_inst = GetGraph()->CreateInstShr(DataType::INT32, pc); + shr_inst->SetInput(0, cast_to_value_inst1); + shr_inst->SetInput(1, cast_to_value_inst2); + current_block->InsertBefore(shr_inst, intrinsics); + + cast_to_any_inst->SetAnyType(AnyBaseType::ECMASCRIPT_INT_TYPE); + cast_to_any_inst->SetInput(0, shr_inst); + intrinsics->ReplaceUsers(cast_to_any_inst); + intrinsics->SetInlined(false); + intrinsics->ClearFlag(compiler::inst_flags::NO_DCE); + return true; +} + +bool TypesResolving::InlineCompareDyn(IntrinsicInst *intrinsics, AnyBaseType type1, AnyBaseType type2, ConditionCode cc) +{ + ASSERT(type1 != AnyBaseType::UNDEFINED_TYPE); + ASSERT(type2 != AnyBaseType::UNDEFINED_TYPE); + if (type1 != AnyBaseType::ECMASCRIPT_INT_TYPE || type2 != AnyBaseType::ECMASCRIPT_INT_TYPE) { + return false; + } + auto pc = intrinsics->GetPc(); + auto current_block = intrinsics->GetBasicBlock(); + // Create and insert CastAnyTypeValueType inst + auto cast_to_value_inst1 = InsertAnyCastInst(intrinsics->GetInput(0).GetInst(), intrinsics, type1); + auto cast_to_value_inst2 = InsertAnyCastInst(intrinsics->GetInput(1).GetInst(), intrinsics, type2); + + // Create and insert CastValueToAnyType inst + auto *cast_to_any_inst = GetGraph()->CreateInstCastValueToAnyType(DataType::ANY, pc); + current_block->InsertAfter(cast_to_any_inst, intrinsics); + auto less_inst = GetGraph()->CreateInstCompare(DataType::BOOL, pc); + less_inst->SetInput(0, cast_to_value_inst1); + less_inst->SetInput(1, cast_to_value_inst2); + less_inst->SetCc(cc); + less_inst->SetOperandsType(DataType::INT32); + current_block->InsertBefore(less_inst, intrinsics); + + cast_to_any_inst->SetAnyType(AnyBaseType::ECMASCRIPT_BOOLEAN_TYPE); + cast_to_any_inst->SetInput(0, less_inst); + intrinsics->ReplaceUsers(cast_to_any_inst); + intrinsics->SetInlined(false); + intrinsics->ClearFlag(compiler::inst_flags::NO_DCE); + return true; +} + +bool TypesResolving::InlineNotDyn(IntrinsicInst *intrinsics, AnyBaseType type) +{ + ASSERT(type != AnyBaseType::UNDEFINED_TYPE); + if (type != AnyBaseType::ECMASCRIPT_INT_TYPE) { + return false; + } + auto pc = intrinsics->GetPc(); + auto current_block = intrinsics->GetBasicBlock(); + // Create and insert CastAnyTypeValueType inst + auto cast_to_value_inst = InsertAnyCastInst(intrinsics->GetInput(0).GetInst(), intrinsics, type); + + // Create and insert CastValueToAnyType inst + auto *cast_to_any_inst = GetGraph()->CreateInstCastValueToAnyType(DataType::ANY, pc); + current_block->InsertAfter(cast_to_any_inst, intrinsics); + auto not_inst = GetGraph()->CreateInstNot(DataType::INT32, pc); + not_inst->SetInput(0, cast_to_value_inst); + current_block->InsertBefore(not_inst, intrinsics); + + cast_to_any_inst->SetAnyType(AnyBaseType::ECMASCRIPT_INT_TYPE); + cast_to_any_inst->SetInput(0, not_inst); + intrinsics->ReplaceUsers(cast_to_any_inst); + intrinsics->SetInlined(false); + intrinsics->ClearFlag(compiler::inst_flags::NO_DCE); + return true; +} + +bool TypesResolving::InlineNegDyn(IntrinsicInst *intrinsics, AnyBaseType type) +{ + ASSERT(type != AnyBaseType::UNDEFINED_TYPE); + if (type != AnyBaseType::ECMASCRIPT_INT_TYPE && type != AnyBaseType::ECMASCRIPT_DOUBLE_TYPE) { + return false; + } + auto pc = intrinsics->GetPc(); + auto current_block = intrinsics->GetBasicBlock(); + // Create and insert CastAnyTypeValueType inst + auto cast_to_value_inst = InsertAnyCastInst(intrinsics->GetInput(0).GetInst(), intrinsics, type); + + Inst *input = cast_to_value_inst; + if (type == AnyBaseType::ECMASCRIPT_INT_TYPE) { + auto cast = GetGraph()->CreateInstCast(DataType::FLOAT64, pc); + cast->SetInput(0, input); + input = cast; + cast->SetOperandsType(DataType::INT32); + current_block->InsertBefore(cast, intrinsics); + } + + // Create and insert CastValueToAnyType inst + auto *cast_to_any_inst = GetGraph()->CreateInstCastValueToAnyType(DataType::ANY, pc); + current_block->InsertAfter(cast_to_any_inst, intrinsics); + auto not_inst = GetGraph()->CreateInstNeg(DataType::FLOAT64, pc); + not_inst->SetInput(0, input); + current_block->InsertBefore(not_inst, intrinsics); + + cast_to_any_inst->SetAnyType(AnyBaseType::ECMASCRIPT_DOUBLE_TYPE); + cast_to_any_inst->SetInput(0, not_inst); + intrinsics->ReplaceUsers(cast_to_any_inst); + intrinsics->SetInlined(false); + intrinsics->ClearFlag(compiler::inst_flags::NO_DCE); + return true; +} + +bool TypesResolving::InlineToBooleanDyn(IntrinsicInst *intrinsics, AnyBaseType type) +{ + ASSERT(type != AnyBaseType::UNDEFINED_TYPE); + if (type != AnyBaseType::ECMASCRIPT_INT_TYPE) { + return false; + } + auto pc = intrinsics->GetPc(); + auto current_block = intrinsics->GetBasicBlock(); + // Create and insert CastAnyTypeValueType inst + auto cast_to_value_inst = InsertAnyCastInst(intrinsics->GetInput(0).GetInst(), intrinsics, type); + + // Create and insert CastValueToAnyType inst + auto *cast_to_any_inst = GetGraph()->CreateInstCastValueToAnyType(DataType::ANY, pc); + auto *const_inst = GetGraph()->FindOrCreateConstant(0); + current_block->InsertAfter(cast_to_any_inst, intrinsics); + auto less_inst = GetGraph()->CreateInstCompare(DataType::BOOL, pc); + less_inst->SetInput(0, cast_to_value_inst); + less_inst->SetInput(1, const_inst); + less_inst->SetCc(CC_NE); + less_inst->SetOperandsType(DataType::INT32); + current_block->InsertBefore(less_inst, intrinsics); + + cast_to_any_inst->SetAnyType(AnyBaseType::ECMASCRIPT_BOOLEAN_TYPE); + cast_to_any_inst->SetInput(0, less_inst); + intrinsics->ReplaceUsers(cast_to_any_inst); + intrinsics->SetInlined(false); + intrinsics->ClearFlag(compiler::inst_flags::NO_DCE); + return true; +} + +bool TypesResolving::InlineToNumberDyn(IntrinsicInst *intrinsics, AnyBaseType type) +{ + ASSERT(type != AnyBaseType::UNDEFINED_TYPE); + if (type != AnyBaseType::ECMASCRIPT_INT_TYPE && type != AnyBaseType::ECMASCRIPT_DOUBLE_TYPE) { + return false; + } + intrinsics->ReplaceUsers(intrinsics->GetInput(0).GetInst()); + intrinsics->SetInlined(false); + intrinsics->ClearFlag(compiler::inst_flags::NO_DCE); + return true; +} +} // namespace panda::compiler diff --git a/compiler/intrinsics_type_resolving_ecmascript.inl.h b/compiler/intrinsics_type_resolving_ecmascript.inl.h new file mode 100644 index 000000000..229928698 --- /dev/null +++ b/compiler/intrinsics_type_resolving_ecmascript.inl.h @@ -0,0 +1,33 @@ +/* + * Copyright (c) 2021-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 PLUGINS_ECMASCRIPT_COMPILER_INTRINSICS_TYPE_RESOLVING_ECMASCRIPT_INL_H +#define PLUGINS_ECMASCRIPT_COMPILER_INTRINSICS_TYPE_RESOLVING_ECMASCRIPT_INL_H + +bool InlineIncDyn(IntrinsicInst *intrinsics, AnyBaseType type); +bool InlineAddDyn(IntrinsicInst *intrinsics, AnyBaseType type1, AnyBaseType type2); +bool InlineDecDyn(IntrinsicInst *intrinsics, AnyBaseType type); +bool InlineSubDyn(IntrinsicInst *intrinsics, AnyBaseType type1, AnyBaseType type2); +bool InlineAndDyn(IntrinsicInst *intrinsics, AnyBaseType type1, AnyBaseType type2); +bool InlineMulDyn(IntrinsicInst *intrinsics, AnyBaseType type1, AnyBaseType type2); +bool InlineCompareDyn(IntrinsicInst *intrinsics, AnyBaseType type1, AnyBaseType type2, ConditionCode cc); +bool InlineShlDyn(IntrinsicInst *intrinsics, AnyBaseType type1, AnyBaseType type2); +bool InlineShrDyn(IntrinsicInst *intrinsics, AnyBaseType type1, AnyBaseType type2); +bool InlineNotDyn(IntrinsicInst *intrinsics, AnyBaseType type); +bool InlineNegDyn(IntrinsicInst *intrinsics, AnyBaseType type); +bool InlineToBooleanDyn(IntrinsicInst *intrinsics, AnyBaseType type); +bool InlineToNumberDyn(IntrinsicInst *intrinsics, AnyBaseType type); + +#endif // PLUGINS_ECMASCRIPT_COMPILER_INTRINSICS_TYPE_RESOLVING_ECMASCRIPT_INL_H \ No newline at end of file diff --git a/compiler/optimizer/code_generator/compiler_base_types.cpp b/compiler/optimizer/code_generator/compiler_base_types.cpp new file mode 100644 index 000000000..7c2fc4deb --- /dev/null +++ b/compiler/optimizer/code_generator/compiler_base_types.cpp @@ -0,0 +1,390 @@ +/** + * Copyright (c) 2021-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 "compiler_base_types.h" + +#include "compiler/optimizer/ir/datatype.h" +#include "compiler/optimizer/code_generator/codegen.h" +#include "ir-dyn-base-types.h" +#include "runtime/include/coretypes/tagged_value.h" + +namespace panda::compiler { + +static void CompareAnyTypeGenDouble(Encoder *enc, Reg dst, Reg src, + LabelHolder::LabelId id = LabelHolder::INVALID_LABEL) +{ + static constexpr uint16_t CMP_VAL = static_cast(UINT16_MAX) >> 1U; // 0x7fff + static constexpr uint8_t SHIFT_VAL = coretypes::TaggedValue::TAG_BITS_SHIFT + 1; + + auto dst_ext = dst.As(TypeInfo(TypeInfo::TypeId::INT64)); + + // (u16) tag + 0xffff is [0xffff for Object, 0xfffe for Int, other - Double] + enc->EncodeAdd(dst_ext, src, Imm(panda::coretypes::TaggedValue::TAG_MASK)); + // Examine tag and shift it by one -> 0x7fff for both Object and Int + enc->EncodeShr(dst_ext, dst_ext, Imm(SHIFT_VAL)); + ScopedTmpReg reg_cmp(enc, TypeInfo(TypeInfo::TypeId::INT64)); + enc->EncodeMov(reg_cmp, Imm(CMP_VAL)); + // check if not Object and not Int + if (id == LabelHolder::INVALID_LABEL) { + enc->EncodeCompare(dst, dst_ext, reg_cmp, Condition::LO); + } else { + enc->EncodeJump(id, dst_ext, reg_cmp, Condition::GE); + } +} + +static void CompareAnyTypeGenBool(Encoder *enc, Reg dst, Reg src, LabelHolder::LabelId id = LabelHolder::INVALID_LABEL) +{ + static constexpr uint64_t BOOL_DIFF_MASK = + ~(panda::coretypes::TaggedValue::VALUE_FALSE ^ panda::coretypes::TaggedValue::VALUE_TRUE); + static constexpr uint64_t BOOL_MASK = + panda::coretypes::TaggedValue::VALUE_FALSE & panda::coretypes::TaggedValue::VALUE_TRUE; + + ScopedTmpReg reg_cmp(enc, TypeInfo(TypeInfo::TypeId::INT64)); + auto dst_ext = dst.As(TypeInfo(TypeInfo::TypeId::INT64)); + + enc->EncodeAnd(dst_ext, src, Imm(BOOL_DIFF_MASK)); + enc->EncodeMov(reg_cmp, Imm(BOOL_MASK)); + if (id == LabelHolder::INVALID_LABEL) { + enc->EncodeCompare(dst, dst_ext, reg_cmp, Condition::EQ); + } else { + enc->EncodeJump(id, dst_ext, reg_cmp, Condition::NE); + } +} + +static void IsHasTagMaskGen(Encoder *enc, Reg dst, Reg src, Imm val_tag, Imm val_mask, + LabelHolder::LabelId id = LabelHolder::INVALID_LABEL) +{ + ScopedTmpReg reg_val_tag(enc, TypeInfo(TypeInfo::TypeId::INT64)); + auto dst_ext = dst.As(TypeInfo(TypeInfo::TypeId::INT64)); + + enc->EncodeMov(reg_val_tag, val_tag); + if (val_tag != val_mask) { + enc->EncodeAnd(dst_ext, src, val_mask); + } else { + enc->EncodeAnd(dst_ext, src, reg_val_tag); + } + if (id == LabelHolder::INVALID_LABEL) { + enc->EncodeCompare(dst, dst_ext, reg_val_tag, Condition::EQ); + } else { + enc->EncodeJump(id, dst_ext, reg_val_tag, Condition::NE); + } +} + +static void IsEqualToValGen(Encoder *enc, Reg dst, Reg src, Imm val, + LabelHolder::LabelId id = LabelHolder::INVALID_LABEL) +{ + ScopedTmpReg reg_val(enc, TypeInfo(TypeInfo::TypeId::INT64)); + enc->EncodeMov(reg_val, val); + if (id == LabelHolder::INVALID_LABEL) { + enc->EncodeCompare(dst, src, reg_val, Condition::EQ); + } else { + enc->EncodeJump(id, src, reg_val, Condition::NE); + } +} + +static void CompareAnyTypeGenObject(Encoder *enc, const Reg &src, LabelHolder::LabelId label) +{ + ScopedTmpReg tmp_reg(enc, TypeInfo(TypeInfo::TypeId::INT64)); + + // IsObject() + enc->EncodeAnd(tmp_reg, src, Imm(panda::coretypes::TaggedValue::TAG_MASK)); + enc->EncodeJump(label, tmp_reg, Imm(panda::coretypes::TaggedValue::TAG_OBJECT), Condition::NE); + + // !IsHole() + enc->EncodeJump(label, src, Imm(panda::coretypes::TaggedValue::VALUE_HOLE), Condition::EQ); + + // !IsSpecial + // It's enough to check that `src` > `TAG_SPECIAL_MASK`, as it's guaranteed that special values + // are not in object address space + ASSERT(!IsInObjectsAddressSpace(panda::coretypes::TaggedValue::TAG_SPECIAL_MASK)); + enc->EncodeJump(label, src, Imm(panda::coretypes::TaggedValue::TAG_SPECIAL_MASK), Condition::LE); +} + +static void CompareAnyTypeGenObject(Encoder *enc, const Reg &dst, const Reg &src) +{ + auto label = enc->CreateLabel(); + enc->EncodeMov(dst, Imm(0)); + CompareAnyTypeGenObject(enc, src, label); + enc->EncodeMov(dst, Imm(1U)); + enc->BindLabel(label); +} + +static void CompareAnyTypeGenObjectType(Codegen *codegen, Encoder *enc, const Reg &dst, const Reg &src, uint32_t type, + Condition fail_cc = Condition::NE) +{ + auto label = enc->CreateLabel(); + enc->EncodeMov(dst, Imm(0)); + CompareAnyTypeGenObject(enc, src, label); + + ScopedTmpReg tmp_reg(enc, codegen->ConvertDataType(DataType::REFERENCE, enc->GetArch())); + + auto arch = codegen->GetArch(); + enc->EncodeLdr(tmp_reg, false, MemRef(src, codegen->GetRuntime()->GetObjClassOffset(arch))); + enc->EncodeLdr(tmp_reg, false, MemRef(tmp_reg, cross_values::GetHclassDataOffset(arch))); + enc->EncodeAnd(tmp_reg, tmp_reg, Imm(static_cast(cross_values::GetJshclassBitfieldTypeMask(arch)))); + auto type_start_bit = cross_values::GetJshclassBitfieldTypeStartBit(arch); + if (type_start_bit != 0) { + enc->EncodeShr(tmp_reg, tmp_reg, Imm(type_start_bit)); + } + enc->EncodeJump(label, tmp_reg, Imm(type), fail_cc); + + enc->EncodeMov(dst, Imm(1U)); + enc->BindLabel(label); +} + +bool ecmascript::CompareAnyTypeGen(const CompareAnyTypeInst *cati, EncodeVisitor *enc_v) +{ + auto *codegen = enc_v->GetCodegen(); + auto dst = enc_v->GetCodegen()->ConvertRegister(cati->GetDstReg(), DataType::Type::BOOL); + auto src = enc_v->GetCodegen()->ConvertRegister(cati->GetSrcReg(0), DataType::Type::INT64); + + Encoder *enc = enc_v->GetEncoder(); + switch (cati->GetAnyType()) { + case AnyBaseType::ECMASCRIPT_UNDEFINED_TYPE: + IsEqualToValGen(enc, dst, src, Imm(panda::coretypes::TaggedValue::VALUE_UNDEFINED)); + return true; + case AnyBaseType::ECMASCRIPT_INT_TYPE: + IsHasTagMaskGen(enc, dst, src, Imm(panda::coretypes::TaggedValue::TAG_INT), + Imm(panda::coretypes::TaggedValue::TAG_MASK)); + return true; + case AnyBaseType::ECMASCRIPT_OBJECT_TYPE: + IsHasTagMaskGen(enc, dst, src, Imm(panda::coretypes::TaggedValue::TAG_OBJECT), + Imm(panda::coretypes::TaggedValue::TAG_MASK)); + return true; + case AnyBaseType::ECMASCRIPT_HEAP_OBJECT_TYPE: + CompareAnyTypeGenObject(enc, dst, src); + return true; + case AnyBaseType::ECMASCRIPT_DOUBLE_TYPE: + CompareAnyTypeGenDouble(enc, dst, src); + return true; + case AnyBaseType::ECMASCRIPT_STRING_TYPE: + CompareAnyTypeGenObjectType(codegen, enc, dst, src, cross_values::GetJstypeString(codegen->GetArch())); + return true; + case AnyBaseType::ECMASCRIPT_ARRAY_TYPE: + CompareAnyTypeGenObjectType(codegen, enc, dst, src, cross_values::GetJstypeJsArray(codegen->GetArch())); + return true; + case AnyBaseType::ECMASCRIPT_TRANSITION_HANDLER_TYPE: + CompareAnyTypeGenObjectType(codegen, enc, dst, src, + cross_values::GetJstypeTransitionHandler(codegen->GetArch())); + return true; + case AnyBaseType::ECMASCRIPT_PROTOTYPE_HANDLER_TYPE: + CompareAnyTypeGenObjectType(codegen, enc, dst, src, + cross_values::GetJstypePrototypeHandler(codegen->GetArch())); + return true; + case AnyBaseType::ECMASCRIPT_SPECIAL_INDEXED_TYPE: + CompareAnyTypeGenObjectType(codegen, enc, dst, src, cross_values::GetJstypeJsArray(codegen->GetArch()), + Condition::LE); + return true; + case AnyBaseType::ECMASCRIPT_BOOLEAN_TYPE: { + CompareAnyTypeGenBool(enc, dst, src); + return true; + } + case AnyBaseType::ECMASCRIPT_NULL_TYPE: + IsEqualToValGen(enc, dst, src, Imm(panda::coretypes::TaggedValue::VALUE_NULL)); + return true; + case AnyBaseType::ECMASCRIPT_HOLE_TYPE: + IsEqualToValGen(enc, dst, src, Imm(panda::coretypes::TaggedValue::VALUE_HOLE)); + return true; + default: + return false; + } + + return false; +} + +bool ecmascript::CastAnyTypeValueGen(const CastAnyTypeValueInst *cati, EncodeVisitor *enc_v) +{ + auto dst_reg_type = cati->GetType(); + dst_reg_type = DataType::IsFloatType(dst_reg_type) ? DataType::FLOAT64 : dst_reg_type; + auto dst = enc_v->GetCodegen()->ConvertRegister(cati->GetDstReg(), dst_reg_type); + auto src = enc_v->GetCodegen()->ConvertRegister(cati->GetSrcReg(0), DataType::INT64); + + auto *enc = enc_v->GetEncoder(); + switch (cati->GetAnyType()) { + case AnyBaseType::ECMASCRIPT_NULL_TYPE: + case AnyBaseType::ECMASCRIPT_HOLE_TYPE: + case AnyBaseType::ECMASCRIPT_UNDEFINED_TYPE: + enc->EncodeMov(dst, Imm(0)); + return true; + case AnyBaseType::ECMASCRIPT_BOOLEAN_TYPE: + enc->EncodeAnd(dst, src, Imm(1)); + return true; + case AnyBaseType::ECMASCRIPT_INT_TYPE: { + auto src_reg_type = dst_reg_type; + if (DataType::GetTypeSize(dst_reg_type, RUNTIME_ARCH) > + DataType::GetTypeSize(DataType::INT32, RUNTIME_ARCH)) { + ASSERT(false && "sign-extension by dst type is not supported"); + src_reg_type = DataType::INT32; + } + src = enc_v->GetCodegen()->ConvertRegister(cati->GetSrcReg(0), src_reg_type); + if (dst.GetId() == src.GetId() && !dst.IsFloat() && !src.IsFloat()) { + enc->EncodeCast(dst, false, src, false); + } else { + enc->EncodeMov(dst, src); + } + return true; + } + case AnyBaseType::ECMASCRIPT_DOUBLE_TYPE: { + ScopedTmpReg reg_tmp(enc, TypeInfo(TypeInfo::TypeId::INT64)); + enc->EncodeSub(reg_tmp, src, Imm(panda::coretypes::TaggedValue::DOUBLE_ENCODE_OFFSET)); + enc->EncodeMov(dst, reg_tmp); + return true; + } + case AnyBaseType::ECMASCRIPT_OBJECT_TYPE: + case AnyBaseType::ECMASCRIPT_HEAP_OBJECT_TYPE: + case AnyBaseType::ECMASCRIPT_STRING_TYPE: + case AnyBaseType::ECMASCRIPT_ARRAY_TYPE: + case AnyBaseType::ECMASCRIPT_TRANSITION_HANDLER_TYPE: + case AnyBaseType::ECMASCRIPT_PROTOTYPE_HANDLER_TYPE: + case AnyBaseType::ECMASCRIPT_SPECIAL_INDEXED_TYPE: { + enc->EncodeMov(dst, src); + return true; + } + default: + return false; + } + + return false; +} + +bool ecmascript::CastValueToAnyTypeGen(const CastValueToAnyTypeInst *cvai, EncodeVisitor *enc_v) +{ + auto src_reg_type = cvai->GetInputType(0); + src_reg_type = DataType::IsFloatType(src_reg_type) ? DataType::FLOAT64 : src_reg_type; + auto dst = enc_v->GetCodegen()->ConvertRegister(cvai->GetDstReg(), DataType::INT64); + auto src = enc_v->GetCodegen()->ConvertRegister(cvai->GetSrcReg(0), src_reg_type); + + auto *enc = enc_v->GetEncoder(); + switch (cvai->GetAnyType()) { + case AnyBaseType::ECMASCRIPT_NULL_TYPE: + enc->EncodeMov(dst, Imm(panda::coretypes::TaggedValue::VALUE_NULL)); + return true; + case AnyBaseType::ECMASCRIPT_HOLE_TYPE: + enc->EncodeMov(dst, Imm(panda::coretypes::TaggedValue::VALUE_HOLE)); + return true; + case AnyBaseType::ECMASCRIPT_UNDEFINED_TYPE: + enc->EncodeMov(dst, Imm(panda::coretypes::TaggedValue::VALUE_UNDEFINED)); + return true; + case AnyBaseType::ECMASCRIPT_BOOLEAN_TYPE: + enc->EncodeOr(dst, src, Imm(panda::coretypes::TaggedValue::VALUE_FALSE)); + return true; + case AnyBaseType::ECMASCRIPT_INT_TYPE: + if (dst.GetId() != src.GetId()) { + if (!cvai->GetInput(0).GetInst()->IsConst()) { + ASSERT(src_reg_type == DataType::INT32); + } +#ifndef NDEBUG + enc->EncodeMov(dst, src); + constexpr uint32_t INT32_SIZE = 32U; + enc->EncodeShr(dst, dst, Imm(INT32_SIZE)); + auto ok_label = enc->CreateLabel(); + enc->EncodeJump(ok_label, dst, Imm(0), Condition::EQ); + enc->EncodeAbort(); + enc->BindLabel(ok_label); +#endif + enc->EncodeMov(dst, src); + } + enc->EncodeOr(dst, dst, Imm(panda::coretypes::TaggedValue::TAG_INT)); + return true; + case AnyBaseType::ECMASCRIPT_DOUBLE_TYPE: + enc->EncodeMov(dst, src); + enc->EncodeAdd(dst, dst, Imm(panda::coretypes::TaggedValue::DOUBLE_ENCODE_OFFSET)); + return true; + case AnyBaseType::ECMASCRIPT_STRING_TYPE: + case AnyBaseType::ECMASCRIPT_OBJECT_TYPE: + case AnyBaseType::ECMASCRIPT_HEAP_OBJECT_TYPE: + case AnyBaseType::ECMASCRIPT_ARRAY_TYPE: + case AnyBaseType::ECMASCRIPT_TRANSITION_HANDLER_TYPE: + case AnyBaseType::ECMASCRIPT_PROTOTYPE_HANDLER_TYPE: + case AnyBaseType::ECMASCRIPT_SPECIAL_INDEXED_TYPE: + // With current boxing scheme, we must guarantee that boxed objects have upper 16 bits set to 0. + // Boxed values are no less than 64 bits wide (to hold double-precision floating point numbers). + // As long as pointers are no more than 32 bits wide, a move from a 32-bit source to a 64-bit + // destination will zero out upper 32 bits of the destination. Otherwise we fallback to the safe + // path where we explicitly zero-out upper 16 bits by applying a mask. + if (src.GetSize() <= WORD_SIZE) { + ASSERT(dst.GetSize() >= DOUBLE_WORD_SIZE); + enc->EncodeMov(dst, src); + } else { + enc->EncodeAnd(dst, src, Imm(panda::coretypes::TaggedValue::OBJECT_MASK)); + } + return true; + default: + return false; + } + + return false; +} + +bool ecmascript::AnyTypeCheckGen(const AnyTypeCheckInst *check_inst, EncodeVisitor *enc_v, LabelHolder::LabelId id) +{ + auto src = enc_v->GetCodegen()->ConvertRegister(check_inst->GetSrcReg(0), DataType::Type::INT64); + Encoder *enc = enc_v->GetEncoder(); + Codegen *codegen = enc_v->GetCodegen(); + ScopedTmpReg tmp_reg(enc, TypeInfo(TypeInfo::TypeId::INT64)); + switch (check_inst->GetAnyType()) { + case AnyBaseType::ECMASCRIPT_UNDEFINED_TYPE: + IsEqualToValGen(enc, tmp_reg, src, Imm(panda::coretypes::TaggedValue::VALUE_UNDEFINED), id); + return true; + case AnyBaseType::ECMASCRIPT_INT_TYPE: + IsHasTagMaskGen(enc, tmp_reg, src, Imm(panda::coretypes::TaggedValue::TAG_INT), + Imm(panda::coretypes::TaggedValue::TAG_MASK), id); + return true; + case AnyBaseType::ECMASCRIPT_OBJECT_TYPE: + IsHasTagMaskGen(enc, tmp_reg, src, Imm(panda::coretypes::TaggedValue::TAG_OBJECT), + Imm(panda::coretypes::TaggedValue::TAG_MASK), id); + return true; + case AnyBaseType::ECMASCRIPT_HEAP_OBJECT_TYPE: + CompareAnyTypeGenObject(enc, tmp_reg, src); + return true; + case AnyBaseType::ECMASCRIPT_DOUBLE_TYPE: + CompareAnyTypeGenDouble(enc, tmp_reg, src, id); + return true; + case AnyBaseType::ECMASCRIPT_STRING_TYPE: + CompareAnyTypeGenObjectType(codegen, enc, tmp_reg, src, cross_values::GetJstypeString(codegen->GetArch())); + return true; + case AnyBaseType::ECMASCRIPT_ARRAY_TYPE: + CompareAnyTypeGenObjectType(codegen, enc, tmp_reg, src, cross_values::GetJstypeJsArray(codegen->GetArch())); + return true; + case AnyBaseType::ECMASCRIPT_TRANSITION_HANDLER_TYPE: + CompareAnyTypeGenObjectType(codegen, enc, tmp_reg, src, + cross_values::GetJstypeTransitionHandler(codegen->GetArch())); + return true; + case AnyBaseType::ECMASCRIPT_PROTOTYPE_HANDLER_TYPE: + CompareAnyTypeGenObjectType(codegen, enc, tmp_reg, src, + cross_values::GetJstypePrototypeHandler(codegen->GetArch())); + return true; + case AnyBaseType::ECMASCRIPT_SPECIAL_INDEXED_TYPE: + CompareAnyTypeGenObjectType(codegen, enc, tmp_reg, src, cross_values::GetJstypeJsArray(codegen->GetArch()), + Condition::LE); + return true; + case AnyBaseType::ECMASCRIPT_BOOLEAN_TYPE: { + CompareAnyTypeGenBool(enc, tmp_reg, src, id); + return true; + } + case AnyBaseType::ECMASCRIPT_NULL_TYPE: + IsEqualToValGen(enc, tmp_reg, src, Imm(panda::coretypes::TaggedValue::VALUE_NULL), id); + return true; + case AnyBaseType::ECMASCRIPT_HOLE_TYPE: + IsEqualToValGen(enc, tmp_reg, src, Imm(panda::coretypes::TaggedValue::VALUE_HOLE), id); + return true; + default: + return false; + } + + return false; +} + +} // namespace panda::compiler diff --git a/compiler/optimizer/code_generator/compiler_base_types.h b/compiler/optimizer/code_generator/compiler_base_types.h new file mode 100644 index 000000000..47d21c069 --- /dev/null +++ b/compiler/optimizer/code_generator/compiler_base_types.h @@ -0,0 +1,38 @@ +/** + * Copyright (c) 2021-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 PLUGINS_ECMASCRIPT_COMPILER_OPTIMIZER_CODE_GENERATOR_COMPILER_BASE_TYPES_H +#define PLUGINS_ECMASCRIPT_COMPILER_OPTIMIZER_CODE_GENERATOR_COMPILER_BASE_TYPES_H +#include "compiler/optimizer/code_generator/encode.h" + +namespace panda::compiler { + +class CompareAnyTypeInst; +class CastAnyTypeValueInst; +class CastValueToAnyTypeInst; +class AnyTypeCheckInst; +class EncodeVisitor; + +namespace ecmascript { + +bool CompareAnyTypeGen(const CompareAnyTypeInst *cati, EncodeVisitor *enc_v); +bool CastAnyTypeValueGen(const CastAnyTypeValueInst *cati, EncodeVisitor *enc_v); +bool CastValueToAnyTypeGen(const CastValueToAnyTypeInst *cvai, EncodeVisitor *enc_v); +bool AnyTypeCheckGen(const AnyTypeCheckInst *check_inst, EncodeVisitor *enc_v, LabelHolder::LabelId id); + +} // namespace ecmascript +} // namespace panda::compiler + +#endif // PLUGINS_ECMASCRIPT_COMPILER_OPTIMIZER_CODE_GENERATOR_COMPILER_BASE_TYPES_H diff --git a/compiler/optimizer/ir/dyn_datatype.h b/compiler/optimizer/ir/dyn_datatype.h new file mode 100644 index 000000000..efc7767a6 --- /dev/null +++ b/compiler/optimizer/ir/dyn_datatype.h @@ -0,0 +1,122 @@ +/* + * Copyright (c) 2021-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 PLUGINS_ECMASCRIPT_COMPILER_OPTIMIZER_IR_DYN_DATATYPE_H +#define PLUGINS_ECMASCRIPT_COMPILER_OPTIMIZER_IR_DYN_DATATYPE_H + +#include +#include "compiler/optimizer/ir/datatype.h" +#include "ir-dyn-base-types.h" + +namespace panda::compiler { +namespace ecmascript { +static inline panda::compiler::AnyBaseType NumericDataTypeToAnyType(panda::compiler::DataType::Type type) +{ + switch (type) { + case panda::compiler::DataType::Type::INT8: + case panda::compiler::DataType::Type::UINT8: + case panda::compiler::DataType::Type::INT16: + case panda::compiler::DataType::Type::UINT16: + case panda::compiler::DataType::Type::INT32: + return panda::compiler::AnyBaseType::ECMASCRIPT_INT_TYPE; + case panda::compiler::DataType::Type::UINT32: + case panda::compiler::DataType::Type::INT64: + case panda::compiler::DataType::Type::UINT64: + case panda::compiler::DataType::Type::FLOAT32: + // There is no direct method of boxing these types. Cast to f64 is an + // option, but for now we miss a mechanism to signal that the cast + // is needed. TODO(asoldatov): Implement when this becomes an issue. + return panda::compiler::AnyBaseType::UNDEFINED_TYPE; + case panda::compiler::DataType::Type::FLOAT64: + return panda::compiler::AnyBaseType::ECMASCRIPT_DOUBLE_TYPE; + default: + UNREACHABLE(); + return panda::compiler::AnyBaseType::UNDEFINED_TYPE; + } +} + +static inline panda::compiler::AnyBaseType GetAnyStringType() +{ + return panda::compiler::AnyBaseType::ECMASCRIPT_STRING_TYPE; +} + +static inline std::optional IsAnyTypeCanBeSubtypeOf(panda::compiler::AnyBaseType super_type, + panda::compiler::AnyBaseType type) +{ + if (super_type == type) { + return true; + } + + switch (super_type) { + case panda::compiler::AnyBaseType::ECMASCRIPT_OBJECT_TYPE: + switch (type) { + case panda::compiler::AnyBaseType::ECMASCRIPT_HEAP_OBJECT_TYPE: + case panda::compiler::AnyBaseType::ECMASCRIPT_STRING_TYPE: + case panda::compiler::AnyBaseType::ECMASCRIPT_ARRAY_TYPE: + case panda::compiler::AnyBaseType::ECMASCRIPT_TRANSITION_HANDLER_TYPE: + case panda::compiler::AnyBaseType::ECMASCRIPT_PROTOTYPE_HANDLER_TYPE: + case panda::compiler::AnyBaseType::ECMASCRIPT_SPECIAL_INDEXED_TYPE: + return true; + default: + break; + } + break; + case panda::compiler::AnyBaseType::ECMASCRIPT_HEAP_OBJECT_TYPE: + switch (type) { + case panda::compiler::AnyBaseType::ECMASCRIPT_STRING_TYPE: + case panda::compiler::AnyBaseType::ECMASCRIPT_ARRAY_TYPE: + case panda::compiler::AnyBaseType::ECMASCRIPT_TRANSITION_HANDLER_TYPE: + case panda::compiler::AnyBaseType::ECMASCRIPT_PROTOTYPE_HANDLER_TYPE: + case panda::compiler::AnyBaseType::ECMASCRIPT_SPECIAL_INDEXED_TYPE: + return true; + default: + break; + } + break; + case panda::compiler::AnyBaseType::ECMASCRIPT_STRING_TYPE: + case panda::compiler::AnyBaseType::ECMASCRIPT_ARRAY_TYPE: + case panda::compiler::AnyBaseType::ECMASCRIPT_TRANSITION_HANDLER_TYPE: + case panda::compiler::AnyBaseType::ECMASCRIPT_PROTOTYPE_HANDLER_TYPE: + switch (type) { + case panda::compiler::AnyBaseType::ECMASCRIPT_OBJECT_TYPE: + case panda::compiler::AnyBaseType::ECMASCRIPT_HEAP_OBJECT_TYPE: + return std::nullopt; + default: + break; + } + break; + case panda::compiler::AnyBaseType::ECMASCRIPT_SPECIAL_INDEXED_TYPE: + switch (type) { + case panda::compiler::AnyBaseType::ECMASCRIPT_OBJECT_TYPE: + case panda::compiler::AnyBaseType::ECMASCRIPT_HEAP_OBJECT_TYPE: + return std::nullopt; + case panda::compiler::AnyBaseType::ECMASCRIPT_STRING_TYPE: + case panda::compiler::AnyBaseType::ECMASCRIPT_TRANSITION_HANDLER_TYPE: + case panda::compiler::AnyBaseType::ECMASCRIPT_PROTOTYPE_HANDLER_TYPE: + return true; + default: + break; + } + break; + default: + break; + } + + return false; +} +} // namespace ecmascript +} // namespace panda::compiler + +#endif // PLUGINS_ECMASCRIPT_COMPILER_OPTIMIZER_IR_DYN_DATATYPE_H diff --git a/compiler/optimizer/ir_builder/ecmascript_inst_builder.cpp b/compiler/optimizer/ir_builder/ecmascript_inst_builder.cpp new file mode 100644 index 000000000..a7f78f200 --- /dev/null +++ b/compiler/optimizer/ir_builder/ecmascript_inst_builder.cpp @@ -0,0 +1,16 @@ +/* + * Copyright (c) 2021-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_inst_builder_gen.cpp" diff --git a/compiler/optimizer/ir_builder/ecmascript_inst_builder.h b/compiler/optimizer/ir_builder/ecmascript_inst_builder.h new file mode 100644 index 000000000..d4a6ed0ed --- /dev/null +++ b/compiler/optimizer/ir_builder/ecmascript_inst_builder.h @@ -0,0 +1,24 @@ +/* + * Copyright (c) 2021-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 PLUGINS_ECMASCRIPT_COMPILER_OPTIMIZER_IR_BUILDER_ECMASCRIPT_INST_BUILDER_H +#define PLUGINS_ECMASCRIPT_COMPILER_OPTIMIZER_IR_BUILDER_ECMASCRIPT_INST_BUILDER_H + +void BuildEcma([[maybe_unused]] const BytecodeInstruction *bc_inst); +template +void BuildEcmaAsIntrinsics([[maybe_unused]] const BytecodeInstruction *bc_inst); +void BuildEcmaFromIrtoc([[maybe_unused]] const BytecodeInstruction *bc_inst); + +#endif // PLUGINS_ECMASCRIPT_COMPILER_OPTIMIZER_IR_BUILDER_ECMASCRIPT_INST_BUILDER_H diff --git a/compiler/optimizer/ir_builder/ecmascript_inst_templates.yaml b/compiler/optimizer/ir_builder/ecmascript_inst_templates.yaml new file mode 100644 index 000000000..c928fd906 --- /dev/null +++ b/compiler/optimizer/ir_builder/ecmascript_inst_templates.yaml @@ -0,0 +1,37 @@ + ecma: |- + % name = inst.opcode.upcase.split('_')[1] + % case name + % when "RETURNUNDEFINED" + auto cvat_input = FindOrCreateConstant(0); + cvat_input->SetType(DataType::Type::INT64); + auto cvat = graph_->CreateInstCastValueToAnyType(0); + cvat->SetAnyType(panda::compiler::AnyBaseType::ECMASCRIPT_UNDEFINED_TYPE); + cvat->SetInput(0, cvat_input); + auto inst = graph_->CreateInstReturn(DataType::ANY, GetPc(instruction->GetAddress())); + inst->SetInput(0, cvat); + AddInstruction(cvat); + AddInstruction(inst); + % when "RETURN" + auto inst = graph_->CreateInstReturn(DataType::ANY, GetPc(instruction->GetAddress())); + inst->SetInput(0, GetDefinitionAcc()); + AddInstruction(inst); + % when "JFALSE", "JTRUE" + % cmp_imm = name == "JFALSE" ? 0 : 1 + auto cvat_input = FindOrCreateConstant(<%= cmp_imm %>); + cvat_input->SetType(DataType::INT64); + auto cvat = graph_->CreateInstCastValueToAnyType(0); + cvat->SetAnyType(panda::compiler::AnyBaseType::ECMASCRIPT_BOOLEAN_TYPE); + cvat->SetInput(0, cvat_input); + auto cmp_inst = graph_->CreateInstCompare(DataType::BOOL, GetPc(instruction->GetAddress()), ConditionCode::CC_EQ); + cmp_inst->SetOperandsType(DataType::ANY); + cmp_inst->SetInput(0, GetDefinitionAcc()); + cmp_inst->SetInput(1, cvat); + auto jmp_inst = graph_->CreateInstIfImm(DataType::NO_TYPE, GetPc(instruction->GetAddress()), ConditionCode::CC_NE, 0); + jmp_inst->SetOperandsType(DataType::BOOL); + jmp_inst->SetInput(0, cmp_inst); + AddInstruction(cvat); + AddInstruction(cmp_inst); + AddInstruction(jmp_inst); + % else + BuildEcma(instruction); + % end diff --git a/compiler/templates/ecmascript_inst_builder_gen.cpp.erb b/compiler/templates/ecmascript_inst_builder_gen.cpp.erb new file mode 100644 index 000000000..3a70992e2 --- /dev/null +++ b/compiler/templates/ecmascript_inst_builder_gen.cpp.erb @@ -0,0 +1,303 @@ +/** + * Copyright (c) 2021-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 "compiler_logger.h" +#include "optimizer/ir_builder/inst_builder.h" +#include "optimizer/ir_builder/ir_builder.h" +#include "optimizer/ir/inst.h" +#include "bytecode_instruction.h" +#include "bytecode_instruction-inl.h" +#include "include/coretypes/tagged_value.h" +#include "irtoc_builder.cpp" + +namespace panda::compiler { + +// Currently we support two strategies for building IR from ecma.* instructions: +// 1) Each ecma.* instruction is translated to a corresponding intrinsic call. +// This is used for bytecode optimizer and slow paths during compiling +// to native code (in both JIT and AOT modes). +// 2) Semantics of each ecma.* instruction is taken from the corresponding +// IRtoC handler and is inlined into compiled function. +// This is used only for native compilation (again, both JIT and AOT). +// InstBuilder::BuildEcma selects proper strategy and calls relevant builder. + +// NOLINTNEXTLINE(readability-function-size) +void InstBuilder::BuildEcma([[maybe_unused]] const BytecodeInstruction* bc_inst) +{ +#ifdef ENABLE_BYTECODE_OPT + switch (bc_inst->GetOpcode()) { +% Panda::instructions.select{|b| b.namespace == "ecmascript"}.each do |inst| + case BytecodeInstruction::Opcode::<%= inst.opcode.upcase %>: { +% if inst.compilable? && inst.inlinable? + // +compilable, +inlinable: ecma.* -> intrinsics for BCO, inline IRtoC otherwise: + if (GetGraph()->IsBytecodeOptimizer()) { + BuildEcmaAsIntrinsics(bc_inst); + } else { + if (options.IsCompilerInlineFullIntrinsics()) { + BuildEcmaFromIrtoc(bc_inst); + } else { + BuildEcmaAsIntrinsics(bc_inst); + } + } +% elsif inst.compilable? + // +compilable, -inlinable: ecma.* -> intrinsics for all scenarios: + BuildEcmaAsIntrinsics(bc_inst); +% else +% abort "isa.yaml inconsistency: #{inst.opcode.upcase} is not compilable, but inlinable" if inst.inlinable? + // -compilable, -inlinable: ecma.* -> intrinsics for BCO, fail IR builder otherwise: + if (GetGraph()->IsBytecodeOptimizer()) { + BuildEcmaAsIntrinsics(bc_inst); + } else { + failed_ = true; + } +% end + break; + } +% end + default: { + failed_ = true; + LOG(ERROR, COMPILER) << "Unknown ecma.* opcode: " << static_cast(bc_inst->GetOpcode()); + return; + } + } +#endif +} + +template +void InstBuilder::BuildEcmaAsIntrinsics(const BytecodeInstruction* bc_inst) // NOLINT(readability-function-size) +{ + switch (bc_inst->GetOpcode()) { +% Panda::instructions.select{|b| b.namespace == "ecmascript"}.each do |inst| +% opc = inst.opcode.upcase +% name = opc.split('_')[1] +% acc_read = inst.acc.include?("in") +% acc_write = inst.acc.include?("out") +% ret_type = acc_write ? "compiler::DataType::ANY" : "compiler::DataType::VOID" +% iname = inst.intrinsic_name ? inst.intrinsic_name : opc + case BytecodeInstruction::Opcode::<%= opc %>: { + auto intrinsic_id = <%= "compiler::RuntimeInterface::IntrinsicId::" + iname %>; + auto inst = GetGraph()->CreateInstIntrinsic(<%= ret_type %>, GetPc(bc_inst->GetAddress()), intrinsic_id); +% if inst.throwing? + inst->SetFlag(inst_flags::CAN_THROW); +% end +% if inst.exceptions.include?('x_throw') || inst.properties.include?('return') + inst->SetFlag(inst_flags::CF); + inst->SetFlag(inst_flags::TERMINATOR); +% end +% params_arr = inst.operands +% format = "BytecodeInstruction::Format::" + inst.format.pretty.upcase +% range_should_exclude_last = name == "NEWOBJDYNRANGE" || name == "SUPERCALL" || name == "CREATEOBJECTWITHEXCLUDEDKEYS" +% is_range_call = name == "CALLIRANGEDYN" || name == "CALLITHISRANGEDYN" || range_should_exclude_last +% need_newtarget = name == "SUPERCALLSPREAD" || name == "SUPERCALL" +% num_vregs = params_arr.select{|b| b.reg?}.length +% num_imms = params_arr.select{|b| b.imm?}.length +% num_ids = params_arr.select{|b| b.id?}.length +% num_inputs = acc_read ? num_vregs + 2 : num_vregs + 1 +% use_ic = inst.properties.include? 'use_ic' +% if range_should_exclude_last +% num_inputs = num_inputs - 1 +% end +% if need_newtarget +% num_inputs = num_inputs + 1 +% end +% if is_range_call + size_t args_count = <%= num_inputs %>U + static_cast(bc_inst->GetImm<<%= format %>, 0>()); +% else + size_t args_count {<%= num_inputs %>U}; +% end +% if need_newtarget + if (GetGraph()->IsBytecodeOptimizer()) { + --args_count; + } +% end +% if use_ic +% num_inputs = num_inputs + 1 + if (!GetGraph()->IsBytecodeOptimizer()) { + args_count++; // Instruction supports IC: +1 input for IC slot index + } +% end + inst->ReserveInputs(args_count); + inst->AllocateInputTypes(GetGraph()->GetAllocator(), args_count); + // NOLINTNEXTLINE(readability-braces-around-statements, bugprone-suspicious-semicolon) + if constexpr (with_speculative) { + inst->SetInlined(true); + } + +% imm_index = 0 +% vreg_index = 0 +% id16_index = 0 +% id32_index = 0 + auto inst_save_state = CreateSaveState(Opcode::SaveState, GetPc(bc_inst->GetAddress())); + AddInstruction(inst_save_state); +% params_arr.each do |param| +% if param.imm? + auto imm<%= imm_index %> = static_cast(bc_inst->GetImm<<%= format %>, <%= imm_index %>>()); + inst->AddImm(GetGraph()->GetAllocator(), imm<%= imm_index %>); +% imm_index = imm_index + 1 +% elsif param.reg? + { + auto input = GetDefinition(bc_inst->GetVReg<<%= format %>, <%= vreg_index %>>()); + // NOLINTNEXTLINE(readability-braces-around-statements, bugprone-suspicious-semicolon) + if constexpr (with_speculative) { + input = BuildAnyTypeCheckInst(GetPc(bc_inst->GetAddress()), input, inst_save_state); + } + inst->AppendInput(input); + inst->AddInputType(DataType::ANY); + } +% vreg_index = vreg_index + 1 +% elsif param.id? +% if inst.properties.include?("method_id") + auto m_idx<%= id16_index %> = bc_inst->template GetId<<%= format %>>().AsRawValue(); + if (GetGraph()->IsBytecodeOptimizer()) { + m_idx<%= id16_index %> = GetRuntime()->ResolveMethodIndex(GetGraph()->GetMethod(), m_idx<%= id16_index %>); + } + inst->AddImm(GetGraph()->GetAllocator(), m_idx<%= id16_index %>); +% id16_index = id16_index + 1 +% elsif inst.properties.include?("string_id") + auto string_id<%= id32_index %> = bc_inst->template GetId<<%= format %>>().AsFileId().GetOffset(); + inst->AddImm(GetGraph()->GetAllocator(), string_id<%= id32_index %>); +% id32_index = id32_index + 1 +% end +% end +% end +% if need_newtarget + if (!GetGraph()->IsBytecodeOptimizer()) { + auto input = GetDefinition(GetGraph()->GetRuntime()->GetMethodRegistersCount(GetMethod()) + 1); + // NOLINTNEXTLINE(readability-braces-around-statements, bugprone-suspicious-semicolon) + if constexpr (with_speculative) { + input = BuildAnyTypeCheckInst(GetPc(bc_inst->GetAddress()), input, inst_save_state); + } + inst->AppendInput(input); + inst->AddInputType(DataType::ANY); + } +% end +% if is_range_call +% range_reg_idx = name == "CREATEOBJECTWITHEXCLUDEDKEYS" ? 1 : 0 +% num_actual_vregs = range_should_exclude_last ? "imm0" : "imm0 + 1" + size_t start_reg = bc_inst->GetVReg<<%= format %>, <%= range_reg_idx %>>(); + for (uint32_t i = 1; i < <%= num_actual_vregs %>; ++i) { + auto input = GetDefinition(start_reg + i); + // NOLINTNEXTLINE(readability-braces-around-statements, bugprone-suspicious-semicolon) + if constexpr (with_speculative) { + input = BuildAnyTypeCheckInst(GetPc(bc_inst->GetAddress()), input, inst_save_state); + } + inst->AppendInput(input); + inst->AddInputType(DataType::ANY); + } +% end +% if acc_read + { + auto input = GetDefinitionAcc(); + // NOLINTNEXTLINE(readability-braces-around-statements, bugprone-suspicious-semicolon) + if constexpr (with_speculative) { + input = BuildAnyTypeCheckInst(GetPc(bc_inst->GetAddress()), input, inst_save_state); + } + inst->AppendInput(input); + inst->AddInputType(DataType::ANY); + inst->SetFlag(compiler::inst_flags::ACC_READ); + } +% end +% if use_ic + if (!GetGraph()->IsBytecodeOptimizer()) { + inst->AppendInput(FindOrCreate32BitConstant(GetPc(bc_inst->GetAddress()))); + inst->AddInputType(DataType::UINT16); + } +% end + inst->AppendInput(inst_save_state); + inst->AddInputType(DataType::NO_TYPE); + AddInstruction(inst); +% if acc_write + UpdateDefinitionAcc(inst); + inst->SetFlag(compiler::inst_flags::ACC_WRITE); +% end + break; + } +% end + default: + failed_ = true; + LOG(ERROR,COMPILER) << "unknown Ecma opcode!" << static_cast(bc_inst->GetOpcode()); + return; + } +} + +// NOLINTNEXTLINE(readability-function-size) +void InstBuilder::BuildEcmaFromIrtoc([[maybe_unused]] const BytecodeInstruction* bc_inst) +{ +#ifdef ENABLE_BYTECODE_OPT + ASSERT(!GetGraph()->IsBytecodeOptimizer()); // Not applicable for optimizing bytecode + switch (bc_inst->GetOpcode()) { +% Panda::instructions.select{|b| b.namespace == "ecmascript"}.each do |inst| +% format = "BytecodeInstruction::Format::" + inst.format.pretty.upcase +% vreg_index = 0 +% imm_index = 0 +% id_index = 0 +% inputs = [] +% +% # Corner case, no inlinable IRtoC handler found: +% unless inst.inlinable? + case BytecodeInstruction::Opcode::<%= inst.opcode.upcase %>: { + UNREACHABLE(); // Inlinable IRtoC handler is not implemented yet + break; + } +% next +% end +% +% # Generate code for inlining IRtoC handler: + case BytecodeInstruction::Opcode::<%= inst.opcode.upcase %>: { +% inst.operands.each do |param| +% if param.reg? + auto vreg<%= vreg_index %> = GetDefinition(bc_inst->GetVReg<<%= format %>, <%= vreg_index %>>()); +% inputs.push("vreg#{vreg_index}") +% vreg_index = vreg_index + 1 +% elsif param.imm? + auto imm<%= imm_index %>_payload = static_cast(bc_inst->GetImm<<%= format %>, <%= imm_index %>>()); + auto imm<%= imm_index %> = GetGraph()->FindOrCreateConstant(imm<%= imm_index %>_payload); +% inputs.push("imm#{imm_index}") +% imm_index = imm_index + 1 +% elsif param.id? + auto id<%= id_index %>_payload = static_cast(bc_inst->template GetId<<%= format %>>().AsIndex()); + auto id<%= id_index %> = GetGraph()->FindOrCreateConstant(id<%= id_index %>_payload); +% inputs.push("id#{id_index}") +% id_index = id_index + 1 +% else +% abort 'Unexpected param type' +% end +% end +% if inst.acc.include?('in') + auto acc = GetDefinitionAcc(); // According to ecma.* convention, always goes last +% inputs.push('acc') +% end +% if inst.properties.include?('use_ic') +% inputs.push('GetGraph()->FindOrCreateConstant(GetPc(bc_inst->GetAddress()))') +% end +% builder = 'Build' + inst.opcode.split('_')[0..1].map do |_| _.capitalize end.join() + [[maybe_unused]] auto inst = <%= builder %>(GetGraph(), this, ¤t_bb_, <%= inputs.empty? ? '' : inputs.join(', ') + ', ' %>GetPc(bc_inst->GetAddress()), visited_block_marker_); +% if inst.acc.include?('out') + UpdateDefinitionAcc(inst); +% end + SyncWithGraph(); + break; + } +% end + default: { + failed_ = true; + LOG(ERROR, COMPILER) << "Unknown ecma.* opcode: " << static_cast(bc_inst->GetOpcode()); + return; + } + } +#endif +} + +} // namespace panda::compiler diff --git a/ecmascript_plugin_options.yaml b/ecmascript_plugin_options.yaml new file mode 100644 index 000000000..4adf5cda8 --- /dev/null +++ b/ecmascript_plugin_options.yaml @@ -0,0 +1,86 @@ +# Copyright (c) 2021-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. + + - ECMASCRIPT: + lang_context_class: panda::EcmaLanguageContext + lang_context_header_path: plugins/ecmascript/runtime/ecma_language_context.h + additional_interpter_inl: plugins/ecmascript/runtime/interpreter/ecma-interpreter-inl.h + lang_enum_id: 0 + language_config: + lang_type: dynamic + mt_mode: single + has_value_object_types: false + directive_name: ECMAScript + ctor_name: .ctor + cctor_name: .cctor + string_class_descriptor: Lpanda/JSString; + bytecodeopt: + codegen_intrinsics_inc: plugins/ecmascript/bytecode_optimizer/visitors/ecmascript_codegen_intrinsics.inc + compiler_extensions: + main_header_path: plugins/ecmascript/compiler/extensions.h + header_path_implementation_codegen: plugins/ecmascript/compiler/ecmascript_extensions/ecmascript_codegen_extensions.h + header_path_compiler_interface_extension: plugins/ecmascript/compiler/ecmascript_extensions/ecmascript_compiler_interface.h + header_path_compiler_inst_builder_extension: plugins/ecmascript/compiler/optimizer/ir_builder/ecmascript_inst_builder.h + function_codegen_prologue: GenerateEcmascriptEnvInPrologue + function_codegen_epilogue: GenerateEcmascriptEnvInEpilogue + compiler_base_types: + header_path_implementation_codegen: plugins/ecmascript/compiler/optimizer/code_generator/compiler_base_types.h + func_compare_implementation_codegen: panda::compiler::ecmascript::CompareAnyTypeGen + func_cast_implementation_codegen: panda::compiler::ecmascript::CastAnyTypeValueGen + func_cast_to_any_implementation_codegen: panda::compiler::ecmascript::CastValueToAnyTypeGen + func_any_type_check_implementation_codegen: panda::compiler::ecmascript::AnyTypeCheckGen + func_resolve_numeric_type: panda::compiler::ecmascript::NumericDataTypeToAnyType + func_resolve_string_type: panda::compiler::ecmascript::GetAnyStringType + func_is_any_type_can_be_subtype_of: panda::compiler::ecmascript::IsAnyTypeCanBeSubtypeOf + list_types: + HOLE_TYPE: panda::compiler::DataType::Type::VOID + NULL_TYPE: panda::compiler::DataType::Type::VOID + UNDEFINED_TYPE: panda::compiler::DataType::Type::VOID + INT_TYPE: panda::compiler::DataType::Type::INT32 + DOUBLE_TYPE: panda::compiler::DataType::Type::FLOAT64 + OBJECT_TYPE: panda::compiler::DataType::Type::REFERENCE + HEAP_OBJECT_TYPE: panda::compiler::DataType::Type::REFERENCE + STRING_TYPE: panda::compiler::DataType::Type::REFERENCE + ARRAY_TYPE: panda::compiler::DataType::Type::REFERENCE + TRANSITION_HANDLER_TYPE: panda::compiler::DataType::Type::REFERENCE + PROTOTYPE_HANDLER_TYPE: panda::compiler::DataType::Type::REFERENCE + SPECIAL_INDEXED_TYPE: panda::compiler::DataType::Type::REFERENCE + BOOLEAN_TYPE: panda::compiler::DataType::Type::BOOL + Intrinsics: + intrinsic_inline_inl: plugins/ecmascript/compiler/intrinsics_inline_ecmascript.inl + intrinsic_type_resolving_inl_h: plugins/ecmascript/compiler/intrinsics_type_resolving_ecmascript.inl.h + Metadatas: + - RecordMetadata: + new_class_name: panda::pandasm::extensions::ecmascript::RecordMetadata + header_path: plugins/ecmascript/assembler/extension/ecmascript_meta.h + - FieldMetadata: + new_class_name: panda::pandasm::extensions::ecmascript::FieldMetadata + header_path: plugins/ecmascript/assembler/extension/ecmascript_meta.h + - FunctionMetadata: + new_class_name: panda::pandasm::extensions::ecmascript::FunctionMetadata + header_path: plugins/ecmascript/assembler/extension/ecmascript_meta.h + - ParamMetadata: + new_class_name: panda::pandasm::extensions::ecmascript::ParamMetadata + header_path: plugins/ecmascript/assembler/extension/ecmascript_meta.h + irtoc_files: + - plugins_interpreter_handlers: plugins/ecmascript/irtoc_scripts/interpreter_handlers.irt + - plugins_main_loop: plugins/ecmascript/irtoc_scripts/interpreter_main_loop.irt + runtime_defines: + defines_header_path: plugins/ecmascript/runtime/asm_defines/defines.h + asm_defines_def: plugins/ecmascript/runtime/asm_defines/asm_defines.def + irtoc_files: + - plugins_interpreter_handlers: plugins/ecmascript/irtoc_scripts/interpreter_handlers.irt + - plugins_main_loop: plugins/ecmascript/irtoc_scripts/interpreter_main_loop.irt + logger: + components: + - name: ecmascript diff --git a/ecmastdlib/CMakeLists.txt b/ecmastdlib/CMakeLists.txt new file mode 100644 index 000000000..e942e4f58 --- /dev/null +++ b/ecmastdlib/CMakeLists.txt @@ -0,0 +1,29 @@ +# Copyright (c) 2021-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. + +cmake_minimum_required(VERSION 3.10) + +project(ecmastdlib) + +add_panda_assembly(TARGET ecmastdlib SOURCE ecmastdlib.pa) + +set(ECMASTDLIB_INLINE_GEN "${CMAKE_CURRENT_BINARY_DIR}/ecmastdlib_inline_gen.h") +set(ECMASTDLIB_INLINE_GEN_RB "${CMAKE_CURRENT_SOURCE_DIR}/ecmastdlib_inline_gen.rb") +set(ECMASTDLIB_INLINE_GEN_ERB "${CMAKE_CURRENT_SOURCE_DIR}/ecmastdlib_inline_gen.h.erb") + +add_custom_command(OUTPUT "${ECMASTDLIB_INLINE_GEN}" + COMMENT "Generate inlined header with ecmastdlib" + COMMAND "${ECMASTDLIB_INLINE_GEN_RB}" -t "${ECMASTDLIB_INLINE_GEN_ERB}" -d ${CMAKE_CURRENT_BINARY_DIR}/ecmastdlib.abc -o "${ECMASTDLIB_INLINE_GEN}" + DEPENDS "${ECMASTDLIB_INLINE_GEN_RB}" "${ECMASTDLIB_INLINE_GEN_ERB}" ecmastdlib) + +add_custom_target(ecmastdlib_inline_h DEPENDS "${ECMASTDLIB_INLINE_GEN}") diff --git a/ecmastdlib/ecmastdlib.pa b/ecmastdlib/ecmastdlib.pa new file mode 100644 index 000000000..5358c0f2e --- /dev/null +++ b/ecmastdlib/ecmastdlib.pa @@ -0,0 +1,182 @@ +# Copyright (c) 2021-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. +.language ECMAScript + +.record Ecmascript.Intrinsics {} + +# Ecmascript.Intrinsics methods +.function any Ecmascript.Intrinsics.ldnan() +.function any Ecmascript.Intrinsics.ldinfinity() +.function any Ecmascript.Intrinsics.ldglobalthis() +.function any Ecmascript.Intrinsics.ldundefined() +.function any Ecmascript.Intrinsics.ldboolean(any a0) +.function any Ecmascript.Intrinsics.ldnumber(any a0) +.function any Ecmascript.Intrinsics.ldstring(any a0) +.function any Ecmascript.Intrinsics.ldbigint(any a0) +.function any Ecmascript.Intrinsics.ldnull() +.function any Ecmascript.Intrinsics.ldsymbol() +.function any Ecmascript.Intrinsics.ldobject(any a0, any a1) +.function any Ecmascript.Intrinsics.ldfunction(any a0, any a1) +.function any Ecmascript.Intrinsics.ldglobal() +.function any Ecmascript.Intrinsics.ldtrue() +.function any Ecmascript.Intrinsics.ldfalse() +.function any Ecmascript.Intrinsics.add2Dyn(any a0, any a1) +.function any Ecmascript.Intrinsics.sub2Dyn(any a0, any a1) +.function any Ecmascript.Intrinsics.mul2Dyn(any a0, any a1) +.function any Ecmascript.Intrinsics.div2Dyn(any a0, any a1) +.function any Ecmascript.Intrinsics.mod2Dyn(any a0, any a1) +.function any Ecmascript.Intrinsics.eqDyn(any a0, any a1) +.function any Ecmascript.Intrinsics.noteqDyn(any a0, any a1) +.function any Ecmascript.Intrinsics.lessDyn(any a0, any a1) +.function any Ecmascript.Intrinsics.lesseqDyn(any a0, any a1) +.function any Ecmascript.Intrinsics.greaterDyn(any a0, any a1) +.function any Ecmascript.Intrinsics.greatereqDyn(any a0, any a1) +.function any Ecmascript.Intrinsics.ldObjByValue(any a0, any a1, u16 a2) +.function any Ecmascript.Intrinsics.tryLdGlobalByValue(u16 a0, any a1) +.function any Ecmascript.Intrinsics.stObjByValue(any a0, any a1, any a2, u16 a3) +.function void Ecmascript.Intrinsics.tryStGlobalByValue(u16 a0, any a1, any a2) +.function any Ecmascript.Intrinsics.shl2Dyn(any a0, any a1) +.function any Ecmascript.Intrinsics.shr2Dyn(any a0, any a1) +.function any Ecmascript.Intrinsics.ashr2Dyn(any a0, any a1) +.function any Ecmascript.Intrinsics.and2Dyn(any a0, any a1) +.function any Ecmascript.Intrinsics.or2Dyn(any a0, any a1) +.function any Ecmascript.Intrinsics.xor2Dyn(any a0, any a1) +.function any Ecmascript.Intrinsics.tonumber(any a0) +.function any Ecmascript.Intrinsics.negDyn(any a0) +.function any Ecmascript.Intrinsics.notDyn(any a0) +.function any Ecmascript.Intrinsics.incDyn(any a0) +.function any Ecmascript.Intrinsics.decDyn(any a0) +.function void Ecmascript.Intrinsics.throwDyn(any a0) +.function any Ecmascript.Intrinsics.rethrowDyn(any a0) +.function any Ecmascript.Intrinsics.delobjprop(any a0, any a1) +.function any Ecmascript.Intrinsics.defineglobalvar(any a0, any a1) +.function any Ecmascript.Intrinsics.definelocalvar(any a0, any a1) +.function any Ecmascript.Intrinsics.definefuncexpr(any a0, any a1) +.function any Ecmascript.Intrinsics.definefuncDyn(u32 a0, any a1) +.function any Ecmascript.Intrinsics.defineNCFuncDyn(u32 a0, any a1, any a2) +.function any Ecmascript.Intrinsics.newobjDynrange(u16 a0, any a1) +.function any Ecmascript.Intrinsics.refeqDyn(any a0, any a1) +.function any Ecmascript.Intrinsics.expDyn(any a0, any a1) +.function any Ecmascript.Intrinsics.typeofDyn(any a0) +.function any Ecmascript.Intrinsics.callruntimerange(any a0, any a1) +.function any Ecmascript.Intrinsics.isinDyn(any a0, any a1) +.function any Ecmascript.Intrinsics.instanceofDyn(any a0, any a1) +.function any Ecmascript.Intrinsics.strictEqDyn(any a0, any a1) +.function any Ecmascript.Intrinsics.strictNotEqDyn(any a0, any a1) +.function any Ecmascript.Intrinsics.newobjspreadDyn(any a0, any a1, any a2) +.function any Ecmascript.Intrinsics.callspreadDyn(any a0, any a1, any a2) +.function void Ecmascript.Intrinsics.jtrueDyn(u16 a0, any a1) +.function void Ecmascript.Intrinsics.jfalseDyn(u16 a0, any a1) +.function any Ecmascript.Intrinsics.newlexenvDyn(u16 a0) +.function any Ecmascript.Intrinsics.copylexenvDyn() +.function void Ecmascript.Intrinsics.StLexVarDyn(u16 a0, u16 a1, any a2) +.function any Ecmascript.Intrinsics.LdLexVarDyn(u16 a0, u16 a1) +.function any Ecmascript.Intrinsics.ldlexenvDyn() +.function void Ecmascript.Intrinsics.popLexenvDyn() +.function any Ecmascript.Intrinsics.getUnmappedArgs() +.function any Ecmascript.Intrinsics.toboolean(any a0) +.function any Ecmascript.Intrinsics.negate(any a0) +.function any Ecmascript.Intrinsics.isUndefined(any a0) +.function any Ecmascript.Intrinsics.isTrue(any a0) +.function any Ecmascript.Intrinsics.isFalse(any a0) +.function any Ecmascript.Intrinsics.isCoercible(any a0) +.function any Ecmascript.Intrinsics.getPropIterator(any a0) +.function any Ecmascript.Intrinsics.defineGeneratorFunc(u32 a0, any a1) +.function any Ecmascript.Intrinsics.createIterResultObj(any a0, u8 a1) +.function any Ecmascript.Intrinsics.setGeneratorState(any a0, u8 a1) +.function any Ecmascript.Intrinsics.suspendGenerator(any a0, any a1) +.function any Ecmascript.Intrinsics.suspendAsyncGenerator(any a0, any a1) +.function any Ecmascript.Intrinsics.resumeGenerator(any a0) +.function any Ecmascript.Intrinsics.getResumeMode(any a0) +.function any Ecmascript.Intrinsics.createGeneratorObj(any a0) +.function any Ecmascript.Intrinsics.createAsyncGeneratorObj(any a0) +.function any Ecmascript.Intrinsics.defineAsyncFunc(u32 a0, any a1) +.function any Ecmascript.Intrinsics.defineAsyncGeneratorFunc(u32 a0, any a1) +.function any Ecmascript.Intrinsics.asyncFunctionEnter() +.function any Ecmascript.Intrinsics.asyncFunctionAwait(any a0, any a1) +.function any Ecmascript.Intrinsics.asyncFunctionResolve(any a0, any a1) +.function any Ecmascript.Intrinsics.asyncFunctionReject(any a0, any a1) +.function any Ecmascript.Intrinsics.asyncGeneratorResolve(any a0, any a1) +.function any Ecmascript.Intrinsics.asyncGeneratorReject(any a0, any a1) +.function void Ecmascript.Intrinsics.throwUndefined(any a0) +.function void Ecmascript.Intrinsics.throwConstAssignment(u32 a0) +.function any Ecmascript.Intrinsics.throwUndefinedIfHole(u32 a0, any a1) +.function any Ecmascript.Intrinsics.copyrestargs(u16 a0) +.function any Ecmascript.Intrinsics.ldHole() +.function any Ecmascript.Intrinsics.getMethod(u32 a0, any a1) +.function any Ecmascript.Intrinsics.getTemplateObject(any a0) +.function any Ecmascript.Intrinsics.TryLdGlobalByName(u32 a0, u16 a1) +.function any Ecmascript.Intrinsics.TryStGlobalByName(u32 a0, any a1, u16 a2) +.function any Ecmascript.Intrinsics.LdGlobalVar(u32 a0, u16 a1) +.function any Ecmascript.Intrinsics.StGlobalVar(u32 a0, any a1, u16 a2) +.function any Ecmascript.Intrinsics.StGlobalLet(u32 a0, any a1) +.function any Ecmascript.Intrinsics.LdObjByName(u32 a0, any a1, u16 a2) +.function any Ecmascript.Intrinsics.StObjByName(u32 a0, any a1, any a2, u16 a3) +.function any Ecmascript.Intrinsics.LdObjByIndex(u32 a0, any a1) +.function any Ecmascript.Intrinsics.StObjByIndex(u32 a0, any a1, any a2) +.function any Ecmascript.Intrinsics.getnextpropname(any a0) +.function void Ecmascript.Intrinsics.ReturnUndefined() +.function any Ecmascript.Intrinsics.returnDyn(any a0) +.function any Ecmascript.Intrinsics.Call0Dyn(any a0) +.function any Ecmascript.Intrinsics.Call1Dyn(any a0, any a1) +.function any Ecmascript.Intrinsics.Call2Dyn(any a0, any a1, any a2) +.function any Ecmascript.Intrinsics.Call3Dyn(any a0, any a1, any a2, any a3) +.function any Ecmascript.Intrinsics.CalliRangeDyn(u16 a0, any a1) +.function any Ecmascript.Intrinsics.CalliThisRangeDyn(u16 a0, any a1) +.function any Ecmascript.Intrinsics.createemptyobject() +.function any Ecmascript.Intrinsics.createobjectwithbuffer(u16 a0) +.function any Ecmascript.Intrinsics.setobjectwithproto(any a0, any a1) +.function any Ecmascript.Intrinsics.copydataproperties(any a0, any a1) +.function any Ecmascript.Intrinsics.definegettersetterbyvalue(any a0, any a1, any a2, any a3, any a4) +.function any Ecmascript.Intrinsics.createemptyarray() +.function any Ecmascript.Intrinsics.createarraywithbuffer(u16 a0) +.function any Ecmascript.Intrinsics.StOwnByName(u32 a0, any a1, any a2) +.function any Ecmascript.Intrinsics.StOwnByIndex(u32 a0, any a1, any a2) +.function any Ecmascript.Intrinsics.StOwnByValue(any a0, any a1, any a2) +.function any Ecmascript.Intrinsics.starrayspread(any a0, any a1, any a2) +.function any Ecmascript.Intrinsics.GetIterator(any a0) +.function any Ecmascript.Intrinsics.GetAsyncIterator(any a0) +.function any Ecmascript.Intrinsics.ThrowIfNotObject(any a0) +.function void Ecmascript.Intrinsics.ThrowThrowNotExists() +.function any Ecmascript.Intrinsics.CreateObjectWithExcludedKeys(u16 a0, any a1, any a2) +.function void Ecmascript.Intrinsics.ThrowPatternNonCoercible() +.function any Ecmascript.Intrinsics.CloseIterator(any a0, any a1) +.function any Ecmascript.Intrinsics.ImportModule(u32 a0) +.function void Ecmascript.Intrinsics.StModuleVar(u32 a0, any a1) +.function void Ecmascript.Intrinsics.CopyModule(any a0) +.function any Ecmascript.Intrinsics.LdModvarByName(u32 a0, any a1) +.function any Ecmascript.Intrinsics.DefineClassWithBuffer(u32 a0, u16 a1, any a2, any a3) +.function any Ecmascript.Intrinsics.SuperCall(u16 a0, any a1, any a2) +.function any Ecmascript.Intrinsics.SuperCallSpread(any a0, any a1, any a2) +.function any Ecmascript.Intrinsics.defineMethod(u32 a0, any a1, any a2) +.function any Ecmascript.Intrinsics.LdSuperByName(u32 a0, any a1) +.function any Ecmascript.Intrinsics.StSuperByName(u32 a0, any a1, any a2) +.function any Ecmascript.Intrinsics.StSuperByValue(any a0, any a1, any a2) +.function any Ecmascript.Intrinsics.LdSuperByValue(any a0, any a1) +.function any Ecmascript.Intrinsics.createobjecthavingmethod(u16 a0, any a1) +.function any Ecmascript.Intrinsics.ThrowIfSuperNotCorrectCall(u16 a0, any a1) +.function any Ecmascript.Intrinsics.LdHomeObject() +.function void Ecmascript.Intrinsics.ThrowDeleteSuperProperty() +.function void Ecmascript.Intrinsics.debugger() + +.function void Ecmascript.Intrinsics.NativeMethodWrapper(any a0) +.function void Ecmascript.Intrinsics.GetObjectClassType(any a0) +.function any Ecmascript.Intrinsics.IsNan(f64 a0) +.function any Ecmascript.Intrinsics.GetEcmaConstantPool() +.function any Ecmascript.Intrinsics.GetEcmaThisFunc() +.function any Ecmascript.Intrinsics.GetWeakReferent(any a0) + +.function u1 Ecmascript.Intrinsics.DynClassIsDictionaryElement(any a0) +.function u1 Ecmascript.Intrinsics.DynClassIsExtensible(any a0) +.function any Ecmascript.Intrinsics.DynObjectGetClass(any a0) +.function void Ecmascript.Intrinsics.DynObjectSetClass(any a0, any a1) diff --git a/ecmastdlib/ecmastdlib_inline_gen.h.erb b/ecmastdlib/ecmastdlib_inline_gen.h.erb new file mode 100644 index 000000000..6fdcc444c --- /dev/null +++ b/ecmastdlib/ecmastdlib_inline_gen.h.erb @@ -0,0 +1,41 @@ +/** + * 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. + */ + +namespace panda::ecmascript::ecmastdlib_inline { +unsigned char const* GetEcmastdlibData(size_t &fsize) +{ + static unsigned char const data[] = + { + +% if file_exist +% file.each_byte do |b| + <%= b %>, +% end +% else + 0, +% end + + }; + +% if file_exist + fsize = sizeof(data); +% else + fsize = 0; +% end + + return data; +} + +} // namespace panda::ecmascript::ecmastdlib_inline diff --git a/ecmastdlib/ecmastdlib_inline_gen.rb b/ecmastdlib/ecmastdlib_inline_gen.rb new file mode 100644 index 000000000..1ec297068 --- /dev/null +++ b/ecmastdlib/ecmastdlib_inline_gen.rb @@ -0,0 +1,61 @@ +#!/usr/bin/env ruby +# Copyright (c) 2021-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. + +# Huawei Technologies Co.,Ltd. + +require 'erb' +require 'optparse' +require 'ostruct' + +def check_option(optparser, options, key) + return if options[key] + + puts "Missing option: --#{key}" + puts optparser + exit false +end + +options = OpenStruct.new + +optparser = OptionParser.new do |opts| + opts.banner = 'Usage: ecmastdlib_inline_gen.rb [options]' + + opts.on('-t', '--template FILE', 'Template for file generation (required)') + opts.on('-d', '--data FILE', 'Source data in binary format (required)') + opts.on('-o', '--output FILE', 'Output file (default is stdout)') + + opts.on('-h', '--help', 'Prints this help') do + puts opts + exit + end +end +optparser.parse!(into: options) + +file_exist = false +if options[:data] + file = File.open(options.data, "rb") + file_exist = true +end + +check_option(optparser, options, :template) +template = File.read(File.expand_path(options.template)) +output = options.output ? File.open(File.expand_path(options.output), 'w') : $stdout +t = ERB.new(template, nil, '%-') +t.filename = options.template +output.write(t.result(binding)) +output.close + +if file_exist + file.close +end diff --git a/irtoc_scripts/common.irt b/irtoc_scripts/common.irt new file mode 100644 index 000000000..b6cf45fa9 --- /dev/null +++ b/irtoc_scripts/common.irt @@ -0,0 +1,70 @@ +# Copyright (c) 2021-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. + +module Constants + verbose, $VERBOSE = $VERBOSE, nil + + ECMASCRIPT_LEXICAL_ENV_OFFSET = "ECMASCRIPT_ENVIRONMENT_LEXICAL_ENV_OFFSET" + LANGUAGE_EXTENSION_DATA_OFFSET = "MANAGED_THREAD_LANGUAGE_EXTENSION_DATA_OFFSET" + VALUE_TRUE = "coretypes::TaggedValue::VALUE_TRUE" + VALUE_FALSE = "coretypes::TaggedValue::VALUE_FALSE" + VALUE_HOLE = "coretypes::TaggedValue::VALUE_HOLE" + VALUE_NULL = "coretypes::TaggedValue::VALUE_NULL" + VALUE_UNDEFINED = "coretypes::TaggedValue::VALUE_UNDEFINED" + TAG_SPECIAL_MASK = "coretypes::TaggedValue::TAG_SPECIAL_MASK" + TAG_SPECIAL_VALUE = "coretypes::TaggedValue::TAG_SPECIAL_VALUE" + + TAGGED_TRUE = "DataType::Any(coretypes::TaggedValue::VALUE_TRUE)" + TAGGED_FALSE = "DataType::Any(coretypes::TaggedValue::VALUE_FALSE)" + TAGGED_HOLE = "DataType::Any(coretypes::TaggedValue::VALUE_HOLE)" + TAGGED_NULL = "DataType::Any(coretypes::TaggedValue::VALUE_NULL)" + TAGGED_UNDEFINED = "DataType::Any(coretypes::TaggedValue::VALUE_UNDEFINED)" + TAGGED_INFINITY = "DataType::Any(panda::coretypes::TaggedValue(std::numeric_limits::infinity()).GetRawData())" #panda::ecmascript::base::POSITIVE_INFINITY + TAGGED_NAN_VALUE = "DataType::Any(panda::coretypes::TaggedValue(std::numeric_limits::quiet_NaN()).GetRawData())" #panda::ecmascript::base::NAN_VALUE + + ECMASTRING_JSTYPE_STRING = "JSTYPE_STRING" + ECMASTRING_MIX_LENGTH_FIELD = "runtime->GetFieldByOffset(cross_values::GetEcmastringMixLengthOffset(graph->GetArch()))" + + ECMASCRIPT_JSFORINITERATOR_FAST_REMAINING_INDEX_OFFSET = "JSFORINITERATOR_FAST_REMAINING_INDEX_OFFSET" + ECMASCRIPT_JSFORINITERATOR_REMAINING_KEYS_OFFSET = "JSFORINITERATOR_REMAINING_KEYS_OFFSET" + ECMASCRIPT_TAGGEDARRAY_DATA_OFFSET = "TAGGEDARRAY_DATA_OFFSET" + ECMASCRIPT_TAGGEDQUEUE_START_INDEX = "TAGGEDQUEUE_START_INDEX" + ECMASCRIPT_TAGGEDQUEUE_END_INDEX = "TAGGEDQUEUE_END_INDEX" + ECMASCRIPT_TAGGEDQUEUE_ELEMENTS_START_INDEX = "TAGGEDQUEUE_ELEMENTS_START_INDEX" + SIZEOF_UINT64_T = "sizeof(uint64_t)" + TAGGED_TYPE_SIZE = "coretypes::TaggedValue::TaggedTypeSize()" + + JSTYPE_JS_ARRAY = "cross_values::GetJstypeJsArray(graph->GetArch())" + JSTYPE_JS_OBJECT_BEGIN = "cross_values::GetJstypeJsObjectBegin(graph->GetArch())" + JSTYPE_JS_OBJECT_END = "cross_values::GetJstypeJsObjectEnd(graph->GetArch())" + + JS_OBJECT_PROPERTIES_FIELD = "runtime->GetFieldByOffset(cross_values::GetJsobjectPropertiesOffset(graph->GetArch()))" + JS_OBJECT_ELEMENTS_FIELD = "runtime->GetFieldByOffset(cross_values::GetJsobjectElementsOffset(graph->GetArch()))" + JS_ARRAY_LENGTH_FIELD = "runtime->GetFieldByOffset(cross_values::GetJsarrayLengthOffset(graph->GetArch()))" + JS_FUNCTION_PROFILE_TYPE_INFO_FIELD = "runtime->GetFieldByOffset(cross_values::GetJsfunctionProfileTypeInfoOffset(graph->GetArch()))" + JS_PROPERTY_BOX_VALUE_FIELD = "runtime->GetFieldByOffset(cross_values::GetJspropertyBoxValueOffset(graph->GetArch()))" + JS_TRANSITION_HANDLER_HANDLER_INFO_FEILD = "runtime->GetFieldByOffset(cross_values::GetJstransitionHandlerHandlerInfoOffset(graph->GetArch()))" + JS_TRANSITION_HANDLER_HCLASS_FEILD = "runtime->GetFieldByOffset(cross_values::GetJstransitionHandlerHclassOffset(graph->GetArch()))" + JS_PROTOTYPE_HANDLER_HANDLER_INFO_FIELD = "runtime->GetFieldByOffset(cross_values::GetJsprototypeHandlerHandlerInfoOffset(graph->GetArch()))" + JS_PROTOTYPE_HANDLER_PROTO_CELL_FIELD = "runtime->GetFieldByOffset(cross_values::GetJsprototypeHandlerProtoCellOffset(graph->GetArch()))" + JS_PROTOTYPE_HANDLER_HOLDER_FIELD = "runtime->GetFieldByOffset(cross_values::GetJsprototypeHandlerHolderOffset(graph->GetArch()))" + JS_PROTO_CHANGE_MARKER_HAS_CHANGED_FIELD = "runtime->GetFieldByOffset(cross_values::GetJsprotoChangeMarkerHasChangedOffset(graph->GetArch()))" + + JS_HCLASS_BITFIELD1_INLINED_PROPS_START_BIT = "cross_values::GetJshclassBitfieldInlinedPropsStartBitsStartBit(graph->GetArch())" + JS_HCLASS_BITFIELD1_INLINED_PROPS_START_MASK = "cross_values::GetJshclassBitfieldInlinedPropsStartBitsMask(graph->GetArch())" + + DYN_INT_TYPE = "AnyBaseType::ECMASCRIPT_INT_TYPE" + DYN_DOUBLE_TYPE = "AnyBaseType::ECMASCRIPT_DOUBLE_TYPE" + + $VERBOSE = verbose +end diff --git a/irtoc_scripts/interpreter_handlers.irt b/irtoc_scripts/interpreter_handlers.irt new file mode 100644 index 000000000..2e7e6e318 --- /dev/null +++ b/irtoc_scripts/interpreter_handlers.irt @@ -0,0 +1,1578 @@ +# plugin interpreter_handlers +# Copyright (c) 2021-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_relative '../../plugins/ecmascript/irtoc_scripts/common.irt' + +################## ecma fastpath ##################### + +macro(:ecma_intrinsic_setacc) do |sym, *args| + res := Call(*args).Method(sym).u64 + set_value(acc_ptr, res).u64 +end + +macro(:ecma_intrinsic_invoke) do |sym, *args| + Call(*args).Method(sym).any +end + +macro(:ecma_codegen_intrinsic) do |intr, *args| + ss := SaveState() + Intrinsic(intr, *args, ss).void +end + +macro(:anytoi32) do |arg| + CastAnyTypeValue(arg).AnyType("AnyBaseType::ECMASCRIPT_INT_TYPE").i32 +end + +macro(:anytou32) do |arg| + i32tou32(CastAnyTypeValue(arg).AnyType("AnyBaseType::ECMASCRIPT_INT_TYPE").i32) +end + +# macro(:anytoi64) do |arg| # verify sgnext end +# CastAnyTypeValue(arg).AnyType("AnyBaseType::ECMASCRIPT_INT_TYPE").i64 +# end + +macro(:anytof64) do |arg| + CastAnyTypeValue(arg).AnyType("AnyBaseType::ECMASCRIPT_DOUBLE_TYPE").f64 +end + +macro(:booltoany) do |arg| + CastValueToAnyType(arg).AnyType("AnyBaseType::ECMASCRIPT_BOOLEAN_TYPE").any +end + +macro(:u32toany) do |arg| + CastValueToAnyType(u32toi32(arg)).AnyType("AnyBaseType::ECMASCRIPT_INT_TYPE").any +end + +macro(:anytoboolean) do |arg| + CastAnyTypeValue(arg).AnyType("AnyBaseType::ECMASCRIPT_BOOLEAN_TYPE").b +end + +macro(:anytoheapobj) do |arg| + CastAnyTypeValue(arg) + .AnyType("AnyBaseType::ECMASCRIPT_HEAP_OBJECT_TYPE") + .SetFlag("panda::compiler::inst_flags::Flags::NO_CSE") + .SetFlag("panda::compiler::inst_flags::Flags::NO_HOIST") + .ref +end + +macro(:anytou8) do |arg| + CastAnyTypeValue(arg).AnyType("AnyBaseType::ECMASCRIPT_INT_TYPE").u8 +end + +macro(:cmpanyi32) do |arg| + CompareAnyType(arg).AnyType("AnyBaseType::ECMASCRIPT_INT_TYPE").b +end + +macro(:cmpanyboolean) do |arg| + CompareAnyType(arg).AnyType("AnyBaseType::ECMASCRIPT_BOOLEAN_TYPE").b +end + +macro(:cmpanyundefined) do |arg| + CompareAnyType(arg).AnyType("AnyBaseType::ECMASCRIPT_UNDEFINED_TYPE").b +end + +macro(:cmpanynull) do |arg| + CompareAnyType(arg).AnyType("AnyBaseType::ECMASCRIPT_NULL_TYPE").b +end + +macro(:cmpanyhole) do |arg| + CompareAnyType(arg).AnyType("AnyBaseType::ECMASCRIPT_HOLE_TYPE").b +end + +macro(:cmpanyf64) do |arg| + CompareAnyType(arg).AnyType("AnyBaseType::ECMASCRIPT_DOUBLE_TYPE").b +end + +macro(:cmpanyobj) do |arg| + CompareAnyType(arg).AnyType("AnyBaseType::ECMASCRIPT_OBJECT_TYPE").b +end + +macro(:cmpanyheapobj) do |arg| + CompareAnyType(arg).AnyType("AnyBaseType::ECMASCRIPT_HEAP_OBJECT_TYPE").b +end + +macro(:cmpanystring) do |arg| + CompareAnyType(arg).AnyType("AnyBaseType::ECMASCRIPT_STRING_TYPE").b +end + +macro(:cmpany_notNumber) do |arg| # notNumber(a) is not a synonym of isnan(a) + cmpanyobj(arg) +end + +macro(:any_extractNumber) do |arg| + IfImm(cmpanyi32(arg)).Imm(0).CC(:CC_NE) { + arg_i32tof64 := i32tof64(anytoi32(arg)) + } Else { # likely f64 + arg_f64 := anytof64(arg) + } + Phi(arg_i32tof64, arg_f64).f64 +end + +macro(:any_extractBoolean) do |arg| + If(cmpanyi32(arg), 0).CC(:CC_NE) { + arg_i32toboolean := i32tou1(anytoi32(arg)) + } Else { + If(cmpanyf64(arg), 0).CC(:CC_NE) { + cmp_nan_res := Compare(anytof64(arg), anytof64(arg)).CC(:CC_NE).b + arg_f64 := anytof64(arg) + v1 := Not(cmp_nan_res).b + v2 := Compare(arg_f64, i64tof64(0)).CC(:CC_NE).b + arg_f64toboolean := And(v1, v2).b + } Else { + v6 := Or(Or(Or(cmpanyundefined(arg), cmpanyhole(arg)).b, cmpanynull(arg)).b, cmpanyboolean(arg)).b + If(v6, 1).CC(:CC_EQ).b { + arg_rawtoboolean := AndI(anytou8(arg)).Imm(1).b + } Else { + ret_tmp := AddI(0).Imm(1).b + If(cmpanystring(arg), 1).CC(:CC_EQ).b { + string_mix_length := LoadObject(anytoheapobj(arg)).ObjField(Constants::ECMASTRING_MIX_LENGTH_FIELD).u32 + string_length := ShrI(string_mix_length).Imm(2).u32 + arg_stringtoboolean := Compare(string_length, 0).CC(:CC_NE).b + } Else { + arg_objtoboolean := AddI(0).Imm(1).b + } + ret := Phi(arg_stringtoboolean, arg_objtoboolean).b + } + arg_rawtoboolean_ := Phi(arg_rawtoboolean, ret).b + } + arg_not_i32toboolean := Phi(arg_f64toboolean, arg_rawtoboolean_).b + } + Phi(arg_i32toboolean, arg_not_i32toboolean).b +end + +macro(:i32toi64) do |arg| + Cast(arg).SrcType("DataType::INT32").i64 +end + +macro(:i64toi32) do |arg| + Cast(arg).SrcType("DataType::INT64").i32 +end + +# 32-bit ovf handler +# ovf := And(Not(Xor(anytoi32(l.any), anytoi32(r.any)).i32).i32, Xor(anytoi32(l.any), int_sum_i32).i32).i32 +# IfImm(AndI(ovf.u32).Imm(1 << 31).u32).Imm(0).CC(:CC_NE) { # i32 ovf + +#################################################################################### +# ADD +# +macro(:add2dyn_smi_smi) do |l, r| + l_i32 := anytoi32(l.any) + r_i32 := anytoi32(r.any) + if self.mode.include? :IrInline + i32toany(AddOverflowCheck(l_i32, r_i32).i32) + else + int_sum_i32 := AddOverflow(l_i32, r_i32).EQ.i32 { + int_sum_f64 := Add(i32tof64(l_i32), i32tof64(r_i32)).f64 + int_sum_f64 := f64toany(int_sum_f64) + } Else { # likely no overflow + int_sum_i32 := i32toany(int_sum_i32) + } + fp1_res := Phi(int_sum_f64, int_sum_i32).any + end +end + +macro(:add2dyn_smi_double) do |l, r| + f64toany(Add(i32tof64(anytoi32(l)), anytof64(r)).f64) +end + +macro(:add2dyn_double_smi) do |l, r| + f64toany(Add(anytof64(l), i32tof64(anytoi32(r))).f64) +end + +macro(:add2dyn_double_double) do |l, r| + f64toany(Add(anytof64(l), anytof64(r)).f64) +end + +#################################################################################### +# SUB +# +macro(:sub2dyn_smi_smi) do |l, r| + l_i32 := anytoi32(l.any) + r_i32 := anytoi32(r.any) + if self.mode.include? :IrInline + i32toany(SubOverflowCheck(l_i32, r_i32).i32) + else + int_sum_i32 := SubOverflow(l_i32, r_i32).EQ.i32 { + int_sum_f64 := Sub(i32tof64(l_i32), i32tof64(r_i32)).f64 + int_sum_f64 := f64toany(int_sum_f64) + } Else { # likely no overflow + int_sum_i32 := i32toany(int_sum_i32) + } + fp1_res := Phi(int_sum_f64, int_sum_i32).any + end +end + +macro(:sub2dyn_smi_double) do |l, r| + f64toany(Sub(i32tof64(anytoi32(l)), anytof64(r)).f64) +end + +macro(:sub2dyn_double_smi) do |l, r| + f64toany(Sub(anytof64(l), i32tof64(anytoi32(r))).f64) +end + +macro(:sub2dyn_double_double) do |l, r| + f64toany(Sub(anytof64(l), anytof64(r)).f64) +end + +#################################################################### +# AND +# +macro(:and2dyn_smi_smi) do |l, r| + i32toany(And(anytoi32(l), anytoi32(r)).i32) +end + +macro(:and2dyn_smi_double) do |l, r| + i32toany(And(anytoi32(l), i64tou32(f64toi64(anytof64(r)))).i32) +end + +macro(:and2dyn_double_smi) do |l, r| + i32toany(And(i64tou32(f64toi64(anytof64(l))), anytoi32(r)).i32) +end + +macro(:and2dyn_double_double) do |l, r| + i32toany(And(i64tou32(f64toi64(anytof64(l))), i64tou32(f64toi64(anytof64(r)))).i32) +end + +#################################################################### +# OR +# +macro(:or2dyn_smi_smi) do |l, r| + i32toany(Or(anytoi32(l), anytoi32(r)).i32) +end + +macro(:or2dyn_smi_double) do |l, r| + i32toany(Or(anytoi32(l), i64tou32(f64toi64(anytof64(r)))).i32) +end + +macro(:or2dyn_double_smi) do |l, r| + i32toany(Or(i64tou32(f64toi64(anytof64(l))), anytoi32(r)).i32) +end + +macro(:or2dyn_double_double) do |l, r| + i32toany(Or(i64tou32(f64toi64(anytof64(l))), i64tou32(f64toi64(anytof64(r)))).i32) +end + +#################################################################### +# XOR +# +macro(:xor2dyn_smi_smi) do |l, r| + i32toany(Xor(anytoi32(l), anytoi32(r)).i32) +end + +macro(:xor2dyn_smi_double) do |l, r| + i32toany(Xor(anytoi32(l), i64tou32(f64toi64(anytof64(r)))).i32) +end + +macro(:xor2dyn_double_smi) do |l, r| + i32toany(Xor(i64tou32(f64toi64(anytof64(l))), anytoi32(r)).i32) +end + +macro(:xor2dyn_double_double) do |l, r| + i32toany(Xor(i64tou32(f64toi64(anytof64(l))), i64tou32(f64toi64(anytof64(r)))).i32) +end + +#################################################################### +# SHL +# +macro(:shl2dyn_smi_smi) do |l, r| + shift := AndI(anytou32(r.any)).Imm(0x1f).u32 + i32toany(Shl(anytoi32(l), shift).i32) +end + +macro(:shl2dyn_smi_double) do |l, r| + shift := AndI(f64toi32(anytof64(r.any))).Imm(0x1f).u32 + i32toany(Shl(anytoi32(l), shift).i32) +end + +macro(:shl2dyn_double_smi) do |l, r| + shift := AndI(anytou32(r.any)).Imm(0x1f).u32 + i32toany(Shl(i64tou32(f64toi64(anytof64(l))), shift).i32) +end + +macro(:shl2dyn_double_double) do |l, r| + shift := AndI(f64toi32(anytof64(r.any))).Imm(0x1f).u32 + i32toany(Shl(i64tou32(f64toi64(anytof64(l))), shift).i32) +end + +#################################################################### +# SHR +# +macro(:shr2dyn_smi_smi) do |l, r| + shift := AndI(anytou32(r.any)).Imm(0x1f).u32 + i32toany(AShr(anytoi32(l), shift).i32) +end + +macro(:shr2dyn_smi_double) do |l, r| + shift := AndI(f64toi32(anytof64(r.any))).Imm(0x1f).i32 + i32toany(AShr(anytoi32(l), shift).i32) +end + +macro(:shr2dyn_double_smi) do |l, r| + shift := AndI(anytou32(r.any)).Imm(0x1f).u32 + i32toany(AShr(i64tou32(f64toi64(anytof64(l))), shift).i32) +end + +macro(:shr2dyn_double_double) do |l, r| + shift := AndI(f64tou32(anytof64(r.any))).Imm(0x1f).u32 + i32toany(AShr(i64tou32(f64toi64(anytof64(l))), shift).i32) +end + +#################################################################################### +# MUL +# +macro(:mul2dyn_smi_smi) do |l, r| + f64toany(Mul(i32tof64(anytoi32(l.any)), i32tof64(anytoi32(r.any))).f64) +end + +macro(:mul2dyn_smi_double) do |l, r| + f64toany(Mul(i32tof64(anytoi32(l.any)), anytof64(r.any)).f64) +end + +macro(:mul2dyn_double_smi) do |l, r| + f64toany(Mul(anytof64(l.any), i32tof64(anytoi32(r.any))).f64) +end + +macro(:mul2dyn_double_double) do |l, r| + f64toany(Mul(anytof64(l.any), anytof64(r.any)).f64) +end + +#################################################################################### +# DIV +# +macro(:div2dyn_smi_smi) do |l, r| + f64toany(Div(i32tof64(anytoi32(l.any)), i32tof64(anytoi32(r.any))).f64) +end + +macro(:div2dyn_smi_double) do |l, r| + f64toany(Div(i32tof64(anytoi32(l.any)), anytof64(r.any)).f64) +end + +macro(:div2dyn_double_smi) do |l, r| + f64toany(Div(anytof64(l.any), i32tof64(anytoi32(r.any))).f64) +end + +macro(:div2dyn_double_double) do |l, r| + f64toany(Div(anytof64(l.any), anytof64(r.any)).f64) +end + +#################################################################### +# MOD +# +macro(:handle_ecma_mod2dyn) do |l, r| + IfImm(And(cmpanyi32(l), cmpanyi32(r)).b).Imm(0).CC(:CC_EQ).b { + Goto(:Slow) + } + + il := anytoi32(l) + ir := anytoi32(r) + IfImm(And(Compare(il, 0).CC(:CC_GT).b, Compare(ir, 0).CC(:CC_GT).b).b).Imm(0).CC(:CC_EQ).b { + Goto(:Slow) + } + + res := i32toany(Mod(il, ir).i32) + Goto(:Exit) + +Label(:Slow) + slow_res := ecma_intrinsic_invoke("Mod2DynSlow", l, r).any +Label(:Exit) + Phi(res, slow_res).any +end + +function(:EcmaMod2dyn, params: {'a'=>'any', 'b'=>'any'}, mode: [:Interpreter, :DynamicMethod], enable_builder: true) do + Return(handle_ecma_mod2dyn(a, b)) +end + +macro(:handle_ecma_tonumber) do |v| + IfImm(cmpany_notNumber(v.any)).Imm(0).CC(:CC_NE).b { + slow_res := ecma_intrinsic_invoke("TonumberSlow", v.u64) + } # likely num - no actions required + Phi(v.any, slow_res).any +end + +function(:EcmaTonumber, params: {'a'=>'any'}, mode: [:Interpreter, :DynamicMethod], enable_builder: true) do + Return(handle_ecma_tonumber(a)) +end + +#################################################################### +# NOT +# +macro(:notdyn_smi) do |v| + i32toany(Not(anytoi32(v)).i32) +end + +macro(:notdyn_double) do |v| + i32toany(Not(f64toi64(anytof64(v))).i32) +end + +#################################################################### +# NEG +# +macro(:negdyn_smi) do |v| + # We can't use Neg with INT type, because it doesn't work for -0 and INT::MIN values. Thus, we cast it to the float + # and use as float Neg. + f64toany(Neg(i32tof64(anytoi32(v))).f64) +end + +macro(:negdyn_double) do |v| + f64toany(Neg(anytof64(v)).f64) +end + +#################################################################### +# INC +# +macro(:incdyn_smi) do |v| + if self.mode.include? :IrInline + i32toany(AddOverflowCheck(anytoi32(v), 1).i32) + else + value := anytoi32(v.any).i32 + If(value, 0x7fffffff).EQ { + res_float := f64toany(2147483648.0) + } Else { + res_int := i32toany(AddI(value).Imm(1).i32) + } + Phi(res_float, res_int).any + end +end + +macro(:incdyn_double) do |v| + f64toany(Add(anytof64(v.any), i32tof64(1)).f64).any +end + +#################################################################### +# DEC +# +macro(:decdyn_smi) do |v| + if self.mode.include? :IrInline + i32toany(SubOverflowCheck(anytoi32(v), 1).i32) + else + value := anytoi32(v.any).i32 + If(value, 0x80000000).EQ { + res_float := f64toany(2147483647.0) + } Else { + res_int := i32toany(SubI(value).Imm(1).i32) + } + Phi(res_float, res_int).any + end +end + +macro(:decdyn_double) do |v| + f64toany(Sub(anytof64(v.any), i32tof64(1)).f64).any +end + +#################################################################### +# TOBOOLEAN +# +macro(:tobooleandyn_smi) do |v| + booltoany(Compare(anytoi32(v), 0).NE.b) +end + +macro(:tobooleandyn_double) do |v| + value := anytof64(v) + # Check if number is NaN by comparing it with itself + If(value, value).NE { + res_0 := 0 + } Else { + res_1 := Compare(value, 0.0).NE.b + } + booltoany(Phi(res_0, res_1).b) +end + +cpp_function(:"IrtocInlineToBooleanDyn") do + params type1: 'any' + return_type 'bool' + variant(:tobooleandyn_smi) { + condition "type1 == AnyBaseType::ECMASCRIPT_INT_TYPE" + code { Return(tobooleandyn_smi(type1)) } + } + variant(:tobooleandyn_double) { + condition "type1 == AnyBaseType::ECMASCRIPT_DOUBLE_TYPE" + code { Return(tobooleandyn_double(type1)) } + } +end + +#################################################################### +# TONUMBER +# +macro(:tonumberdyn_smi) do |v| + v +end + +macro(:tonumberdyn_double) do |v| + v +end + +cpp_function(:"IrtocInlineToNumberDyn") do + params type1: 'any' + return_type 'bool' + variant(:tonumberdyn_smi) { + condition "type1 == AnyBaseType::ECMASCRIPT_INT_TYPE" + code { Return(tonumberdyn_smi(type1)) } + } + variant(:tonumberdyn_double) { + condition "type1 == AnyBaseType::ECMASCRIPT_DOUBLE_TYPE" + code { Return(tonumberdyn_double(type1)) } + } +end + +#################################################################### +# Create macros for binary arithmetic +# +[:add, :sub, :and, :or, :xor, :mul, :div, :shl, :shr].each do |op| + macro(:"handle_ecma_#{op}2dyn") do |l, r| + IfImm(cmpanyi32(l.any).b).Imm(0).NE.b { + IfImm(cmpanyi32(r.any).b).Imm(0).NE.b { + int_res := send(:"#{op}2dyn_smi_smi", l, r) + Goto(:Exit) + } + IfImm(cmpanyf64(r.any).b).Imm(0).NE.b { + fp1_res := send(:"#{op}2dyn_smi_double", l, r) + Goto(:Exit) + } + Goto(:SlowPath) + } + + IfImm(cmpanyf64(l.any).b).Imm(0).NE.b { + IfImm(cmpanyi32(r.any).b).Imm(0).NE.b { + fp2_res := send(:"#{op}2dyn_double_smi", l, r) + Goto(:Exit) + } + IfImm(cmpanyf64(r.any).b).Imm(0).NE.b { + fp3_res := send(:"#{op}2dyn_double_double", l, r) + Goto(:Exit) + } + Goto(:SlowPath) + } + + Label(:SlowPath) + if self.mode.include? :FastPath + Intrinsic(:SLOW_PATH_ENTRY, l, r).Method("#{op.capitalize}2DynBridge", :AddImm).Relocate.Terminator.ptr + Label(:Exit) + Phi(int_res, fp1_res, fp2_res, fp3_res).any + else + slow_res := ecma_intrinsic_invoke("#{op.capitalize}2DynSlow", l, r).any + Label(:Exit) + Phi(int_res, fp1_res, fp2_res, fp3_res, slow_res).any + end + end + + function(:"Ecma#{op.capitalize}2dyn", params: {'a'=>'any', 'b'=>'any'}, mode: [:Interpreter, :DynamicMethod], enable_builder: true) do + Return(send(:"handle_ecma_#{op}2dyn", a, b)) + end + + function(:"FastPathEcma#{op.capitalize}", + params: {a: 'any', b: 'any'}, + regmap: $full_regmap, + regalloc_set: RegMask.new($full_regmap, :tmp2, :arg0, :arg1, :callee0), + mode: [:FastPath, :DynamicMethod]) do + # Arm32 is not supported + if Options.arch == :arm32 + Intrinsic(:UNREACHABLE).Terminator.void + next + end + Return(send(:"handle_ecma_#{op}2dyn", a, b)).any + end + + i_i_macro = :"#{op}2dyn_smi_smi" + i_f_macro = :"#{op}2dyn_smi_double" + f_i_macro = :"#{op}2dyn_double_smi" + f_f_macro = :"#{op}2dyn_double_double" + + cpp_function(:"IrtocInline#{op.capitalize}Dyn") do + params type1: 'any', type2: 'any' + return_type 'bool' + variant(i_i_macro) { + condition "type1 == AnyBaseType::ECMASCRIPT_INT_TYPE && type2 == AnyBaseType::ECMASCRIPT_INT_TYPE" + code { Return(send(i_i_macro, type1, type2)) } + } + variant(i_f_macro) { + condition "type1 == AnyBaseType::ECMASCRIPT_INT_TYPE && type2 == AnyBaseType::ECMASCRIPT_DOUBLE_TYPE" + code { Return(send(i_f_macro, type1, type2)) } + } + variant(f_i_macro) { + condition "type1 == AnyBaseType::ECMASCRIPT_DOUBLE_TYPE && type2 == AnyBaseType::ECMASCRIPT_INT_TYPE" + code { Return(send(f_i_macro, type1, type2)) } + } + variant(f_f_macro) { + condition "type1 == AnyBaseType::ECMASCRIPT_DOUBLE_TYPE && type2 == AnyBaseType::ECMASCRIPT_DOUBLE_TYPE" + code { Return(send(f_f_macro, type1, type2)) } + } + end +end + +#################################################################### +# Create macros for unary arithmetic +# +[:not, :neg, :inc, :dec].each do |op| + macro(:"handle_ecma_#{op}dyn") do |v| + IfImm(cmpanyi32(v.any).b).Imm(0).NE.b { + int_res := send(:"#{op}dyn_smi", v) + Goto(:Exit) + } + IfImm(cmpanyf64(v.any).b).Imm(0).NE.b { + fp_res := send(:"#{op}dyn_double", v) + Goto(:Exit) + } + + if self.mode.include? :FastPath + Intrinsic(:SLOW_PATH_ENTRY, v).Method("#{op.capitalize}DynBridge", :AddImm).Relocate.Terminator.ptr + Label(:Exit) + Phi(int_res, fp_res).any + else + slow_res := ecma_intrinsic_invoke("#{op.capitalize}DynSlow", v).any + Label(:Exit) + Phi(int_res, fp_res, slow_res).any + end + end + + function(:"Ecma#{op.capitalize}dyn", params: {'a'=>'any'}, mode: [:Interpreter, :DynamicMethod], enable_builder: true) do + Return(send(:"handle_ecma_#{op}dyn", a)) + end + + function(:"FastPathEcma#{op.capitalize}", + params: {a: 'any'}, + regmap: $full_regmap, + regalloc_set: RegMask.new($full_regmap, :tmp1, :tmp2, :arg0), + mode: [:FastPath, :DynamicMethod]) do + # Arm32 is not supported + if Options.arch == :arm32 + Intrinsic(:UNREACHABLE).Terminator.void + next + end + Return(send(:"handle_ecma_#{op}dyn", a)).any + end + + i_macro = :"#{op}dyn_smi" + f_macro = :"#{op}dyn_double" + + cpp_function(:"IrtocInline#{op.capitalize}Dyn") do + params type1: 'any' + return_type 'bool' + variant(i_macro) { + condition "type1 == AnyBaseType::ECMASCRIPT_INT_TYPE" + code { Return(send(i_macro, type1)) } + } + variant(f_macro) { + condition "type1 == AnyBaseType::ECMASCRIPT_DOUBLE_TYPE" + code { Return(send(f_macro, type1)) } + } + end +end + +#################################################################### +# Create cpp functions for compare instructions +# +[:eq, :ne, :ge, :le, :gt, :lt].each do |op| + macro(:"#{op}2dyn_smi_smi") do |l, r| + booltoany(Compare(anytoi32(l.any), anytoi32(r.any)).CC(:"CC_#{op.upcase}").b) + end + + macro(:"#{op}2dyn_smi_double") do |l, r| + booltoany(Compare(i32tof64(anytoi32(l.any)), anytof64(r.any)).CC(:"CC_#{op.upcase}").b) + end + + macro(:"#{op}2dyn_double_smi") do |l, r| + booltoany(Compare(anytof64(l.any), i32tof64(anytoi32(r.any))).CC(:"CC_#{op.upcase}").b) + end + + macro(:"#{op}2dyn_double_double") do |l, r| + booltoany(Compare(anytof64(l.any), anytof64(r.any)).CC(:"CC_#{op.upcase}").b) + end + + i_i_macro = :"#{op}2dyn_smi_smi" + i_f_macro = :"#{op}2dyn_smi_double" + f_i_macro = :"#{op}2dyn_double_smi" + f_f_macro = :"#{op}2dyn_double_double" + + cpp_function(:"IrtocInlineCompare#{op.capitalize}Dyn") do + params type1: 'any', type2: 'any' + return_type 'bool' + variant(i_i_macro) { + condition "type1 == AnyBaseType::ECMASCRIPT_INT_TYPE && type2 == AnyBaseType::ECMASCRIPT_INT_TYPE" + code { Return(send(i_i_macro, type1, type2)) } + } + variant(i_f_macro) { + condition "type1 == AnyBaseType::ECMASCRIPT_INT_TYPE && type2 == AnyBaseType::ECMASCRIPT_DOUBLE_TYPE" + code { Return(send(i_f_macro, type1, type2)) } + } + variant(f_i_macro) { + condition "type1 == AnyBaseType::ECMASCRIPT_DOUBLE_TYPE && type2 == AnyBaseType::ECMASCRIPT_INT_TYPE" + code { Return(send(f_i_macro, type1, type2)) } + } + variant(f_f_macro) { + condition "type1 == AnyBaseType::ECMASCRIPT_DOUBLE_TYPE && type2 == AnyBaseType::ECMASCRIPT_DOUBLE_TYPE" + code { Return(send(f_f_macro, type1, type2)) } + } + end +end + +# overlaps with slowpath +macro(:handle_ecma_toboolean) do |v| + booltoany(any_extractBoolean(v)) +end + +function(:EcmaToboolean, params: {'a'=>'any'}, mode: [:Interpreter, :DynamicMethod], enable_builder: true) do + Return(handle_ecma_toboolean(a)) +end + +# overlaps with slowpath +macro(:handle_ecma_lessdyn) do |l, r| + IfImm(And(cmpanyi32(l.any), cmpanyi32(r.any)).b).Imm(0).CC(:CC_EQ).b { + slow_res := ecma_intrinsic_invoke("LessDynSlow", l.u64, r.u64) + } Else { # likely i32,i32 + fp1_res := booltoany(Compare(anytoi32(l.any), anytoi32(r.any)).CC(:CC_LT).b).any + } + Phi(slow_res, fp1_res).any +end + +function(:EcmaLessdyn, params: {'a'=>'any', 'b'=>'any'}, mode: [:Interpreter, :DynamicMethod], enable_builder: true) do + Return(handle_ecma_lessdyn(a, b)) +end + +# overlaps with slowpath +macro(:handle_ecma_eqdyn) do |l, r| + If(l, r).CC(:CC_EQ).b { + IfImm(cmpanyf64(l)).Imm(0).CC(:CC_NE).b { + fp_eq_res := booltoany(Compare(anytof64(l), anytof64(l)).CC(:CC_EQ).b) + } Else { + nofp_eq_res := Constants::TAGGED_TRUE; + } + eq_res := Phi(fp_eq_res, nofp_eq_res).any + Goto(:Exit) + } + + IfImm(And(cmpanyi32(l), cmpanyi32(r)).b).Imm(0).CC(:CC_NE).b { + int_neq_res := Constants::TAGGED_FALSE; + Goto(:Exit) + } + + IfImm(Or(cmpanyundefined(r), cmpanynull(r)).b).Imm(0).CC(:CC_NE).b { + IfImm(cmpanyheapobj(l)).Imm(0).CC(:CC_NE).b { + spec_obj_neq_res := Constants::TAGGED_FALSE + Goto(:Exit) + } + + IfImm(Or(cmpanyundefined(l), cmpanynull(l)).b).Imm(0).CC(:CC_NE).b { + spec_spec_neq_res := Constants::TAGGED_TRUE + Goto(:Exit) + } + } + + slow_res := ecma_intrinsic_invoke("EqDynSlow", l, r).any +Label(:Exit) + Phi(eq_res, int_neq_res, spec_obj_neq_res, spec_spec_neq_res, slow_res).any +end + +function(:EcmaEqdyn, params: {'a'=>'any', 'b'=>'any'}, mode: [:Interpreter, :DynamicMethod], enable_builder: true) do + Return(handle_ecma_eqdyn(a, b)) +end + +# overlaps with slowpath +macro(:handle_ecma_stricteqdyn) do |l, r| + IfImm(And(cmpanyi32(l.any), cmpanyi32(r.any)).b).Imm(0).CC(:CC_EQ).b { + slow_res := ecma_intrinsic_invoke("StrictEqDynSlow", l.u64, r.u64).any + } Else { # likely i32,i32 + fp1_res := booltoany(Compare(anytoi32(l.any), anytoi32(r.any)).CC(:CC_EQ).b).any + } + Phi(slow_res, fp1_res).any +end + +function(:EcmaStricteqdyn, params: {'a'=>'any', 'b'=>'any'}, mode: [:Interpreter, :DynamicMethod], enable_builder: true) do + Return(handle_ecma_stricteqdyn(a, b)) +end + +# overlaps with slowpath +macro(:handle_ecma_strictnoteqdyn) do |l, r| + IfImm(And(cmpanyi32(l.any), cmpanyi32(r.any)).b).Imm(0).CC(:CC_EQ).b { + slow_res := ecma_intrinsic_invoke("StrictNotEqDynSlow", l.u64, r.u64).any + } Else { # likely i32,i32 + fp1_res := booltoany(Compare(anytoi32(l.any), anytoi32(r.any)).CC(:CC_NE).b).any + } + Phi(slow_res, fp1_res).any +end + +function(:EcmaStrictnoteqdyn, params: {'a'=>'any', 'b'=>'any'}, mode: [:Interpreter, :DynamicMethod], enable_builder: true) do + Return(handle_ecma_strictnoteqdyn(a, b)) +end + +# overlaps with slowpath +macro(:handle_ecma_lesseqdyn) do |l, r| + IfImm(And(cmpanyi32(l.any), cmpanyi32(r.any)).b).Imm(0).CC(:CC_EQ).b { + slow_res := ecma_intrinsic_invoke("LessEqDynSlow", l.u64, r.u64).any + } Else { # likely i32,i32 + fp1_res := booltoany(Compare(anytoi32(l.any), anytoi32(r.any)).CC(:CC_LE).b).any + } + res := Phi(slow_res, fp1_res).any +end + +function(:EcmaLesseqdyn, params: {'a'=>'any', 'b'=>'any'}, mode: [:Interpreter, :DynamicMethod], enable_builder: true) do + Return(handle_ecma_lesseqdyn(a, b)) +end + +# overlaps with slowpath +macro(:handle_ecma_greaterdyn) do |l, r| + IfImm(And(cmpanyi32(l), cmpanyi32(r)).b).Imm(0).CC(:CC_NE).b { + int_res := booltoany(Compare(anytoi32(l), anytoi32(r)).CC(:CC_GT).b) + Goto(:Exit) + } + + IfImm(And(cmpanyi32(l), cmpanyf64(r)).b).Imm(0).CC(:CC_NE).b { + fp1_res := booltoany(Compare(i32tof64(anytoi32(l)), anytof64(r)).CC(:CC_GT).b) + Goto(:Exit) + } + + IfImm(And(cmpanyf64(l), cmpanyi32(r)).b).Imm(0).CC(:CC_NE).b { + fp2_res := booltoany(Compare(anytof64(l), i32tof64(anytoi32(r))).CC(:CC_GT).b) + Goto(:Exit) + } + + IfImm(And(cmpanyf64(l), cmpanyf64(r)).b).Imm(0).CC(:CC_NE).b { + fp3_res := booltoany(Compare(anytof64(l), anytof64(r)).CC(:CC_GT).b) + Goto(:Exit) + } + + slow_res := ecma_intrinsic_invoke("GreaterDynSlow", l, r).any + +Label(:Exit) + Phi(int_res, fp1_res, fp2_res, fp3_res, slow_res).any +end + +function(:EcmaGreaterdyn, params: {'a'=>'any', 'b'=>'any'}, mode: [:Interpreter, :DynamicMethod], enable_builder: true) do + Return(handle_ecma_greaterdyn(a, b)) +end + +# overlaps with slowpath +macro(:handle_ecma_greatereqdyn) do |l, r| + IfImm(And(cmpanyi32(l.any), cmpanyi32(r.any)).b).Imm(0).CC(:CC_EQ).b { + slow_res := ecma_intrinsic_invoke("GreaterEqDynSlow", l.u64, r.u64).any + } Else { # likely i32,i32 + fp1_res := booltoany(Compare(anytoi32(l.any), anytoi32(r.any)).CC(:CC_GE).b).any + } + Phi(slow_res, fp1_res).any +end + +function(:EcmaGreatereqdyn, params: {'a'=>'any', 'b'=>'any'}, mode: [:Interpreter, :DynamicMethod], enable_builder: true) do + Return(handle_ecma_greatereqdyn(a, b)) +end + +# overlaps with slowpath +macro(:handle_ecma_noteqdyn) do |l, r| + IfImm(And(cmpanyi32(l.any), cmpanyi32(r.any)).b).Imm(0).CC(:CC_EQ).b { + slow_res := ecma_intrinsic_invoke("NotEqDynSlow", l.u64, r.u64) + } Else { # likely i32,i32 + fp1_res := booltoany(Compare(anytoi32(l.any), anytoi32(r.any)).CC(:CC_NE).b) + } + Phi(slow_res, fp1_res).any +end + +function(:EcmaNoteqdyn, params: {'a'=>'any', 'b'=>'any'}, mode: [:Interpreter, :DynamicMethod], enable_builder: true) do + Return(handle_ecma_noteqdyn(a, b)) +end + +macro(:handle_ecma_ldlexenvdyn) do || + ecmascript_env_ptr := LoadI(%tr).Imm(Constants::LANGUAGE_EXTENSION_DATA_OFFSET).ptr + lexical_env := LoadI(ecmascript_env_ptr).Imm(Constants::ECMASCRIPT_LEXICAL_ENV_OFFSET) + lexical_env.any +end + +#function(:EcmaLdlexenvdyn, params: {}, mode: [:Interpreter, :DynamicMethod], enable_builder: true) do +# Return(handle_ecma_ldlexenvdyn()) +# end + +function(:EcmaLdnull, params: {}, mode: [:Interpreter, :DynamicMethod], enable_builder: true) do + Return(Constants::TAGGED_NULL) +end + +function(:EcmaLdhole, params: {}, mode: [:Interpreter, :DynamicMethod], enable_builder: true) do + Return(Constants::TAGGED_HOLE) +end + +function(:EcmaLdundefined, params: {}, mode: [:Interpreter, :DynamicMethod], enable_builder: true) do + Return(Constants::TAGGED_UNDEFINED) +end + +function(:EcmaLdinfinity, params: {}, mode: [:Interpreter, :DynamicMethod], enable_builder: true) do + Return(Constants::TAGGED_INFINITY) +end + +function(:EcmaLdnan, params: {}, mode: [:Interpreter, :DynamicMethod], enable_builder: true) do + Return(Constants::TAGGED_NAN_VALUE) +end + +macro(:handle_ecma_ldconst) do |const| + set_value(acc_ptr, const).any +end + +macro(:handle_ecma_getnextpropname) do |iter| + # Get count of prepaired iterator for fast steps. + iter_ptr := anytoheapobj(iter.any) + + fast_rem_tag := LoadObject(iter_ptr) + .ObjField("runtime->GetFieldByOffset(#{Constants::ECMASCRIPT_JSFORINITERATOR_FAST_REMAINING_INDEX_OFFSET})") + .any + + fast_rem := CastAnyTypeValue(fast_rem_tag).AnyType("AnyBaseType::ECMASCRIPT_INT_TYPE").i32 + + If(fast_rem, 0).CC(:CC_EQ).b { + pc_slow_path := Call(iter).Method("GetNextPropNameSlow").any + } Else { + # Get remaining array. + remaining_tag := LoadObject(iter_ptr) + .ObjField("runtime->GetFieldByOffset(#{Constants::ECMASCRIPT_JSFORINITERATOR_REMAINING_KEYS_OFFSET})") + .any + + remaining_ptr := anytoheapobj(remaining_tag) + + # Get remaining start position. + remaining_start_pos_tag := LoadArray(remaining_ptr, Constants::ECMASCRIPT_TAGGEDQUEUE_START_INDEX).any + + remaining_start_pos := CastAnyTypeValue(remaining_start_pos_tag) + .AnyType("AnyBaseType::ECMASCRIPT_INT_TYPE") + .i32 + + # Get remaining size. + remaining_end_pos_tag := LoadArray(remaining_ptr, Constants::ECMASCRIPT_TAGGEDQUEUE_END_INDEX).any + + remaining_end_pos := CastAnyTypeValue(remaining_end_pos_tag) + .AnyType("AnyBaseType::ECMASCRIPT_INT_TYPE") + .i32 + + remaining_size := Sub(remaining_end_pos, remaining_start_pos).i32 + + # Calculate next position. + tmp_new_pos := Add(remaining_size, remaining_start_pos).i32 + new_pos := Sub(tmp_new_pos, fast_rem).i32 + + # Decrease fast_rem and update it. + new_fast_rem := SubI(fast_rem).Imm(1).i32 + new_fast_rem_tag := CastValueToAnyType(new_fast_rem) + .AnyType("AnyBaseType::ECMASCRIPT_INT_TYPE") + .any + + StoreObject(iter_ptr, new_fast_rem_tag) + .ObjField("runtime->GetFieldByOffset(#{Constants::ECMASCRIPT_JSFORINITERATOR_FAST_REMAINING_INDEX_OFFSET})") + .any + + # Get next key at new_pos index. + pc_fast_path := LoadArray(remaining_ptr, AddI(new_pos).Imm(Constants::ECMASCRIPT_TAGGEDQUEUE_ELEMENTS_START_INDEX).i32).any + } + + Phi(pc_slow_path, pc_fast_path).any +end + +function(:EcmaGetnextpropname, params: {'a'=>'any'}, mode: [:Interpreter, :DynamicMethod], enable_builder: true) do + Return(handle_ecma_getnextpropname(a)) +end + +macro(:handle_ecma_jcconst) do |const, pc, imm32, size| + If(acc_value.u64, const).CC(:CC_EQ).b { + pc1 := advance_pc_var(pc, i32tou64(imm32)) + } Else { + pc2 := advance_pc_imm(pc, size) + } + Phi(pc1, pc2).ptr +end + +macro(:handle_ecma_negate) do |arg| + booltoany(AndI(Not(any_extractBoolean(arg)).b).Imm(1).b) +end + +function(:EcmaNegate, params: {'a'=>'any'}, mode: [:Interpreter, :DynamicMethod], enable_builder: true) do + Return(handle_ecma_negate(a)) +end + +macro(:handle_ecma_isundefined) do |arg| + booltoany(cmpanyundefined(arg)) +end + +function(:EcmaIsundefined, params: {'a'=>'any'}, mode: [:Interpreter, :DynamicMethod], enable_builder: true) do + Return(handle_ecma_isundefined(a)) +end + +function(:EcmaLdtrue, params: {}, mode: [:Interpreter, :DynamicMethod], enable_builder: true) do + Return(Constants::TAGGED_TRUE) +end + +function(:EcmaLdfalse, params: {}, mode: [:Interpreter, :DynamicMethod], enable_builder: true) do + Return(Constants::TAGGED_FALSE) +end + +###################################################### + +# Dynamic object loads/stores + +macro(:cmpanyarr) do |arg| + CompareAnyType(arg).AnyType("AnyBaseType::ECMASCRIPT_ARRAY_TYPE").b +end + +macro(:cmpanytranshandler) do |arg| + CompareAnyType(arg).AnyType("AnyBaseType::ECMASCRIPT_TRANSITION_HANDLER_TYPE").b +end + +macro(:cmpanyprotohandler) do |arg| + CompareAnyType(arg).AnyType("AnyBaseType::ECMASCRIPT_PROTOTYPE_HANDLER_TYPE").b +end + +macro(:cmpanyspecialindexedobj) do |arg| + CompareAnyType(arg).AnyType("AnyBaseType::ECMASCRIPT_SPECIAL_INDEXED_TYPE").b +end + +macro(:get_class) do |arg| + Intrinsic(:DYN_OBJECT_GET_CLASS, arg).ref +end + +macro(:set_class) do |obj, cls| + Intrinsic(:DYN_OBJECT_SET_CLASS, obj, cls).void +end + +macro(:get_bits) do |data, offset, mask| + AndI(ShrI(data).Imm(offset).i32).Imm(mask).i32 +end + +macro(:get_this_func) do || + Intrinsic(:GET_ECMA_THIS_FUNC).ref +end + +macro(:get_ic) do || + LoadObject(get_this_func()).ObjField(Constants::JS_FUNCTION_PROFILE_TYPE_INFO_FIELD).any +end + +macro(:access_global_var_ic) do |key, value, ic_slot, access| + ic := get_ic() + IfImm(cmpanyundefined(ic)).Imm(0).CC(:CC_EQ).b { + handler := LoadArray(anytoheapobj(ic), ic_slot).any + IfImm(cmpanyheapobj(handler)).Imm(0).CC(:CC_NE).b { + r = access.call(anytoheapobj(handler), value) + r00 := r + } Else { + r01 := Constants::TAGGED_HOLE + } + r0 := Phi(r00, r01).any + } Else { + r1 := Constants::TAGGED_HOLE + } + Phi(r0, r1).any +end + +macro(:load_global_var_ic) do |key, ic_slot| + access_global_var_ic(key, nil, ic_slot, lambda do |box, _| + LoadObject(box).ObjField(Constants::JS_PROPERTY_BOX_VALUE_FIELD).any + end) +end + +macro(:store_global_var_ic) do |key, value, ic_slot| + access_global_var_ic(key, value, ic_slot, lambda do |box, value| + IfImm(cmpanyheapobj(value)).Imm(0).CC(:CC_EQ).b { + StoreObject(box, value).ObjField(Constants::JS_PROPERTY_BOX_VALUE_FIELD).any + } Else { + StoreObject(box, value).ObjField(Constants::JS_PROPERTY_BOX_VALUE_FIELD).SetNeedBarrier(true).any + } + NOP() + Constants::TAGGED_UNDEFINED + end) +end + +macro(:get_weak_referent) do |arg| + Intrinsic(:GET_WEAK_REFERENT, arg).ref +end + +macro(:access_obj_ic_with_handler) do |obj, handler, value, access_inlined, access_props| + IfImm(ic_handler_is_field(handler)).Imm(0).CC(:CC_NE).b { + index := ic_handler_get_offset(handler) + IfImm(ic_handler_is_inlined(handler)).Imm(0).CC(:CC_NE).b { + r = access_inlined.call(anytoheapobj(obj), index, value) + inl_res := r + } Else { + properties := LoadObject(anytoheapobj(obj)).ObjField(Constants::JS_OBJECT_PROPERTIES_FIELD).any + r = access_props.call(anytoheapobj(properties), index, value) + prop_res := r + } + field_res := Phi(inl_res, prop_res).any + } Else { + acc_res := Constants::TAGGED_HOLE + } + Phi(field_res, acc_res).any +end + +macro(:load_obj_dyn) do |obj, index| + Load(obj, Mul(index, Constants::TAGGED_TYPE_SIZE).i32).any +end + +macro(:access_obj_ic_with_prototype) do |obj, handler, value, access_inlined, access_props| + cell := anytoheapobj(LoadObject(handler).ObjField(Constants::JS_PROTOTYPE_HANDLER_PROTO_CELL_FIELD).any) + has_changed := LoadObject(cell).ObjField(Constants::JS_PROTO_CHANGE_MARKER_HAS_CHANGED_FIELD).b + IfImm(has_changed).Imm(0).CC(:CC_EQ).b { + handler_info := LoadObject(handler).ObjField(Constants::JS_PROTOTYPE_HANDLER_HANDLER_INFO_FIELD).any + IfImm(cmpanyi32(handler_info)).Imm(0).CC(:CC_NE).b { + holder := LoadObject(handler).ObjField(Constants::JS_PROTOTYPE_HANDLER_HOLDER_FIELD).any + r00 := access_obj_ic_with_handler(holder, anytoi32(handler_info), value, access_inlined, access_props) + } Else { + r01 := Constants::TAGGED_HOLE + } + r0 := Phi(r00, r01).any + } Else { + r1 := Constants::TAGGED_HOLE + } + Phi(r0, r1).any +end + +macro(:load_obj_ic_with_handler) do |obj, handler| + IfImm(cmpanyi32(handler)).Imm(0).CC(:CC_NE).b { + res0 := access_obj_ic_with_handler(obj, anytoi32(handler), nil, + lambda do |obj, index, _| + load_obj_dyn(obj, index) + end, + lambda do |props, index, _| + LoadArray(props, index).any + end) + Goto(:ExitLoad) + } + + IfImm(cmpanyprotohandler(handler)).Imm(0).CC(:CC_NE).b { + res1 := access_obj_ic_with_prototype(obj, anytoheapobj(handler), nil, + lambda do |obj, index, _| + load_obj_dyn(obj, index) + end, + lambda do |props, index, _| + LoadArray(props, index).any + end) + Goto(:ExitLoad) + } + + res2 := Constants::TAGGED_HOLE + NOP() + +Label(:ExitLoad) + Phi(res0, res1, res2).any +end + +macro(:store_obj_dyn) do |obj, index, value| + IfImm(cmpanyheapobj(value)).Imm(0).CC(:CC_EQ).b { + Store(obj, Mul(index, Constants::TAGGED_TYPE_SIZE).i32, value).any + } Else { + Store(obj, Mul(index, Constants::TAGGED_TYPE_SIZE).i32, value).SetNeedBarrier(true).any + } + NOP() +end + +macro(:store_element) do |elements, index, value| + IfImm(cmpanyheapobj(value)).Imm(0).CC(:CC_EQ).b { + StoreArray(elements, index, value).any + } Else { + StoreArray(elements, index, value).SetNeedBarrier(true).any + } +end + +macro(:store_obj_ic_with_transition) do |obj, handler, value| + handler_info := LoadObject(handler).ObjField(Constants::JS_TRANSITION_HANDLER_HANDLER_INFO_FEILD).any + res := access_obj_ic_with_handler(obj, anytoi32(handler_info), value, + lambda do |obj, index, value| + store_obj_dyn(obj, index, value) + Constants::TAGGED_UNDEFINED + end, + lambda do |props, index, value| + len := LenArray(props).i32 + If(index, len).CC(:CC_LT).b { + store_element(props, index, value) + NOP() + r0 := Constants::TAGGED_UNDEFINED + } Else { + r1 := Constants::TAGGED_HOLE + } + Phi(r0, r1).any + end) + + IfImm(cmpanyhole(res)).Imm(0).CC(:CC_EQ).b { + new_hclass := anytoheapobj(LoadObject(handler).ObjField(Constants::JS_TRANSITION_HANDLER_HCLASS_FEILD).any) + set_class(anytoheapobj(obj), new_hclass) + } + + res +end + +macro(:store_obj_ic_with_handler) do |obj, handler, value| + IfImm(cmpanyi32(handler)).Imm(0).CC(:CC_NE).b { + res0 := access_obj_ic_with_handler(obj, anytoi32(handler), value, + lambda do |obj, index, value| + store_obj_dyn(obj, index, value) + Constants::TAGGED_UNDEFINED + end, + lambda do |props, index, value| + store_element(props, index, value) + NOP() + Constants::TAGGED_UNDEFINED + end) + Goto(:ExitStore) + } + + IfImm(cmpanytranshandler(handler)).Imm(0).CC(:CC_NE).b { + res1 := store_obj_ic_with_transition(obj, anytoheapobj(handler), value) + Goto(:ExitStore) + } + + res2 := Constants::TAGGED_HOLE + NOP() + +Label(:ExitStore) + Phi(res0, res1, res2).any +end + +macro(:access_obj_ic) do |obj, value, ic_slot, access| + IfImm(cmpanyheapobj(obj)).Imm(0).CC(:CC_EQ).b { + Goto(:Miss) + } + + ic := get_ic() + IfImm(cmpanyundefined(ic)).Imm(0).CC(:CC_NE).b { + Goto(:Miss) + } + + ic_cls := LoadArray(anytoheapobj(ic), ic_slot).any + IfImm(cmpanyheapobj(ic_cls)).Imm(0).CC(:CC_EQ).b { + Goto(:Miss) + } + + cls := get_class(obj) + If(get_weak_referent(ic_cls), cls).CC(:CC_NE).b { + Goto(:Miss) + } + + handler := LoadArray(anytoheapobj(ic), AddI(ic_slot).Imm(1).i32).any + r = access.call(obj, handler, value) + res := r + Goto(:Exit) + +Label(:Miss) + miss_res := Constants::TAGGED_HOLE + +Label(:Exit) + Phi(res, miss_res).any +end + +macro(:try_load_obj_ic) do |obj, ic_slot| + access_obj_ic(obj, nil, ic_slot, lambda do |obj, handler, _| + load_obj_ic_with_handler(obj, handler) + end) +end + +macro(:try_store_obj_ic) do |obj, value, ic_slot| + access_obj_ic(obj, value, ic_slot, lambda do |obj, handler, value| + store_obj_ic_with_handler(obj, handler, value) + end) +end + +macro(:ic_handler_get_kind) do |handler| + get_bits(handler, Constants::IC_HANDLER_KIND_BIT, Constants::IC_HANDLER_KIND_MASK) +end + +macro(:ic_handler_is_field) do |handler| + kind := ic_handler_get_kind(handler) + Compare(kind, Constants::IC_HANDLER_KIND_FIELD).CC(:CC_EQ).b +end + +macro(:ic_handler_is_inlined) do |handler| + get_bits(handler, Constants::IC_HANDLER_INLINE_BIT, Constants::IC_HANDLER_INLINE_MASK) +end + +macro(:ic_handler_get_offset) do |handler| + get_bits(handler, Constants::IC_HANDLER_OFFSET_BIT, Constants::IC_HANDLER_OFFSET_MASK) +end + +macro(:is_dictionary_element) do |arg| + cls := Intrinsic(:DYN_OBJECT_GET_CLASS, arg).ref + Intrinsic(:DYN_CLASS_IS_DICTIONARY_ELEMENT, cls).b +end + +macro(:is_extensible) do |arg| + cls := Intrinsic(:DYN_OBJECT_GET_CLASS, arg).ref + Intrinsic(:DYN_CLASS_IS_EXTENSIBLE, cls).b +end + +macro(:handle_access_object_dynamic_by_index) do |obj, index, value, access| + IfImm(Or(is_dictionary_element(obj), cmpanyspecialindexedobj(obj)).b).Imm(0).CC(:CC_EQ).b { + elements := LoadObject(anytoheapobj(obj)).ObjField(Constants::JS_OBJECT_ELEMENTS_FIELD).ref + length := i32tou32(LenArray(elements).i32) + If(index, length).CC(:CC_B).b { + res = access.call(elements, index, value) + access_res := res + } Else { + oob_res := Constants::TAGGED_HOLE + } + array_res := Phi(access_res, oob_res).any + } Else { + dict_res := Constants::TAGGED_HOLE + } + Phi(array_res, dict_res).any +end + +macro(:handle_access_object_dynamic) do |obj, key, value, array_access, obj_access| + IfImm(cmpanyi32(key)).Imm(0).CC(:CC_NE) { + byindex_res := handle_access_object_dynamic_by_index(obj, anytou32(key), value, array_access) + } Else { + res = obj_access.call(obj, key, value) + byvalue_res := res + } + Phi(byindex_res, byvalue_res).any +end + +macro(:handle_load_object_dynamic) do |obj, key, ic_slot| + handle_access_object_dynamic(obj, key, nil, + lambda do |elements, index, _| + LoadArray(elements, index).any + end, + lambda do |obj, key, _| + unless ic_slot.nil? + try_load_obj_ic(obj, ic_slot) + else + Constants::TAGGED_HOLE + end + end) +end + +macro(:handle_load_object_dynamic_by_index) do |obj, key| + handle_access_object_dynamic_by_index(obj, key, nil, + lambda do |elements, index, _| + LoadArray(elements, index).any + end) +end + +macro(:get_array_length) do |obj| + len := LoadObject(anytoheapobj(obj)).ObjField(Constants::JS_ARRAY_LENGTH_FIELD).any + IfImm(cmpanyi32(len)).Imm(0).CC(:CC_NE) { + res1 := anytou32(len) + } Else { + res2 := f64tou32(anytof64(len)) + } + Phi(res1, res2).u32 +end + +macro(:set_array_length) do |obj, length| + StoreObject(anytoheapobj(obj), i32toany(length)).ObjField(Constants::JS_ARRAY_LENGTH_FIELD).any +end + +macro(:store_value_to_elements) do |obj, elements, index, value| + element := LoadArray(elements, index).any + IfImm(cmpanyhole(element)).Imm(0).CC(:CC_EQ).b { + store_element(elements, index, value) + store_res := Constants::TAGGED_UNDEFINED + NOP() + } Else { + IfImm(is_extensible(obj)).Imm(0).CC(:CC_NE).b { + IfImm(cmpanyarr(obj)).Imm(0).CC(:CC_NE).b { + len := get_array_length(obj) + If(index, len).CC(:CC_AE).b { + set_array_length(obj, AddI(index).Imm(1).i32) + } + } + store_element(elements, index, value) + hole_res1 := Constants::TAGGED_UNDEFINED + NOP() + } Else { + hole_res2 := Constants::TAGGED_HOLE + } + hole_res := Phi(hole_res1, hole_res2).any + NOP() + } + Phi(store_res, hole_res).any +end + +macro(:handle_store_object_dynamic) do |obj, key, value, ic_slot| + handle_access_object_dynamic(obj, key, value, + lambda do |elements, index, value| + store_value_to_elements(obj, elements, index, value) + end, + lambda do |obj, _, value| + unless ic_slot.nil? + try_store_obj_ic(obj, value, ic_slot) + else + Constants::TAGGED_HOLE + end + end) +end + +macro(:handle_store_object_dynamic_by_index) do |obj, key, value| + handle_access_object_dynamic_by_index(obj, key, value, + lambda do |elements, index, value| + store_value_to_elements(obj, elements, index, value) + end) +end + +macro(:handle_ecma_ldobjbyindex) do |index, obj| + IfImm(cmpanyheapobj(obj)).Imm(0).CC(:CC_NE).b { + load_res := handle_load_object_dynamic_by_index(obj, index) + IfImm(cmpanyhole(load_res)).Imm(0).CC(:CC_NE) { + slow_res := ecma_intrinsic_invoke("LdObjByIndexSlow", index, obj).any + } + res := Phi(load_res, slow_res).any + } Else { + slow_res_exception := ecma_intrinsic_invoke("LdObjByIndexSlow", index, obj).any + } + Phi(res, slow_res_exception).any +end + +function(:EcmaLdobjbyindex, params: {'index'=>'i32', 'obj'=>'any'}, mode: [:Interpreter, :DynamicMethod], enable_builder: true) do + Return(handle_ecma_ldobjbyindex(index, obj)) +end + +macro(:handle_ecma_stobjbyindex) do |index, obj, value| + IfImm(cmpanyheapobj(obj)).Imm(0).CC(:CC_NE).b { + store_res := handle_store_object_dynamic_by_index(obj, index, value) + IfImm(cmpanyhole(store_res)).Imm(0).CC(:CC_NE) { + ecma_intrinsic_invoke("StObjByIndexSlow", index, obj, value).any + } + } Else { + ecma_intrinsic_invoke("StObjByIndexSlow", index, obj, value).any + } +end + +function(:EcmaStobjbyindex, params: {'index'=>'i32', 'obj'=>'any', 'value'=>'any'}, mode: [:Interpreter, :DynamicMethod], enable_builder: true) do + handle_ecma_stobjbyindex(index, obj, value) + ReturnVoid() +end + +macro(:handle_ecma_stownbyindex) do |index, obj, value| + IfImm(cmpanyheapobj(obj)).Imm(0).CC(:CC_NE).b { + store_res := handle_store_object_dynamic_by_index(obj, index, value) + IfImm(cmpanyhole(store_res)).Imm(0).CC(:CC_NE) { + ecma_intrinsic_invoke("StOwnByIndexSlow", index, obj, value).any + } + } Else { + ecma_intrinsic_invoke("StOwnByIndexSlow", index, obj, value).any + } +end + +function(:EcmaStownbyindex, params: {'index'=>'i32', 'obj'=>'any', 'value'=>'any'}, mode: [:Interpreter, :DynamicMethod], enable_builder: true) do + handle_ecma_stownbyindex(index, obj, value) + ReturnVoid() +end + +macro(:handle_ecma_ldobjbyvalue) do |obj, key, ic_slot| + IfImm(cmpanyheapobj(obj)).Imm(0).CC(:CC_NE).b { + load_res := handle_load_object_dynamic(obj, key, ic_slot) + IfImm(cmpanyhole(load_res)).Imm(0).CC(:CC_NE) { + slow_res := ecma_intrinsic_invoke("LdObjByValueSlow", obj, key, ic_slot).any + } + res := Phi(load_res, slow_res).any + } Else { + slow_res_exception := ecma_intrinsic_invoke("LdObjByValueSlow", obj, key, ic_slot).any + } + Phi(res, slow_res_exception).any +end + +function(:EcmaLdobjbyvalue, params: {'obj'=>'any', 'key'=>'any', 'ic_slot'=>'i32'}, mode: [:Interpreter, :DynamicMethod], enable_builder: true) do + Return(handle_ecma_ldobjbyvalue(obj, key, ic_slot)) + end + +macro(:handle_ecma_stobjbyvalue) do |obj, key, value, ic_slot| + IfImm(cmpanyheapobj(obj)).Imm(0).CC(:CC_NE).b { + store_res := handle_store_object_dynamic(obj, key, value, ic_slot) + IfImm(cmpanyhole(store_res)).Imm(0).CC(:CC_NE) { + ecma_intrinsic_invoke("StObjByValueSlow", obj, key, value, ic_slot).any + } + } Else { + ecma_intrinsic_invoke("StObjByValueSlow", obj, key, value, ic_slot).any + } +end + +function(:EcmaStobjbyvalue, params: {'obj'=>'any', 'key'=>'any', 'value'=>'any', 'ic_slot'=>'i32'}, mode: [:Interpreter, :DynamicMethod], enable_builder: true) do + handle_ecma_stobjbyvalue(obj, key, value, ic_slot) + ReturnVoid() +end + +macro(:resolve_id) do |id| + cp := Intrinsic(:GET_ECMA_CONSTANT_POOL).ref + LoadArray(cp, id).any +end + +macro(:handle_ecma_ldobjbyname) do |obj, id, ic_slot| + key := resolve_id(id) + IfImm(cmpanyheapobj(obj)).Imm(0).CC(:CC_NE).b { + load_res := handle_load_object_dynamic(obj, key, ic_slot) + IfImm(cmpanyhole(load_res)).Imm(0).CC(:CC_NE).b { + slow_res := ecma_intrinsic_invoke("LdObjByNameSlow", id, obj, ic_slot).any + } + res := Phi(load_res, slow_res).any + } Else { + slow_res_exception := ecma_intrinsic_invoke("LdObjByNameSlow", id, obj, ic_slot).any + } + Phi(res, slow_res_exception).any +end + +function(:EcmaLdobjbyname, params: {'id'=>'i32', 'obj'=>'any','ic_slot'=>'i32'}, mode: [:Interpreter, :DynamicMethod], enable_builder: true) do + Return(handle_ecma_ldobjbyname(obj, id, ic_slot)) +end + +macro(:handle_ecma_stobjbyname) do |obj, id, value, ic_slot| + key := resolve_id(id) + IfImm(cmpanyheapobj(obj)).Imm(0).CC(:CC_NE).b { + store_res := handle_store_object_dynamic(obj, key, value, ic_slot) + IfImm(cmpanyhole(store_res)).Imm(0).CC(:CC_NE).b { + ecma_intrinsic_invoke("StObjByNameSlow", id, obj, value, ic_slot).any + } + } Else { + ecma_intrinsic_invoke("StObjByNameSlow", id, obj, value, ic_slot).any + } +end + +function(:EcmaStobjbyname, params: {'id'=>'i32', 'obj'=>'any', 'value'=>'any', 'ic_slot'=>'i32'}, mode: [:Interpreter, :DynamicMethod], enable_builder: true) do + handle_ecma_stobjbyname(obj, id, value, ic_slot) + ReturnVoid() +end + +macro(:handle_ecma_tryldglobalbyname) do |id, ic_slot| + key := resolve_id(id) + ic_res := load_global_var_ic(key, ic_slot) + IfImm(cmpanyhole(ic_res)).Imm(0).CC(:CC_EQ).b { + res0 := ic_res + } Else { + res1 := ecma_intrinsic_invoke("TryLdGlobalByNameSlow", id, ic_slot).any + } + Phi(res0, res1).any +end + +function(:EcmaTryldglobalbyname, params: {'id'=>'i32', 'ic_slot'=>'i32'}, mode: [:Interpreter, :DynamicMethod], enable_builder: true) do + Return(handle_ecma_tryldglobalbyname(id, ic_slot)) +end + +macro(:handle_ecma_ldglobalvar) do |id, ic_slot| + key := resolve_id(id) + ic_res := load_global_var_ic(key, ic_slot) + IfImm(cmpanyhole(ic_res)).Imm(0).CC(:CC_EQ).b { + res0 := ic_res + } Else { + res1 := ecma_intrinsic_invoke("LdGlobalVarSlow", id, ic_slot).any + } + Phi(res0, res1).any +end + +function(:EcmaLdglobalvar, params: {'id'=>'i32', 'ic_slot'=>'i32'}, mode: [:Interpreter, :DynamicMethod], enable_builder: true) do + Return(handle_ecma_ldglobalvar(id, ic_slot)) +end + +macro(:handle_ecma_stglobalvar) do |id, value, ic_slot| + key := resolve_id(id) + ic_res := store_global_var_ic(key, value, ic_slot) + IfImm(cmpanyhole(ic_res)).Imm(0).CC(:CC_EQ).b { + res0 := ic_res + } Else { + res1 := ecma_intrinsic_invoke("StGlobalVarSlow", id, value, ic_slot).any + } + Phi(res0, res1).any +end + +function(:EcmaStglobalvar, params: {'id'=>'i32', 'value'=>'any', 'ic_slot'=>'i32'}, mode: [:Interpreter, :DynamicMethod], enable_builder: true) do + handle_ecma_stglobalvar(id, value, ic_slot) + ReturnVoid() +end + +###################################################### diff --git a/irtoc_scripts/interpreter_main_loop.irt b/irtoc_scripts/interpreter_main_loop.irt new file mode 100644 index 000000000..d28e1dcf0 --- /dev/null +++ b/irtoc_scripts/interpreter_main_loop.irt @@ -0,0 +1,248 @@ +# plugin interpreter_main_loop +# Copyright (c) 2021-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. + + # ecma basic + + when "ECMA_ADD2DYN_PREF_V8" + res := handle_ecma_add2dyn(vreg_value(op[0]), acc_value) + set_value(acc_ptr, res).any + #ecma_intrinsic_setacc("Add2Dyn", vreg_value(op[0]).u64, acc_value.u64) # slow version + when "ECMA_MOD2DYN_PREF_V8" + res := handle_ecma_mod2dyn(vreg_value(op[0]).any, acc_value.any) + set_value(acc_ptr, res).any + when "ECMA_SUB2DYN_PREF_V8" + res := handle_ecma_sub2dyn(vreg_value(op[0]), acc_value) + set_value(acc_ptr, res).any + #ecma_intrinsic_setacc("Sub2Dyn", vreg_value(op[0]).u64, acc_value.u64) + when "ECMA_MUL2DYN_PREF_V8" + res := handle_ecma_mul2dyn(vreg_value(op[0]), acc_value) + set_value(acc_ptr, res).any + #ecma_intrinsic_setacc("Mul2Dyn", vreg_value(op[0]).u64, acc_value.u64) + when "ECMA_DIV2DYN_PREF_V8" + res := handle_ecma_div2dyn(vreg_value(op[0]), acc_value) + set_value(acc_ptr, res).any + #ecma_intrinsic_setacc("Mul2Dyn", vreg_value(op[0]).u64, acc_value.u64) + when "ECMA_EXPDYN_PREF_V8" + ecma_intrinsic_setacc("ExpDynSlow", vreg_value(op[0]).u64, acc_value.u64) + #handle_ecma_expdyn(vreg_value(op[0]), acc_value) + when "ECMA_AND2DYN_PREF_V8" + res := handle_ecma_and2dyn(vreg_value(op[0]).any, acc_value.any) + set_value(acc_ptr, res).any + #ecma_intrinsic_setacc("And2Dyn", vreg_value(op[0]).u64, acc_value.u64) + when "ECMA_OR2DYN_PREF_V8" + res := handle_ecma_or2dyn(vreg_value(op[0]), acc_value) + set_value(acc_ptr, res).any + when "ECMA_XOR2DYN_PREF_V8" + res := handle_ecma_xor2dyn(vreg_value(op[0]), acc_value) + set_value(acc_ptr, res).any + #ecma_intrinsic_setacc("And2Dyn", vreg_value(op[0]).u64, acc_value.u64) + when "ECMA_NOTDYN_PREF_V8" + res := handle_ecma_notdyn(vreg_value(op[0]).any) + set_value(acc_ptr, res).any + when "ECMA_SHL2DYN_PREF_V8" + res := handle_ecma_shl2dyn(vreg_value(op[0]), acc_value) + set_value(acc_ptr, res).any + # ecma_intrinsic_setacc("Shl2Dyn", vreg_value(op[0]).u64, acc_value.u64) + when "ECMA_SHR2DYN_PREF_V8" + res := handle_ecma_shr2dyn(vreg_value(op[0]), acc_value) + set_value(acc_ptr, res).any + # ecma_intrinsic_setacc("Shr2Dyn", vreg_value(op[0]).u64, acc_value.u64) + when "ECMA_STRICTEQDYN_PREF_V8" + res := handle_ecma_stricteqdyn(vreg_value(op[0]), acc_value) + set_value(acc_ptr, res).any + when "ECMA_STRICTNOTEQDYN_PREF_V8" + res := handle_ecma_strictnoteqdyn(vreg_value(op[0]), acc_value) + set_value(acc_ptr, res).any + when "ECMA_EQDYN_PREF_V8" + res := handle_ecma_eqdyn(vreg_value(op[0]).any, acc_value.any) + set_value(acc_ptr, res).any + #ecma_intrinsic_setacc("EqDyn", vreg_value(op[0]).u64, acc_value.u64) + when "ECMA_NOTEQDYN_PREF_V8" + res := handle_ecma_noteqdyn(vreg_value(op[0]), acc_value) + set_value(acc_ptr, res).any + #ecma_intrinsic_setacc("NotEqDyn", vreg_value(op[0]).u64, acc_value.u64) + when "ECMA_LESSDYN_PREF_V8" + res := handle_ecma_lessdyn(vreg_value(op[0]), acc_value) + set_value(acc_ptr, res).any + #ecma_intrinsic_setacc("LessDyn", vreg_value(op[0]).u64, acc_value.u64) + when "ECMA_LESSEQDYN_PREF_V8" + res := handle_ecma_lesseqdyn(vreg_value(op[0]), acc_value) + set_value(acc_ptr, res).any + #ecma_intrinsic_setacc("LessEqDyn", vreg_value(op[0]).u64, acc_value.u64) + when "ECMA_GREATERDYN_PREF_V8" + #ecma_intrinsic_setacc("GreaterDyn", vreg_value(op[0]).u64, acc_value.u64) + res := handle_ecma_greaterdyn(vreg_value(op[0]).any, acc_value.any) + set_value(acc_ptr, res).any + when "ECMA_GREATEREQDYN_PREF_V8" + res := handle_ecma_greatereqdyn(vreg_value(op[0]), acc_value) + set_value(acc_ptr, res).any + when "ECMA_INCDYN_PREF_V8" + res := handle_ecma_incdyn(vreg_value(op[0])) + set_value(acc_ptr, res).any + #ecma_intrinsic_setacc("IncDyn", vreg_value(op[0]).u64) + when "ECMA_DECDYN_PREF_V8" + res := handle_ecma_decdyn(vreg_value(op[0])) + set_value(acc_ptr, res).any + when "ECMA_NEGDYN_PREF_V8" + res := handle_ecma_negdyn(vreg_value(op[0]).any) + set_value(acc_ptr, res).any + #ecma_intrinsic_setacc("NegDyn", vreg_value(op[0]).u64) + when "ECMA_TONUMBER_PREF_V8" + res := handle_ecma_tonumber(vreg_value(op[0])) + set_value(acc_ptr, res).any + #ecma_intrinsic_setacc("Tonumber", vreg_value(op[0]).u64) + when "ECMA_TOBOOLEAN_PREF_NONE" + res := handle_ecma_toboolean(acc_value.any) + set_value(acc_ptr, res).any + #ecma_intrinsic_setacc("Toboolean", acc_value.u64) + when "ECMA_LDTRUE_PREF_NONE" + handle_ecma_ldconst(Constants::VALUE_TRUE) + when "ECMA_LDFALSE_PREF_NONE" + handle_ecma_ldconst(Constants::VALUE_FALSE) + when "ECMA_LDUNDEFINED_PREF_NONE" + handle_ecma_ldconst(Constants::VALUE_UNDEFINED) + when "ECMA_LDINFINITY_PREF_NONE" + ecma_intrinsic_setacc("Ldinfinity") + when "ECMA_LDNULL_PREF_NONE" + handle_ecma_ldconst(Constants::VALUE_NULL) + when "ECMA_LDHOLE_PREF_NONE" + handle_ecma_ldconst(Constants::VALUE_HOLE) + when "ECMA_JTRUE_PREF_IMM8" + pc := handle_ecma_jcconst(Constants::VALUE_TRUE, pc, i8toi32(as_imm(op[0])), i.format.size) + when "ECMA_JTRUE_PREF_IMM16" + pc := handle_ecma_jcconst(Constants::VALUE_TRUE, pc, i16toi32(as_imm(op[0])), i.format.size) + when "ECMA_JTRUE_PREF_IMM32" + pc := handle_ecma_jcconst(Constants::VALUE_TRUE, pc, as_imm(op[0]), i.format.size) + when "ECMA_JFALSE_PREF_IMM8" + pc := handle_ecma_jcconst(Constants::VALUE_FALSE, pc, i8toi32(as_imm(op[0])), i.format.size) + when "ECMA_JFALSE_PREF_IMM16" + pc := handle_ecma_jcconst(Constants::VALUE_FALSE, pc, i16toi32(as_imm(op[0])), i.format.size) + when "ECMA_JFALSE_PREF_IMM32" + pc := handle_ecma_jcconst(Constants::VALUE_FALSE, pc, as_imm(op[0]), i.format.size) + when "ECMA_NEGATE_PREF_NONE" + res := handle_ecma_negate(acc_value.any) + set_value(acc_ptr, res).any + when "ECMA_LDNAN_PREF_NONE" + ecma_intrinsic_setacc("Ldnan") + when "ECMA_ISUNDEFINED_PREF_NONE" + res := handle_ecma_isundefined(acc_value.any) + set_value(acc_ptr, res).any + when "ECMA_CREATEOBJECTWITHBUFFER_PREF_IMM16" + ecma_intrinsic_setacc("CreateObjectWithBuffer", as_imm(op[0])) + when "ECMA_GETPROPITERATOR_PREF_NONE" + ecma_intrinsic_setacc("GetPropIterator", acc_value.u64) + when "ECMA_GETNEXTPROPNAME_PREF_V8" + res := handle_ecma_getnextpropname(vreg_value(op[0]).u64) + set_value(acc_ptr, res).u64 + + # ecma runtime + when "ECMA_DEFINEFUNCDYN_PREF_ID16_V8" + ecma_intrinsic_setacc("DefinefuncDyn", as_id(op[0]), vreg_value(op[1]).u64) + + # ecma frames + when "ECMA_CALL0DYN_PREF_V8" + ecma_intrinsic_setacc("Call0Dyn", vreg_value(op[0]).u64) + pc := advance_pc_imm(pc, i.format.size) + when "ECMA_CALL1DYN_PREF_V8_V8" + ecma_intrinsic_setacc("Call1Dyn", vreg_value(op[0]).u64, vreg_value(op[1]).u64) + pc := advance_pc_imm(pc, i.format.size) + when "ECMA_CALL2DYN_PREF_V8_V8_V8" + ecma_intrinsic_setacc("Call2Dyn", vreg_value(op[0]).u64, vreg_value(op[1]).u64, vreg_value(op[2]).u64) + pc := advance_pc_imm(pc, i.format.size) + when "ECMA_CALL3DYN_PREF_V8_V8_V8_V8" + ecma_intrinsic_setacc("Call3Dyn", vreg_value(op[0]).u64, vreg_value(op[1]).u64, vreg_value(op[2]).u64, vreg_value(op[3]).u64) + pc := advance_pc_imm(pc, i.format.size) + when "ECMA_LDLEXENVDYN_PREF_NONE" + res := handle_ecma_ldlexenvdyn() + set_value(acc_ptr, res).any + # ecma_intrinsic_setacc("LdlexenvDyn") + when "ECMA_NEWLEXENVDYN_PREF_IMM16" + ecma_intrinsic_setacc("NewlexenvDyn", as_imm(op[0])) + when "ECMA_STLEXVARDYN_PREF_IMM4_IMM4" + ecma_intrinsic_setacc("StLexVarDyn", i8tou16(as_imm(op[0])), i8tou16(as_imm(op[1])), acc_value.u64) + when "ECMA_STLEXVARDYN_PREF_IMM8_IMM8" + ecma_intrinsic_setacc("StLexVarDyn", i8tou16(as_imm(op[0])), i8tou16(as_imm(op[1])), acc_value.u64) + when "ECMA_LEXVARDYN_PREF_IMM16_IMM16" + ecma_intrinsic_setacc("StLexVarDyn", as_imm(op[0]), as_imm(op[1]), acc_value.u64) + when "ECMA_LDLEXVARDYN_PREF_IMM4_IMM4" + ecma_intrinsic_setacc("LdLexVarDyn", i8tou16(as_imm(op[0])), i8tou16(as_imm(op[1]))) + when "ECMA_LDLEXVARDYN_PREF_IMM8_IMM8" + ecma_intrinsic_setacc("LdLexVarDyn", i8tou16(as_imm(op[0])), i8tou16(as_imm(op[1]))) + when "ECMA_LDLEXVARDYN_PREF_IMM16_IMM16" + ecma_intrinsic_setacc("LdLexVarDyn", as_imm(op[0]), as_imm(op[1])) + when "ECMA_CREATEEMPTYOBJECT_PREF_NONE" + ecma_intrinsic_setacc("CreateEmptyObject") + when "ECMA_RETURNUNDEFINED_PREF_NONE" + set_value(acc_ptr, 10).u64 + Intrinsic(:INTERPRETER_RETURN).ptr + when "ECMA_RETURN_DYN_PREF_NONE" + Intrinsic(:INTERPRETER_RETURN).ptr + when "ECMA_CREATEARRAYWITHBUFFER_PREF_IMM16" + ecma_intrinsic_setacc("CreateArrayWithBuffer", as_imm(op[0])) + + # ecma load/stores by index + when "ECMA_STOBJBYINDEX_PREF_IMM8_V8" + handle_ecma_stobjbyindex(i8tou32(as_imm(op[0])), vreg_value(op[1]).any, acc_value.any) + when "ECMA_STOBJBYINDEX_PREF_IMM16_V8" + handle_ecma_stobjbyindex(i16tou32(as_imm(op[0])), vreg_value(op[1]).any, acc_value.any) + when "ECMA_STOBJBYINDEX_PREF_IMM32_V8" + handle_ecma_stobjbyindex(i32tou32(as_imm(op[0])), vreg_value(op[1]).any, acc_value.any) + + when "ECMA_STOWNBYINDEX_PREF_IMM8_V8" + handle_ecma_stownbyindex(i8tou32(as_imm(op[0])), vreg_value(op[1]).any, acc_value.any) + when "ECMA_STOWNBYINDEX_PREF_IMM16_V8" + handle_ecma_stownbyindex(i16tou32(as_imm(op[0])), vreg_value(op[1]).any, acc_value.any) + when "ECMA_STOWNBYINDEX_PREF_IMM32_V8" + handle_ecma_stownbyindex(i32tou32(as_imm(op[0])), vreg_value(op[1]).any, acc_value.any) + + when "ECMA_LDOBJBYINDEX_PREF_IMM8_V8" + res := handle_ecma_ldobjbyindex(i8tou32(as_imm(op[0])), vreg_value(op[1]).any) + set_value(acc_ptr, res).any + when "ECMA_LDOBJBYINDEX_PREF_IMM16_V8" + res := handle_ecma_ldobjbyindex(i16tou32(as_imm(op[0])), vreg_value(op[1]).any) + set_value(acc_ptr, res).any + when "ECMA_LDOBJBYINDEX_PREF_IMM32_V8" + res := handle_ecma_ldobjbyindex(i32tou32(as_imm(op[0])), vreg_value(op[1]).any) + set_value(acc_ptr, res).any + + # ecma load/stores by value + when "ECMA_STOBJBYVALUE_PREF_V8_V8" + handle_ecma_stobjbyvalue(vreg_value(op[0]).any, vreg_value(op[1]).any, acc_value.any, ins_offset) + when "ECMA_LDOBJBYVALUE_PREF_V8_V8" + res := handle_ecma_ldobjbyvalue(vreg_value(op[0]).any, vreg_value(op[1]).any, ins_offset) + set_value(acc_ptr, res).any + + # ecma load/stores by name + when "ECMA_STOBJBYNAME_PREF_ID32_V8" + handle_ecma_stobjbyname(vreg_value(op[1]).any, as_id(op[0]), acc_value.any, ins_offset) + when "ECMA_LDOBJBYNAME_PREF_ID32_V8" + res := handle_ecma_ldobjbyname(vreg_value(op[1]).any, as_id(op[0]), ins_offset) + set_value(acc_ptr, res).any + + # load/stores from global object + when "ECMA_TRYLDGLOBALBYNAME_PREF_ID32" + res := handle_ecma_tryldglobalbyname(as_id(op[0]), ins_offset) + set_value(acc_ptr, res).any + when "ECMA_LDGLOBALVAR_PREF_ID32" + res := handle_ecma_ldglobalvar(as_id(op[0]), ins_offset) + set_value(acc_ptr, res).any + when "ECMA_STGLOBALVAR_PREF_ID32" + handle_ecma_stglobalvar(as_id(op[0]), acc_value.any, ins_offset) + + # ecma exceptions + when "ECMA_THROWDYN_PREF_NONE" + ecma_intrinsic_setacc("ThrowDyn", acc_value.u64) + Intrinsic(:INTERPRETER_RETURN).ptr # TODO: goto exc handler + when "ECMA_THROWUNDEFINEDIFHOLE_PREF_ID32" + ecma_intrinsic_setacc("ThrowUndefinedIfHole", as_id(op[0]), acc_value.any) diff --git a/irtoc_scripts/irtoc_scripts.gn b/irtoc_scripts/irtoc_scripts.gn new file mode 100644 index 000000000..11c5be3b3 --- /dev/null +++ b/irtoc_scripts/irtoc_scripts.gn @@ -0,0 +1,17 @@ +# Copyright (c) 2021-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. + +srcs = [ + "interpreter_handlers.irt", + "interpreter_main_loop.irt", +] diff --git a/isa/CMakeLists.txt b/isa/CMakeLists.txt new file mode 100644 index 000000000..3a7ca5701 --- /dev/null +++ b/isa/CMakeLists.txt @@ -0,0 +1,14 @@ +# Copyright (c) 2021-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. + +add_isa_plugin(${PANDA_ECMASCRIPT_PLUGIN_SOURCE}/isa/isa.yaml) diff --git a/isa/isa.yaml b/isa/isa.yaml new file mode 100644 index 000000000..8c957bf11 --- /dev/null +++ b/isa/isa.yaml @@ -0,0 +1,877 @@ +# Copyright (c) 2021-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. + +prefixes: + - name: ecma + description: Extension for ecma support. + +exceptions: + - tag: x_ecma + description: Bytecode may throw ECMA-specific exception. + +properties: + - tag: not_compilable + description: Not yet supported by JIT/AOT. + - tag: inlinable + description: Handler is implemented in IRtoC and can be inlined by JIT/AOT. + - tag: use_ic + description: Instruction uses inline cache + +groups: + - title: Ecma extension intrunctions + description: Ecma extension intrunctions with prefix ecma + properties: + - dynamic + verification: + - none + exceptions: + - x_ecma + namespace: ecmascript + pseudo: | + acc = ecma_op(acc, operand_0, ..., operands_n) + instructions: + - sig: ecma.ldnan + acc: out:top + prefix: ecma + format: [pref_op_none] + properties: [inlinable] + intrinsic_name: INTRINSIC_LDNAN + - sig: ecma.ldinfinity + acc: out:top + prefix: ecma + format: [pref_op_none] + properties: [inlinable] + intrinsic_name: INTRINSIC_LDINFINITY + - sig: ecma.ldglobalthis + acc: out:top + prefix: ecma + format: [pref_op_none] + intrinsic_name: INTRINSIC_LDGLOBALTHIS + - sig: ecma.ldundefined + acc: out:top + prefix: ecma + format: [pref_op_none] + properties: [inlinable] + intrinsic_name: INTRINSIC_LDUNDEFINED + - sig: ecma.ldnull + acc: out:top + prefix: ecma + format: [pref_op_none] + properties: [inlinable] + intrinsic_name: INTRINSIC_LDNULL + - sig: ecma.ldsymbol + acc: out:top + prefix: ecma + format: [pref_op_none] + intrinsic_name: INTRINSIC_LDSYMBOL + - sig: ecma.ldglobal + acc: out:top + prefix: ecma + format: [pref_op_none] + intrinsic_name: INTRINSIC_LDGLOBAL + - sig: ecma.ldtrue + acc: out:top + prefix: ecma + properties: [inlinable] + format: [pref_op_none] + intrinsic_name: INTRINSIC_LDTRUE + - sig: ecma.ldfalse + acc: out:top + prefix: ecma + properties: [inlinable] + format: [pref_op_none] + intrinsic_name: INTRINSIC_LDFALSE + - sig: ecma.throwdyn + acc: in:top + prefix: ecma + format: [pref_op_none] + exceptions: [x_throw] + intrinsic_name: INTRINSIC_THROW_DYN + - sig: ecma.rethrowdyn + acc: in:top + prefix: ecma + format: [pref_op_none] + intrinsic_name: INTRINSIC_RETHROW_DYN + - sig: ecma.typeofdyn + acc: inout:top + prefix: ecma + format: [pref_op_none] + intrinsic_name: INTRINSIC_TYPEOF_DYN + - sig: ecma.ldlexenvdyn + acc: out:top + prefix: ecma + format: [pref_op_none] + intrinsic_name: INTRINSIC_LDLEXENV_DYN + - sig: ecma.poplexenvdyn + acc: out:top + prefix: ecma + format: [pref_op_none] + intrinsic_name: INTRINSIC_POP_LEXENV_DYN + - sig: ecma.getunmappedargs + acc: out:top + prefix: ecma + format: [pref_op_none] + properties: [not_compilable] + intrinsic_name: INTRINSIC_GET_UNMAPPED_ARGS + - sig: ecma.getpropiterator + acc: inout:top + prefix: ecma + format: [pref_op_none] + intrinsic_name: INTRINSIC_GET_PROP_ITERATOR + - sig: ecma.asyncfunctionenter + acc: out:top + prefix: ecma + format: [pref_op_none] + intrinsic_name: INTRINSIC_ASYNC_FUNCTION_ENTER + - sig: ecma.ldhole + acc: out:top + prefix: ecma + format: [pref_op_none] + properties: [inlinable] + intrinsic_name: INTRINSIC_LDHOLE + - sig: ecma.returnundefined + acc: out:top + prefix: ecma + format: [pref_op_none] + properties: [return] + intrinsic_name: INTRINSIC_RETURN_UNDEFINED + - sig: ecma.createemptyobject + acc: out:top + prefix: ecma + format: [pref_op_none] + intrinsic_name: INTRINSIC_CREATE_EMPTY_OBJECT + - sig: ecma.createemptyarray + acc: out:top + prefix: ecma + format: [pref_op_none] + intrinsic_name: INTRINSIC_CREATE_EMPTY_ARRAY + - sig: ecma.getiterator + acc: inout:top + prefix: ecma + format: [pref_op_none] + intrinsic_name: INTRINSIC_GET_ITERATOR + - sig: ecma.getasynciterator + acc: inout:top + prefix: ecma + format: [pref_op_none] + intrinsic_name: INTRINSIC_GET_ASYNC_ITERATOR + - sig: ecma.throwthrownotexists + acc: none + prefix: ecma + format: [pref_op_none] + exceptions: [x_throw] + intrinsic_name: INTRINSIC_THROW_THROW_NOT_EXISTS + - sig: ecma.throwpatternnoncoercible + acc: none + prefix: ecma + format: [pref_op_none] + exceptions: [x_throw] + intrinsic_name: INTRINSIC_THROW_PATTERN_NON_COERCIBLE + - sig: ecma.ldhomeobject + acc: out:top + prefix: ecma + format: [pref_op_none] + intrinsic_name: INTRINSIC_LD_HOME_OBJECT + - sig: ecma.throwdeletesuperproperty + acc: none + prefix: ecma + format: [pref_op_none] + exceptions: [x_throw] + intrinsic_name: INTRINSIC_THROW_DELETE_SUPER_PROPERTY + - sig: ecma.debugger + acc: none + prefix: ecma + format: [pref_op_none] + intrinsic_name: INTRINSIC_DEBUGGER + - sig: ecma.add2dyn v:in:top + acc: inout:top + prefix: ecma + format: [pref_op_v_8] + properties: [inlinable] + intrinsic_name: INTRINSIC_ADD2_DYN + - sig: ecma.sub2dyn v:in:top + acc: inout:top + prefix: ecma + format: [pref_op_v_8] + properties: [inlinable] + intrinsic_name: INTRINSIC_SUB2_DYN + - sig: ecma.mul2dyn v:in:top + acc: inout:top + prefix: ecma + format: [pref_op_v_8] + properties: [inlinable] + intrinsic_name: INTRINSIC_MUL2_DYN + - sig: ecma.div2dyn v:in:top + acc: inout:top + prefix: ecma + format: [pref_op_v_8] + properties: [inlinable] + intrinsic_name: INTRINSIC_DIV2_DYN + - sig: ecma.mod2dyn v:in:top + acc: inout:top + prefix: ecma + format: [pref_op_v_8] + properties: [inlinable] + intrinsic_name: INTRINSIC_MOD2_DYN + - sig: ecma.eqdyn v:in:top + acc: inout:top + prefix: ecma + format: [pref_op_v_8] + properties: [inlinable] + intrinsic_name: INTRINSIC_EQ_DYN + - sig: ecma.noteqdyn v:in:top + acc: inout:top + prefix: ecma + format: [pref_op_v_8] + properties: [inlinable] + intrinsic_name: INTRINSIC_NOT_EQ_DYN + - sig: ecma.lessdyn v:in:top + acc: inout:top + prefix: ecma + format: [pref_op_v_8] + properties: [inlinable] + intrinsic_name: INTRINSIC_LESS_DYN + - sig: ecma.lesseqdyn v:in:top + acc: inout:top + prefix: ecma + format: [pref_op_v_8] + properties: [inlinable] + intrinsic_name: INTRINSIC_LESS_EQ_DYN + - sig: ecma.greaterdyn v:in:top + acc: inout:top + prefix: ecma + format: [pref_op_v_8] + properties: [inlinable] + intrinsic_name: INTRINSIC_GREATER_DYN + - sig: ecma.greatereqdyn v:in:top + acc: inout:top + prefix: ecma + format: [pref_op_v_8] + properties: [inlinable] + intrinsic_name: INTRINSIC_GREATER_EQ_DYN + - sig: ecma.shl2dyn v:in:top + acc: inout:top + prefix: ecma + format: [pref_op_v_8] + properties: [inlinable] + intrinsic_name: INTRINSIC_SHL2_DYN + - sig: ecma.shr2dyn v:in:top + acc: inout:top + prefix: ecma + format: [pref_op_v_8] + properties: [inlinable] + intrinsic_name: INTRINSIC_SHR2_DYN + - sig: ecma.ashr2dyn v:in:top + acc: inout:top + prefix: ecma + format: [pref_op_v_8] + intrinsic_name: INTRINSIC_ASHR2_DYN + - sig: ecma.and2dyn v:in:top + acc: inout:top + prefix: ecma + format: [pref_op_v_8] + properties: [inlinable] + intrinsic_name: INTRINSIC_AND2_DYN + - sig: ecma.or2dyn v:in:top + acc: inout:top + prefix: ecma + format: [pref_op_v_8] + properties: [inlinable] + intrinsic_name: INTRINSIC_OR2_DYN + - sig: ecma.xor2dyn v:in:top + acc: inout:top + prefix: ecma + format: [pref_op_v_8] + properties: [inlinable] + intrinsic_name: INTRINSIC_XOR2_DYN + - sig: ecma.tonumber v:in:top + acc: out:top + prefix: ecma + format: [pref_op_v_8] + properties: [inlinable] + intrinsic_name: INTRINSIC_TONUMBER + - sig: ecma.negdyn v:in:top + acc: out:top + prefix: ecma + format: [pref_op_v_8] + properties: [inlinable] + intrinsic_name: INTRINSIC_NEG_DYN + - sig: ecma.notdyn v:in:top + acc: out:top + prefix: ecma + format: [pref_op_v_8] + properties: [inlinable] + intrinsic_name: INTRINSIC_NOT_DYN + - sig: ecma.incdyn v:in:top + acc: out:top + prefix: ecma + format: [pref_op_v_8] + properties: [inlinable] + intrinsic_name: INTRINSIC_INC_DYN + - sig: ecma.decdyn v:in:top + acc: out:top + prefix: ecma + format: [pref_op_v_8] + properties: [inlinable] + intrinsic_name: INTRINSIC_DEC_DYN + - sig: ecma.expdyn v:in:top + acc: inout:top + prefix: ecma + format: [pref_op_v_8] + intrinsic_name: INTRINSIC_EXP_DYN + - sig: ecma.isindyn v:in:top + acc: inout:top + prefix: ecma + format: [pref_op_v_8] + intrinsic_name: INTRINSIC_ISIN_DYN + - sig: ecma.instanceofdyn v:in:top + acc: inout:top + prefix: ecma + format: [pref_op_v_8] + intrinsic_name: INTRINSIC_INSTANCEOF_DYN + - sig: ecma.strictnoteqdyn v:in:top + acc: inout:top + prefix: ecma + format: [pref_op_v_8] + properties: [inlinable] + intrinsic_name: INTRINSIC_STRICT_NOT_EQ_DYN + - sig: ecma.stricteqdyn v:in:top + acc: inout:top + prefix: ecma + format: [pref_op_v_8] + properties: [inlinable] + intrinsic_name: INTRINSIC_STRICT_EQ_DYN + - sig: ecma.resumegenerator v:in:top + acc: out:top + prefix: ecma + format: [pref_op_v_8] + intrinsic_name: INTRINSIC_RESUME_GENERATOR + - sig: ecma.getresumemode v:in:top + acc: out:top + prefix: ecma + format: [pref_op_v_8] + intrinsic_name: INTRINSIC_GET_RESUME_MODE + - sig: ecma.creategeneratorobj v:in:top + acc: out:top + prefix: ecma + format: [pref_op_v_8] + intrinsic_name: INTRINSIC_CREATE_GENERATOR_OBJ + - sig: ecma.setgeneratorstate v:in:top, imm + acc: none + prefix: ecma + format: [pref_op_v_8_imm_8] + intrinsic_name: INTRINSIC_SET_GENERATOR_STATE + - sig: ecma.createasyncgeneratorobj v:in:top + acc: out:top + prefix: ecma + format: [pref_op_v_8] + intrinsic_name: INTRINSIC_CREATE_ASYNC_GENERATOR_OBJ + - sig: ecma.throwconstassignment string_id + acc: none + prefix: ecma + format: [pref_op_id_32] + exceptions: [x_throw] + properties: [string_id] + intrinsic_name: INTRINSIC_THROW_CONST_ASSIGNMENT + - sig: ecma.getmethod string_id, v:in:top + acc: out:top + prefix: ecma + format: [pref_op_id_32_v_8] + properties: [string_id] + intrinsic_name: INTRINSIC_GET_METHOD + - sig: ecma.gettemplateobject v:in:top + acc: out:top + prefix: ecma + format: [pref_op_v_8] + intrinsic_name: INTRINSIC_GET_TEMPLATE_OBJECT + - sig: ecma.getnextpropname v:in:top + acc: out:top + prefix: ecma + format: [pref_op_v_8] + properties: [inlinable] + intrinsic_name: INTRINSIC_GET_NEXT_PROP_NAME + - sig: ecma.call0dyn v:in:top + acc: out:top + prefix: ecma + format: [pref_op_v_8] + properties: [call] + intrinsic_name: INTRINSIC_CALL0_DYN + - sig: ecma.throwifnotobject + acc: in:top + prefix: ecma + format: [pref_op_none] + intrinsic_name: INTRINSIC_THROW_IF_NOT_OBJECT + - sig: ecma.closeiterator v:in:top + acc: inout:top + prefix: ecma + format: [pref_op_v_8] + intrinsic_name: INTRINSIC_CLOSE_ITERATOR + - sig: ecma.copymodule v:in:top + acc: out:top + prefix: ecma + format: [pref_op_v_8] + intrinsic_name: INTRINSIC_COPY_MODULE + - sig: ecma.supercallspread v:in:top + acc: inout:top + prefix: ecma + format: [pref_op_v_8] + intrinsic_name: INTRINSIC_SUPER_CALL_SPREAD + - sig: ecma.delobjprop v1:in:top, v2:in:top + acc: out:top + prefix: ecma + format: [pref_op_v1_8_v2_8] + intrinsic_name: INTRINSIC_DELOBJPROP + - sig: ecma.newobjspreaddyn v1:in:top, v2:in:top + acc: inout:top + prefix: ecma + format: [pref_op_v1_8_v2_8] + intrinsic_name: INTRINSIC_NEWOBJSPREAD_DYN + - sig: ecma.createiterresultobj imm + acc: inout:top + prefix: ecma + format: [pref_op_imm_8] + intrinsic_name: INTRINSIC_CREATE_ITER_RESULT_OBJ + - sig: ecma.suspendgenerator v:in:top + acc: inout:top + prefix: ecma + format: [pref_op_v_8] + properties: [return, not_compilable, suspend] + intrinsic_name: INTRINSIC_SUSPEND_GENERATOR + - sig: ecma.suspendasyncgenerator v:in:top + acc: inout:top + prefix: ecma + format: [pref_op_v_8] + properties: [return, not_compilable, suspend] + intrinsic_name: INTRINSIC_SUSPEND_ASYNC_GENERATOR + - sig: ecma.asyncfunctionawait v:in:top + acc: inout:top + prefix: ecma + format: [pref_op_v_8] + intrinsic_name: INTRINSIC_ASYNC_FUNCTION_AWAIT + - sig: ecma.throwundefinedifhole string_id + acc: in:top + prefix: ecma + format: [pref_op_id_32] + properties: [string_id] + intrinsic_name: INTRINSIC_THROW_UNDEFINED_IF_HOLE + - sig: ecma.call1dyn v1:in:top, v2:in:top + acc: out:top + prefix: ecma + format: [pref_op_v1_8_v2_8] + properties: [call] + intrinsic_name: INTRINSIC_CALL1_DYN + - sig: ecma.copydataproperties v1:in:top, v2:in:top + acc: out:top + prefix: ecma + format: [pref_op_v1_8_v2_8] + intrinsic_name: INTRINSIC_COPY_DATA_PROPERTIES + - sig: ecma.starrayspread v1:in:top, v2:in:top + acc: inout:top + prefix: ecma + format: [pref_op_v1_8_v2_8] + intrinsic_name: INTRINSIC_ST_ARRAY_SPREAD + - sig: ecma.setobjectwithproto v1:in:top, v2:in:top + acc: none + prefix: ecma + format: [pref_op_v1_8_v2_8] + intrinsic_name: INTRINSIC_SET_OBJECT_WITH_PROTO + - sig: ecma.ldobjbyvalue v1:in:top, v2:in:top + acc: out:top + prefix: ecma + format: [pref_op_v1_8_v2_8] + properties: [use_ic, inlinable] + intrinsic_name: INTRINSIC_LD_OBJ_BY_VALUE + - sig: ecma.stobjbyvalue v1:in:top, v2:in:top + acc: in:top + prefix: ecma + format: [pref_op_v1_8_v2_8] + properties: [use_ic, inlinable] + intrinsic_name: INTRINSIC_ST_OBJ_BY_VALUE + - sig: ecma.stownbyvalue v1:in:top, v2:in:top + acc: in:top + prefix: ecma + format: [pref_op_v1_8_v2_8] + intrinsic_name: INTRINSIC_ST_OWN_BY_VALUE + - sig: ecma.ldsuperbyvalue v1:in:top, v2:in:top + acc: out:top + prefix: ecma + format: [pref_op_v1_8_v2_8] + intrinsic_name: INTRINSIC_LD_SUPER_BY_VALUE + - sig: ecma.stsuperbyvalue v1:in:top, v2:in:top + acc: in:top + prefix: ecma + format: [pref_op_v1_8_v2_8] + intrinsic_name: INTRINSIC_ST_SUPER_BY_VALUE + - sig: ecma.ldobjbyindex imm, v:in:top + acc: out:top + prefix: ecma + format: [pref_op_imm_8_v_8] + properties: [inlinable] + intrinsic_name: INTRINSIC_LD_OBJ_BY_INDEX + - sig: ecma.ldobjbyindex imm, v:in:top + acc: out:top + prefix: ecma + format: [pref_op_imm_16_v_8] + properties: [inlinable] + intrinsic_name: INTRINSIC_LD_OBJ_BY_INDEX + - sig: ecma.ldobjbyindex imm, v:in:top + acc: out:top + prefix: ecma + format: [pref_op_imm_32_v_8] + properties: [inlinable] + intrinsic_name: INTRINSIC_LD_OBJ_BY_INDEX + - sig: ecma.stobjbyindex imm, v:in:top + acc: in:top + prefix: ecma + format: [pref_op_imm_8_v_8] + properties: [inlinable] + intrinsic_name: INTRINSIC_ST_OBJ_BY_INDEX + - sig: ecma.stobjbyindex imm, v:in:top + acc: in:top + prefix: ecma + format: [pref_op_imm_16_v_8] + properties: [inlinable] + intrinsic_name: INTRINSIC_ST_OBJ_BY_INDEX + - sig: ecma.stobjbyindex imm, v:in:top + acc: in:top + prefix: ecma + format: [pref_op_imm_32_v_8] + properties: [inlinable] + intrinsic_name: INTRINSIC_ST_OBJ_BY_INDEX + - sig: ecma.stownbyindex imm, v:in:top + acc: in:top + prefix: ecma + format: [pref_op_imm_8_v_8] + properties: [inlinable] + intrinsic_name: INTRINSIC_ST_OWN_BY_INDEX + - sig: ecma.stownbyindex imm, v:in:top + acc: in:top + prefix: ecma + format: [pref_op_imm_16_v_8] + properties: [inlinable] + intrinsic_name: INTRINSIC_ST_OWN_BY_INDEX + - sig: ecma.stownbyindex imm, v:in:top + acc: in:top + prefix: ecma + format: [pref_op_imm_32_v_8] + properties: [inlinable] + intrinsic_name: INTRINSIC_ST_OWN_BY_INDEX + - sig: ecma.callspreaddyn v1:in:top, v2:in:top, v3:in:top + acc: out:top + prefix: ecma + format: [pref_op_v1_8_v2_8_v3_8] + intrinsic_name: INTRINSIC_CALLSPREAD_DYN + - sig: ecma.asyncfunctionresolve v:in:top + acc: inout:top + prefix: ecma + format: [pref_op_v_8] + intrinsic_name: INTRINSIC_ASYNC_FUNCTION_RESOLVE + - sig: ecma.asyncfunctionreject v:in:top + acc: inout:top + prefix: ecma + format: [pref_op_v_8] + intrinsic_name: INTRINSIC_ASYNC_FUNCTION_REJECT + - sig: ecma.asyncgeneratorresolve v:in:top + acc: inout:top + prefix: ecma + format: [pref_op_v_8] + intrinsic_name: INTRINSIC_ASYNC_GENERATOR_RESOLVE + - sig: ecma.asyncgeneratorreject v:in:top + acc: inout:top + prefix: ecma + format: [pref_op_v_8] + intrinsic_name: INTRINSIC_ASYNC_GENERATOR_REJECT + - sig: ecma.call2dyn v1:in:top, v2:in:top, v3:in:top + acc: out:top + prefix: ecma + format: [pref_op_v1_8_v2_8_v3_8] + properties: [call] + intrinsic_name: INTRINSIC_CALL2_DYN + - sig: ecma.call3dyn v1:in:top, v2:in:top, v3:in:top, v4:in:top + acc: out:top + prefix: ecma + format: [pref_op_v1_8_v2_8_v3_8_v4_8] + properties: [call] + intrinsic_name: INTRINSIC_CALL3_DYN + - sig: ecma.definegettersetterbyvalue v1:in:top, v2:in:top, v3:in:top, v4:in:top + acc: inout:top + prefix: ecma + format: [pref_op_v1_8_v2_8_v3_8_v4_8] + intrinsic_name: INTRINSIC_DEFINE_GETTER_SETTER_BY_VALUE + - sig: ecma.newobjdynrange imm, v:in:top + acc: out:top + prefix: ecma + format: [pref_op_imm_16_v_8] + properties: [not_compilable] + intrinsic_name: INTRINSIC_NEWOBJ_DYNRANGE + - sig: ecma.callirangedyn imm, v:in:top + acc: out:top + prefix: ecma + format: [pref_op_imm_16_v_8] + properties: [call, not_compilable] + intrinsic_name: INTRINSIC_CALLI_RANGE_DYN + - sig: ecma.callithisrangedyn imm, v:in:top + acc: out:top + prefix: ecma + format: [pref_op_imm_16_v_8] + properties: [call, not_compilable] + intrinsic_name: INTRINSIC_CALLI_THIS_RANGE_DYN + - sig: ecma.supercall imm, v:in:top + acc: inout:top + prefix: ecma + format: [pref_op_imm_16_v_8] + properties: [not_compilable] + intrinsic_name: INTRINSIC_SUPER_CALL + - sig: ecma.createobjectwithexcludedkeys imm, v1:in:top, v2:in:top + acc: out:top + prefix: ecma + format: [pref_op_imm_16_v1_8_v2_8] + properties: [not_compilable] + intrinsic_name: INTRINSIC_CREATE_OBJECT_WITH_EXCLUDED_KEYS + - sig: ecma.definefuncdyn method_id, v:in:top + acc: out:top + prefix: ecma + format: [pref_op_id_16_v_8] + properties: [method_id] + intrinsic_name: INTRINSIC_DEFINEFUNC_DYN + - sig: ecma.definencfuncdyn method_id, v:in:top + acc: inout:top + prefix: ecma + format: [pref_op_id_16_v_8] + properties: [method_id] + intrinsic_name: INTRINSIC_DEFINE_NC_FUNC_DYN + - sig: ecma.definegeneratorfunc method_id, v:in:top + acc: out:top + prefix: ecma + format: [pref_op_id_16_v_8] + properties: [method_id] + intrinsic_name: INTRINSIC_DEFINE_GENERATOR_FUNC + - sig: ecma.defineasyncfunc method_id, v:in:top + acc: out:top + prefix: ecma + format: [pref_op_id_16_v_8] + properties: [method_id] + intrinsic_name: INTRINSIC_DEFINE_ASYNC_FUNC + - sig: ecma.defineasyncgeneratorfunc method_id, v:in:top + acc: out:top + prefix: ecma + format: [pref_op_id_16_v_8] + properties: [method_id] + intrinsic_name: INTRINSIC_DEFINE_ASYNC_GENERATOR_FUNC + - sig: ecma.definemethod method_id, v:in:top + acc: inout:top + prefix: ecma + format: [pref_op_id_16_v_8] + properties: [method_id] + intrinsic_name: INTRINSIC_DEFINE_METHOD + - sig: ecma.newlexenvdyn imm + acc: out:top + prefix: ecma + format: [pref_op_imm_16] + intrinsic_name: INTRINSIC_NEWLEXENV_DYN + - sig: ecma.copylexenvdyn + acc: out:top + prefix: ecma + format: [pref_op_none] + intrinsic_name: INTRINSIC_COPYLEXENV_DYN + - sig: ecma.copyrestargs imm + acc: out:top + prefix: ecma + format: [pref_op_imm_16] + properties: [not_compilable] + intrinsic_name: INTRINSIC_COPYRESTARGS + - sig: ecma.createarraywithbuffer imm + acc: out:top + prefix: ecma + format: [pref_op_imm_16] + intrinsic_name: INTRINSIC_CREATE_ARRAY_WITH_BUFFER + - sig: ecma.createobjecthavingmethod imm + acc: inout:top + prefix: ecma + format: [pref_op_imm_16] + intrinsic_name: INTRINSIC_CREATE_OBJECT_HAVING_METHOD + - sig: ecma.throwifsupernotcorrectcall imm + acc: in:top + prefix: ecma + format: [pref_op_imm_16] + intrinsic_name: INTRINSIC_THROW_IF_SUPER_NOT_CORRECT_CALL + - sig: ecma.createobjectwithbuffer imm + acc: out:top + prefix: ecma + format: [pref_op_imm_16] + intrinsic_name: INTRINSIC_CREATE_OBJECT_WITH_BUFFER + - sig: ecma.ldlexvardyn imm1, imm2 + acc: out:top + prefix: ecma + format: [pref_op_imm1_4_imm2_4] + intrinsic_name: INTRINSIC_LD_LEX_VAR_DYN + - sig: ecma.ldlexvardyn imm1, imm2 + acc: out:top + prefix: ecma + format: [pref_op_imm1_8_imm2_8] + intrinsic_name: INTRINSIC_LD_LEX_VAR_DYN + - sig: ecma.ldlexvardyn imm1, imm2 + acc: out:top + prefix: ecma + format: [pref_op_imm1_16_imm2_16] + intrinsic_name: INTRINSIC_LD_LEX_VAR_DYN + - sig: ecma.stlexvardyn imm1, imm2 + acc: inout:top + prefix: ecma + format: [pref_op_imm1_4_imm2_4] + intrinsic_name: INTRINSIC_ST_LEX_VAR_DYN + - sig: ecma.stlexvardyn imm1, imm2 + acc: inout:top + prefix: ecma + format: [pref_op_imm1_8_imm2_8] + intrinsic_name: INTRINSIC_ST_LEX_VAR_DYN + - sig: ecma.stlexvardyn imm1, imm2 + acc: inout:top + prefix: ecma + format: [pref_op_imm1_16_imm2_16] + intrinsic_name: INTRINSIC_ST_LEX_VAR_DYN + - sig: ecma.defineclasswithbuffer method_id, imm, v1:in:top, v2:in:top + acc: out:top + prefix: ecma + format: [pref_op_id_16_imm_16_v1_8_v2_8] + intrinsic_name: INTRINSIC_DEFINE_CLASS_WITH_BUFFER + properties: [method_id] + - sig: ecma.importmodule string_id + acc: out:top + prefix: ecma + format: [pref_op_id_32] + properties: [string_id] + intrinsic_name: INTRINSIC_IMPORT_MODULE + - sig: ecma.stmodulevar string_id + acc: in:top + prefix: ecma + format: [pref_op_id_32] + properties: [string_id] + intrinsic_name: INTRINSIC_ST_MODULE_VAR + - sig: ecma.tryldglobalbyname string_id + acc: out:top + prefix: ecma + format: [pref_op_id_32] + properties: [string_id, use_ic, inlinable] + intrinsic_name: INTRINSIC_TRY_LD_GLOBAL_BY_NAME + - sig: ecma.trystglobalbyname string_id + acc: in:top + prefix: ecma + format: [pref_op_id_32] + properties: [string_id, use_ic] + intrinsic_name: INTRINSIC_TRY_ST_GLOBAL_BY_NAME + - sig: ecma.ldglobalvar string_id + acc: out:top + prefix: ecma + format: [pref_op_id_32] + properties: [string_id, use_ic, inlinable] + intrinsic_name: INTRINSIC_LD_GLOBAL_VAR + - sig: ecma.stglobalvar string_id + acc: in:top + prefix: ecma + format: [pref_op_id_32] + properties: [string_id, use_ic, inlinable] + intrinsic_name: INTRINSIC_ST_GLOBAL_VAR + - sig: ecma.stgloballet string_id + acc: in:top + prefix: ecma + format: [pref_op_id_32] + properties: [string_id] + intrinsic_name: INTRINSIC_ST_GLOBAL_LET + - sig: ecma.ldobjbyname string_id, v:in:top + acc: out:top + prefix: ecma + format: [pref_op_id_32_v_8] + properties: [string_id, use_ic, inlinable] + intrinsic_name: INTRINSIC_LD_OBJ_BY_NAME + - sig: ecma.stobjbyname string_id, v:in:top + acc: in:top + prefix: ecma + format: [pref_op_id_32_v_8] + properties: [string_id, use_ic, inlinable] + intrinsic_name: INTRINSIC_ST_OBJ_BY_NAME + - sig: ecma.stownbyname string_id, v:in:top + acc: in:top + prefix: ecma + format: [pref_op_id_32_v_8] + properties: [string_id] + intrinsic_name: INTRINSIC_ST_OWN_BY_NAME + - sig: ecma.ldsuperbyname string_id, v:in:top + acc: out:top + prefix: ecma + format: [pref_op_id_32_v_8] + properties: [string_id] + intrinsic_name: INTRINSIC_LD_SUPER_BY_NAME + - sig: ecma.stsuperbyname string_id, v:in:top + acc: in:top + prefix: ecma + format: [pref_op_id_32_v_8] + properties: [string_id] + intrinsic_name: INTRINSIC_ST_SUPER_BY_NAME + - sig: ecma.ldmodvarbyname string_id, v:in:top + acc: out:top + prefix: ecma + format: [pref_op_id_32_v_8] + properties: [string_id] + intrinsic_name: INTRINSIC_LD_MODVAR_BY_NAME + - sig: ecma.toboolean + acc: inout:top + prefix: ecma + format: [pref_op_none] + properties: [inlinable] + intrinsic_name: INTRINSIC_TOBOOLEAN + - sig: ecma.negate + acc: inout:top + prefix: ecma + format: [pref_op_none] + properties: [inlinable] + intrinsic_name: INTRINSIC_NEGATE + - sig: ecma.istrue + acc: inout:top + prefix: ecma + format: [pref_op_none] + intrinsic_name: INTRINSIC_IS_TRUE + - sig: ecma.isfalse + acc: inout:top + prefix: ecma + format: [pref_op_none] + intrinsic_name: INTRINSIC_IS_FALSE + - sig: ecma.isundefined + acc: inout:top + prefix: ecma + format: [pref_op_none] + properties: [inlinable] + intrinsic_name: INTRINSIC_IS_UNDEFINED + - sig: ecma.iscoercible + acc: inout:top + prefix: ecma + format: [pref_op_none] + intrinsic_name: INTRINSIC_IS_COERCIBLE + - sig: ecma.jfalse imm:i32 + acc: in:top + prefix: ecma + format: [pref_op_imm_8, pref_op_imm_16, pref_op_imm_32] + properties: [jump, conditional] + intrinsic_name: INTRINSIC_JFALSE_DYN + - sig: ecma.jtrue imm:i32 + acc: in:top + prefix: ecma + format: [pref_op_imm_8, pref_op_imm_16, pref_op_imm_32] + properties: [jump, conditional] + intrinsic_name: INTRINSIC_JTRUE_DYN + - sig: ecma.return.dyn + acc: in:any + prefix: ecma + format: [pref_op_none] + intrinsic_name: INTRINSIC_RETURN_DYN + properties: [return] diff --git a/isa/isa_sources.gn b/isa/isa_sources.gn new file mode 100644 index 000000000..88870306b --- /dev/null +++ b/isa/isa_sources.gn @@ -0,0 +1,14 @@ +# Copyright (c) 2021-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. + +srcs = [ "plugins/ecmascript/isa/isa.yaml" ] diff --git a/runtime/CMakeLists.txt b/runtime/CMakeLists.txt new file mode 100644 index 000000000..507ee2d08 --- /dev/null +++ b/runtime/CMakeLists.txt @@ -0,0 +1,308 @@ +# Copyright (c) 2021-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. + +panda_promote_to_definitions(PANDA_ECMASCRIPT_ENABLE_RUNTIME_STAT) + +set(ECMA_SRC_DIR ${PANDA_ECMASCRIPT_PLUGIN_SOURCE}/runtime) + +# ${BUILTIN_BRIDGE_SOURCE} is unused. Should be files deleted??? +if(PANDA_TARGET_ARM32) + SET(BUILTIN_BRIDGE_SOURCE ${ECMA_SRC_DIR}/arch/arm/builtin_bridge_arm.S) +elseif(PANDA_TARGET_ARM64) + SET(BUILTIN_BRIDGE_SOURCE ${ECMA_SRC_DIR}/arch/aarch64/builtin_bridge_aarch64.S) +elseif(PANDA_TARGET_X86) +elseif(PANDA_TARGET_AMD64) + SET(BUILTIN_BRIDGE_SOURCE ${ECMA_SRC_DIR}/arch/amd64/builtin_bridge_amd64.S) +endif() + +set(ECMA_GEN_DIR "${CMAKE_CURRENT_BINARY_DIR}/generated") +file(MAKE_DIRECTORY "${ECMA_GEN_DIR}") + +set(ECMASCRIPT_SOURCES + ${ECMA_SRC_DIR}/bridge/ecma_bridge_helpers.cpp + ${ECMA_SRC_DIR}/builtins/builtins_collator.cpp + ${ECMA_SRC_DIR}/builtins/builtins_date_time_format.cpp + ${ECMA_SRC_DIR}/builtins/builtins_intl.cpp + ${ECMA_SRC_DIR}/builtins/builtins_locale.cpp + ${ECMA_SRC_DIR}/builtins/builtins_number_format.cpp + ${ECMA_SRC_DIR}/builtins/builtins_plural_rules.cpp + ${ECMA_SRC_DIR}/builtins/builtins_relative_time_format.cpp + ${ECMA_SRC_DIR}/js_collator.cpp + ${ECMA_SRC_DIR}/js_date_time_format.cpp + ${ECMA_SRC_DIR}/js_locale.cpp + ${ECMA_SRC_DIR}/js_number_format.cpp + ${ECMA_SRC_DIR}/js_plural_rules.cpp + ${ECMA_SRC_DIR}/js_relative_time_format.cpp + ${ECMA_SRC_DIR}/base/array_helper.cpp + ${ECMA_SRC_DIR}/base/builtins_base.cpp + ${ECMA_SRC_DIR}/base/error_helper.cpp + ${ECMA_SRC_DIR}/base/json_parser.cpp + ${ECMA_SRC_DIR}/base/json_stringifier.cpp + ${ECMA_SRC_DIR}/base/number_helper.cpp + ${ECMA_SRC_DIR}/base/object_helper.cpp + ${ECMA_SRC_DIR}/base/string_helper.cpp + ${ECMA_SRC_DIR}/base/typed_array_helper.cpp + ${ECMA_SRC_DIR}/base/utf_helper.cpp + ${ECMA_SRC_DIR}/builtins.cpp + ${ECMA_SRC_DIR}/builtins/builtins_ark_tools.cpp + ${ECMA_SRC_DIR}/builtins/builtins_array.cpp + ${ECMA_SRC_DIR}/builtins/builtins_arraybuffer.cpp + ${ECMA_SRC_DIR}/builtins/builtins_async_function.cpp + ${ECMA_SRC_DIR}/builtins/builtins_async_from_sync_iterator.cpp + ${ECMA_SRC_DIR}/builtins/builtins_async_generator.cpp + ${ECMA_SRC_DIR}/builtins/builtins_async_iterator.cpp + ${ECMA_SRC_DIR}/builtins/builtins_boolean.cpp + ${ECMA_SRC_DIR}/builtins/builtins_dataview.cpp + ${ECMA_SRC_DIR}/builtins/builtins_date.cpp + ${ECMA_SRC_DIR}/builtins/builtins_errors.cpp + ${ECMA_SRC_DIR}/builtins/builtins_function.cpp + ${ECMA_SRC_DIR}/builtins/builtins_generator.cpp + ${ECMA_SRC_DIR}/builtins/builtins_global.cpp + ${ECMA_SRC_DIR}/builtins/builtins_iterator.cpp + ${ECMA_SRC_DIR}/builtins/builtins_json.cpp + ${ECMA_SRC_DIR}/builtins/builtins_map.cpp + ${ECMA_SRC_DIR}/builtins/builtins_math.cpp + ${ECMA_SRC_DIR}/builtins/builtins_number.cpp + ${ECMA_SRC_DIR}/builtins/builtins_object.cpp + ${ECMA_SRC_DIR}/builtins/builtins_promise.cpp + ${ECMA_SRC_DIR}/builtins/builtins_promise_handler.cpp + ${ECMA_SRC_DIR}/builtins/builtins_promise_job.cpp + ${ECMA_SRC_DIR}/builtins/builtins_proxy.cpp + ${ECMA_SRC_DIR}/builtins/builtins_reflect.cpp + ${ECMA_SRC_DIR}/builtins/builtins_regexp.cpp + ${ECMA_SRC_DIR}/builtins/builtins_set.cpp + ${ECMA_SRC_DIR}/builtins/builtins_string.cpp + ${ECMA_SRC_DIR}/builtins/builtins_string_iterator.cpp + ${ECMA_SRC_DIR}/builtins/builtins_symbol.cpp + ${ECMA_SRC_DIR}/builtins/builtins_typedarray.cpp + ${ECMA_SRC_DIR}/builtins/builtins_weak_map.cpp + ${ECMA_SRC_DIR}/builtins/builtins_weak_set.cpp + ${ECMA_SRC_DIR}/class_linker/panda_file_translator.cpp + ${ECMA_SRC_DIR}/containers/containers_arraylist.cpp + ${ECMA_SRC_DIR}/containers/containers_private.cpp + ${ECMA_SRC_DIR}/dump.cpp + ${ECMA_SRC_DIR}/ecma_class_linker_extension.cpp + ${ECMA_SRC_DIR}/ecma_exceptions.cpp + ${ECMA_SRC_DIR}/ecma_language_context.cpp + ${ECMA_SRC_DIR}/ecma_module.cpp + ${ECMA_SRC_DIR}/ecma_string.cpp + ${ECMA_SRC_DIR}/ecma_string_table.cpp + ${ECMA_SRC_DIR}/ecma_vm.cpp + ${ECMA_SRC_DIR}/free_object.cpp + ${ECMA_SRC_DIR}/generator_helper.cpp + ${ECMA_SRC_DIR}/global_env.cpp + ${ECMA_SRC_DIR}/global_env_constants.cpp + ${ECMA_SRC_DIR}/hprof/heap_profiler.cpp + ${ECMA_SRC_DIR}/hprof/heap_profiler_interface.cpp + ${ECMA_SRC_DIR}/hprof/heap_root_visitor.cpp + ${ECMA_SRC_DIR}/hprof/heap_snapshot.cpp + ${ECMA_SRC_DIR}/hprof/heap_snapshot_json_serializer.cpp + ${ECMA_SRC_DIR}/hprof/heap_tracker.cpp + ${ECMA_SRC_DIR}/hprof/string_hashmap.cpp + ${ECMA_SRC_DIR}/ic/profile_type_info.cpp + ${ECMA_SRC_DIR}/ic/ic_runtime.cpp + ${ECMA_SRC_DIR}/ic/ic_runtime_stub.cpp + ${ECMA_SRC_DIR}/ic/property_box.cpp + ${ECMA_SRC_DIR}/ic/proto_change_details.cpp + ${ECMA_SRC_DIR}/internal_call_params.cpp + ${ECMA_SRC_DIR}/interpreter/slow_runtime_helper.cpp + ${ECMA_SRC_DIR}/interpreter/slow_runtime_stub.cpp + ${ECMA_SRC_DIR}/intrinsics.cpp + ${ECMA_SRC_DIR}/jobs/micro_job_queue.cpp + ${ECMA_SRC_DIR}/js_arguments.cpp + ${ECMA_SRC_DIR}/js_array.cpp + ${ECMA_SRC_DIR}/js_array_iterator.cpp + ${ECMA_SRC_DIR}/js_arraybuffer.cpp + ${ECMA_SRC_DIR}/js_arraylist.cpp + ${ECMA_SRC_DIR}/js_async_from_sync_iterator_object.cpp + ${ECMA_SRC_DIR}/js_async_function.cpp + ${ECMA_SRC_DIR}/js_async_generator_object.cpp + ${ECMA_SRC_DIR}/js_dataview.cpp + ${ECMA_SRC_DIR}/js_date.cpp + ${ECMA_SRC_DIR}/js_for_in_iterator.cpp + ${ECMA_SRC_DIR}/js_function.cpp + ${ECMA_SRC_DIR}/js_generator_object.cpp + ${ECMA_SRC_DIR}/js_hclass.cpp + ${ECMA_SRC_DIR}/js_invoker.cpp + ${ECMA_SRC_DIR}/js_iterator.cpp + ${ECMA_SRC_DIR}/js_map.cpp + ${ECMA_SRC_DIR}/js_map_iterator.cpp + ${ECMA_SRC_DIR}/js_method.cpp + ${ECMA_SRC_DIR}/js_object.cpp + ${ECMA_SRC_DIR}/js_primitive_ref.cpp + ${ECMA_SRC_DIR}/js_promise.cpp + ${ECMA_SRC_DIR}/js_proxy.cpp + ${ECMA_SRC_DIR}/js_serializer.cpp + ${ECMA_SRC_DIR}/js_set.cpp + ${ECMA_SRC_DIR}/js_set_iterator.cpp + ${ECMA_SRC_DIR}/js_stable_array.cpp + ${ECMA_SRC_DIR}/js_string_iterator.cpp + ${ECMA_SRC_DIR}/js_tagged_value.cpp + ${ECMA_SRC_DIR}/js_thread.cpp + ${ECMA_SRC_DIR}/js_typed_array.cpp + ${ECMA_SRC_DIR}/js_weak_container.cpp + ${ECMA_SRC_DIR}/linked_hash_table.cpp + ${ECMA_SRC_DIR}/literal_data_extractor.cpp + ${ECMA_SRC_DIR}/message_string.cpp + ${ECMA_SRC_DIR}/mem/ecma_reference_processor.cpp + ${ECMA_SRC_DIR}/mem/c_string.cpp + ${ECMA_SRC_DIR}/mem/chunk.cpp + ${ECMA_SRC_DIR}/mem/compress_collector.cpp + ${ECMA_SRC_DIR}/mem/concurrent_marker.cpp + ${ECMA_SRC_DIR}/mem/concurrent_sweeper.cpp + ${ECMA_SRC_DIR}/mem/mem_manager.cpp + ${ECMA_SRC_DIR}/mem/evacuation_allocator.cpp + ${ECMA_SRC_DIR}/mem/free_object_kind.cpp + ${ECMA_SRC_DIR}/mem/free_object_list.cpp + ${ECMA_SRC_DIR}/mem/gc_stats.cpp + ${ECMA_SRC_DIR}/mem/heap.cpp + ${ECMA_SRC_DIR}/mem/mem_controller.cpp + ${ECMA_SRC_DIR}/mem/mix_space_collector.cpp + ${ECMA_SRC_DIR}/mem/parallel_work_helper.cpp + ${ECMA_SRC_DIR}/mem/parallel_evacuation.cpp + ${ECMA_SRC_DIR}/mem/parallel_marker.cpp + ${ECMA_SRC_DIR}/mem/region_factory.cpp + ${ECMA_SRC_DIR}/mem/semi_space_collector.cpp + ${ECMA_SRC_DIR}/mem/space.cpp + ${ECMA_SRC_DIR}/mem/verification.cpp + ${ECMA_SRC_DIR}/napi/jsnapi.cpp + ${ECMA_SRC_DIR}/object_factory.cpp + ${ECMA_SRC_DIR}/object_operator.cpp + ${ECMA_SRC_DIR}/platform/platform.cpp + ${ECMA_SRC_DIR}/platform/runner.cpp + ${ECMA_SRC_DIR}/platform/task_queue.cpp + ${ECMA_SRC_DIR}/layout_info.cpp + ${ECMA_SRC_DIR}/regexp/dyn_chunk.cpp + ${ECMA_SRC_DIR}/regexp/regexp_executor.cpp + ${ECMA_SRC_DIR}/regexp/regexp_opcode.cpp + ${ECMA_SRC_DIR}/regexp/regexp_parser.cpp + ${ECMA_SRC_DIR}/regexp/regexp_parser_cache.cpp + ${ECMA_SRC_DIR}/snapshot/mem/slot_bit.cpp + ${ECMA_SRC_DIR}/snapshot/mem/snapshot.cpp + ${ECMA_SRC_DIR}/snapshot/mem/snapshot_serialize.cpp + ${ECMA_SRC_DIR}/tagged_dictionary.cpp + ${ECMA_SRC_DIR}/template_string.cpp + ${ECMA_SRC_DIR}/vmstat/caller_stat.cpp + ${ECMA_SRC_DIR}/vmstat/runtime_stat.cpp + ${ECMA_SRC_DIR}/weak_vector.cpp + ${ECMA_SRC_DIR}/class_info_extractor.cpp + + ${ECMA_SRC_DIR}/compiler/ecmascript_runtime_interface.cpp + + ${ECMA_SRC_DIR}/tooling/pt_ecmascript_extension.cpp +) + +add_dependencies(arkruntime_static ecmastdlib_inline_h) +target_sources(arkruntime_static PRIVATE ${ECMASCRIPT_SOURCES}) +target_include_directories(arkruntime_static PUBLIC + ${PANDA_ECMASCRIPT_PLUGIN_SOURCE}/runtime + ${PANDA_BINARY_ROOT}/compiler/generated + ${PANDA_BINARY_ROOT}/plugins/ecmascript/ecmastdlib + ${ECMA_GEN_DIR} +) + +set(RUNTIME_TEMPLATES + intrinsics_gen.cpp.erb +) + +panda_gen( + DATA ${ECMA_SRC_DIR}/ecma_runtime.yaml + TARGET_NAME ecma_intrinsics_gen_arkruntime + TEMPLATES ${RUNTIME_TEMPLATES} + REQUIRES + ${CMAKE_SOURCE_DIR}/runtime/templates/intrinsics.rb + ${CMAKE_SOURCE_DIR}/runtime/templates/runtime.rb + SOURCE ${ECMA_SRC_DIR}/templates + DESTINATION ${ECMA_GEN_DIR} +) + +add_dependencies(arkruntime_static ecma_intrinsics_gen_arkruntime) +add_dependencies(intrinsics_gen_arkruntime ecma_intrinsics_gen_arkruntime) + +target_include_directories(arkruntime_interpreter_impl PUBLIC + ${PANDA_BINARY_ROOT}/compiler/generated +) + +if (TARGET arkruntime_test_interpreter_impl) + target_include_directories(arkruntime_test_interpreter_impl PUBLIC + ${PANDA_BINARY_ROOT}/compiler/generated + ) +endif() + +add_dependencies(arkruntime_interpreter_impl arkcompiler) + +add_subdirectory(snapshot/mem) + +# Runtime uses unicode and i18n. +set(ICU_INCLUDE_DIR + ${PANDA_THIRD_PARTY_SOURCES_DIR}/icu/icu4c/source/common + ${PANDA_THIRD_PARTY_SOURCES_DIR}/icu/icu4c/source/i18n + ${PANDA_THIRD_PARTY_SOURCES_DIR}/icu/icu4c/source +) + +target_include_directories(arkruntime_static + PUBLIC ${ICU_INCLUDE_DIR} +) + +target_include_directories(arkruntime_interpreter_impl + PUBLIC ${ICU_INCLUDE_DIR} +) + +target_include_directories(csa_tests_arkruntime_interpreter_impl + PUBLIC ${ICU_INCLUDE_DIR} +) + +if (TARGET arkruntime_test_interpreter_impl) + target_include_directories(arkruntime_test_interpreter_impl + PUBLIC ${ICU_INCLUDE_DIR} + ) +endif() + +target_include_directories(asm_defines + PUBLIC ${ICU_INCLUDE_DIR} +) + +# Allow to link targets which is not built in this directory againt hmicuuc.z hmicui18n.z +if(POLICY CMP0079) + cmake_policy(SET CMP0079 NEW) +endif() + +target_link_libraries(arkruntime_static hmicuuc.z hmicui18n.z) + +if (TARGET arkruntime_for_relayout_static) + target_link_libraries(arkruntime_for_relayout_static hmicuuc.z hmicui18n.z) +endif() + +set_source_files_properties(${ECMASCRIPT_SOURCES} PROPERTIES COMPILE_FLAGS -Wno-unused-parameter) + +set_source_files_properties(${ECMA_SRC_DIR}/js_locale.cpp PROPERTIES COMPILE_FLAGS -Wno-shadow) +set_source_files_properties(${ECMA_SRC_DIR}/js_date_time_format.cpp PROPERTIES COMPILE_FLAGS -Wno-shadow) +set_source_files_properties(${ECMA_SRC_DIR}/js_relative_time_format.cpp PROPERTIES COMPILE_FLAGS -Wno-shadow) +set_source_files_properties(${ECMA_SRC_DIR}/js_number_format.cpp PROPERTIES COMPILE_FLAGS -Wno-shadow) +set_source_files_properties(${ECMA_SRC_DIR}/global_env.cpp PROPERTIES COMPILE_FLAGS -Wno-shadow) +set_source_files_properties(${ECMA_SRC_DIR}/ecma_vm.cpp PROPERTIES COMPILE_FLAGS -Wno-shadow) + +set_source_files_properties(${ECMA_SRC_DIR}/mem/verification.cpp PROPERTIES COMPILE_FLAGS -Wno-shadow) +set_source_files_properties(${ECMA_SRC_DIR}/mem/semi_space_collector.cpp PROPERTIES COMPILE_FLAGS -Wno-shadow) + +set_source_files_properties(${ECMA_SRC_DIR}/snapshot/mem/snapshot_serialize.cpp PROPERTIES COMPILE_FLAGS -Wno-shadow) + +set_source_files_properties(${ECMA_SRC_DIR}/builtins.cpp PROPERTIES COMPILE_FLAGS -Wno-shadow) +set_source_files_properties(${ECMA_SRC_DIR}/builtins/builtins_string.cpp PROPERTIES COMPILE_FLAGS -Wno-shadow) +set_source_files_properties(${ECMA_SRC_DIR}/builtins/builtins_locale.cpp PROPERTIES COMPILE_FLAGS -Wno-shadow) +set_source_files_properties(${ECMA_SRC_DIR}/builtins/builtins_intl.cpp PROPERTIES COMPILE_FLAGS -Wno-shadow) +set_source_files_properties(${ECMA_SRC_DIR}/builtins/builtins_number_format.cpp PROPERTIES COMPILE_FLAGS -Wno-shadow) +set_source_files_properties(${ECMA_SRC_DIR}/builtins/builtins_number.cpp PROPERTIES COMPILE_FLAGS -Wno-shadow) +set_source_files_properties(${ECMA_SRC_DIR}/builtins/builtins_relative_time_format.cpp PROPERTIES COMPILE_FLAGS -Wno-shadow) +set_source_files_properties(${ECMA_SRC_DIR}/builtins/builtins_date.cpp PROPERTIES COMPILE_FLAGS -Wno-shadow) +set_source_files_properties(${ECMA_SRC_DIR}/builtins/builtins_date_time_format.cpp PROPERTIES COMPILE_FLAGS -Wno-shadow) diff --git a/runtime/accessor_data.h b/runtime/accessor_data.h new file mode 100644 index 000000000..86bb5be8b --- /dev/null +++ b/runtime/accessor_data.h @@ -0,0 +1,106 @@ +/* + * Copyright (c) 2021-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_ACCESSOR_DATA_H +#define ECMASCRIPT_ACCESSOR_DATA_H + +#include "plugins/ecmascript/runtime/ecma_macros.h" +#include "plugins/ecmascript/runtime/js_hclass.h" +#include "plugins/ecmascript/runtime/js_native_pointer.h" +#include "plugins/ecmascript/runtime/js_tagged_value.h" +#include "plugins/ecmascript/runtime/record.h" + +namespace panda::ecmascript { +class AccessorData final : public Record { +public: + using InternalGetFunc = JSTaggedValue (*)(JSThread *, const JSHandle &); + using InternalSetFunc = bool (*)(JSThread *, const JSHandle &, const JSHandle &, bool); + + static AccessorData *Cast(ObjectHeader *object) + { + ASSERT(JSTaggedValue(object).IsAccessorData() || JSTaggedValue(object).IsInternalAccessor()); + return static_cast(object); + } + + inline bool IsInternal() const + { + return GetClass()->IsInternalAccessor(); + } + + inline bool HasSetter() const + { + return !GetSetter().IsUndefined(); + } + + JSTaggedValue CallInternalGet(JSThread *thread, const JSHandle &obj) const + { + ASSERT(GetGetter().IsJSNativePointer()); + JSNativePointer *getter = JSNativePointer::Cast(GetGetter().GetTaggedObject()); + auto getFunc = reinterpret_cast(getter->GetExternalPointer()); + return getFunc(thread, obj); + } + + bool CallInternalSet(JSThread *thread, const JSHandle &obj, const JSHandle &value, + bool mayThrow = false) const + { + ASSERT(GetSetter().IsJSNativePointer()); + JSNativePointer *setter = JSNativePointer::Cast(GetSetter().GetTaggedObject()); + auto setFunc = reinterpret_cast(setter->GetExternalPointer()); + return setFunc(thread, obj, value, mayThrow); + } + + static constexpr size_t GETTER_OFFSET = Record::SIZE; + ACCESSORS(Getter, GETTER_OFFSET, SETTER_OFFSET); + ACCESSORS(Setter, SETTER_OFFSET, SIZE); + + DECL_DUMP() + DECL_VISIT_OBJECT(GETTER_OFFSET, SIZE) +}; + +class CompletionRecord final : public Record { +public: + enum : uint8_t { NORMAL = 0U, BREAK, CONTINUE, RETURN, THROW }; + + static CompletionRecord *Cast(ObjectHeader *object) + { + ASSERT(JSTaggedValue(object).IsCompletionRecord()); + return static_cast(object); + } + + bool IsThrow() const + { + return JSTaggedValue::SameValue(this->GetType(), JSTaggedValue(static_cast(THROW))); + } + + bool IsReturn() const + { + return JSTaggedValue::SameValue(this->GetType(), JSTaggedValue(static_cast(RETURN))); + } + + bool IsAbrupt() const + { + return !JSTaggedValue::SameValue(this->GetType(), JSTaggedValue(static_cast(NORMAL))); + } + + static constexpr size_t TYPE_OFFSET = Record::SIZE; + ACCESSORS(Type, TYPE_OFFSET, VALUE_OFFSET); + ACCESSORS(Value, VALUE_OFFSET, SIZE); + + DECL_DUMP() + + DECL_VISIT_OBJECT(TYPE_OFFSET, SIZE) +}; +} // namespace panda::ecmascript +#endif // ECMASCRIPT_ACCESSOR_DATA_H diff --git a/runtime/arch/aarch64/builtin_bridge_aarch64.S b/runtime/arch/aarch64/builtin_bridge_aarch64.S new file mode 100644 index 000000000..d92f90137 --- /dev/null +++ b/runtime/arch/aarch64/builtin_bridge_aarch64.S @@ -0,0 +1,110 @@ +/** + * Copyright (c) 2021-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 "arch/asm_support.h" +#include "arch/aarch64/helpers_aarch64.S" + +// DecodedTaggedValue InvokeBuiltin(ManagedThread* thread, Method* method, uint32_t num_args, +// DecodedTaggedValue* gpr_args, DecodedTaggedValue* stack_args) +.extern InvokeBuiltin + +// DecodedTaggedValue CompiledCodeToBuiltinBridge(Method* method, uint32_t num_args, DecodedTaggedValue func_obj, ...); +.global CompiledCodeToBuiltinBridge +.type CompiledCodeToBuiltinBridge, %function +CompiledCodeToBuiltinBridge: + CFI_STARTPROC + CFI_DEF_CFA(sp, 0) + + // setup frame + stp fp, lr, [sp, #-16]! + CFI_ADJUST_CFA_OFFSET(2 * 8) + CFI_REL_OFFSET(lr, 8) + CFI_REL_OFFSET(fp, 0) + mov fp, sp + CFI_DEF_CFA_REGISTER(fp) + sub sp, sp, #16 + str x0, [sp, #8] + mov x9, #CFRAME_KIND_NATIVE + str x9, [sp] + str fp, [THREAD_REG, #MANAGED_THREAD_FRAME_OFFSET] + mov w9, #1 + strb w9, [THREAD_REG, #MANAGED_THREAD_FRAME_KIND_OFFSET] + // sp must be 16 bytes aligned + + // Skip locals + sub sp, sp, #(CFRAME_LOCALS_COUNT * 8) + + // save all the callee saved registers to the stack + // stack walker will read them during stack unwinding + PUSH_CALLEE_REGS sp + CFI_REL_OFFSET(THREAD_REG, -((CFRAME_CALLEE_REGS_START_SLOT + 0) * 8)) + CFI_REL_OFFSET(x27, -((CFRAME_CALLEE_REGS_START_SLOT + 1) * 8)) + CFI_REL_OFFSET(x26, -((CFRAME_CALLEE_REGS_START_SLOT + 2) * 8)) + CFI_REL_OFFSET(x25, -((CFRAME_CALLEE_REGS_START_SLOT + 3) * 8)) + CFI_REL_OFFSET(x24, -((CFRAME_CALLEE_REGS_START_SLOT + 4) * 8)) + CFI_REL_OFFSET(x23, -((CFRAME_CALLEE_REGS_START_SLOT + 5) * 8)) + CFI_REL_OFFSET(x22, -((CFRAME_CALLEE_REGS_START_SLOT + 6) * 8)) + CFI_REL_OFFSET(x21, -((CFRAME_CALLEE_REGS_START_SLOT + 7) * 8)) + CFI_REL_OFFSET(x20, -((CFRAME_CALLEE_REGS_START_SLOT + 8) * 8)) + CFI_REL_OFFSET(x19, -((CFRAME_CALLEE_REGS_START_SLOT + 9) * 8)) + CFI_REL_OFFSET(d15, -((CFRAME_CALLEE_REGS_START_SLOT + 10) * 8)) + CFI_REL_OFFSET(d14, -((CFRAME_CALLEE_REGS_START_SLOT + 11) * 8)) + CFI_REL_OFFSET(d13, -((CFRAME_CALLEE_REGS_START_SLOT + 12) * 8)) + CFI_REL_OFFSET(d12, -((CFRAME_CALLEE_REGS_START_SLOT + 13) * 8)) + CFI_REL_OFFSET(d11, -((CFRAME_CALLEE_REGS_START_SLOT + 14) * 8)) + CFI_REL_OFFSET(d10, -((CFRAME_CALLEE_REGS_START_SLOT + 15) * 8)) + CFI_REL_OFFSET(d9, -((CFRAME_CALLEE_REGS_START_SLOT + 16) * 8)) + CFI_REL_OFFSET(d8, -((CFRAME_CALLEE_REGS_START_SLOT + 17) * 8)) + + // save arguments to the stack + // stack walker will read them during stack unwinding + PUSH_ARGS_REGS + + mov w2, w1 + mov x1, x0 + mov x0, THREAD_REG + add x3, sp, #16 // skip Method* and uint32_t num_args arguments + add x4, fp, #16 + + bl InvokeBuiltin + + add sp, sp, #64 // skip args regs + POP_CALLEE_REGS sp + CFI_RESTORE(THREAD_REG) + CFI_RESTORE(x27) + CFI_RESTORE(x26) + CFI_RESTORE(x25) + CFI_RESTORE(x24) + CFI_RESTORE(x23) + CFI_RESTORE(x22) + CFI_RESTORE(x21) + CFI_RESTORE(x20) + CFI_RESTORE(x19) + CFI_RESTORE(d15) + CFI_RESTORE(d14) + CFI_RESTORE(d13) + CFI_RESTORE(d12) + CFI_RESTORE(d11) + CFI_RESTORE(d10) + CFI_RESTORE(d9) + CFI_RESTORE(d8) + + mov sp, fp + ldp fp, lr, [sp], #16 + CFI_RESTORE(lr) + CFI_RESTORE(fp) + CFI_DEF_CFA(sp, 0) + ret + CFI_ENDPROC diff --git a/runtime/arch/amd64/builtin_bridge_amd64.S b/runtime/arch/amd64/builtin_bridge_amd64.S new file mode 100644 index 000000000..c5be19d8c --- /dev/null +++ b/runtime/arch/amd64/builtin_bridge_amd64.S @@ -0,0 +1,98 @@ +/** + * Copyright (c) 2021-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 "arch/asm_support.h" +#include "arch/asm_support.h" +#include "arch/amd64/helpers_amd64.S" + +// DecodedTaggedValue InvokeBuiltin(ManagedThread* thread, Method* method, uint32_t num_args, +// DecodedTaggedValue* gpr_args, DecodedTaggedValue* stack_args) +.extern InvokeBuiltin + +// DecodedTaggedValue CompiledCodeToBuiltinBridge(Method* method, uint32_t num_args, DecodedTaggedValue func_obj, ...); +.global CompiledCodeToBuiltinBridge +.type CompiledCodeToBuiltinBridge, %function +CompiledCodeToBuiltinBridge: + CFI_STARTPROC + CFI_DEF_CFA(rsp, 8) + + // setup frame + pushq %rbp + CFI_ADJUST_CFA_OFFSET(8) + CFI_REL_OFFSET(rbp, 0) + + movq %rsp, %rbp + CFI_DEF_CFA_REGISTER(rbp) + + pushq %rdi + pushq $CFRAME_KIND_NATIVE // fill flags slot + + movq %rbp, MANAGED_THREAD_FRAME_OFFSET(%THREAD_REG) + movb $1, MANAGED_THREAD_FRAME_KIND_OFFSET(%THREAD_REG) + // rsp must be 16 bytes aligned + + // Skip locals + subq $(CFRAME_LOCALS_COUNT * 8), %rsp + + // save all the callee saved registers to the stack + // stack walker will read them during stack unwinding + pushq %THREAD_REG + CFI_REL_OFFSET(THREAD_REG, -((CFRAME_CALLEE_REGS_START_SLOT + 0) * 8)) + pushq %r14 + CFI_REL_OFFSET(r14, -((CFRAME_CALLEE_REGS_START_SLOT + 1) * 8)) + pushq %r13 + CFI_REL_OFFSET(r13, -((CFRAME_CALLEE_REGS_START_SLOT + 2) * 8)) + pushq %r12 + CFI_REL_OFFSET(r12, -((CFRAME_CALLEE_REGS_START_SLOT + 3) * 8)) + pushq %rbx + CFI_REL_OFFSET(rbx, -((CFRAME_CALLEE_REGS_START_SLOT + 4) * 8)) + + // save arguments to the stack + // stack walker will read them during stack unwinding + pushq %r9 + pushq %r8 + pushq %rcx + pushq %rdx + pushq %rsi + pushq %rdi + + sub $8, %rsp // make %rsp 16 bytes aligned + + movl %esi, %edx + movq %rdi, %rsi + movq %THREAD_REG, %rdi + leaq 24(%rsp), %rcx // skip Method* and uint32_t num_args arguments + leaq 16(%rbp), %r8 // skip return address + + callq InvokeBuiltin@plt + + // Restore callee registers, since GC may change its values while moving objects. + movq -((CFRAME_CALLEE_REGS_START_SLOT + 0) * 8)(%rbp), %r15 + CFI_RESTORE(r15) + movq -((CFRAME_CALLEE_REGS_START_SLOT + 1) * 8)(%rbp), %r14 + CFI_RESTORE(r14) + movq -((CFRAME_CALLEE_REGS_START_SLOT + 2) * 8)(%rbp), %r13 + CFI_RESTORE(r13) + movq -((CFRAME_CALLEE_REGS_START_SLOT + 3) * 8)(%rbp), %r12 + CFI_RESTORE(r12) + movq -((CFRAME_CALLEE_REGS_START_SLOT + 4) * 8)(%rbp), %rbx + CFI_RESTORE(rbx) + + movq %rbp, %rsp + popq %rbp + CFI_RESTORE(rbp) + CFI_DEF_CFA(rsp, (1 * 8)) + retq + CFI_ENDPROC diff --git a/runtime/arch/arm/builtin_bridge_arm.S b/runtime/arch/arm/builtin_bridge_arm.S new file mode 100644 index 000000000..abd721f80 --- /dev/null +++ b/runtime/arch/arm/builtin_bridge_arm.S @@ -0,0 +1,24 @@ +/* +* Copyright (c) 2021-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. +*/ + +// DecodedTaggedValue InvokeBuiltin(ManagedThread* thread, Method* method, uint32_t num_args, +// DecodedTaggedValue* gpr_args, DecodedTaggedValue* stack_args) +.extern InvokeBuiltin + +// DecodedTaggedValue CompiledCodeToBuiltinBridge(Method* method, uint32_t num_args, DecodedTaggedValue func_obj, ...); +.global CompiledCodeToBuiltinBridge +.type CompiledCodeToBuiltinBridge, %function +CompiledCodeToBuiltinBridge: + bx lr diff --git a/runtime/asm_defines/asm_defines.def b/runtime/asm_defines/asm_defines.def new file mode 100644 index 000000000..bb99f6c06 --- /dev/null +++ b/runtime/asm_defines/asm_defines.def @@ -0,0 +1,45 @@ +/** + * Copyright (c) Huawei Technologies Co., Ltd. 2019-2021. All rights reserved. + */ + +DEFINE_VALUE(ECMASCRIPT_ENVIRONMENT_LEXICAL_ENV_OFFSET, panda::ecmascript::EcmascriptEnvironment::GetLexicalEnvOffset()) +DEFINE_VALUE(JSTYPE_JS_OBJECT_BEGIN, static_cast(panda::ecmascript::JSType::JS_OBJECT_BEGIN)) +DEFINE_VALUE(JSTYPE_JS_OBJECT_END, static_cast(panda::ecmascript::JSType::JS_OBJECT_END)) +DEFINE_VALUE(JSTYPE_STRING, static_cast(panda::ecmascript::JSType::STRING)) +DEFINE_VALUE(JSTYPE_JS_ARRAY, static_cast(panda::ecmascript::JSType::JS_ARRAY)) +DEFINE_VALUE(JSTYPE_PROTOTYPE_HANDLER, static_cast(panda::ecmascript::JSType::PROTOTYPE_HANDLER)) +DEFINE_VALUE(JSTYPE_TRANSITION_HANDLER, static_cast(panda::ecmascript::JSType::TRANSITION_HANDLER)) +DEFINE_VALUE(ECMASTRING_MIX_LENGTH_OFFSET, panda::ecmascript::EcmaString::MIX_LENGTH_OFFSET) +DEFINE_VALUE(JSFORINITERATOR_FAST_REMAINING_INDEX_OFFSET, panda::ecmascript::JSForInIterator::FAST_REMAINING_INDEX_OFFSET) +DEFINE_VALUE(JSFORINITERATOR_REMAINING_KEYS_OFFSET, panda::ecmascript::JSForInIterator::REMAINING_KEYS_OFFSET) +DEFINE_VALUE(TAGGEDARRAY_DATA_OFFSET, panda::ecmascript::TaggedArray::DATA_OFFSET) +DEFINE_VALUE(TAGGEDQUEUE_START_INDEX, panda::ecmascript::TaggedQueue::START_INDEX) +DEFINE_VALUE(TAGGEDQUEUE_END_INDEX, panda::ecmascript::TaggedQueue::END_INDEX) +DEFINE_VALUE(TAGGEDQUEUE_ELEMENTS_START_INDEX, panda::ecmascript::TaggedQueue::ELEMENTS_START_INDEX) +DEFINE_VALUE(JSHCLASS_BITFIELD_OFFSET, panda::ecmascript::JSHClass::BIT_FIELD_OFFSET) +DEFINE_VALUE(JSHCLASS_BITFIELD_TYPE_MASK, panda::ecmascript::JSHClass::ObjectTypeBits::MaxValue()) +DEFINE_VALUE(JSHCLASS_BITFIELD_TYPE_START_BIT, panda::ecmascript::JSHClass::ObjectTypeBits::START_BIT) +DEFINE_VALUE(JSHCLASS_BITFIELD_IS_DICTIONARY_START_BIT, panda::ecmascript::JSHClass::IsDictionaryBit::START_BIT) +DEFINE_VALUE(JSHCLASS_BITFIELD_EXTENSIBLE_START_BIT, panda::ecmascript::JSHClass::ExtensibleBit::START_BIT) +DEFINE_VALUE(JSHCLASS_BITFIELD_INLINED_PROPS_START_BITS_START_BIT, panda::ecmascript::JSHClass::InlinedPropsStartBits::START_BIT) +DEFINE_VALUE(JSHCLASS_BITFIELD_INLINED_PROPS_START_BITS_MASK, panda::ecmascript::JSHClass::InlinedPropsStartBits::MaxValue()) +DEFINE_VALUE(JSHCLASS_HCLASS_OFFSET, panda::ecmascript::JSHClass::GetHClassOffset()) +DEFINE_VALUE(JSOBJECT_PROPERTIES_OFFSET, panda::ecmascript::JSObject::PROPERTIES_OFFSET) +DEFINE_VALUE(JSOBJECT_ELEMENTS_OFFSET, panda::ecmascript::JSObject::ELEMENTS_OFFSET) +DEFINE_VALUE(JSARRAY_LENGTH_OFFSET, panda::ecmascript::JSArray::LENGTH_OFFSET) +DEFINE_VALUE(JSFUNCTION_PROFILE_TYPE_INFO_OFFSET, panda::ecmascript::JSFunction::PROFILE_TYPE_INFO_OFFSET) +DEFINE_VALUE(JSPROPERTY_BOX_VALUE_OFFSET, panda::ecmascript::PropertyBox::VALUE_OFFSET) +DEFINE_VALUE(JSTRANSITION_HANDLER_HANDLER_INFO_OFFSET, panda::ecmascript::TransitionHandler::HANDLER_INFO_OFFSET) +DEFINE_VALUE(JSTRANSITION_HANDLER_HCLASS_OFFSET, panda::ecmascript::TransitionHandler::TRANSITION_HCLASS_OFFSET) +DEFINE_VALUE(JSPROTOTYPE_HANDLER_HANDLER_INFO_OFFSET, panda::ecmascript::PrototypeHandler::HANDLER_INFO_OFFSET) +DEFINE_VALUE(JSPROTOTYPE_HANDLER_PROTO_CELL_OFFSET, panda::ecmascript::PrototypeHandler::PROTO_CELL_OFFSET) +DEFINE_VALUE(JSPROTOTYPE_HANDLER_HOLDER_OFFSET, panda::ecmascript::PrototypeHandler::HOLDER_OFFSET) +DEFINE_VALUE(JSPROTO_CHANGE_MARKER_HAS_CHANGED_OFFSET, panda::ecmascript::ProtoChangeMarker::HAS_CHANGED_OFFSET) +DEFINE_VALUE(IC_HANDLER_KIND_BIT_START_BIT, panda::ecmascript::HandlerBase::KindBit::START_BIT) +DEFINE_VALUE(IC_HANDLER_KIND_BIT_MASK, panda::ecmascript::HandlerBase::KindBit::MaxValue()) +DEFINE_VALUE(IC_HANDLER_OFFSET_BIT_START_BIT, panda::ecmascript::HandlerBase::OffsetBit::START_BIT) +DEFINE_VALUE(IC_HANDLER_OFFSET_BIT_MASK, panda::ecmascript::HandlerBase::OffsetBit::MaxValue()) +DEFINE_VALUE(IC_HANDLER_INLINED_PROPS_BIT_START_BIT, panda::ecmascript::HandlerBase::InlinedPropsBit::START_BIT) +DEFINE_VALUE(IC_HANDLER_INLINED_PROPS_BIT_MASK, panda::ecmascript::HandlerBase::InlinedPropsBit::MaxValue()) +DEFINE_VALUE(IC_HANDLER_HANDLER_KIND_FIELD, static_cast(panda::ecmascript::HandlerBase::HandlerKind::FIELD)) +DEFINE_VALUE(HCLASS_DATA_OFFSET, HClass::GetDataOffset()) diff --git a/runtime/asm_defines/defines.h b/runtime/asm_defines/defines.h new file mode 100644 index 000000000..fc02bf404 --- /dev/null +++ b/runtime/asm_defines/defines.h @@ -0,0 +1,31 @@ +/* +* Copyright (c) 2021-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 PLUGINS_ECMASCRIPT_RUNTIME_ASM_DEFINES_DEFINES_H +#define PLUGINS_ECMASCRIPT_RUNTIME_ASM_DEFINES_DEFINES_H + +#include "plugins/ecmascript/compiler/ecmascript_extensions/ecmascript_environment.h" +#include "plugins/ecmascript/runtime/ic/ic_handler.h" +#include "plugins/ecmascript/runtime/ic/proto_change_details.h" +#include "plugins/ecmascript/runtime/js_array.h" +#include "plugins/ecmascript/runtime/js_function.h" +#include "plugins/ecmascript/runtime/js_tagged_value.h" +#include "plugins/ecmascript/runtime/js_hclass.h" +#include "plugins/ecmascript/runtime/ecma_string.h" +#include "plugins/ecmascript/runtime/js_for_in_iterator.h" +#include "plugins/ecmascript/runtime/tagged_array.h" +#include "plugins/ecmascript/runtime/tagged_queue.h" + +#endif // PLUGINS_ECMASCRIPT_RUNTIME_ASM_DEFINES_DEFINES_H diff --git a/runtime/base/array_helper.cpp b/runtime/base/array_helper.cpp new file mode 100644 index 000000000..9ad0588e7 --- /dev/null +++ b/runtime/base/array_helper.cpp @@ -0,0 +1,144 @@ +/* + * Copyright (c) 2021-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 "plugins/ecmascript/runtime/base/array_helper.h" + +#include "plugins/ecmascript/runtime/base/typed_array_helper-inl.h" +#include "plugins/ecmascript/runtime/ecma_macros.h" +#include "plugins/ecmascript/runtime/ecma_vm.h" +#include "plugins/ecmascript/runtime/global_env.h" +#include "plugins/ecmascript/runtime/internal_call_params.h" +#include "plugins/ecmascript/runtime/js_array.h" +#include "plugins/ecmascript/runtime/js_hclass.h" +#include "plugins/ecmascript/runtime/js_tagged_number.h" +#include "plugins/ecmascript/runtime/js_tagged_value-inl.h" + +namespace panda::ecmascript::base { +bool ArrayHelper::IsConcatSpreadable(JSThread *thread, const JSHandle &obj) +{ + // 1. If Type(O) is not Object, return false. + if (!obj->IsECMAObject()) { + return false; + } + + // 2. Let spreadable be Get(O, @@isConcatSpreadable). + auto ecmaVm = thread->GetEcmaVM(); + JSHandle env = ecmaVm->GetGlobalEnv(); + JSHandle isConcatsprKey = env->GetIsConcatSpreadableSymbol(); + JSHandle spreadable = JSTaggedValue::GetProperty(thread, obj, isConcatsprKey).GetValue(); + // 3. ReturnIfAbrupt(spreadable). + RETURN_VALUE_IF_ABRUPT_COMPLETION(thread, false); + + // 4. If spreadable is not undefined, return ToBoolean(spreadable). + if (!spreadable->IsUndefined()) { + return spreadable->ToBoolean(); + } + + // 5. Return IsArray(O). + return obj->IsArray(thread); +} + +int32_t ArrayHelper::SortCompare(JSThread *thread, const JSHandle &callbackfnHandle, + const JSHandle &valueX, const JSHandle &valueY) +{ + // 1. If x and y are both undefined, return +0. + if (valueX->IsHole()) { + if (valueY->IsHole()) { + return 0; + } + return 1; + } + if (valueY->IsHole()) { + return -1; + } + if (valueX->IsUndefined()) { + if (valueY->IsUndefined()) { + return 0; + } + // 2. If x is undefined, return 1. + return 1; + } + // 3. If y is undefined, return -1. + if (valueY->IsUndefined()) { + return -1; + } + // 4. If the argument comparefn is not undefined, then + // a. Let v be ToNumber(Call(comparefn, undefined, «x, y»)). + // b. ReturnIfAbrupt(v). + // c. If v is NaN, return +0. + // d. Return v. + if (!callbackfnHandle->IsUndefined()) { + JSHandle thisArgHandle(thread, JSTaggedValue::Undefined()); + InternalCallParams *arguments = thread->GetInternalCallParams(); + arguments->MakeArgv(valueX, valueY); + JSTaggedValue callResult = + JSFunction::Call(thread, callbackfnHandle, thisArgHandle, 2, arguments->GetArgv()); // 2: two args + if (callResult.IsInt()) { + return callResult.GetInt(); + } + RETURN_VALUE_IF_ABRUPT_COMPLETION(thread, 0); + JSHandle testResult(thread, callResult); + JSTaggedNumber v = JSTaggedValue::ToNumber(thread, testResult); + RETURN_VALUE_IF_ABRUPT_COMPLETION(thread, 0); + double value = v.GetNumber(); + if (std::isnan(value)) { + return +0; + } + return static_cast(value); + } + // 5. Let xString be ToString(x). + // 6. ReturnIfAbrupt(xString). + // 7. Let yString be ToString(y). + // 8. ReturnIfAbrupt(yString). + // 9. If xString < yString, return -1. + // 10. If xString > yString, return 1. + // 11. Return +0. + JSHandle xValueHandle(JSTaggedValue::ToString(thread, valueX)); + RETURN_VALUE_IF_ABRUPT_COMPLETION(thread, 0); + JSHandle yValueHandle(JSTaggedValue::ToString(thread, valueY)); + RETURN_VALUE_IF_ABRUPT_COMPLETION(thread, 0); + ComparisonResult compareResult = JSTaggedValue::Compare(thread, xValueHandle, yValueHandle); + return compareResult == ComparisonResult::GREAT ? 1 : 0; +} + +double ArrayHelper::GetLength(JSThread *thread, const JSHandle &thisHandle) +{ + if (thisHandle->IsJSArray()) { + return JSArray::Cast(thisHandle->GetTaggedObject())->GetArrayLength(); + } + if (thisHandle->IsTypedArray()) { + return TypedArrayHelper::GetArrayLength(thread, JSHandle::Cast(thisHandle)); + } + JSHandle lengthKey = thread->GlobalConstants()->GetHandledLengthString(); + JSHandle lenResult = JSTaggedValue::GetProperty(thread, thisHandle, lengthKey).GetValue(); + RETURN_VALUE_IF_ABRUPT_COMPLETION(thread, 0); + JSTaggedNumber len = JSTaggedValue::ToLength(thread, lenResult); + RETURN_VALUE_IF_ABRUPT_COMPLETION(thread, 0); + return len.GetNumber(); +} + +double ArrayHelper::GetArrayLength(JSThread *thread, const JSHandle &thisHandle) +{ + if (thisHandle->IsJSArray()) { + return JSArray::Cast(thisHandle->GetTaggedObject())->GetArrayLength(); + } + JSHandle lengthKey = thread->GlobalConstants()->GetHandledLengthString(); + JSHandle lenResult = JSTaggedValue::GetProperty(thread, thisHandle, lengthKey).GetValue(); + RETURN_VALUE_IF_ABRUPT_COMPLETION(thread, 0); + JSTaggedNumber len = JSTaggedValue::ToLength(thread, lenResult); + RETURN_VALUE_IF_ABRUPT_COMPLETION(thread, 0); + return len.GetNumber(); +} +} // namespace panda::ecmascript::base diff --git a/runtime/base/array_helper.h b/runtime/base/array_helper.h new file mode 100644 index 000000000..f6e790909 --- /dev/null +++ b/runtime/base/array_helper.h @@ -0,0 +1,35 @@ +/* + * Copyright (c) 2021-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_BASE_ARRAY_HELPER_H +#define ECMASCRIPT_BASE_ARRAY_HELPER_H + +#include +#include + +#include "plugins/ecmascript/runtime/base/builtins_base.h" + +namespace panda::ecmascript::base { +class ArrayHelper { +public: + static bool IsConcatSpreadable(JSThread *thread, const JSHandle &obj); + static int32_t SortCompare(JSThread *thread, const JSHandle &callbackfnHandle, + const JSHandle &valueX, const JSHandle &valueY); + static double GetLength(JSThread *thread, const JSHandle &thisHandle); + static double GetArrayLength(JSThread *thread, const JSHandle &thisHandle); +}; +} // namespace panda::ecmascript::base + +#endif // ECMASCRIPT_BASE_ARRAY_HELPER_H diff --git a/runtime/base/builtins_base.cpp b/runtime/base/builtins_base.cpp new file mode 100644 index 000000000..3f06afaa4 --- /dev/null +++ b/runtime/base/builtins_base.cpp @@ -0,0 +1,32 @@ +/* + * Copyright (c) 2021-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 "plugins/ecmascript/runtime/base/builtins_base.h" +#include "plugins/ecmascript/runtime/js_tagged_value-inl.h" +#include "plugins/ecmascript/runtime/tagged_array-inl.h" + +namespace panda::ecmascript::base { +JSHandle BuiltinsBase::GetArgsArray(EcmaRuntimeCallInfo *msg) +{ + JSThread *thread = msg->GetThread(); + ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); + uint32_t length = msg->GetArgsNumber(); + JSHandle array = factory->NewTaggedArray(length); + for (uint32_t i = 0; i < length; ++i) { + array->Set(thread, i, GetCallArg(msg, i).GetTaggedValue()); + } + return array; +} +} // namespace panda::ecmascript::base diff --git a/runtime/base/builtins_base.h b/runtime/base/builtins_base.h new file mode 100644 index 000000000..36ac84387 --- /dev/null +++ b/runtime/base/builtins_base.h @@ -0,0 +1,85 @@ +/* + * Copyright (c) 2021-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_BASE_BUILTINS_BASE_H +#define ECMASCRIPT_BASE_BUILTINS_BASE_H + +#include "plugins/ecmascript/runtime/base/string_helper.h" +#include "plugins/ecmascript/runtime/ecma_runtime_call_info.h" +#include "plugins/ecmascript/runtime/ecma_string.h" +#include "plugins/ecmascript/runtime/ecma_vm.h" +#include "plugins/ecmascript/runtime/global_env_constants-inl.h" +#include "plugins/ecmascript/runtime/js_symbol.h" +#include "plugins/ecmascript/runtime/js_tagged_value.h" +#include "plugins/ecmascript/runtime/object_factory.h" +#include "plugins/ecmascript/runtime/runtime_call_id.h" +#include "plugins/ecmascript/runtime/tagged_array.h" +#include "plugins/ecmascript/runtime/vmstat/runtime_stat.h" + +namespace panda::ecmascript { +class JSArray; +namespace base { +class BuiltinsBase { +public: + enum ArgsPosition : uint32_t { FIRST = 0, SECOND, THIRD, FOURTH, FIFTH }; + static JSHandle GetArgsArray(EcmaRuntimeCallInfo *msg); + static inline JSHandle GetConstructor(EcmaRuntimeCallInfo *msg) + { + return msg->GetFunction(); + } + + static inline JSHandle GetThis(EcmaRuntimeCallInfo *msg) + { + return msg->GetThis(); + } + + static inline JSHandle GetNewTarget(EcmaRuntimeCallInfo *msg) + { + return msg->GetNewTarget(); + } + + static inline JSHandle GetCallArg(EcmaRuntimeCallInfo *msg, uint32_t position) + { + if (position >= msg->GetArgsNumber()) { + JSThread *thread = msg->GetThread(); + return thread->GlobalConstants()->GetHandledUndefined(); + } + return msg->GetCallArg(position); + } + + static inline JSTaggedValue GetTaggedInt(int32_t value) + { + return JSTaggedValue(value); + } + + static inline JSTaggedValue GetTaggedDouble(double value) + { + return JSTaggedValue(value); + } + + static inline JSTaggedValue GetTaggedBoolean(bool value) + { + return JSTaggedValue(value); + } + + static inline JSTaggedValue GetTaggedString(JSThread *thread, const char *str) + { + return thread->GetEcmaVM()->GetFactory()->NewFromCanBeCompressString(str).GetTaggedValue(); + } +}; +} // namespace base +} // namespace panda::ecmascript + +#endif // ECMASCRIPT_BASE_BUILTINS_BASE_H diff --git a/runtime/base/config.h b/runtime/base/config.h new file mode 100644 index 000000000..d25346190 --- /dev/null +++ b/runtime/base/config.h @@ -0,0 +1,58 @@ +/* + * Copyright (c) 2021-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_BASE_CONFIG_H +#define ECMASCRIPT_BASE_CONFIG_H + +namespace panda::ecmascript { +#define ARK_INLINE __attribute__((always_inline)) // NOLINTNEXTLINE(cppcoreguidelines-macro-usage) +#define ARK_NOINLINE __attribute__((noinline)) // NOLINTNEXTLINE(cppcoreguidelines-macro-usage) + +#define ECMASCRIPT_ENABLE_DEBUG_MODE 0 // NOLINTNEXTLINE(cppcoreguidelines-macro-usage) +#define ECMASCRIPT_ENABLE_ARK_CONTAINER 0 // NOLINTNEXTLINE(cppcoreguidelines-macro-usage) +#define ECMASCRIPT_ENABLE_RUNTIME_STAT 1 // NOLINTNEXTLINE(cppcoreguidelines-macro-usage) + +/* + * 1. close ic + * 2. close parallel gc + * 3. enable gc logs + * 4. enable handle-scope zap, zap reclaimed regions + * 5. switch gc mode to full gc + * 6. enable Cast() check + * 7. enable verify heap + * 9. enable Proactively interrogating and collecting information in the call stack + */ +#if ECMASCRIPT_ENABLE_DEBUG_MODE +#define ECMASCRIPT_ENABLE_IC 0 // NOLINTNEXTLINE(cppcoreguidelines-macro-usage) +#define ECMASCRIPT_DISABLE_PARALLEL_GC 1 // NOLINTNEXTLINE(cppcoreguidelines-macro-usage) +#define ECMASCRIPT_ENABLE_GC_LOG 1 // NOLINTNEXTLINE(cppcoreguidelines-macro-usage) +#define ECMASCRIPT_ENABLE_ZAP_MEM 1 // NOLINTNEXTLINE(cppcoreguidelines-macro-usage) +#define ECMASCRIPT_SWITCH_GC_MODE_TO_COMPRESS_GC 1 // NOLINTNEXTLINE(cppcoreguidelines-macro-usage) +#define ECMASCRIPT_ENABLE_CAST_CHECK 1 // NOLINTNEXTLINE(cppcoreguidelines-macro-usage) +#define ECMASCRIPT_ENABLE_HEAP_VERIFY 1 // NOLINTNEXTLINE(cppcoreguidelines-macro-usage) +#define ECMASCRIPT_ENABLE_THREAD_CHECK 1 // NOLINTNEXTLINE(cppcoreguidelines-macro-usage) +#else +#define ECMASCRIPT_ENABLE_IC 1 // NOLINTNEXTLINE(cppcoreguidelines-macro-usage) +#define ECMASCRIPT_DISABLE_PARALLEL_GC 0 // NOLINTNEXTLINE(cppcoreguidelines-macro-usage) +#define ECMASCRIPT_ENABLE_GC_LOG 0 // NOLINTNEXTLINE(cppcoreguidelines-macro-usage) +#define ECMASCRIPT_ENABLE_ZAP_MEM 0 // NOLINTNEXTLINE(cppcoreguidelines-macro-usage) +#define ECMASCRIPT_SWITCH_GC_MODE_TO_COMPRESS_GC 0 // NOLINTNEXTLINE(cppcoreguidelines-macro-usage) +#define ECMASCRIPT_ENABLE_CAST_CHECK 0 // NOLINTNEXTLINE(cppcoreguidelines-macro-usage) +#define ECMASCRIPT_ENABLE_HEAP_VERIFY 0 // NOLINTNEXTLINE(cppcoreguidelines-macro-usage) +#define ECMASCRIPT_ENABLE_THREAD_CHECK 1 // NOLINTNEXTLINE(cppcoreguidelines-macro-usage) +#endif +} // namespace panda::ecmascript + +#endif // ECMASCRIPT_BASE_CONFIG_H diff --git a/runtime/base/error_helper.cpp b/runtime/base/error_helper.cpp new file mode 100644 index 000000000..e87756cbd --- /dev/null +++ b/runtime/base/error_helper.cpp @@ -0,0 +1,221 @@ +/* + * Copyright (c) 2021-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 "plugins/ecmascript/runtime/base/error_helper.h" +#include "include/stack_walker.h" +#include "plugins/ecmascript/runtime/base/builtins_base.h" +#include "plugins/ecmascript/runtime/base/error_type.h" +#include "plugins/ecmascript/runtime/ecma_macros.h" +#include "plugins/ecmascript/runtime/ecma_vm.h" +#include "plugins/ecmascript/runtime/global_env.h" +#include "plugins/ecmascript/runtime/interpreter/interpreter.h" +#include "plugins/ecmascript/runtime/js_object-inl.h" +#include "plugins/ecmascript/runtime/js_tagged_value-inl.h" +#include "plugins/ecmascript/runtime/object_factory.h" +#include "plugins/ecmascript/runtime/tooling/pt_js_extractor.h" + +namespace panda::ecmascript::base { +using panda::tooling::ecmascript::PtJSExtractor; + +JSTaggedValue ErrorHelper::ErrorCommonToString(EcmaRuntimeCallInfo *argv, const ErrorType &errorType) +{ + ASSERT(argv); + JSThread *thread = argv->GetThread(); + [[maybe_unused]] EcmaHandleScope handleScope(thread); + + // 1. Let O be the this value. + // 2. If Type(O) is not Object, throw a TypeError exception + JSHandle thisValue = BuiltinsBase::GetThis(argv); + if (!thisValue->IsECMAObject()) { + THROW_TYPE_ERROR_AND_RETURN(thread, "ErrorToString:not an object", JSTaggedValue::Exception()); + } + // 3. Let name be Get(O, "name"). + // 4. ReturnIfAbrupt(name). + auto globalConst = thread->GlobalConstants(); + JSHandle handleName = globalConst->GetHandledNameString(); + JSHandle name = JSObject::GetProperty(thread, thisValue, handleName).GetValue(); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + + // 5. If name is undefined, let name be "Error"; otherwise let name be ToString(name). + // 6. ReturnIfAbrupt(name). + name = ErrorHelper::GetErrorName(thread, name, errorType); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + + // 7. Let msg be Get(O, "message"). + // 8. ReturnIfAbrupt(msg). + JSHandle handleMsg = globalConst->GetHandledMessageString(); + JSHandle msg = JSObject::GetProperty(thread, thisValue, handleMsg).GetValue(); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + + // 9. If msg is undefined, let msg be the empty String; otherwise let msg be ToString(msg). + // 10. ReturnIfAbrupt(msg). + ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); + if (msg->IsUndefined()) { + msg = JSHandle::Cast(factory->GetEmptyString()); + } else { + msg = JSHandle::Cast(JSTaggedValue::ToString(thread, msg)); + } + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + + // 11. If name is the empty String, return msg. + // 12. If msg is the empty String, return name. + if (JSHandle::Cast(name)->GetLength() == 0) { + return msg.GetTaggedValue(); + } + + if (JSHandle::Cast(msg)->GetLength() == 0) { + return name.GetTaggedValue(); + } + + // 13. Return the result of concatenating name, the code unit 0x003A (COLON), the code unit 0x0020 (SPACE), and msg. + JSHandle space = factory->NewFromCanBeCompressString(": "); + JSHandle jsHandleName = JSHandle::Cast(name); + JSHandle jsHandleMsg = JSHandle::Cast(msg); + JSHandle handleNameSpace = factory->ConcatFromString(jsHandleName, space); + JSHandle result = factory->ConcatFromString(handleNameSpace, jsHandleMsg); + return result.GetTaggedValue(); +} + +JSHandle ErrorHelper::GetErrorName(JSThread *thread, const JSHandle &name, + const ErrorType &errorType) +{ + auto globalConst = thread->GlobalConstants(); + if (name->IsUndefined()) { + switch (errorType) { + case ErrorType::RANGE_ERROR: + return globalConst->GetHandledRangeErrorString(); + case ErrorType::EVAL_ERROR: + return globalConst->GetHandledEvalErrorString(); + case ErrorType::REFERENCE_ERROR: + return globalConst->GetHandledReferenceErrorString(); + case ErrorType::TYPE_ERROR: + return globalConst->GetHandledTypeErrorString(); + case ErrorType::URI_ERROR: + return globalConst->GetHandledURIErrorString(); + case ErrorType::SYNTAX_ERROR: + return globalConst->GetHandledSyntaxErrorString(); + default: + return globalConst->GetHandledErrorString(); + } + } + return JSHandle::Cast(JSTaggedValue::ToString(thread, name)); +} + +JSTaggedValue ErrorHelper::ErrorCommonConstructor(EcmaRuntimeCallInfo *argv, + [[maybe_unused]] const ErrorType &errorType) +{ + JSThread *thread = argv->GetThread(); + [[maybe_unused]] EcmaHandleScope handleScope(thread); + + // 1. If NewTarget is undefined, let newTarget be the active function object, else let newTarget be NewTarget. + auto ecmaVm = thread->GetEcmaVM(); + ObjectFactory *factory = ecmaVm->GetFactory(); + JSHandle ctor = BuiltinsBase::GetConstructor(argv); + JSMutableHandle newTarget(BuiltinsBase::GetNewTarget(argv)); + if (newTarget->IsUndefined()) { + newTarget.Update(ctor.GetTaggedValue()); + } + JSHandle message = BuiltinsBase::GetCallArg(argv, 0); + + // 2. Let O be OrdinaryCreateFromConstructor(newTarget, "%ErrorPrototype%", «[[ErrorData]]»). + JSHandle nativeInstanceObj = factory->NewJSObjectByConstructor(JSHandle(ctor), newTarget); + + // 3. ReturnIfAbrupt(O). + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + + // 4. If message is not undefined, then + // a. Let msg be ToString(message). + // b. ReturnIfAbrupt(msg). + // c. Let msgDesc be the PropertyDescriptor{[[Value]]: msg, [[Writable]]: true, [[Enumerable]]: false, + // [[Configurable]]: true}. + // d. Let status be DefinePropertyOrThrow(O, "message", msgDesc). + // e. Assert: status is not an abrupt completion + auto globalConst = thread->GlobalConstants(); + if (!message->IsUndefined()) { + JSHandle handleStr = JSTaggedValue::ToString(thread, message); + LOG(DEBUG, ECMASCRIPT) << "Ark throw error: " << utf::Mutf8AsCString(handleStr->GetDataUtf8()); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + JSHandle msgKey = globalConst->GetHandledMessageString(); + PropertyDescriptor msgDesc(thread, JSHandle::Cast(handleStr), true, false, true); + [[maybe_unused]] bool status = JSObject::DefineOwnProperty(thread, nativeInstanceObj, msgKey, msgDesc); + ASSERT_PRINT(status, "return result exception!"); + } + + JSHandle handleStack = BuildEcmaStackTrace(thread); + JSHandle stackkey = globalConst->GetHandledStackString(); + PropertyDescriptor stackDesc(thread, JSHandle::Cast(handleStack), true, false, true); + [[maybe_unused]] bool status = JSObject::DefineOwnProperty(thread, nativeInstanceObj, stackkey, stackDesc); + ASSERT_PRINT(status, "return result exception!"); + + // 5. Return O. + return nativeInstanceObj.GetTaggedValue(); +} + +CString ErrorHelper::DecodeFunctionName(const CString &name) +{ + if (name.empty()) { + return "anonymous"; + } + return name; +} + +JSHandle ErrorHelper::BuildEcmaStackTrace(JSThread *thread) +{ + CString data = BuildNativeEcmaStackTrace(thread); + ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); + LOG(DEBUG, ECMASCRIPT) << data; + return factory->NewFromString(data); +} + +CString ErrorHelper::BuildNativeEcmaStackTrace(JSThread *thread) +{ + auto ecmaVm = thread->GetEcmaVM(); + CString data; + + for (auto stack = StackWalker::Create(thread); stack.HasFrame(); stack.NextFrame()) { + auto method = stack.GetMethod(); + if (method->IsNative()) { + data += INTRINSIC_METHOD_NAME; + } else { + data.append(" at "); + data += DecodeFunctionName(JSMethod::Cast(method)->ParseFunctionName()); + data.append(" ("); + // source file + PtJSExtractor *debugExtractor = ecmaVm->GetDebugInfoExtractor(method->GetPandaFile()); + const CString &sourceFile = debugExtractor->GetSourceFile(method->GetFileId()); + if (sourceFile.empty()) { + data.push_back('?'); + } else { + data += sourceFile; + } + data.push_back(':'); + // line number and column number + auto callbackFunc = [&data](size_t line, size_t column) -> bool { + data += ToCString(line + 1); + data.push_back(':'); + data += ToCString(column + 1); + return true; + }; + if (!debugExtractor->MatchWithOffset(callbackFunc, method->GetFileId(), stack.GetBytecodePc())) { + data.push_back('?'); + } + data.push_back(')'); + } + data.push_back('\n'); + } + + return data; +} +} // namespace panda::ecmascript::base diff --git a/runtime/base/error_helper.h b/runtime/base/error_helper.h new file mode 100644 index 000000000..34e584cbf --- /dev/null +++ b/runtime/base/error_helper.h @@ -0,0 +1,44 @@ +/* + * Copyright (c) 2021-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_BASE_ERROR_HELPER_H +#define ECMASCRIPT_BASE_ERROR_HELPER_H + +#include "plugins/ecmascript/runtime/base/error_type.h" +#include "plugins/ecmascript/runtime/ecma_runtime_call_info.h" +#include "plugins/ecmascript/runtime/js_handle.h" +#include "plugins/ecmascript/runtime/mem/c_string.h" + +namespace panda::ecmascript::base { +constexpr char DEFAULT_EMPTY_STACK_TRACE[] = "stack is empty"; // NOLINT (modernize-avoid-c-arrays) +constexpr char INTRINSIC_METHOD_NAME[] = "Intrinsic method"; // NOLINT (modernize-avoid-c-arrays) + +class ErrorHelper { +public: + static JSTaggedValue ErrorCommonToString(EcmaRuntimeCallInfo *argv, const ErrorType &errorType); + + static JSTaggedValue ErrorCommonConstructor(EcmaRuntimeCallInfo *argv, const ErrorType &errorType); + + static CString BuildNativeEcmaStackTrace(JSThread *thread); +private: + static CString DecodeFunctionName(const CString &name); + static JSHandle BuildEcmaStackTrace(JSThread *thread); + + static JSHandle GetErrorName(JSThread *thread, const JSHandle &name, + const ErrorType &errorType); +}; +} // namespace panda::ecmascript::base + +#endif // ECMASCRIPT_BASE_ERROR_HELPER_H diff --git a/runtime/base/error_type.h b/runtime/base/error_type.h new file mode 100644 index 000000000..37783ac29 --- /dev/null +++ b/runtime/base/error_type.h @@ -0,0 +1,33 @@ +/* + * Copyright (c) 2021-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_BASE_ERROR_TYPE_H +#define ECMASCRIPT_BASE_ERROR_TYPE_H + +#include + +namespace panda::ecmascript::base { +enum class ErrorType : uint8_t { + ERROR = 0, + EVAL_ERROR, + RANGE_ERROR, + REFERENCE_ERROR, + SYNTAX_ERROR, + TYPE_ERROR, + URI_ERROR, +}; +} // namespace panda::ecmascript::base + +#endif // ECMASCRIPT_BASE_ERROR_TYPE_H diff --git a/runtime/base/gc_ring_buffer.h b/runtime/base/gc_ring_buffer.h new file mode 100644 index 000000000..372fb5e24 --- /dev/null +++ b/runtime/base/gc_ring_buffer.h @@ -0,0 +1,77 @@ +/* + * Copyright (c) 2021-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_BASE_GC_RING_BUFFER_H +#define ECMASCRIPT_BASE_GC_RING_BUFFER_H + +#include + +#include "libpandabase/macros.h" + +namespace panda::ecmascript::base { +// GC Ring Buffer is base tool class. It will be used to collect the data of the last N GCs. The max length is +// fixed, so we call it a GC ring buffer. In this class, we define the functions to push data, count the length, +// return the sum and reset data. +template +class GCRingBuffer { +public: + GCRingBuffer() = default; + ~GCRingBuffer() = default; + NO_COPY_SEMANTIC(GCRingBuffer); + NO_MOVE_SEMANTIC(GCRingBuffer); + + void Push(const T &value) + { + if (count_ == N) { + elements_[start_++] = value; + if (start_ == N) { + start_ = 0; + } + } else { + ASSERT(start_ == 0); + elements_[count_++] = value; + } + } + + int Count() const + { + return count_; + } + + // This function will return the sum of the elements_. The parameter callback + // should define the sum function of data type T. + template + T Sum(Callback callback, const T &initial) const + { + T result = initial; + for (int i = 0; i < count_; i++) { + result = callback(result, elements_[i]); + } + return result; + } + + void Reset() + { + start_ = count_ = 0; + } + +private: + std::array elements_; + int start_ {0}; + int count_ {0}; +}; +} // namespace panda::ecmascript::base + +#endif // ECMASCRIPT_BASE_GC_RING_BUFFER_H \ No newline at end of file diff --git a/runtime/base/json_parser.cpp b/runtime/base/json_parser.cpp new file mode 100644 index 000000000..f24b6a04a --- /dev/null +++ b/runtime/base/json_parser.cpp @@ -0,0 +1,79 @@ +/* + * Copyright (c) 2021-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 "plugins/ecmascript/runtime/base/json_parser.h" + +namespace panda::ecmascript::base { +JSHandle Internalize::InternalizeJsonProperty(JSThread *thread, const JSHandle &holder, + const JSHandle &name, + const JSHandle &receiver) +{ + JSHandle objHandle(holder); + JSHandle val = JSTaggedValue::GetProperty(thread, objHandle, name).GetValue(); + JSHandle lengthKey = thread->GlobalConstants()->GetHandledLengthString(); + if (val->IsECMAObject()) { + JSHandle obj = JSTaggedValue::ToObject(thread, val); + bool isArray = val->IsArray(thread); + if (isArray) { + JSHandle lenResult = JSTaggedValue::GetProperty(thread, val, lengthKey).GetValue(); + RETURN_HANDLE_IF_ABRUPT_COMPLETION(JSTaggedValue, thread); + JSTaggedNumber lenNumber = JSTaggedValue::ToLength(thread, lenResult); + RETURN_HANDLE_IF_ABRUPT_COMPLETION(JSTaggedValue, thread); + uint32_t length = lenNumber.ToUint32(); + JSMutableHandle keyUnknow(thread, JSTaggedValue::Undefined()); + JSMutableHandle keyName(thread, JSTaggedValue::Undefined()); + for (uint32_t i = 0; i < length; i++) { + // Let prop be ! ToString((I)). + keyUnknow.Update(JSTaggedValue(i)); + keyName.Update(JSTaggedValue::ToString(thread, keyUnknow).GetTaggedValue()); + RecurseAndApply(thread, obj, keyName, receiver); + } + } else { + // Let keys be ? EnumerableOwnPropertyNames(val, key). + JSHandle ownerNames(JSObject::EnumerableOwnNames(thread, obj)); + RETURN_HANDLE_IF_ABRUPT_COMPLETION(JSTaggedValue, thread); + uint32_t namesLength = ownerNames->GetLength(); + JSMutableHandle keyName(thread, JSTaggedValue::Undefined()); + for (uint32_t i = 0; i < namesLength; i++) { + keyName.Update(JSTaggedValue::GetProperty(thread, JSHandle(ownerNames), i) + .GetValue().GetTaggedValue()); + RecurseAndApply(thread, obj, keyName, receiver); + } + } + } + + // Return ? Call(receiver, holder, « name, val »). + InternalCallParams *arguments = thread->GetInternalCallParams(); + arguments->MakeArgv(name, val); + JSTaggedValue result = JSFunction::Call(thread, receiver, objHandle, 2, arguments->GetArgv()); // 2: two args + return JSHandle(thread, result); +} + +bool Internalize::RecurseAndApply(JSThread *thread, const JSHandle &holder, + const JSHandle &name, const JSHandle &receiver) +{ + JSHandle value = InternalizeJsonProperty(thread, holder, name, receiver); + bool changeResult = false; + + // If newElement is undefined, then Perform ? val.[[Delete]](P). + if (value->IsUndefined()) { + changeResult = JSObject::DeleteProperty(thread, holder, name); + } else { + // Perform ? CreateDataProperty(val, P, newElement) + changeResult = JSObject::CreateDataProperty(thread, holder, name, value); + } + return changeResult; +} +} // namespace panda::ecmascript::base diff --git a/runtime/base/json_parser.h b/runtime/base/json_parser.h new file mode 100644 index 000000000..d5747a11b --- /dev/null +++ b/runtime/base/json_parser.h @@ -0,0 +1,767 @@ +/* + * Copyright (c) 2021-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_BASE_JSON_PARSE_INL_H +#define ECMASCRIPT_BASE_JSON_PARSE_INL_H + +#include "plugins/ecmascript/runtime/base/json_parser.h" +#include "plugins/ecmascript/runtime/base/builtins_base.h" +#include "plugins/ecmascript/runtime/base/number_helper.h" +#include "plugins/ecmascript/runtime/base/string_helper.h" +#include "plugins/ecmascript/runtime/base/utf_helper.h" +#include "plugins/ecmascript/runtime/ecma_string-inl.h" +#include "plugins/ecmascript/runtime/ecma_string.h" +#include "plugins/ecmascript/runtime/internal_call_params.h" +#include "plugins/ecmascript/runtime/interpreter/fast_runtime_stub-inl.h" +#include "plugins/ecmascript/runtime/js_array.h" +#include "plugins/ecmascript/runtime/js_function.h" +#include "plugins/ecmascript/runtime/js_handle.h" +#include "plugins/ecmascript/runtime/js_tagged_value.h" +#include "plugins/ecmascript/runtime/object_factory.h" + +namespace panda::ecmascript::base { +constexpr unsigned int UNICODE_DIGIT_LENGTH = 4; +constexpr unsigned int NUMBER_TEN = 10; +constexpr unsigned int NUMBER_SIXTEEN = 16; + +constexpr unsigned char CODE_SPACE = 0x20; +constexpr unsigned char ASCII_END = 0X7F; +enum class Tokens : uint8_t { + // six structural tokens + OBJECT = 0, + ARRAY, + NUMBER, + STRING, + LITERAL_TRUE, + LITERAL_FALSE, + LITERAL_NULL, + TOKEN_ILLEGAL, + }; + +template +class JsonParser { +public: + using Text = const T *; + explicit JsonParser() = default; + explicit JsonParser(JSThread *thread) : thread_(thread) {} + ~JsonParser() = default; + NO_COPY_SEMANTIC(JsonParser); + NO_MOVE_SEMANTIC(JsonParser); + JSHandle Parse(Text begin, Text end) + { + end_ = end - 1; + current_ = begin; + + auto vm = thread_->GetEcmaVM(); + factory_ = vm->GetFactory(); + env_ = *vm->GetGlobalEnv(); + + SkipEndWhiteSpace(); + range_ = end_; + JSTaggedValue result = ParseJSONText(); + return JSHandle(thread_, result); + } + + JSHandle ParseUtf8(EcmaString *str) + { + ASSERT(str != nullptr); + isAsciiString_ = true; + uint32_t len = str->GetUtf8Length(); + CVector buf(len); + str->CopyDataUtf8(buf.data(), len); + Text begin = buf.data(); + return Parse(begin, begin + str->GetLength()); + } + + JSHandle ParseUtf16(EcmaString *str) + { + ASSERT(str != nullptr); + uint32_t len = str->GetLength(); + CVector buf(len); + str->CopyDataUtf16(buf.data(), len); + Text begin = buf.data(); + return Parse(begin, begin + len); + } + +private: + template + JSTaggedValue ParseJSONText() + { + SkipStartWhiteSpace(); + Tokens token = ParseToken(); + switch (token) { + case Tokens::OBJECT: + return ParseObject(); + case Tokens::ARRAY: + return ParseArray(); + case Tokens::LITERAL_TRUE: + return ParseLiteral("true", Tokens::LITERAL_TRUE); + case Tokens::LITERAL_FALSE: + return ParseLiteral("false", Tokens::LITERAL_FALSE); + case Tokens::LITERAL_NULL: + return ParseLiteral("null", Tokens::LITERAL_NULL); + case Tokens::NUMBER: + return ParseNumber(); + case Tokens::STRING: + return ParseString(); + default: + THROW_SYNTAX_ERROR_AND_RETURN(thread_, "Unexpected Text in JSON", JSTaggedValue::Exception()); + } + } + + template + JSTaggedValue ParseNumber() + { + if (inObjOrArr) { + bool isFast = true; + bool isNumber = ReadNumberRange(isFast); + if (!isNumber) { + THROW_SYNTAX_ERROR_AND_RETURN(thread_, "Unexpected Number in JSON", JSTaggedValue::Exception()); + } + if (isFast) { + std::string strNum(current_, end_ + 1); + current_ = end_; + return JSTaggedValue(std::stod(strNum)); + } + } + + Text current = current_; + bool hasExponent = false; + if (*current_ == '-') { + if (UNLIKELY(current_++ == end_)) { + THROW_SYNTAX_ERROR_AND_RETURN(thread_, "Unexpected Number in JSON", JSTaggedValue::Exception()); + } + } + if (*current_ == '0') { + if (!CheckZeroBeginNumber(hasExponent)) { + THROW_SYNTAX_ERROR_AND_RETURN(thread_, "Unexpected Number in JSON", JSTaggedValue::Exception()); + } + } else if (*current_ >= '1' && *current_ <= '9') { + if (!CheckNonZeroBeginNumber(hasExponent)) { + THROW_SYNTAX_ERROR_AND_RETURN(thread_, "Unexpected Number in JSON", JSTaggedValue::Exception()); + } + } else { + THROW_SYNTAX_ERROR_AND_RETURN(thread_, "Unexpected Number in JSON", JSTaggedValue::Exception()); + } + + std::string strNum(current, end_ + 1); + current_ = end_; + return JSTaggedValue(std::stod(strNum)); + } + + bool ReadJsonStringRange(bool &isFastString, bool &isAscii) + { + current_++; + if (isAsciiString_) { + return ReadAsciiStringRange(isFastString); + } + return ReadStringRange(isFastString, isAscii); + } + + bool IsFastParseJsonString(bool &isFastString, bool &isAscii) + { + current_++; + if (isAsciiString_) { + return IsFastParseString(isFastString, isAscii); + } + return IsFastParseAsciiString(isFastString); + } + + bool ParseBackslash(CString &res) + { + if (current_ == end_) { + return false; + } + current_++; + switch (*current_) { + case '\"': + res += "\""; + break; + case '\\': + res += "\\"; + break; + case '/': + res += "/"; + break; + case 'b': + res += "\b"; + break; + case 'f': + res += "\f"; + break; + case 'n': + res += "\n"; + break; + case 'r': + res += "\r"; + break; + case 't': + res += "\t"; + break; + case 'u': { + CVector vec; + if (UNLIKELY(!ConvertStringUnicode(vec))) { + return false; + } + std::u16string u16Str; + u16Str.assign(vec.begin(), vec.end()); + res += base::StringHelper::U16stringToString(u16Str); + break; + } + default: + return false; + } + return true; + } + + JSTaggedValue SlowParseString() + { + end_--; + CString res; + while (current_ <= end_) { + if (*current_ == '\\') { + bool isLegalChar = ParseBackslash(res); + if (!isLegalChar) { + THROW_SYNTAX_ERROR_AND_RETURN(thread_, "Unexpected string in JSON", JSTaggedValue::Exception()); + } + } else { + res += *current_; + } + current_++; + } + return factory_->NewFromUtf8Literal(reinterpret_cast(res.c_str()), res.length()) + .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_; + return factory_->NewFromUtf8LiteralUnCheck( + reinterpret_cast(value.c_str()), value.length(), true).GetTaggedValue(); + } + std::u16string value(current_, end_); + current_ = end_; + return factory_->NewFromUtf16LiteralUnCheck( + reinterpret_cast(value.c_str()), value.length(), false).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_); + return factory_->NewFromUtf8LiteralUnCheck( + reinterpret_cast(value.c_str()), value.length(), true).GetTaggedValue(); + } + std::u16string value(current_, end_); + return factory_->NewFromUtf16LiteralUnCheck( + reinterpret_cast(value.c_str()), value.length(), false).GetTaggedValue(); + } + } + return SlowParseString(); + } + + template + JSTaggedValue ParseArray() + { + if (UNLIKELY(*range_ != ']' && !inObjorArr)) { + THROW_SYNTAX_ERROR_AND_RETURN(thread_, "Unexpected Array in JSON", JSTaggedValue::Exception()); + } + + current_++; + JSHandle arr = factory_->NewJSArray(); + if (*current_ == ']') { + return arr.GetTaggedValue(); + } + + JSTaggedValue value; + uint32_t index = 0; + while (current_ <= range_) { + value = ParseJSONText(); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread_); + FastRuntimeStub::SetPropertyByIndex(thread_, arr.GetTaggedValue(), index++, value); + GetNextNonSpaceChar(); + if (*current_ == ',') { + current_++; + } else if (*current_ == ']') { + if (inObjorArr || current_ == range_) { + return arr.GetTaggedValue(); + } + THROW_SYNTAX_ERROR_AND_RETURN(thread_, "Unexpected Array in JSON", JSTaggedValue::Exception()); + } + } + THROW_SYNTAX_ERROR_AND_RETURN(thread_, "Unexpected Array in JSON", JSTaggedValue::Exception()); + } + + template + JSTaggedValue ParseObject() + { + if (UNLIKELY(*range_ != '}' && !inObjorArr)) { + THROW_SYNTAX_ERROR_AND_RETURN(thread_, "Unexpected Object in JSON", JSTaggedValue::Exception()); + } + + JSHandle proto = env_->GetObjectFunction(); + JSHandle result = factory_->NewJSObjectByConstructor(JSHandle(proto), proto); + current_++; + if (*current_ == '}') { + return result.GetTaggedValue(); + } + + JSMutableHandle keyHandle(thread_, JSTaggedValue::Undefined()); + JSTaggedValue value; + while (current_ <= range_) { + SkipStartWhiteSpace(); + if (*current_ == '"') { + keyHandle.Update(ParseString()); + } else { + if (*current_ == '}' && (inObjorArr || current_ == range_)) { + return result.GetTaggedValue(); + } + THROW_SYNTAX_ERROR_AND_RETURN(thread_, "Unexpected Object in JSON", JSTaggedValue::Exception()); + } + GetNextNonSpaceChar(); + if (*current_ == ':') { + current_++; + } else { + THROW_SYNTAX_ERROR_AND_RETURN(thread_, "Unexpected Object in JSON", JSTaggedValue::Exception()); + } + value = ParseJSONText(); + FastRuntimeStub::SetPropertyByValue( + thread_, result.GetTaggedValue(), keyHandle.GetTaggedValue(), value); + GetNextNonSpaceChar(); + if (*current_ == ',') { + current_++; + } else if (*current_ == '}') { + if (inObjorArr || current_ == range_) { + return result.GetTaggedValue(); + } + THROW_SYNTAX_ERROR_AND_RETURN(thread_, "Unexpected Object in JSON", JSTaggedValue::Exception()); + } + } + THROW_SYNTAX_ERROR_AND_RETURN(thread_, "Unexpected Object in JSON", JSTaggedValue::Exception()); + } + + void SkipEndWhiteSpace() + { + while (current_ != end_) { + if (*end_ == ' ' || *end_ == '\r' || *end_ == '\n' || *end_ == '\t') { + end_--; + } else { + break; + } + } + } + + void SkipStartWhiteSpace() + { + while (current_ != end_) { + if (*current_ == ' ' || *current_ == '\r' || *current_ == '\n' || *current_ == '\t') { + current_++; + } else { + break; + } + } + } + + void GetNextNonSpaceChar() + { + current_++; + SkipStartWhiteSpace(); + } + + Tokens ParseToken() + { + switch (*current_) { + case '{': + return Tokens::OBJECT; + case '[': + return Tokens::ARRAY; + case '"': + return Tokens::STRING; + case '0': + case '1': + case '2': + case '3': + case '4': + case '5': + case '6': + case '7': + case '8': + case '9': + case '-': + return Tokens::NUMBER; + case 't': + return Tokens::LITERAL_TRUE; + case 'f': + return Tokens::LITERAL_FALSE; + case 'n': + return Tokens::LITERAL_NULL; + default: + return Tokens::TOKEN_ILLEGAL; + } + } + + JSTaggedValue ParseLiteral(const CString &str, Tokens literalToken) + { + uint32_t strLen = str.size() - 1; + uint32_t remainingLength = range_ - current_; + if (UNLIKELY(remainingLength < strLen)) { + THROW_SYNTAX_ERROR_AND_RETURN(thread_, "Unexpected Text in JSON", JSTaggedValue::Exception()); + } + + bool isMatch = MatchText(str, strLen); + if (LIKELY(isMatch)) { + switch (literalToken) { + case Tokens::LITERAL_TRUE: + return JSTaggedValue::True(); + case Tokens::LITERAL_FALSE: + return JSTaggedValue::False(); + case Tokens::LITERAL_NULL: + return JSTaggedValue::Null(); + default: + UNREACHABLE(); + } + } + THROW_SYNTAX_ERROR_AND_RETURN(thread_, "Unexpected Text in JSON", JSTaggedValue::Exception()); + } + + bool MatchText(const CString &str, uint32_t matchLen) + { + const char *text = str.c_str(); + uint32_t pos = 1; + while (pos <= matchLen) { + // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-pointer-arithmetic) + if (current_[pos] != text[pos]) { + return false; + } + pos++; + } + current_ += matchLen; + return true; + } + + bool ReadNumberRange(bool &isFast) + { + Text current = current_; + if (*current == '0') { + isFast = false; + current++; + } else if (*current == '-') { + current++; + if (*current == '0') { + isFast = false; + current++; + } + } + + while (current != range_) { + if (IsNumberCharacter(*current)) { + current++; + continue; + } + if (IsNumberSignalCharacter(*current)) { + isFast = false; + current++; + continue; + } + Text end = current; + while (current != range_) { + if (*current == ' ' || *current == '\r' || *current == '\n' || *current == '\t') { + current++; + } else if (*current == ',' || *current == ']' || *current == '}') { + end_ = end - 1; + return true; + } else { + return false; + } + } + return false; + } + end_ = range_ - 1; + return true; + } + + bool IsNumberCharacter(T ch) + { + return ch >= '0' && ch <= '9'; + } + + bool IsNumberSignalCharacter(T ch) + { + return ch == '.' || ch == 'e' || ch == 'E' || ch == '+' || ch == '-'; + } + + bool IsExponentNumber() + { + if (IsNumberCharacter(*current_)) { + return true; + } + if (*current_ == '-' || *current_ == '+') { + if (current_ == end_) { + return false; + } + current_++; + if (IsNumberCharacter(*current_)) { + return true; + } + } + return false; + } + + bool IsDecimalsLegal(bool &hasExponent) + { + if (current_ == end_ && !IsNumberCharacter(*++current_)) { + return false; + } + + while (current_ != end_) { + current_++; + if (IsNumberCharacter(*current_)) { + continue; + } + if (*current_ == 'e' || *current_ == 'E') { + if (hasExponent || current_ == end_) { + return false; + } + current_++; + if (!IsExponentNumber()) { + return false; + } + hasExponent = true; + } else { + return false; + } + } + return true; + } + + bool IsExponentLegal(bool &hasExponent) + { + if (hasExponent || current_ == end_) { + return false; + } + current_++; + if (!IsExponentNumber()) { + return false; + } + while (current_ != end_) { + if (!IsNumberCharacter(*current_)) { + return false; + } + current_++; + } + return true; + } + + bool ReadStringRange(bool &isFast, bool &isAscii) + { + T c = 0; + Text current = current_; + + while (current != range_) { + c = *current; + if (c == '"') { + end_ = current; + return true; + } + if (UNLIKELY(c == '\\')) { + if (*(current + 1) == '"') { + current++; + } + isFast = false; + } + if (!IsLegalAsciiCharacter(c, isAscii)) { + 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; + } + if (UNLIKELY(c == '\\')) { + if (*(current + 1) == '"') { + current++; + } + isFast = false; + } else if (UNLIKELY(c < CODE_SPACE)) { + return false; + } + current++; + } + return false; + } + + bool IsFastParseString(bool &isFast, bool &isAscii) + { + Text current = current_; + while (current != end_) { + if (!IsLegalAsciiCharacter(*current, isAscii)) { + 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; + } + if (*current == '\\') { + isFast = false; + } + 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; + } + } + if (res < CODE_SPACE) { + 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; + } + + bool CheckZeroBeginNumber(bool &hasExponent) + { + if (current_++ != end_) { + if (*current_ == '.') { + if (!IsDecimalsLegal(hasExponent)) { + return false; + } + } else if (*current_ == 'e' || *current_ == 'E') { + if (!IsExponentLegal(hasExponent)) { + return false; + } + } else { + return false; + } + } + return true; + } + + bool CheckNonZeroBeginNumber(bool &hasExponent) + { + while (current_ != end_) { + current_++; + if (IsNumberCharacter(*current_)) { + continue; + } + if (*current_ == '.') { + if (!IsDecimalsLegal(hasExponent)) { + return false; + } + } else if (*current_ == 'e' || *current_ == 'E') { + if (!IsExponentLegal(hasExponent)) { + return false; + } + } else { + return false; + } + } + return true; + } + + bool IsLegalAsciiCharacter(T c, bool &isAscii) + { + if (c <= ASCII_END) { + return c >= CODE_SPACE; + } + isAscii = false; + return true; + } + + bool isAsciiString_{false}; + Text end_{nullptr}; + Text current_{nullptr}; + Text range_{nullptr}; + JSThread *thread_{nullptr}; + ObjectFactory *factory_{nullptr}; + GlobalEnv *env_{nullptr}; +}; + +class Internalize { +public: + static JSHandle InternalizeJsonProperty(JSThread *thread, const JSHandle &holder, + const JSHandle &name, + const JSHandle &receiver); +private: + static bool RecurseAndApply(JSThread *thread, const JSHandle &holder, const JSHandle &name, + const JSHandle &receiver); +}; +} // namespace panda::ecmascript::base + +#endif // ECMASCRIPT_BASE_JSON_PARSE_INL_H diff --git a/runtime/base/json_stringifier.cpp b/runtime/base/json_stringifier.cpp new file mode 100644 index 000000000..798647214 --- /dev/null +++ b/runtime/base/json_stringifier.cpp @@ -0,0 +1,724 @@ +/* + * Copyright (c) 2021-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 "plugins/ecmascript/runtime/base/json_stringifier.h" +#include +#include +#include +#include "plugins/ecmascript/runtime/base/builtins_base.h" +#include "plugins/ecmascript/runtime/base/number_helper.h" +#include "plugins/ecmascript/runtime/builtins/builtins_errors.h" +#include "plugins/ecmascript/runtime/ecma_runtime_call_info.h" +#include "plugins/ecmascript/runtime/ecma_string-inl.h" +#include "plugins/ecmascript/runtime/ecma_vm.h" +#include "plugins/ecmascript/runtime/internal_call_params.h" +#include "plugins/ecmascript/runtime/interpreter/fast_runtime_stub-inl.h" +#include "plugins/ecmascript/runtime/js_array.h" +#include "plugins/ecmascript/runtime/js_function.h" +#include "plugins/ecmascript/runtime/js_handle.h" +#include "plugins/ecmascript/runtime/js_invoker.h" +#include "plugins/ecmascript/runtime/js_object-inl.h" +#include "plugins/ecmascript/runtime/js_primitive_ref.h" +#include "plugins/ecmascript/runtime/js_tagged_value-inl.h" +#include "plugins/ecmascript/runtime/js_tagged_value.h" + +namespace panda::ecmascript::base { +constexpr unsigned char CODE_SPACE = 0x20; +constexpr int GAP_MAX_LEN = 10; +constexpr int FOUR_HEX = 4; + +bool JsonStringifier::IsFastValueToQuotedString(const char *value) +{ + if (strpbrk(value, "\"\\\b\f\n\r\t") != nullptr) { + return false; + } + while (*value != '\0') { + if (*value > 0 && *value < CODE_SPACE) { + return false; + } + // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-pointer-arithmetic) + value++; + } + return true; +} + +CString JsonStringifier::ValueToQuotedString(const 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 + // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-pointer-arithmetic) + for (const char *c = value; *c != 0; ++c) { + switch (*c) { + /* + * a. If C is 0x0022 (QUOTATION MARK) or 0x005C (REVERSE SOLIDUS), then + * i. Let product be the concatenation of product and code unit 0x005C (REVERSE SOLIDUS). + * ii. Let product be the concatenation of product and C. + */ + case '\"': + product += "\\\""; + break; + case '\\': + product += "\\\\"; + break; + /* + * b. Else if C is 0x0008 (BACKSPACE), 0x000C (FORM FEED), 0x000A (LINE FEED), 0x000D (CARRIAGE RETURN), + * or 0x000B (LINE TABULATION), then + * i. Let product be the concatenation of product and code unit 0x005C (REVERSE SOLIDUS). + * ii. Let abbrev be the String value corresponding to the value of C as follows: + * BACKSPACE "b" + * FORM FEED (FF) "f" + * LINE FEED (LF) "n" + * CARRIAGE RETURN (CR) "r" + * LINE TABULATION "t" + * iii. Let product be the concatenation of product and abbrev. + */ + case '\b': + product += "\\b"; + break; + case '\f': + product += "\\f"; + break; + case '\n': + product += "\\n"; + break; + case '\r': + product += "\\r"; + break; + case '\t': + product += "\\t"; + break; + default: + // c. Else if C has a code unit value less than 0x0020 (SPACE), then + if (*c > 0 && *c < CODE_SPACE) { + /* + * i. Let product be the concatenation of product and code unit 0x005C (REVERSE SOLIDUS). + * ii. Let product be the concatenation of product and "u". + * iii. Let hex be the string result of converting the numeric code unit value of C to a String of + * four hexadecimal digits. Alphabetic hexadecimal digits are presented as lowercase Latin letters. + * iv. Let product be the concatenation of product and hex. + */ + std::ostringstream oss; + oss << "\\u" << std::hex << std::setfill('0') << std::setw(FOUR_HEX) << static_cast(*c); + product += oss.str(); + } else { + // Else, + // i. Let product be the concatenation of product and C. + product += *c; + } + } + } + // 3. Let product be the concatenation of product and code unit 0x0022 (QUOTATION MARK). + product += "\""; + // Return product. + return product; +} + +JSHandle JsonStringifier::Stringify(const JSHandle &value, + const JSHandle &replacer, + const JSHandle &gap) +{ + factory_ = thread_->GetEcmaVM()->GetFactory(); + handleValue_.Update(JSTaggedValue::Undefined()); + handleKey_.Update(JSTaggedValue::Undefined()); + // Let isArray be IsArray(replacer). + bool isArray = replacer->IsArray(thread_); + // ReturnIfAbrupt(isArray). + RETURN_HANDLE_IF_ABRUPT_COMPLETION(JSTaggedValue, thread_); + // If isArray is true, then + if (isArray) { + uint32_t len; + if (replacer->IsJSArray()) { + // FastPath + JSHandle arr(replacer); + len = arr->GetArrayLength(); + } else { + // Let len be ToLength(Get(replacer, "length")). + JSHandle lengthKey = thread_->GlobalConstants()->GetHandledLengthString(); + JSHandle lenResult = JSTaggedValue::GetProperty(thread_, replacer, lengthKey).GetValue(); + // ReturnIfAbrupt(len). + RETURN_HANDLE_IF_ABRUPT_COMPLETION(JSTaggedValue, thread_); + JSTaggedNumber lenNumber = JSTaggedValue::ToLength(thread_, lenResult); + RETURN_HANDLE_IF_ABRUPT_COMPLETION(JSTaggedValue, thread_); + len = lenNumber.ToUint32(); + } + if (len > 0) { + JSMutableHandle propHandle(thread_, JSTaggedValue::Undefined()); + // Repeat while kGetValue(); + if (primitive.IsNumber() || primitive.IsString()) { + AddDeduplicateProp(propHandle); + RETURN_HANDLE_IF_ABRUPT_COMPLETION(JSTaggedValue, thread_); + } + } + } + } + } + + // If Type(space) is Object, then + if (gap->IsJSPrimitiveRef()) { + JSTaggedValue primitive = JSPrimitiveRef::Cast(gap->GetTaggedObject())->GetValue(); + // a. If space has a [[NumberData]] internal slot, then + if (primitive.IsNumber()) { + // i. Let space be ToNumber(space). + JSTaggedNumber num = JSTaggedValue::ToNumber(thread_, gap); + // ii. ReturnIfAbrupt(space). + RETURN_HANDLE_IF_ABRUPT_COMPLETION(JSTaggedValue, thread_); + CalculateNumberGap(num); + } else if (primitive.IsString()) { + // b. Else if space has a [[StringData]] internal slot, then + // i. Let space be ToString(space). + auto str = JSTaggedValue::ToString(thread_, gap); + // ii. ReturnIfAbrupt(space). + RETURN_HANDLE_IF_ABRUPT_COMPLETION(JSTaggedValue, thread_); + CalculateStringGap(JSHandle(thread_, str.GetTaggedValue())); + } + } else if (gap->IsNumber()) { + // If Type(space) is Number + CalculateNumberGap(gap.GetTaggedValue()); + } else if (gap->IsString()) { + // Else if Type(space) is String + CalculateStringGap(JSHandle::Cast(gap)); + } + + ObjectFactory *factory = thread_->GetEcmaVM()->GetFactory(); + JSHandle env = thread_->GetEcmaVM()->GetGlobalEnv(); + JSHandle key(factory_->GetEmptyString()); + JSHandle proto = env->GetObjectFunction(); + JSHandle wrapper = factory->NewJSObjectByConstructor(JSHandle(proto), proto); + bool success = JSObject::CreateDataProperty(thread_, wrapper, key, value); // key empty string + if (!success) { + return JSHandle(thread_, JSTaggedValue::Exception()); + } + + JSTaggedValue serializeValue = GetSerializeValue(JSHandle::Cast(wrapper), key, value, replacer); + RETURN_HANDLE_IF_ABRUPT_COMPLETION(JSTaggedValue, thread_); + handleValue_.Update(serializeValue); + JSTaggedValue result = SerializeJSONProperty(handleValue_, replacer); + RETURN_HANDLE_IF_ABRUPT_COMPLETION(JSTaggedValue, thread_); + if (!result.IsUndefined()) { + return JSHandle( + factory_->NewFromUtf8Literal(reinterpret_cast(result_.c_str()), result_.size())); + } + return thread_->GlobalConstants()->GetHandledUndefined(); +} + +void JsonStringifier::AddDeduplicateProp(const JSHandle &property) +{ + uint32_t propLen = propList_.size(); + for (uint32_t i = 0; i < propLen; i++) { + if (JSTaggedValue::SameValue(propList_[i], property)) { + return; + } + } + JSHandle primString = JSTaggedValue::ToString(thread_, property); + RETURN_IF_ABRUPT_COMPLETION(thread_); + JSHandle addVal(thread_, *primString); + propList_.emplace_back(addVal); +} + +bool JsonStringifier::CalculateNumberGap(JSTaggedValue gap) +{ + double numValue = gap.GetNumber(); + int num = static_cast(numValue); + if (num > 0) { + int gapLength = std::min(num, GAP_MAX_LEN); + for (int i = 0; i < gapLength; i++) { + gap_ += " "; + } + gap_.append("\0"); + } + return true; +} + +bool JsonStringifier::CalculateStringGap(const JSHandle &primString) +{ + CString gapString = ConvertToString(*primString); + int gapLen = gapString.length(); + if (gapLen > 0) { + int gapLength = std::min(gapLen, GAP_MAX_LEN); + CString str = gapString.substr(0, gapLength); + gap_ += str; + gap_.append("\0"); + } + return true; +} + +JSTaggedValue JsonStringifier::GetSerializeValue(const JSHandle &object, + const JSHandle &key, + const JSHandle &value, + const JSHandle &replacer) +{ + JSTaggedValue tagValue = value.GetTaggedValue(); + // If Type(value) is Object, then + if (value->IsECMAObject()) { + // a. Let toJSON be Get(value, "toJSON"). + JSHandle toJson = thread_->GlobalConstants()->GetHandledToJsonString(); + JSHandle toJsonFun( + thread_, FastRuntimeStub::FastGetPropertyByValue(thread_, tagValue, toJson.GetTaggedValue())); + // b. ReturnIfAbrupt(toJSON). + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread_); + + // c. If IsCallable(toJSON) is true + if (UNLIKELY(toJsonFun->IsCallable())) { + // Let value be Call(toJSON, value, «key»). + InternalCallParams *arguments = thread_->GetInternalCallParams(); + arguments->MakeArgv(key); + tagValue = JSFunction::Call(thread_, toJsonFun, value, 1, arguments->GetArgv()); + // ii. ReturnIfAbrupt(value). + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread_); + } + } + + if (UNLIKELY(replacer->IsCallable())) { + handleValue_.Update(tagValue); + // a. Let value be Call(ReplacerFunction, holder, «key, value»). + InternalCallParams *arguments = thread_->GetInternalCallParams(); + arguments->MakeArgv(key, handleValue_); + tagValue = JSFunction::Call(thread_, replacer, object, 2, arguments->GetArgv()); // 2: two args + // b. ReturnIfAbrupt(value). + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread_); + } + return tagValue; +} + +JSTaggedValue JsonStringifier::SerializeJSONProperty(const JSHandle &value, + const JSHandle &replacer) +{ + JSTaggedValue tagValue = value.GetTaggedValue(); + if (!tagValue.IsHeapObject()) { + TaggedType type = tagValue.GetRawData(); + switch (type) { + // If value is false, return "false". + case JSTaggedValue::VALUE_FALSE: + result_ += "false"; + return tagValue; + // If value is true, return "true". + case JSTaggedValue::VALUE_TRUE: + result_ += "true"; + return tagValue; + // If value is null, return "null". + case JSTaggedValue::VALUE_NULL: + result_ += "null"; + return tagValue; + default: + // If Type(value) is Number, then + if (tagValue.IsNumber()) { + // a. If value is finite, return ToString(value). + if (std::isfinite(tagValue.GetNumber())) { + result_ += ConvertToString(*base::NumberHelper::NumberToString(thread_, tagValue)); + } else { + // b. Else, return "null". + result_ += "null"; + } + return tagValue; + } + } + } else { + JSType jsType = tagValue.GetTaggedObject()->GetClass()->GetObjectType(); + JSHandle valHandle(thread_, tagValue); + switch (jsType) { + case JSType::JS_ARRAY: { + SerializeJSArray(valHandle, replacer); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread_); + return tagValue; + } + // If Type(value) is String, return QuoteJSONString(value). + case JSType::STRING: { + CString str = ConvertToString(*JSHandle(valHandle)); + str = ValueToQuotedString(str); + result_ += str; + return tagValue; + } + case JSType::JS_PRIMITIVE_REF: { + SerilaizePrimitiveRef(valHandle); + return tagValue; + } + case JSType::SYMBOL: + return JSTaggedValue::Undefined(); + default: { + if (!tagValue.IsCallable()) { + if (UNLIKELY(tagValue.IsJSProxy())) { + SerializeJSProxy(valHandle, replacer); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread_); + } else { + SerializeJSONObject(valHandle, replacer); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread_); + } + return tagValue; + } + } + } + } + return JSTaggedValue::Undefined(); +} + +void JsonStringifier::SerializeObjectKey(const JSHandle &key, bool hasContent) +{ + CString stepBegin; + CString stepEnd; + if (hasContent) { + result_ += ","; + } + if (!gap_.empty()) { + stepBegin += "\n"; + stepBegin += indent_; + stepEnd += " "; + } + CString str; + if (key->IsString()) { + str = ConvertToString(EcmaString::Cast(key->GetTaggedObject())); + } else if (key->IsInt()) { + str = NumberHelper::IntToString(static_cast(key->GetInt())); + } else { + str = ConvertToString(*JSTaggedValue::ToString(thread_, key)); + } + result_ += stepBegin; + str = ValueToQuotedString(str); + result_ += str; + result_ += ":"; + result_ += stepEnd; +} + +bool JsonStringifier::PushValue(const JSHandle &value) +{ + 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 JsonStringifier::PopValue() +{ + stack_.pop_back(); +} + +bool JsonStringifier::SerializeJSONObject(const JSHandle &value, const JSHandle &replacer) +{ + bool isContain = PushValue(value); + if (isContain) { + THROW_TYPE_ERROR_AND_RETURN(thread_, "stack contains value", true); + } + + CString stepback = indent_; + indent_ += gap_; + + result_ += "{"; + bool hasContent = false; + + JSHandle obj(value); + if (!replacer->IsArray(thread_)) { + uint32_t numOfKeys = obj->GetNumberOfKeys(); + uint32_t numOfElements = obj->GetNumberOfElements(); + if (numOfElements > 0) { + hasContent = JsonStringifier::SerializeElements(obj, replacer, hasContent); + RETURN_VALUE_IF_ABRUPT_COMPLETION(thread_, false); + } + if (numOfKeys > 0) { + hasContent = JsonStringifier::SerializeKeys(obj, replacer, hasContent); + RETURN_VALUE_IF_ABRUPT_COMPLETION(thread_, false); + } + } else { + uint32_t propLen = propList_.size(); + for (uint32_t i = 0; i < propLen; i++) { + JSTaggedValue tagVal = + FastRuntimeStub::FastGetPropertyByValue(thread_, obj.GetTaggedValue(), propList_[i].GetTaggedValue()); + handleValue_.Update(tagVal); + JSTaggedValue serializeValue = GetSerializeValue(value, propList_[i], handleValue_, replacer); + RETURN_VALUE_IF_ABRUPT_COMPLETION(thread_, false); + if (UNLIKELY(serializeValue.IsUndefined() || serializeValue.IsSymbol() || + (serializeValue.IsECMAObject() && serializeValue.IsCallable()))) { + continue; + } + handleValue_.Update(serializeValue); + SerializeObjectKey(propList_[i], hasContent); + JSTaggedValue res = SerializeJSONProperty(handleValue_, replacer); + RETURN_VALUE_IF_ABRUPT_COMPLETION(thread_, false); + if (!res.IsUndefined()) { + hasContent = true; + } + } + } + if (hasContent && gap_.length() != 0) { + result_ += "\n"; + result_ += stepback; + } + result_ += "}"; + PopValue(); + indent_ = stepback; + return true; +} + +bool JsonStringifier::SerializeJSProxy(const JSHandle &object, const JSHandle &replacer) +{ + bool isContain = PushValue(object); + if (isContain) { + THROW_TYPE_ERROR_AND_RETURN(thread_, "stack contains value", true); + } + + CString stepback = indent_; + CString stepBegin; + indent_ += gap_; + + if (!gap_.empty()) { + stepBegin += "\n"; + stepBegin += indent_; + } + result_ += "["; + JSHandle proxy(object); + JSHandle lengthKey = thread_->GlobalConstants()->GetHandledLengthString(); + JSHandle lenghHandle = JSProxy::GetProperty(thread_, proxy, lengthKey).GetValue(); + RETURN_VALUE_IF_ABRUPT_COMPLETION(thread_, false); + JSTaggedNumber lenNumber = JSTaggedValue::ToLength(thread_, lenghHandle); + RETURN_VALUE_IF_ABRUPT_COMPLETION(thread_, false); + uint32_t length = lenNumber.ToUint32(); + for (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_ += ","; + } + result_ += stepBegin; + JSTaggedValue serializeValue = GetSerializeValue(object, handleKey_, valHandle, replacer); + RETURN_VALUE_IF_ABRUPT_COMPLETION(thread_, false); + handleValue_.Update(serializeValue); + JSTaggedValue res = SerializeJSONProperty(handleValue_, replacer); + RETURN_VALUE_IF_ABRUPT_COMPLETION(thread_, false); + if (res.IsUndefined()) { + result_ += "null"; + } + } + + if (length > 0 && !gap_.empty()) { + result_ += "\n"; + result_ += stepback; + } + result_ += "]"; + PopValue(); + indent_ = stepback; + return true; +} + +bool JsonStringifier::SerializeJSArray(const JSHandle &value, const JSHandle &replacer) +{ + // If state.[[Stack]] contains value, throw a TypeError exception because the structure is cyclical. + bool isContain = PushValue(value); + if (isContain) { + THROW_TYPE_ERROR_AND_RETURN(thread_, "stack contains value", true); + } + + CString stepback = indent_; + CString stepBegin; + indent_ += gap_; + + if (!gap_.empty()) { + stepBegin += "\n"; + stepBegin += indent_; + } + result_ += "["; + JSHandle jsArr(value); + uint32_t len = jsArr->GetArrayLength(); + if (len > 0) { + for (uint32_t i = 0; i < len; i++) { + JSTaggedValue tagVal = FastRuntimeStub::FastGetPropertyByIndex(thread_, value.GetTaggedValue(), i); + if (UNLIKELY(tagVal.IsAccessor())) { + tagVal = JSObject::CallGetter(thread_, AccessorData::Cast(tagVal.GetTaggedObject()), value); + } + handleKey_.Update(JSTaggedNumber(i).ToString(thread_).GetTaggedValue()); + handleValue_.Update(tagVal); + + if (i > 0) { + result_ += ","; + } + result_ += stepBegin; + JSTaggedValue serializeValue = GetSerializeValue(value, handleKey_, handleValue_, replacer); + RETURN_VALUE_IF_ABRUPT_COMPLETION(thread_, false); + handleValue_.Update(serializeValue); + JSTaggedValue res = SerializeJSONProperty(handleValue_, replacer); + RETURN_VALUE_IF_ABRUPT_COMPLETION(thread_, false); + if (res.IsUndefined()) { + result_ += "null"; + } + } + + if (!gap_.empty()) { + result_ += "\n"; + result_ += stepback; + } + } + + result_ += "]"; + PopValue(); + indent_ = stepback; + return true; +} + +void JsonStringifier::SerilaizePrimitiveRef(const JSHandle &primitiveRef) +{ + JSTaggedValue primitive = JSPrimitiveRef::Cast(primitiveRef.GetTaggedValue().GetTaggedObject())->GetValue(); + if (primitive.IsString()) { + auto priStr = JSTaggedValue::ToString(thread_, primitiveRef); + RETURN_IF_ABRUPT_COMPLETION(thread_); + CString str = ConvertToString(*priStr); + str = ValueToQuotedString(str); + result_ += str; + } else if (primitive.IsNumber()) { + auto priNum = JSTaggedValue::ToNumber(thread_, primitiveRef); + RETURN_IF_ABRUPT_COMPLETION(thread_); + if (std::isfinite(priNum.GetNumber())) { + result_ += ConvertToString(*base::NumberHelper::NumberToString(thread_, priNum)); + } else { + result_ += "null"; + } + } else if (primitive.IsBoolean()) { + result_ += primitive.IsTrue() ? "true" : "false"; + } +} + +bool JsonStringifier::SerializeElements(const JSHandle &obj, const JSHandle &replacer, + bool hasContent) +{ + JSHandle elementsArr(thread_, obj->GetElements()); + if (!elementsArr->IsDictionaryMode()) { + uint32_t elementsLen = elementsArr->GetLength(); + for (uint32_t i = 0; i < elementsLen; ++i) { + if (!elementsArr->Get(i).IsHole()) { + handleKey_.Update(JSTaggedValue(i)); + handleValue_.Update(elementsArr->Get(i)); + hasContent = JsonStringifier::AppendJsonString(obj, replacer, hasContent); + RETURN_VALUE_IF_ABRUPT_COMPLETION(thread_, false); + } + } + } else { + JSHandle numberDic(elementsArr); + CVector> sortArr; + int size = numberDic->Size(); + for (int hashIndex = 0; hashIndex < size; hashIndex++) { + JSTaggedValue key = numberDic->GetKey(hashIndex); + if (!key.IsUndefined() && !key.IsHole()) { + PropertyAttributes attr = numberDic->GetAttributes(hashIndex); + if (attr.IsEnumerable()) { + auto numberKey = JSTaggedValue(static_cast(key.GetInt())); + sortArr.emplace_back(JSHandle(thread_, numberKey)); + } + } + } + std::sort(sortArr.begin(), sortArr.end(), CompareNumber); + for (const auto &entry : sortArr) { + JSTaggedValue entryKey = entry.GetTaggedValue(); + handleKey_.Update(entryKey); + int index = numberDic->FindEntry(entryKey); + JSTaggedValue value = numberDic->GetValue(index); + handleValue_.Update(value); + hasContent = JsonStringifier::AppendJsonString(obj, replacer, hasContent); + RETURN_VALUE_IF_ABRUPT_COMPLETION(thread_, false); + } + } + return hasContent; +} + +bool JsonStringifier::SerializeKeys(const JSHandle &obj, const JSHandle &replacer, + bool hasContent) +{ + JSHandle ownKeys = JSTaggedValue::GetOwnPropertyKeys(thread_, JSHandle(obj)); + + uint32_t length = ownKeys->GetLength(); + for (uint32_t i = 0; i < length; i++) { + JSTaggedValue key = ownKeys->Get(thread_, i); + if (!key.IsString()) { + continue; + } + handleKey_.Update(key); + TaggedArray *properties = TaggedArray::Cast(obj->GetProperties().GetHeapObject()); + PropertyAttributes attr; + uint32_t indexOrEntry = 0; + JSTaggedValue value = FastRuntimeStub::FindOwnProperty(thread_, *obj, properties, key, &attr, &indexOrEntry); + if (!value.IsHole()) { + if (!attr.IsEnumerable()) { + continue; + } + + if (UNLIKELY(value.IsAccessor())) { + value = JSObject::CallGetter(thread_, AccessorData::Cast(value.GetTaggedObject()), + JSHandle(obj)); + } + } else { + value = JSTaggedValue::Undefined(); + } + handleValue_.Update(value); + hasContent = JsonStringifier::AppendJsonString(obj, replacer, hasContent); + RETURN_VALUE_IF_ABRUPT_COMPLETION(thread_, false); + } + return hasContent; +} + +bool JsonStringifier::AppendJsonString(const JSHandle &obj, const JSHandle &replacer, + bool hasContent) +{ + JSTaggedValue serializeValue = GetSerializeValue(JSHandle(obj), handleKey_, handleValue_, replacer); + RETURN_VALUE_IF_ABRUPT_COMPLETION(thread_, false); + if (UNLIKELY(serializeValue.IsUndefined() || serializeValue.IsSymbol() || + (serializeValue.IsECMAObject() && serializeValue.IsCallable()))) { + return hasContent; + } + handleValue_.Update(serializeValue); + SerializeObjectKey(handleKey_, hasContent); + JSTaggedValue res = SerializeJSONProperty(handleValue_, replacer); + RETURN_VALUE_IF_ABRUPT_COMPLETION(thread_, false); + if (!res.IsUndefined()) { + return true; + } + return hasContent; +} +} // namespace panda::ecmascript::base diff --git a/runtime/base/json_stringifier.h b/runtime/base/json_stringifier.h new file mode 100644 index 000000000..68399af06 --- /dev/null +++ b/runtime/base/json_stringifier.h @@ -0,0 +1,92 @@ +/* + * Copyright (c) 2021-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_BASE_JSON_STRINGIFY_INL_H +#define ECMASCRIPT_BASE_JSON_STRINGIFY_INL_H + +#include "plugins/ecmascript/runtime/js_tagged_value.h" +#include "plugins/ecmascript/runtime/js_handle.h" +#include "plugins/ecmascript/runtime/object_factory.h" +#include "plugins/ecmascript/runtime/global_env.h" +#include "plugins/ecmascript/runtime/mem/c_containers.h" + +namespace panda::ecmascript::base { +class JsonStringifier { +public: + explicit JsonStringifier(JSThread *thread) + : thread_(thread), + handleKey_(thread, JSTaggedValue::Undefined()), + handleValue_(thread, JSTaggedValue::Undefined()) {} + + ~JsonStringifier() = default; + NO_COPY_SEMANTIC(JsonStringifier); + NO_MOVE_SEMANTIC(JsonStringifier); + + JSHandle Stringify(const JSHandle &value, const JSHandle &replacer, + const JSHandle &gap); + +private: + CString ValueToQuotedString(const CString &str); + + bool IsFastValueToQuotedString(const char *value); + + void AddDeduplicateProp(const JSHandle &property); + + JSTaggedValue SerializeJSONProperty(const JSHandle &value, const JSHandle &replacer); + JSTaggedValue GetSerializeValue(const JSHandle &object, const JSHandle &key, + const JSHandle &value, const JSHandle &replacer); + void SerializeObjectKey(const JSHandle &key, bool hasContent); + + bool SerializeJSONObject(const JSHandle &value, const JSHandle &replacer); + + bool SerializeJSArray(const JSHandle &value, const JSHandle &replacer); + bool SerializeJSProxy(const JSHandle &object, const JSHandle &replacer); + + void SerilaizePrimitiveRef(const JSHandle &primitiveRef); + + bool PushValue(const JSHandle &value); + + void PopValue(); + + bool CalculateNumberGap(JSTaggedValue gap); + + bool CalculateStringGap(const JSHandle &primString); + bool AppendJsonString(const JSHandle &obj, const JSHandle &replacer, bool hasContent); + bool SerializeElements(const JSHandle &obj, const JSHandle &replacer, bool hasContent); + bool SerializeKeys(const JSHandle &obj, const JSHandle &replacer, bool hasContent); + + static 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_; + JSThread *thread_ {nullptr}; + ObjectFactory *factory_ {nullptr}; + CVector> stack_; + CVector> propList_; + JSMutableHandle handleKey_; + JSMutableHandle handleValue_; +}; +} // namespace panda::ecmascript::base +#endif // ECMASCRIPT_BASE_JSON_STRINGIFY_INL_H diff --git a/runtime/base/number_helper.cpp b/runtime/base/number_helper.cpp new file mode 100644 index 000000000..e97a28dc1 --- /dev/null +++ b/runtime/base/number_helper.cpp @@ -0,0 +1,697 @@ +/* + * Copyright (c) 2021-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 "plugins/ecmascript/runtime/base/number_helper.h" +#include +#include +#include +#include +#include +#include "plugins/ecmascript/runtime/base/builtins_base.h" +#include "plugins/ecmascript/runtime/js_tagged_value-inl.h" +#include "plugins/ecmascript/runtime/object_factory.h" + +namespace panda::ecmascript::base { +enum class Sign { NONE, NEG, POS }; + +// NOLINTNEXTLINE(cppcoreguidelines-macro-usage) +#define RETURN_IF_CONVERSION_END(p, end, result) \ + if ((p) == (end)) { \ + return (result); \ + } + +constexpr char CHARS[] = "0123456789abcdefghijklmnopqrstuvwxyz"; // NOLINT (modernize-avoid-c-arrays) +constexpr uint64_t MAX_MANTISSA = 0x1ULL << 52U; + +static inline uint8_t ToDigit(uint8_t c) +{ + if (c >= '0' && c <= '9') { + return c - '0'; + } + if (c >= 'A' && c <= 'Z') { + return c - 'A' + DECIMAL; + } + if (c >= 'a' && c <= 'z') { + return c - 'a' + DECIMAL; + } + return '$'; +} + +bool NumberHelper::IsNonspace(uint16_t c) +{ + int i; + int len = sizeof(SPACE_OR_LINE_TERMINAL) / sizeof(SPACE_OR_LINE_TERMINAL[0]); + for (i = 0; i < len; i++) { + if (c == SPACE_OR_LINE_TERMINAL[i]) { + return false; + } + if (c < SPACE_OR_LINE_TERMINAL[i]) { + return true; + } + } + return true; +} + +bool NumberHelper::GotoNonspace(uint8_t **ptr, const uint8_t *end) +{ + while (*ptr < end) { + uint16_t c = **ptr; + size_t size = 1; + if (c > INT8_MAX) { + size = 0; + uint16_t utf8Bit = INT8_MAX + 1; // equal 0b1000'0000 + while (utf8Bit > 0 && (c & utf8Bit) == utf8Bit) { + ++size; + utf8Bit >>= 1UL; + } + if (base::utf_helper::ConvertRegionUtf8ToUtf16(*ptr, &c, end - *ptr, 1, 0) <= 0) { + return true; + } + } + if (IsNonspace(c)) { + return true; + } + *ptr += size; // NOLINT(cppcoreguidelines-pro-bounds-pointer-arithmetic) + } + return false; +} + +static inline double SignedZero(Sign sign) +{ + return sign == Sign::NEG ? -0.0 : 0.0; +} + +bool NumberHelper::IsEmptyString(const uint8_t *start, const uint8_t *end) +{ + auto p = const_cast(start); + return !NumberHelper::GotoNonspace(&p, end); +} + +JSTaggedValue NumberHelper::DoubleToString(JSThread *thread, double number, int radix) +{ + bool negative = false; + if (number < 0.0) { + negative = true; + number = -number; + } + + double numberInteger = std::floor(number); + double numberFraction = number - numberInteger; + + auto value = bit_cast(number); + value += 1; + double delta = HALF * (bit_cast(value) - number); + + CString result; + if (numberFraction != 0 && numberFraction >= delta) { + result += "."; + result += DecimalsToString(&numberInteger, numberFraction, radix, delta); + } + + result = IntegerToString(numberInteger, radix) + result; + + if (negative) { + result = "-" + result; + } + + return BuiltinsBase::GetTaggedString(thread, result.c_str()); +} + +JSTaggedValue NumberHelper::DoubleToExponential(JSThread *thread, double number, int digit) +{ + CStringStream ss; + if (digit < 0) { + ss << std::setiosflags(std::ios::scientific) << std::setprecision(base::MAX_PRECISION) << number; + } else { + ss << std::setiosflags(std::ios::scientific) << std::setprecision(digit) << number; + } + CString result = ss.str(); + size_t found = result.find_last_of('e'); + if (found != CString::npos && found < result.size() - 2 && result[found + 2] == '0') { + result.erase(found + 2, 1); // 2:offset of e + } + if (digit < 0) { + size_t end = found; + while (--found > 0) { + if (result[found] != '0') { + break; + } + } + if (result[found] == '.') { + found--; + } + if (found < end - 1) { + result.erase(found + 1, end - found - 1); + } + } + return BuiltinsBase::GetTaggedString(thread, result.c_str()); +} + +JSTaggedValue NumberHelper::DoubleToFixed(JSThread *thread, double number, int digit) +{ + CStringStream ss; + ss << std::setiosflags(std::ios::fixed) << std::setprecision(digit) << number; + return BuiltinsBase::GetTaggedString(thread, ss.str().c_str()); +} + +JSTaggedValue NumberHelper::DoubleToPrecision(JSThread *thread, double number, int digit) +{ + if (number == 0.0) { + return DoubleToFixed(thread, number, digit - 1); + } + CStringStream ss; + double positiveNumber = number > 0 ? number : -number; + int logDigit = std::floor(log10(positiveNumber)); + int radixDigit = digit - logDigit - 1; + const int MAX_EXPONENT_DIGIT = 6; + if ((logDigit >= 0 && radixDigit >= 0) || (logDigit < 0 && radixDigit <= MAX_EXPONENT_DIGIT)) { + return DoubleToFixed(thread, number, std::abs(radixDigit)); + } + return DoubleToExponential(thread, number, digit - 1); +} + +JSTaggedValue NumberHelper::StringToDoubleWithRadix(const uint8_t *start, const uint8_t *end, int radix) +{ + auto p = const_cast(start); + JSTaggedValue nanResult = BuiltinsBase::GetTaggedDouble(NAN_VALUE); + // 1. skip space and line terminal + if (!NumberHelper::GotoNonspace(&p, end)) { + return nanResult; + } + + // 2. sign bit + bool negative = false; + if (*p == '-') { + negative = true; + RETURN_IF_CONVERSION_END(++p, end, nanResult); + } else if (*p == '+') { + RETURN_IF_CONVERSION_END(++p, end, nanResult); + } + // 3. 0x or 0X + bool stripPrefix = true; + // 4. If R  0, then + // a. If R < 2 or R > 36, return NaN. + // b. If R  16, let stripPrefix be false. + if (radix != 0) { + if (radix < MIN_RADIX || radix > MAX_RADIX) { + return nanResult; + } + if (radix != HEXADECIMAL) { + stripPrefix = false; + } + } else { + radix = DECIMAL; + } + int size = 0; + if (stripPrefix) { + if (*p == '0') { + size++; + if (++p != end && (*p == 'x' || *p == 'X')) { + RETURN_IF_CONVERSION_END(++p, end, nanResult); + radix = HEXADECIMAL; + } + } + } + + double result = 0; + bool isDone = false; + do { + double part = 0; + uint32_t multiplier = 1; + for (; p != end; ++p) { + // The maximum value to ensure that uint32_t will not overflow + const uint32_t MAX_MULTIPER = 0xffffffffU / 36; + uint32_t m = multiplier * radix; + if (m > MAX_MULTIPER) { + break; + } + + int currentBit = ToDigit(*p); + if (currentBit >= radix) { + isDone = true; + break; + } + size++; + part = part * radix + currentBit; + multiplier = m; + } + result = result * multiplier + part; + if (isDone) { + break; + } + } while (p != end); + + if (size == 0) { + return nanResult; + } + + if (negative) { + result = -result; + } + return BuiltinsBase::GetTaggedDouble(result); +} + +char NumberHelper::Carry(char current, int radix) +{ + int digit = (current > '9') ? (current - 'a' + DECIMAL) : (current - '0'); + digit = (digit == (radix - 1)) ? 0 : digit + 1; + return CHARS[digit]; +} + +CString NumberHelper::IntegerToString(double number, int radix) +{ + ASSERT(radix >= MIN_RADIX && radix <= MAX_RADIX); + CString result; + while (number / radix > MAX_MANTISSA) { + number /= radix; + result = CString("0").append(result); + } + do { + double remainder = std::fmod(number, radix); + result = CHARS[static_cast(remainder)] + result; + number = (number - remainder) / radix; + } while (number > 0); + return result; +} + +CString NumberHelper::DecimalsToString(double *numberInteger, double fraction, int radix, double delta) +{ + CString result; + while (fraction >= delta) { + fraction *= radix; + delta *= radix; + int64_t integer = std::floor(fraction); + fraction -= integer; + result += CHARS[integer]; + if (fraction > HALF && fraction + delta > 1) { + size_t fractionEnd = result.size() - 1; + result[fractionEnd] = Carry(*result.rbegin(), radix); + for (; fractionEnd > 0; fractionEnd--) { + if (result[fractionEnd] == '0') { + result[fractionEnd - 1] = Carry(result[fractionEnd - 1], radix); + } else { + break; + } + } + if (fractionEnd == 0) { + (*numberInteger)++; + } + break; + } + } + // delete 0 in the end + size_t found = result.find_last_not_of('0'); + if (found != CString::npos) { + result.erase(found + 1); + } + + return result; +} + +CString NumberHelper::IntToString(int number) +{ + return ToCString(number); +} + +JSTaggedValue NumberHelper::Pow(double base, double exponent) +{ + // The result of base ** exponent when base is 1 or -1 and exponent is +Infinity + // or -Infinity, or when base is 1 and exponent is NaN, differs from IEEE 754-2019 + // https://262.ecma-international.org/12.0/#sec-numeric-types-number-exponentiate + if (std::abs(base) == 1 && !std::isfinite(exponent)) { + return JSTaggedValue(base::NAN_VALUE); + } + + return JSTaggedValue(std::pow(base, exponent)); +} + +// 7.1.12.1 ToString Applied to the Number Type +JSHandle NumberHelper::NumberToString(const JSThread *thread, JSTaggedValue number) +{ + ASSERT(number.IsNumber()); + ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); + if (number.IsInt()) { + return factory->NewFromCanBeCompressString(IntToString(number.GetInt())); + } + + double d = number.GetDouble(); + if (std::isnan(d)) { + return factory->NewFromCanBeCompressString("NaN"); + } + if (d == 0.0) { + return factory->NewFromCanBeCompressString("0"); + } + if (d >= INT32_MIN + 1 && d <= INT32_MAX && d == static_cast(static_cast(d))) { + return factory->NewFromCanBeCompressString(IntToString(static_cast(d))); + } + + std::string result; + if (d < 0) { + result += "-"; + d = -d; + } + + if (std::isinf(d)) { + result += "Infinity"; + return factory->NewFromStdStringUnCheck(result, true); + } + + ASSERT(d > 0); + + // 5. Otherwise, let n, k, and s be integers such that k ≥ 1, 10k−1 ≤ s < 10k, the Number value for s × 10n−k is m, + // and k is as small as possible. If there are multiple possibilities for s, choose the value of s for which s × + // 10n−k is closest in value to m. If there are two such possible values of s, choose the one that is even. Note + // that k is the number of digits in the decimal representation of s and that s is not divisible by 10. + CStringStream str; + str << std::scientific << std::showpoint << std::setprecision(DOUBLE_MAX_PRECISION) << d; + if (strtod(str.str().c_str(), nullptr) != d) { + str.clear(); + str.str(""); + str << std::scientific << std::showpoint << std::setprecision(DOUBLE_MAX_PRECISION + 1) << d; + } + std::string scientificStr(str.str()); + + auto indexOfE = scientificStr.find_last_of('e'); + ASSERT(indexOfE != std::string::npos); + std::string base = scientificStr.substr(0, indexOfE); + // skip trailing zeros, and base must not be empty. + base = base.substr(0, base.find_last_not_of('0') + 1); + int k = static_cast(base.size()) - 1; + int n = std::stoi(scientificStr.substr(indexOfE + 1)) + 1; + if (n > 0 && n <= 21) { // NOLINT(readability-magic-numbers) + base.erase(1, 1); + if (k <= n) { + // 6. If k ≤ n ≤ 21, return the String consisting of the code units of the k digits of the decimal + // representation of s (in order, with no leading zeroes), followed by n−k occurrences of the code unit + // 0x0030 (DIGIT ZERO). + base += std::string(n - k, '0'); + } else { + // 7. If 0 < n ≤ 21, return the String consisting of the code units of the most significant n digits of the + // decimal representation of s, followed by the code unit 0x002E (FULL STOP), followed by the code units of + // the remaining k−n digits of the decimal representation of s. + base.insert(n, 1, '.'); + } + } else if (-6 < n && n <= 0) { // NOLINT(readability-magic-numbers) + // 8. If −6 < n ≤ 0, return the String consisting of the code unit 0x0030 (DIGIT ZERO), followed by the code + // unit 0x002E (FULL STOP), followed by −n occurrences of the code unit 0x0030 (DIGIT ZERO), followed by the + // code units of the k digits of the decimal representation of s. + base.erase(1, 1); + base = std::string("0.") + std::string(-n, '0') + base; + } else { + if (k == 1) { + // 9. Otherwise, if k = 1, return the String consisting of the code unit of the single digit of s + base.erase(1, 1); + } + // followed by code unit 0x0065 (LATIN SMALL LETTER E), followed by the code unit 0x002B (PLUS SIGN) or the code + // unit 0x002D (HYPHEN-MINUS) according to whether n−1 is positive or negative, followed by the code units of + // the decimal representation of the integer abs(n−1) (with no leading zeroes). + base += "e" + (n >= 1 ? std::string("+") : "") + std::to_string(n - 1); + } + result += base; + return factory->NewFromStdStringUnCheck(result, true); +} + +double NumberHelper::TruncateDouble(double d) +{ + if (std::isnan(d)) { + return 0; + } + if (!std::isfinite(d)) { + return d; + } + // -0 to +0 + if (d == 0.0) { + return 0; + } + return (d >= 0) ? std::floor(d) : std::ceil(d); +} + +double NumberHelper::StringToDouble(const uint8_t *start, const uint8_t *end, uint8_t radix, uint32_t flags) +{ + auto p = const_cast(start); + // 1. skip space and line terminal + if (!NumberHelper::GotoNonspace(&p, end)) { + return 0.0; + } + + // 2. get number sign + Sign sign = Sign::NONE; + if (*p == '+') { + RETURN_IF_CONVERSION_END(++p, end, NAN_VALUE); + sign = Sign::POS; + } else if (*p == '-') { + RETURN_IF_CONVERSION_END(++p, end, NAN_VALUE); + sign = Sign::NEG; + } + bool ignoreTrailing = (flags & IGNORE_TRAILING) != 0; + + // 3. judge Infinity + static const char INF[] = "Infinity"; // NOLINT(modernize-avoid-c-arrays) + if (*p == INF[0]) { + // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-pointer-arithmetic) + for (const char *i = &INF[1]; *i != '\0'; ++i) { + if (++p == end || *p != *i) { + return NAN_VALUE; + } + } + ++p; + if (!ignoreTrailing && NumberHelper::GotoNonspace(&p, end)) { + return NAN_VALUE; + } + return sign == Sign::NEG ? -POSITIVE_INFINITY : POSITIVE_INFINITY; + } + + // 4. get number radix + bool leadingZero = false; + bool prefixRadix = false; + if (*p == '0' && radix == 0) { + RETURN_IF_CONVERSION_END(++p, end, SignedZero(sign)); + if (*p == 'x' || *p == 'X') { + if ((flags & ALLOW_HEX) == 0) { + return ignoreTrailing ? SignedZero(sign) : NAN_VALUE; + } + RETURN_IF_CONVERSION_END(++p, end, NAN_VALUE); + if (sign != Sign::NONE) { + return NAN_VALUE; + } + prefixRadix = true; + radix = HEXADECIMAL; + } else if (*p == 'o' || *p == 'O') { + if ((flags & ALLOW_OCTAL) == 0) { + return ignoreTrailing ? SignedZero(sign) : NAN_VALUE; + } + RETURN_IF_CONVERSION_END(++p, end, NAN_VALUE); + if (sign != Sign::NONE) { + return NAN_VALUE; + } + prefixRadix = true; + radix = OCTAL; + } else if (*p == 'b' || *p == 'B') { + if ((flags & ALLOW_BINARY) == 0) { + return ignoreTrailing ? SignedZero(sign) : NAN_VALUE; + } + RETURN_IF_CONVERSION_END(++p, end, NAN_VALUE); + if (sign != Sign::NONE) { + return NAN_VALUE; + } + prefixRadix = true; + radix = BINARY; + } else { + leadingZero = true; + } + } + + if (radix == 0) { + radix = DECIMAL; + } + auto pStart = p; + // 5. skip leading '0' + while (*p == '0') { + RETURN_IF_CONVERSION_END(++p, end, SignedZero(sign)); + leadingZero = true; + } + // 6. parse to number + uint64_t intNumber = 0; + uint64_t numberMax = (UINT64_MAX - (radix - 1)) / radix; + int digits = 0; + int exponent = 0; + do { + uint8_t c = ToDigit(*p); + if (c >= radix) { + if (!prefixRadix || ignoreTrailing || (pStart != p && !NumberHelper::GotoNonspace(&p, end))) { + break; + } + // "0b" "0x1.2" "0b1e2" ... + return NAN_VALUE; + } + ++digits; + if (intNumber < numberMax) { + intNumber = intNumber * radix + c; + } else { + ++exponent; + } + } while (++p != end); + + auto number = static_cast(intNumber); + if (sign == Sign::NEG) { + if (number == 0) { + number = -0.0; + } else { + number = -number; + } + } + + // 7. deal with other radix except DECIMAL + if (p == end || radix != DECIMAL) { + if ((digits == 0 && !leadingZero) || (p != end && !ignoreTrailing && NumberHelper::GotoNonspace(&p, end))) { + // no digits there, like "0x", "0xh", or error trailing of "0x3q" + return NAN_VALUE; + } + return number * std::pow(radix, exponent); + } + + // 8. parse '.' + if (radix == DECIMAL && *p == '.') { + RETURN_IF_CONVERSION_END(++p, end, (digits > 0) ? (number * std::pow(radix, exponent)) : NAN_VALUE); + while (ToDigit(*p) < radix) { + --exponent; + ++digits; + if (++p == end) { + break; + } + } + } + if (digits == 0 && !leadingZero) { + // no digits there, like ".", "sss", or ".e1" + return NAN_VALUE; + } + auto pEnd = p; + + // 9. parse 'e/E' with '+/-' + char exponentSign = '+'; + int additionalExponent = 0; + constexpr int MAX_EXPONENT = INT32_MAX / 2; + if (radix == DECIMAL && (p != end && (*p == 'e' || *p == 'E'))) { + RETURN_IF_CONVERSION_END(++p, end, NAN_VALUE); + + // 10. parse exponent number + if (*p == '+' || *p == '-') { + exponentSign = static_cast(*p); + RETURN_IF_CONVERSION_END(++p, end, NAN_VALUE); + } + uint8_t digit; + while ((digit = ToDigit(*p)) < radix) { + if (additionalExponent > MAX_EXPONENT / radix) { + additionalExponent = MAX_EXPONENT; + } else { + additionalExponent = additionalExponent * radix + digit; + } + if (++p == end) { + break; + } + } + } + exponent += (exponentSign == '-' ? -additionalExponent : additionalExponent); + if (!ignoreTrailing && NumberHelper::GotoNonspace(&p, end)) { + return NAN_VALUE; + } + + // 10. build StringNumericLiteral string + CString buffer; + if (sign == Sign::NEG) { + buffer += "-"; + } + for (uint8_t *i = pStart; i < pEnd; ++i) { // NOLINT(cppcoreguidelines-pro-bounds-pointer-arithmetic) + if (*i != static_cast('.')) { + buffer += *i; + } + } + + // 11. convert none-prefix radix string + return Strtod(buffer.c_str(), exponent, radix); +} + +double NumberHelper::Strtod(const char *str, int exponent, uint8_t radix) +{ + ASSERT(str != nullptr); + ASSERT(radix >= base::MIN_RADIX && radix <= base::MAX_RADIX); + auto p = const_cast(str); + Sign sign = Sign::NONE; + uint64_t number = 0; + uint64_t numberMax = (UINT64_MAX - (radix - 1)) / radix; + double result = 0.0; + if (*p == '-') { + sign = Sign::NEG; + ++p; + } + while (*p == '0') { + ++p; + } + while (*p != '\0') { + uint8_t digit = ToDigit(static_cast(*p)); + if (digit >= radix) { + break; + } + if (number < numberMax) { + number = number * radix + digit; + } else { + ++exponent; + } + ++p; + } + if (exponent < 0) { + result = number / std::pow(radix, -exponent); + } else { + result = number * std::pow(radix, exponent); + } + return sign == Sign::NEG ? -result : result; +} + +int32_t NumberHelper::DoubleToInt(double d, size_t bits) +{ + int32_t ret = 0; + auto u64 = bit_cast(d); + int exp = static_cast((u64 & DOUBLE_EXPONENT_MASK) >> DOUBLE_SIGNIFICAND_SIZE) - DOUBLE_EXPONENT_BIAS; + if (exp < static_cast(bits - 1)) { + // smaller than INT_MAX, fast conversion + ret = static_cast(d); + } else if (exp < static_cast(bits + DOUBLE_SIGNIFICAND_SIZE)) { + // Still has significand bits after mod 2^ + // Get low bits by shift left <64 - bits> and shift right <64 - bits> + uint64_t value = (((u64 & DOUBLE_SIGNIFICAND_MASK) | DOUBLE_HIDDEN_BIT) + << (exp - DOUBLE_SIGNIFICAND_SIZE + INT64_BITS - bits)) >> + (INT64_BITS - bits); + ret = static_cast(value); + if ((u64 & DOUBLE_SIGN_MASK) == DOUBLE_SIGN_MASK && ret != INT32_MIN) { + ret = -ret; + } + } else { + // No significand bits after mod 2^, contains NaN and INF + ret = 0; + } + return ret; +} + +int32_t NumberHelper::DoubleInRangeInt32(double d) +{ + if (d > INT_MAX) { + return INT_MAX; + } + if (d < INT_MIN) { + return INT_MIN; + } + return base::NumberHelper::DoubleToInt(d, base::INT32_BITS); +} +} // namespace panda::ecmascript::base diff --git a/runtime/base/number_helper.h b/runtime/base/number_helper.h new file mode 100644 index 000000000..61c544dff --- /dev/null +++ b/runtime/base/number_helper.h @@ -0,0 +1,103 @@ +/* + * Copyright (c) 2021-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_BASE_NUMBER_HELPER_H +#define ECMASCRIPT_BASE_NUMBER_HELPER_H + +#include +#include "plugins/ecmascript/runtime/ecma_string.h" + +#include "plugins/ecmascript/runtime/js_tagged_value.h" + +namespace panda::ecmascript::base { +// NOLINTNEXTLINE(modernize-avoid-c-arrays) +static constexpr uint16_t SPACE_OR_LINE_TERMINAL[] = { + 0x0009, 0x000A, 0x000B, 0x000C, 0x000D, 0x0020, 0x00A0, 0x1680, 0x2000, 0x2001, 0x2002, 0x2003, 0x2004, + 0x2005, 0x2006, 0x2007, 0x2008, 0x2009, 0x200A, 0x2028, 0x2029, 0x202F, 0x205F, 0x3000, 0xFEFF, +}; + +constexpr double MIN_RADIX = 2; +constexpr double MAX_RADIX = 36; +constexpr double MIN_FRACTION = 0; +constexpr double MAX_FRACTION = 100; + +// Coversion flags +static constexpr uint32_t NO_FLAGS = 0U; +static constexpr uint32_t ALLOW_BINARY = 1U << 0U; +static constexpr uint32_t ALLOW_OCTAL = 1U << 1U; +static constexpr uint32_t ALLOW_HEX = 1U << 2U; +static constexpr uint32_t IGNORE_TRAILING = 1U << 3U; + +static constexpr uint32_t MAX_PRECISION = 16; +static constexpr uint8_t BINARY = 2; +static constexpr uint8_t OCTAL = 8; +static constexpr uint8_t DECIMAL = 10; +static constexpr uint8_t HEXADECIMAL = 16; +static constexpr double HALF = 0.5; +static constexpr double EPSILON = std::numeric_limits::epsilon(); +static constexpr double MAX_SAFE_INTEGER = 9007199254740991; +static constexpr double MAX_VALUE = std::numeric_limits::max(); +static constexpr double MIN_VALUE = std::numeric_limits::min(); +static constexpr double POSITIVE_INFINITY = std::numeric_limits::infinity(); +static constexpr double NAN_VALUE = std::numeric_limits::quiet_NaN(); + +// Helper defines for double +static constexpr int DOUBLE_MAX_PRECISION = 15; +static constexpr int DOUBLE_EXPONENT_BIAS = 0x3FF; +static constexpr size_t DOUBLE_SIGNIFICAND_SIZE = 52; +static constexpr uint64_t DOUBLE_SIGN_MASK = 0x8000000000000000ULL; +static constexpr uint64_t DOUBLE_EXPONENT_MASK = 0x7FFULL << DOUBLE_SIGNIFICAND_SIZE; +static constexpr uint64_t DOUBLE_SIGNIFICAND_MASK = 0x000FFFFFFFFFFFFFULL; +static constexpr uint64_t DOUBLE_HIDDEN_BIT = 1ULL << DOUBLE_SIGNIFICAND_SIZE; + +static constexpr size_t INT64_BITS = 64; +static constexpr size_t INT32_BITS = 32; +static constexpr size_t INT16_BITS = 16; +static constexpr size_t INT8_BITS = 8; + +class NumberHelper { +public: + static bool IsFinite(JSTaggedValue number) + { + return number.IsInt() || (number.IsDouble() && std::isfinite(number.GetDouble())); + } + static bool IsNaN(JSTaggedValue number) + { + return number.IsDouble() && std::isnan(number.GetDouble()); + } + static JSTaggedValue DoubleToString(JSThread *thread, double number, int radix); + static bool IsEmptyString(const uint8_t *start, const uint8_t *end); + static JSHandle NumberToString(const JSThread *thread, JSTaggedValue number); + static double TruncateDouble(double d); + static double StringToDouble(const uint8_t *start, const uint8_t *end, uint8_t radix, uint32_t flags = NO_FLAGS); + static int32_t DoubleToInt(double d, size_t bits); + static int32_t DoubleInRangeInt32(double d); + static JSTaggedValue Pow(double base, double exponent); + static JSTaggedValue DoubleToExponential(JSThread *thread, double number, int digit); + static JSTaggedValue DoubleToFixed(JSThread *thread, double number, int digit); + static JSTaggedValue DoubleToPrecision(JSThread *thread, double number, int digit); + static JSTaggedValue StringToDoubleWithRadix(const uint8_t *start, const uint8_t *end, int radix); + static CString IntToString(int number); + +private: + static char Carry(char current, int radix); + static double Strtod(const char *str, int exponent, uint8_t radix); + static CString IntegerToString(double number, int radix); + static CString DecimalsToString(double *numberInteger, double fraction, int radix, double delta); + static bool IsNonspace(uint16_t c); + static bool GotoNonspace(uint8_t **ptr, const uint8_t *end); +}; +} // namespace panda::ecmascript::base +#endif // ECMASCRIPT_BASE_NUMBER_HELPER_H diff --git a/runtime/base/object_helper.cpp b/runtime/base/object_helper.cpp new file mode 100644 index 000000000..22eec2ae4 --- /dev/null +++ b/runtime/base/object_helper.cpp @@ -0,0 +1,106 @@ +/** + * Copyright (c) Huawei Technologies Co., Ltd. 2019-2021. All rights reserved. + */ + +#include "plugins/ecmascript/runtime/accessor_data.h" +#include "plugins/ecmascript/runtime/base/builtins_base.h" +#include "plugins/ecmascript/runtime/base/object_helper.h" +#include "plugins/ecmascript/runtime/internal_call_params.h" +#include "plugins/ecmascript/runtime/js_function.h" +#include "plugins/ecmascript/runtime/js_iterator.h" + +namespace panda::ecmascript::base { +// ES2021 20.1.2.7.1 +JSTaggedValue ObjectHelper::CreateDataPropertyOnObject(EcmaRuntimeCallInfo *argv) +{ + ASSERT(argv); + JSThread *thread = argv->GetThread(); + + // 1. Let O be the this value. + JSHandle object = BuiltinsBase::GetThis(argv); + + // 2. Assert: Type(O) is Object. + ASSERT(object->IsObject()); + + // 3. Assert: O is an extensible ordinary object. + ASSERT(object->IsExtensible(thread)); + + // 4. Let propertyKey be ? ToPropertyKey(key). + JSHandle key = BuiltinsBase::GetCallArg(argv, 0); + JSHandle propertyKey = JSTaggedValue::ToPropertyKey(thread, key); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + + // 5. Perform ! CreateDataPropertyOrThrow(O, propertyKey, value). + JSHandle value = BuiltinsBase::GetCallArg(argv, 1); + JSObject::CreateDataPropertyOrThrow(thread, JSTaggedValue::ToObject(thread, object), propertyKey, value); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + + // 6. Return undefined. + return JSTaggedValue::Undefined(); +} + +JSTaggedValue ObjectHelper::AddEntriesFromIterable(JSThread *thread, const JSHandle &target, + const JSHandle &iterable, + JSHandle &adder) +{ + // If IsCallable(adder) is false, throw a TypeError exception + if (!adder->IsCallable()) { + THROW_TYPE_ERROR_AND_RETURN(thread, "adder is not callable", adder.GetTaggedValue()); + } + // Let iter be GetIterator(iterable). + JSHandle iter(JSIterator::GetIterator(thread, iterable)); + // ReturnIfAbrupt(iter). + RETURN_VALUE_IF_ABRUPT_COMPLETION(thread, iter.GetTaggedValue()); + JSHandle keyIndex(thread, JSTaggedValue(0)); + JSHandle valueIndex(thread, JSTaggedValue(1)); + JSHandle next = JSIterator::IteratorStep(thread, iter); + JSMutableHandle status(thread, JSTaggedValue::Undefined()); + RETURN_VALUE_IF_ABRUPT_COMPLETION(thread, next.GetTaggedValue()); + ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); + while (!next->IsFalse()) { + // ReturnIfAbrupt(next). + RETURN_VALUE_IF_ABRUPT_COMPLETION(thread, next.GetTaggedValue()); + // Let nextValue be IteratorValue(next). + JSHandle nextValue(JSIterator::IteratorValue(thread, next)); + // ReturnIfAbrupt(nextValue). + RETURN_VALUE_IF_ABRUPT_COMPLETION(thread, next.GetTaggedValue()); + + // If Type(nextItem) is not Object + if (!nextValue->IsECMAObject()) { + JSHandle typeError = factory->GetJSError(ErrorType::TYPE_ERROR, "nextItem is not Object"); + JSHandle record( + factory->NewCompletionRecord(CompletionRecord::THROW, JSHandle(typeError))); + JSTaggedValue ret = JSIterator::IteratorClose(thread, iter, record).GetTaggedValue(); + if (!thread->HasPendingException()) { + THROW_NEW_ERROR_AND_RETURN_VALUE(thread, typeError.GetTaggedValue(), ret); + }; + return ret; + } + // Let k be Get(nextItem, "0"). + JSHandle key = JSObject::GetProperty(thread, nextValue, keyIndex).GetValue(); + // If k is an abrupt completion, return IteratorClose(iter, k). + if (thread->HasPendingException()) { + return JSIterator::IteratorCloseAndReturn(thread, iter, key); + } + // Let v be Get(nextItem, "1"). + JSHandle value = JSObject::GetProperty(thread, nextValue, valueIndex).GetValue(); + // If v is an abrupt completion, return IteratorClose(iter, v). + if (thread->HasPendingException()) { + return JSIterator::IteratorCloseAndReturn(thread, iter, value); + } + InternalCallParams *arguments = thread->GetInternalCallParams(); + arguments->MakeArgv(key, value); // 2: key and value pair + // Let status be Call(adder, target, «nextValue.[[value]]»). + JSTaggedValue ret = JSFunction::Call(thread, adder, JSHandle(target), 2U, arguments->GetArgv()); + + status.Update(ret); + // If status is an abrupt completion, return IteratorClose(iter, status). + if (thread->HasPendingException()) { + return JSIterator::IteratorCloseAndReturn(thread, iter, status); + } + // Let next be IteratorStep(iter). + next = JSIterator::IteratorStep(thread, iter); + } + return target.GetTaggedValue(); +} +} // namespace panda::ecmascript::base diff --git a/runtime/base/object_helper.h b/runtime/base/object_helper.h new file mode 100644 index 000000000..f7cf6741b --- /dev/null +++ b/runtime/base/object_helper.h @@ -0,0 +1,20 @@ +/** + * Copyright (c) Huawei Technologies Co., Ltd. 2019-2021. All rights reserved. + */ + +#ifndef PANDA_RUNTIME_ECMASCRIPT_BASE_OBJECT_HELP_H +#define PANDA_RUNTIME_ECMASCRIPT_BASE_OBJECT_HELP_H + +#include "plugins/ecmascript/runtime/ecma_runtime_call_info.h" +#include "plugins/ecmascript/runtime/js_tagged_value.h" + +namespace panda::ecmascript::base { +class ObjectHelper { +public: + static JSTaggedValue CreateDataPropertyOnObject(EcmaRuntimeCallInfo *argv); + static JSTaggedValue AddEntriesFromIterable(JSThread *thread, const JSHandle &target, + const JSHandle &iterable, + JSHandle &adder); +}; +} // namespace panda::ecmascript::base +#endif // PANDA_RUNTIME_ECMASCRIPT_BASE_OBJECT_HELP_H diff --git a/runtime/base/string_helper.cpp b/runtime/base/string_helper.cpp new file mode 100644 index 000000000..8640b5c13 --- /dev/null +++ b/runtime/base/string_helper.cpp @@ -0,0 +1,162 @@ +/* + * Copyright (c) 2021-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 "plugins/ecmascript/runtime/base/string_helper.h" +#include "plugins/ecmascript/runtime/ecma_string-inl.h" + +namespace panda::ecmascript::base { +std::string StringHelper::ToStdString(EcmaString *string) +{ + return std::string(ConvertToString(string)); +} + +bool StringHelper::CheckDuplicate(EcmaString *string) +{ + if (string->IsUtf8()) { + const uint8_t *array = string->GetDataUtf8(); + size_t length = string->GetUtf8Length() - 1; + std::bitset bitSet; + for (size_t i = 0; i < length; ++i) { + char idx = *array; + if (bitSet.test(idx)) { + return true; + } + bitSet.set(idx); + array++; // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-pointer-arithmetic) + } + } else { + UNREACHABLE(); + } + return false; +} + +EcmaString *StringHelper::Repeat(JSThread *thread, const std::u16string &thisStr, int32_t repeatLen, bool canBeCompress) +{ + ecmascript::ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); + if (repeatLen == 0) { + return *factory->GetEmptyString(); // Create empty EcmaString. + } + std::u16string tmpStr = thisStr; + for (int32_t i = 1; i < repeatLen; i++) { + tmpStr.append(thisStr); + } + const char16_t *constChar16tData = tmpStr.data(); + auto *char16tData = const_cast(constChar16tData); + auto *uint16tData = reinterpret_cast(char16tData); + int32_t length = tmpStr.size(); + return *factory->NewFromUtf16UnCheck(uint16tData, length, canBeCompress); +} + +EcmaString *StringHelper::Trim(JSThread *thread, const std::u16string &thisStr, TrimKind kind) +{ + ecmascript::ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); + std::u16string tmpStr = thisStr; + if (tmpStr.empty()) { + return *factory->GetEmptyString(); + } + std::string str = U16stringToString(tmpStr); + std::wstring wstr = StringToWstring(str); + std::wregex r; + if (kind == TrimKind::TRIM_START) { + r = ( + L"^[" + L"\u0009\u000A\u000B\u000C\u000D\u0020\u00A0\u1680\u2000\u2001\u2002\u2003\u2004\u2005\u2006\u2007" + L"\u2008\u2009\u200A\u2028\u2029\u202F\u205F\u3000\uFEFF]+"); + } else if (kind == TrimKind::TRIM_END) { + r = ( + L"[" + L"\u0009\u000A\u000B\u000C\u000D\u0020\u00A0\u1680\u2000\u2001\u2002\u2003\u2004\u2005\u2006\u2007" + L"\u2008\u2009\u200A\u2028\u2029\u202F\u205F\u3000\uFEFF]+$"); + } else { + ASSERT(kind == TrimKind::TRIM_START_END); + r = ( + L"^[" + L"\u0009\u000A\u000B\u000C\u000D\u0020\u00A0\u1680\u2000\u2001\u2002\u2003\u2004\u2005\u2006\u2007" + L"\u2008\u2009\u200A\u2028\u2029\u202F\u205F\u3000\uFEFF]+|[" + L"\u0009\u000A\u000B\u000C\u000D\u0020\u00A0\u1680\u2000\u2001\u2002\u2003\u2004\u2005\u2006\u2007" + L"\u2008\u2009\u200A\u2028\u2029\u202F\u205F\u3000\uFEFF]+$"); + } + wstr = regex_replace(wstr, r, L""); + str = WstringToString(wstr); + tmpStr = StringToU16string(str); + const char16_t *constChar16tData = tmpStr.data(); + auto *char16tData = const_cast(constChar16tData); + auto *uint16tData = reinterpret_cast(char16tData); + int32_t length = tmpStr.size(); + return *factory->NewFromUtf16(uint16tData, length); +} + +// ES2021 22.1.3.15.1 StringPad ( O, maxLength, fillString, placement ) +panda::ecmascript::JSTaggedValue StringHelper::StringPad(JSThread *thread, JSHandle obj, + JSHandle maxLength, + JSHandle fillString, PadPlacement placement) +{ + // 1. Assert: placement is start or end. + ASSERT(placement == PadPlacement::START || placement == PadPlacement::END); + + // 2. Let S be ? ToString(O). + JSHandle string = JSTaggedValue::ToString(thread, obj); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + + // 4. Let stringLength be the length of S. + size_t stringLength = string->GetLength(); + + // 3. Let intMaxLength be ? ToLength(maxLength). + size_t intMaxLength = JSTaggedValue::ToLength(thread, maxLength).ToUint32(); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + + // 5. If intMaxLength ≤ stringLength, return S. + if (intMaxLength <= stringLength) { + return string.GetTaggedValue(); + } + + // 6. If fillString is undefined, let filler be the String value consisting solely of the code unit 0x0020 (SPACE). + // 7. Else, let filler be ? ToString(fillString). + ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); + JSHandle filler; + if (fillString->IsUndefined()) { + filler = factory->NewFromString(" "); + } else { + filler = JSTaggedValue::ToString(thread, fillString); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + } + + // 8. If filler is the empty String, return S. + size_t fillerLength = filler->GetLength(); + if (fillerLength == 0) { + return string.GetTaggedValue(); + } + + // 9. Let fillLen be intMaxLength - stringLength. + uint32_t fillLen = intMaxLength - stringLength; + + // 10. Let truncatedStringFiller be the String value consisting of repeated concatenations of filler truncated to + // length fillLen. + std::string truncatedString; + std::string fillerString = ToStdString(filler.GetObject()); + for (size_t i = 0; i < fillLen; i++) { + truncatedString += fillerString[i % fillerLength]; + } + JSHandle truncatedStringFiller = factory->NewFromStdString(truncatedString); + + // 11. If placement is start, return the string-concatenation of truncatedStringFiller and S. + if (placement == PadPlacement::START) { + return factory->ConcatFromString(truncatedStringFiller, string).GetTaggedValue(); + } + + // 12. Else, return the string-concatenation of S and truncatedStringFiller. + return factory->ConcatFromString(string, truncatedStringFiller).GetTaggedValue(); +} +} // namespace panda::ecmascript::base diff --git a/runtime/base/string_helper.h b/runtime/base/string_helper.h new file mode 100644 index 000000000..96c31b62c --- /dev/null +++ b/runtime/base/string_helper.h @@ -0,0 +1,204 @@ +/* + * Copyright (c) 2021-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_BASE_STRING_HELP_H +#define ECMASCRIPT_BASE_STRING_HELP_H + +#include +#include +#include +#include +#include +#include + +#include "plugins/ecmascript/runtime/base/utf_helper.h" +#include "plugins/ecmascript/runtime/ecma_string-inl.h" +#include "plugins/ecmascript/runtime/ecma_vm.h" +#include "plugins/ecmascript/runtime/js_thread.h" +#include "plugins/ecmascript/runtime/mem/assert_scope-inl.h" +#include "plugins/ecmascript/runtime/object_factory.h" +#include "ark-third-party/icu/icu4c/source/common/unicode/unistr.h" +#include "libpandafile/file_items.h" + +namespace panda::ecmascript::base { +enum PadPlacement { START, END }; + +enum TrimKind { TRIM_START, TRIM_END, TRIM_START_END }; + +class StringHelper { +public: + static std::string ToStdString(EcmaString *string); + + static bool CheckDuplicate(EcmaString *string); + + static inline bool Contains(const EcmaString *string, const EcmaString *other) + { + [[maybe_unused]] DisallowGarbageCollection noGc; + CString str = ConvertToString(string, StringConvertedUsage::LOGICOPERATION); + CString oth = ConvertToString(other, StringConvertedUsage::LOGICOPERATION); + CString::size_type index = str.find(oth); + return (index != CString::npos); + } + + static inline CString RepalceAll(CString str, const CString &oldValue, + const CString &newValue) + { + if (oldValue.empty() || oldValue == newValue) { + return str; + } + CString::size_type pos(0); + while ((pos = str.find(oldValue, pos)) != CString::npos) { + str.replace(pos, oldValue.length(), newValue); + pos += newValue.length(); + } + return str; + } + + static inline std::string SubString(const JSHandle &string, uint32_t start, uint32_t length) + { + return std::string(ConvertToString(string.GetObject(), start, length, + StringConvertedUsage::LOGICOPERATION)); + } + + static inline std::u16string Utf16ToU16String(const uint16_t *utf16Data, uint32_t dataLen) + { + auto *char16tData = reinterpret_cast(utf16Data); + std::u16string u16str(char16tData, dataLen); + return u16str; + } + + static inline std::string Utf8ToString(const uint8_t *utf8Data, uint32_t dataLen) + { + auto *charData = reinterpret_cast(utf8Data); + std::string str(charData, dataLen); + return str; + } + + static inline std::u16string Utf8ToU16String(const uint8_t *utf8Data, uint32_t dataLen) + { + auto *charData = reinterpret_cast(utf8Data); + std::string str(charData, dataLen); + std::u16string u16str = std::wstring_convert, char16_t>{}.from_bytes(str); + return u16str; + } + + static inline std::string WstringToString(const std::wstring &wstr) + { + return std::wstring_convert, wchar_t>{}.to_bytes(wstr); + } + + static inline std::wstring StringToWstring(const std::string &str) + { + return std::wstring_convert, wchar_t>{}.from_bytes(str); + } + + static inline std::string U16stringToString(const std::u16string &u16str) + { + return std::wstring_convert, char16_t>{}.to_bytes(u16str); + } + + static inline std::u16string StringToU16string(const std::string &str) + { + return std::wstring_convert, char16_t>{}.from_bytes(str); + } + + static inline size_t Find(const std::string &thisStr, const std::string &searchStr, int32_t pos) + { + size_t idx = thisStr.find(searchStr, pos); + return idx; + } + + static inline size_t Find(const std::u16string &thisStr, const std::u16string &searchStr, int32_t pos) + { + size_t idx = thisStr.find(searchStr, pos); + return idx; + } + + static inline size_t RFind(const std::u16string &thisStr, const std::u16string &searchStr, int32_t pos) + { + size_t idx = thisStr.rfind(searchStr, pos); + return idx; + } + + static inline EcmaString *ToUpper(JSThread *thread, const std::u16string &str) + { + ecmascript::ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); + std::u16string tmpStr = str; + const char16_t *constChar16tData = tmpStr.data(); + icu::UnicodeString uString(constChar16tData); + icu::UnicodeString up = uString.toUpper(); + std::string res; + up.toUTF8String(res); + return *factory->NewFromStdString(res); + } + + static inline EcmaString *ToLower(JSThread *thread, const std::u16string &str) + { + ecmascript::ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); + std::u16string tmpStr = str; + const char16_t *constChar16tData = tmpStr.data(); + icu::UnicodeString uString(constChar16tData); + icu::UnicodeString low = uString.toLower(); + std::string res; + low.toUTF8String(res); + return *factory->NewFromStdString(res); + } + + static inline size_t FindFromU16ToUpper(const std::u16string &thisStr, uint16_t *u16Data) + { + std::u16string tmpStr = Utf16ToU16String(u16Data, 1); + const char16_t *constChar16tData = tmpStr.data(); + icu::UnicodeString uString(constChar16tData); + icu::UnicodeString up = uString.toUpper(); + std::string res; + up.toUTF8String(res); + std::u16string searchStr = StringToU16string(res); + size_t idx = Find(thisStr, searchStr, 0); + return idx; + } + + static EcmaString *Repeat(JSThread *thread, const std::u16string &thisStr, int32_t repeatLen, bool canBeCompress); + + static EcmaString *Trim(JSThread *thread, const std::u16string &thisStr, TrimKind kind); + + static panda::ecmascript::JSTaggedValue StringPad(JSThread *thread, JSHandle obj, + JSHandle maxLength, + JSHandle fillString, PadPlacement placement); + + static inline std::u16string Append(const std::u16string &str1, const std::u16string &str2) + { + std::u16string tmpStr = str1; + return tmpStr.append(str2); + } + + static inline uint32_t Utf8ToU32String(const std::vector &data) + { + std::string str(data.begin(), data.end()); + std::u32string u32str = std::wstring_convert, char32_t>{}.from_bytes(str); + auto u32data = reinterpret_cast(u32str.data()); + return *u32data; + } + + static inline std::string Utf32ToString(uint32_t u32Data) + { + UChar32 charData = u32Data; + icu::UnicodeString uString(charData); + std::string res; + uString.toUTF8String(res); + return res; + } +}; +} // namespace panda::ecmascript::base +#endif // ECMASCRIPT_BASE_STRING_HELP_H diff --git a/runtime/base/typed_array_helper-inl.h b/runtime/base/typed_array_helper-inl.h new file mode 100644 index 000000000..4c9defbbd --- /dev/null +++ b/runtime/base/typed_array_helper-inl.h @@ -0,0 +1,203 @@ +/* + * Copyright (c) 2021-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_BASE_TYPED_ARRAY_HELPER_INL_H +#define ECMASCRIPT_BASE_TYPED_ARRAY_HELPER_INL_H + +#include "plugins/ecmascript/runtime/builtins/builtins_arraybuffer.h" +#include "plugins/ecmascript/runtime/base/builtins_base.h" +#include "plugins/ecmascript/runtime/base/typed_array_helper.h" +#include "plugins/ecmascript/runtime/js_tagged_value.h" +#include "plugins/ecmascript/runtime/object_factory.h" +#include "plugins/ecmascript/runtime/js_hclass.h" +#include "plugins/ecmascript/runtime/ecma_macros.h" +#include "plugins/ecmascript/runtime/ecma_vm.h" +#include "plugins/ecmascript/runtime/global_env.h" +#include "plugins/ecmascript/runtime/js_object.h" +#include "plugins/ecmascript/runtime/js_array_iterator.h" +#include "plugins/ecmascript/runtime/js_arraybuffer.h" +#include "plugins/ecmascript/runtime/base/error_helper.h" +#include "plugins/ecmascript/runtime/js_tagged_value-inl.h" +#include "plugins/ecmascript/runtime/js_typed_array.h" +#include "plugins/ecmascript/runtime/base/error_type.h" + +namespace panda::ecmascript::base { +DataViewType TypedArrayHelper::GetType(const JSHandle &obj) +{ + JSType type = obj->GetJSHClass()->GetObjectType(); + switch (type) { + case JSType::JS_INT8_ARRAY: + return DataViewType::INT8; + case JSType::JS_UINT8_ARRAY: + return DataViewType::UINT8; + case JSType::JS_UINT8_CLAMPED_ARRAY: + return DataViewType::UINT8_CLAMPED; + case JSType::JS_INT16_ARRAY: + return DataViewType::INT16; + case JSType::JS_UINT16_ARRAY: + return DataViewType::UINT16; + case JSType::JS_INT32_ARRAY: + return DataViewType::INT32; + case JSType::JS_UINT32_ARRAY: + return DataViewType::UINT32; + case JSType::JS_FLOAT32_ARRAY: + return DataViewType::FLOAT32; + default: + return DataViewType::FLOAT64; + } +} + +int32_t TypedArrayHelper::GetElementSize(const JSHandle &obj) +{ + JSType type = obj->GetJSHClass()->GetObjectType(); + switch (type) { + case JSType::JS_INT8_ARRAY: + case JSType::JS_UINT8_ARRAY: + case JSType::JS_UINT8_CLAMPED_ARRAY: + return ElementSize::ONE; + case JSType::JS_INT16_ARRAY: + case JSType::JS_UINT16_ARRAY: + return ElementSize::TWO; + case JSType::JS_INT32_ARRAY: + case JSType::JS_UINT32_ARRAY: + case JSType::JS_FLOAT32_ARRAY: + return ElementSize::FOUR; + default: + return ElementSize::EIGHT; + } +} + +DataViewType TypedArrayHelper::GetTypeFromName(JSThread *thread, const JSHandle &typeName) +{ + const GlobalEnvConstants *globalConst = thread->GlobalConstants(); + if (JSTaggedValue::SameValue(typeName, globalConst->GetHandledInt8ArrayString())) { + return DataViewType::INT8; + } + if (JSTaggedValue::SameValue(typeName, globalConst->GetHandledUint8ArrayString())) { + return DataViewType::UINT8; + } + if (JSTaggedValue::SameValue(typeName, globalConst->GetHandledUint8ClampedArrayString())) { + return DataViewType::UINT8_CLAMPED; + } + if (JSTaggedValue::SameValue(typeName, globalConst->GetHandledInt16ArrayString())) { + return DataViewType::INT16; + } + if (JSTaggedValue::SameValue(typeName, globalConst->GetHandledUint16ArrayString())) { + return DataViewType::UINT16; + } + if (JSTaggedValue::SameValue(typeName, globalConst->GetHandledInt32ArrayString())) { + return DataViewType::INT32; + } + if (JSTaggedValue::SameValue(typeName, globalConst->GetHandledUint32ArrayString())) { + return DataViewType::UINT32; + } + if (JSTaggedValue::SameValue(typeName, globalConst->GetHandledFloat32ArrayString())) { + return DataViewType::FLOAT32; + } + return DataViewType::FLOAT64; +} + +JSHandle TypedArrayHelper::GetConstructor(JSThread *thread, const JSHandle &obj) +{ + JSHandle env = thread->GetEcmaVM()->GetGlobalEnv(); + JSType type = obj->GetTaggedObject()->GetClass()->GetObjectType(); + switch (type) { + case JSType::JS_INT8_ARRAY: + return env->GetInt8ArrayFunction(); + case JSType::JS_UINT8_ARRAY: + return env->GetUint8ArrayFunction(); + case JSType::JS_UINT8_CLAMPED_ARRAY: + return env->GetUint8ClampedArrayFunction(); + case JSType::JS_INT16_ARRAY: + return env->GetInt16ArrayFunction(); + case JSType::JS_UINT16_ARRAY: + return env->GetUint16ArrayFunction(); + case JSType::JS_INT32_ARRAY: + return env->GetInt32ArrayFunction(); + case JSType::JS_UINT32_ARRAY: + return env->GetUint32ArrayFunction(); + case JSType::JS_FLOAT32_ARRAY: + return env->GetFloat32ArrayFunction(); + default: + return env->GetFloat64ArrayFunction(); + } +} + +JSHandle TypedArrayHelper::GetConstructorFromName(JSThread *thread, const JSHandle &typeName) +{ + JSHandle env = thread->GetEcmaVM()->GetGlobalEnv(); + auto globalConst = thread->GlobalConstants(); + if (JSTaggedValue::SameValue(typeName, globalConst->GetHandledInt8ArrayString())) { + return JSHandle(env->GetInt8ArrayFunction()); + } + if (JSTaggedValue::SameValue(typeName, globalConst->GetHandledUint8ArrayString())) { + return JSHandle(env->GetUint8ArrayFunction()); + } + if (JSTaggedValue::SameValue(typeName, globalConst->GetHandledUint8ClampedArrayString())) { + return JSHandle(env->GetUint8ClampedArrayFunction()); + } + if (JSTaggedValue::SameValue(typeName, globalConst->GetHandledInt16ArrayString())) { + return JSHandle(env->GetInt16ArrayFunction()); + } + if (JSTaggedValue::SameValue(typeName, globalConst->GetHandledUint16ArrayString())) { + return JSHandle(env->GetUint16ArrayFunction()); + } + if (JSTaggedValue::SameValue(typeName, globalConst->GetHandledInt32ArrayString())) { + return JSHandle(env->GetInt32ArrayFunction()); + } + if (JSTaggedValue::SameValue(typeName, globalConst->GetHandledUint32ArrayString())) { + return JSHandle(env->GetUint32ArrayFunction()); + } + if (JSTaggedValue::SameValue(typeName, globalConst->GetHandledFloat32ArrayString())) { + return JSHandle(env->GetFloat32ArrayFunction()); + } + return JSHandle(env->GetFloat64ArrayFunction()); +} + +int32_t TypedArrayHelper::GetSizeFromName(JSThread *thread, const JSHandle &typeName) +{ + int32_t elementSize; + auto globalConst = thread->GlobalConstants(); + if (JSTaggedValue::SameValue(typeName, globalConst->GetHandledInt8ArrayString()) || + JSTaggedValue::SameValue(typeName, globalConst->GetHandledUint8ArrayString()) || + JSTaggedValue::SameValue(typeName, globalConst->GetHandledUint8ClampedArrayString())) { + elementSize = ElementSize::ONE; + } else if (JSTaggedValue::SameValue(typeName, globalConst->GetHandledInt16ArrayString()) || + JSTaggedValue::SameValue(typeName, globalConst->GetHandledUint16ArrayString())) { + elementSize = ElementSize::TWO; + } else if (JSTaggedValue::SameValue(typeName, globalConst->GetHandledInt32ArrayString()) || + JSTaggedValue::SameValue(typeName, globalConst->GetHandledUint32ArrayString()) || + JSTaggedValue::SameValue(typeName, globalConst->GetHandledFloat32ArrayString())) { + elementSize = ElementSize::FOUR; + } else { + elementSize = ElementSize::EIGHT; + } + return elementSize; +} + +int32_t TypedArrayHelper::GetByteOffset(JSThread *thread, const JSHandle &obj) +{ + JSTaggedValue length = JSTypedArray::Cast(*obj)->GetByteOffset(); + return JSTaggedValue::ToLength(thread, JSHandle(thread, length)).ToInt32(); +} + +int32_t TypedArrayHelper::GetArrayLength(JSThread *thread, const JSHandle &obj) +{ + JSTaggedValue length = JSTypedArray::Cast(*obj)->GetArrayLength(); + return JSTaggedValue::ToLength(thread, JSHandle(thread, length)).ToInt32(); +} +} // namespace panda::ecmascript::base + +#endif // ECMASCRIPT_BASE_TYPED_ARRAY_HELPER_INL_H diff --git a/runtime/base/typed_array_helper.cpp b/runtime/base/typed_array_helper.cpp new file mode 100644 index 000000000..776da6e7d --- /dev/null +++ b/runtime/base/typed_array_helper.cpp @@ -0,0 +1,557 @@ +/* + * Copyright (c) 2021-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 "plugins/ecmascript/runtime/base/typed_array_helper.h" +#include "plugins/ecmascript/runtime/base/builtins_base.h" +#include "plugins/ecmascript/runtime/base/error_helper.h" +#include "plugins/ecmascript/runtime/base/error_type.h" +#include "plugins/ecmascript/runtime/base/typed_array_helper-inl.h" +#include "plugins/ecmascript/runtime/builtins/builtins_arraybuffer.h" +#include "plugins/ecmascript/runtime/ecma_macros.h" +#include "plugins/ecmascript/runtime/ecma_vm.h" +#include "plugins/ecmascript/runtime/global_env.h" +#include "plugins/ecmascript/runtime/internal_call_params.h" +#include "plugins/ecmascript/runtime/js_array_iterator.h" +#include "plugins/ecmascript/runtime/js_arraybuffer.h" +#include "plugins/ecmascript/runtime/js_hclass.h" +#include "plugins/ecmascript/runtime/js_object-inl.h" +#include "plugins/ecmascript/runtime/js_tagged_value-inl.h" +#include "plugins/ecmascript/runtime/js_tagged_value.h" +#include "plugins/ecmascript/runtime/js_typed_array.h" +#include "plugins/ecmascript/runtime/object_factory.h" + +namespace panda::ecmascript::base { +using BuiltinsArrayBuffer = builtins::BuiltinsArrayBuffer; + +// es11 22.2.4 The TypedArray Constructors +JSTaggedValue TypedArrayHelper::TypedArrayConstructor(EcmaRuntimeCallInfo *argv, + const JSHandle &constructorName) +{ + ASSERT(argv); + JSThread *thread = argv->GetThread(); + [[maybe_unused]] EcmaHandleScope handleScope(thread); + EcmaVM *ecmaVm = thread->GetEcmaVM(); + JSHandle newTarget = BuiltinsBase::GetNewTarget(argv); + // 2. If NewTarget is undefined, throw a TypeError exception. + if (newTarget->IsUndefined()) { + THROW_TYPE_ERROR_AND_RETURN(thread, "The NewTarget is undefined.", JSTaggedValue::Exception()); + } + // 3. Let constructorName be the String value of the Constructor Name value specified in Table 61 for this + // TypedArray constructor. + // 4. Let O be ? AllocateTypedArray(constructorName, NewTarget, "%TypedArray.prototype%"). + ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); + JSHandle firstArg = BuiltinsBase::GetCallArg(argv, 0); + if (!firstArg->IsECMAObject()) { + // es11 22.2.4.1 TypedArray ( ) + int32_t elementLength = 0; + // es11 22.2.4.2 TypedArray ( length ) + if (!firstArg->IsUndefined()) { + JSTaggedNumber index = JSTaggedValue::ToIndex(thread, firstArg); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + elementLength = static_cast(index.GetNumber()); + } + JSHandle obj = + TypedArrayHelper::AllocateTypedArray(factory, ecmaVm, constructorName, newTarget, elementLength); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + return obj.GetTaggedValue(); + } + JSHandle obj = TypedArrayHelper::AllocateTypedArray(factory, ecmaVm, constructorName, newTarget); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + if (firstArg->IsTypedArray()) { + return TypedArrayHelper::CreateFromTypedArray(argv, obj, constructorName); + } + if (firstArg->IsArrayBuffer()) { + return TypedArrayHelper::CreateFromArrayBuffer(argv, obj, constructorName); + } + return TypedArrayHelper::CreateFromOrdinaryObject(argv, obj); +} + +// es11 22.2.4.4 TypedArray ( object ) +JSTaggedValue TypedArrayHelper::CreateFromOrdinaryObject(EcmaRuntimeCallInfo *argv, const JSHandle &obj) +{ + ASSERT(argv); + JSThread *thread = argv->GetThread(); + [[maybe_unused]] EcmaHandleScope handleScope(thread); + EcmaVM *ecmaVm = thread->GetEcmaVM(); + JSHandle env = ecmaVm->GetGlobalEnv(); + JSHandle objectArg = BuiltinsBase::GetCallArg(argv, 0); + JSHandle object(objectArg); + // 5. Let usingIterator be ? GetMethod(object, @@iterator). + JSHandle iteratorSymbol = env->GetIteratorSymbol(); + JSHandle usingIterator = + JSObject::GetMethod(thread, JSHandle::Cast(object), iteratorSymbol); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + + // 6. If usingIterator is not undefined, then + if (!usingIterator->IsUndefined()) { + CVector> vec; + // a. Let values be ? IterableToList(object, usingIterator). + // b. Let len be the number of elements in values. + // c. Perform ? AllocateTypedArrayBuffer(O, len). + JSHandle iterator = JSIterator::GetIterator(thread, objectArg, usingIterator); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + JSHandle next(thread, JSTaggedValue::True()); + while (!next->IsFalse()) { + next = JSIterator::IteratorStep(thread, iterator); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + if (!next->IsFalse()) { + JSHandle nextValue = JSIterator::IteratorValue(thread, next); + vec.push_back(nextValue); + } + } + int32_t len = vec.size(); + TypedArrayHelper::AllocateTypedArrayBuffer(thread, ecmaVm, obj, len); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + // d. Let k be 0. + // e. Repeat, while k < len + // i. Let Pk be ! ToString(k). + // ii. Let kValue be the first element of values and remove that element from values. + // iii. Perform ? Set(O, Pk, kValue, true). + // iv. Set k to k + 1. + JSMutableHandle tKey(thread, JSTaggedValue::Undefined()); + double k = 0; + while (k < len) { + tKey.Update(JSTaggedValue(k)); + JSHandle kKey(JSTaggedValue::ToString(thread, tKey)); + JSHandle kValue = vec[k]; + JSTaggedValue::SetProperty(thread, JSHandle::Cast(obj), kKey, kValue, true); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + k++; + } + // f. Assert: values is now an empty List. + // g. Return O. + return obj.GetTaggedValue(); + } + + // 7. NOTE: object is not an Iterable so assume it is already an array-like object. + // 8. Let arrayLike be object. + // 9. Let len be ? LengthOfArrayLike(arrayLike). + JSHandle lengthKey = thread->GlobalConstants()->GetHandledLengthString(); + JSTaggedNumber lenTemp = + JSTaggedValue::ToLength(thread, JSObject::GetProperty(thread, objectArg, lengthKey).GetValue()); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + double len = lenTemp.GetNumber(); + // 10. Perform ? AllocateTypedArrayBuffer(O, len). + TypedArrayHelper::AllocateTypedArrayBuffer(thread, ecmaVm, obj, len); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + // 11. Let k be 0. + // 12. Repeat, while k < len + // a. Let Pk be ! ToString(k). + // b. Let kValue be ? Get(arrayLike, Pk). + // c. Perform ? Set(O, Pk, kValue, true). + // d. Set k to k + 1. + JSMutableHandle tKey(thread, JSTaggedValue::Undefined()); + double k = 0; + while (k < len) { + tKey.Update(JSTaggedValue(k)); + JSHandle kKey(JSTaggedValue::ToString(thread, tKey)); + JSHandle kValue = JSObject::GetProperty(thread, objectArg, kKey).GetValue(); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + JSTaggedValue::SetProperty(thread, JSHandle::Cast(obj), kKey, kValue, true); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + k++; + } + // 13. Return O. + return obj.GetTaggedValue(); +} + +// es11 22.2.4.3 TypedArray ( typedArray ) +JSTaggedValue TypedArrayHelper::CreateFromTypedArray(EcmaRuntimeCallInfo *argv, const JSHandle &obj, + const JSHandle &constructorName) +{ + ASSERT(argv); + JSThread *thread = argv->GetThread(); + [[maybe_unused]] EcmaHandleScope handleScope(thread); + JSHandle env = thread->GetEcmaVM()->GetGlobalEnv(); + const GlobalEnvConstants *globalConst = thread->GlobalConstants(); + // 5. Let srcArray be typedArray. + JSHandle srcArray = BuiltinsBase::GetCallArg(argv, 0); + JSHandle srcObj(srcArray); + // 6. Let srcData be srcArray.[[ViewedArrayBuffer]]. + JSHandle srcData(thread, JSTypedArray::Cast(*srcObj)->GetViewedArrayBuffer()); + // 7. If IsDetachedBuffer(srcData) is true, throw a TypeError exception. + if (BuiltinsArrayBuffer::IsDetachedBuffer(srcData.GetTaggedValue())) { + THROW_TYPE_ERROR_AND_RETURN(thread, "The srcData is detached buffer.", JSTaggedValue::Exception()); + } + // 8. Let elementType be the Element Type value in Table 61 for constructorName. + DataViewType elementType = TypedArrayHelper::GetTypeFromName(thread, constructorName); + // 9. Let elementLength be srcArray.[[ArrayLength]]. + // 10. Let srcName be the String value of srcArray.[[TypedArrayName]]. + // 11. Let srcType be the Element Type value in Table 61 for srcName. + // 12. Let srcElementSize be the Element Size value specified in Table 61 for srcName. + int32_t elementLength = TypedArrayHelper::GetArrayLength(thread, srcObj); + JSHandle srcName(thread, JSTypedArray::Cast(*srcObj)->GetTypedArrayName()); + DataViewType srcType = TypedArrayHelper::GetTypeFromName(thread, srcName); + int32_t srcElementSize = TypedArrayHelper::GetSizeFromName(thread, srcName); + // 13. Let srcByteOffset be srcArray.[[ByteOffset]]. + // 14. Let elementSize be the Element Size value specified in Table 61 for constructorName. + // 15. Let byteLength be elementSize × elementLength. + int32_t srcByteOffset = TypedArrayHelper::GetByteOffset(thread, srcObj); + int32_t elementSize = TypedArrayHelper::GetSizeFromName(thread, constructorName); + int32_t byteLength = elementSize * elementLength; + // 16. If IsSharedArrayBuffer(srcData) is false, then + // a. Let bufferConstructor be ? SpeciesConstructor(srcData, %ArrayBuffer%). + + JSTaggedValue data; + // 18. If elementType is the same as srcType, then + // a. Let data be ? CloneArrayBuffer(srcData, srcByteOffset, byteLength, bufferConstructor). + if (elementType == srcType) { + data = + BuiltinsArrayBuffer::CloneArrayBuffer(thread, srcData, srcByteOffset, globalConst->GetHandledUndefined()); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + } else { + // 19. Else, + // a. Let data be ? AllocateArrayBuffer(bufferConstructor, byteLength). + JSHandle bufferConstructor = + JSObject::SpeciesConstructor(thread, JSHandle(srcData), env->GetArrayBufferFunction()); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + data = BuiltinsArrayBuffer::AllocateArrayBuffer(thread, bufferConstructor, byteLength); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + // b. If IsDetachedBuffer(srcData) is true, throw a TypeError exception. + if (BuiltinsArrayBuffer::IsDetachedBuffer(srcData.GetTaggedValue())) { + THROW_TYPE_ERROR_AND_RETURN(thread, "The srcData is detached buffer.", JSTaggedValue::Exception()); + } + // d. Let srcByteIndex be srcByteOffset. + // e. Let targetByteIndex be 0. + int32_t srcByteIndex = srcByteOffset; + int32_t targetByteIndex = 0; + // f. Let count be elementLength. + // g. Repeat, while count > 0 + JSMutableHandle value(thread, JSTaggedValue::Undefined()); + for (int32_t count = elementLength; count > 0; count--) { + // i. Let value be GetValueFromBuffer(srcData, srcByteIndex, srcType, true, Unordered). + JSTaggedValue taggedData = + BuiltinsArrayBuffer::GetValueFromBuffer(srcData.GetTaggedValue(), srcByteIndex, srcType, true); + value.Update(taggedData); + JSTaggedNumber numVal = JSTaggedValue::ToNumber(thread, value); + // ii. Perform SetValueInBuffer(data, targetByteIndex, elementType, value, true, Unordered). + BuiltinsArrayBuffer::SetValueInBuffer(data, targetByteIndex, elementType, numVal, true); + // iii. Set srcByteIndex to srcByteIndex + srcElementSize. + // iv. Set targetByteIndex to targetByteIndex + elementSize. + // v. Set count to count - 1. + srcByteIndex = srcByteIndex + srcElementSize; + targetByteIndex = targetByteIndex + elementSize; + } + } + // 19. Set O’s [[ViewedArrayBuffer]] internal slot to data. + // 20. Set O’s [[ByteLength]] internal slot to byteLength. + // 21. Set O’s [[ByteOffset]] internal slot to 0. + // 22. Set O’s [[ArrayLength]] internal slot to elementLength. + JSTypedArray::Cast(*obj)->SetViewedArrayBuffer(thread, data); + JSTypedArray::Cast(*obj)->SetByteLength(thread, JSTaggedValue(byteLength)); + JSTypedArray::Cast(*obj)->SetByteOffset(thread, JSTaggedValue(0)); + JSTypedArray::Cast(*obj)->SetArrayLength(thread, JSTaggedValue(elementLength)); + // 23. Return O. + return obj.GetTaggedValue(); +} + +// es11 22.2.4.5 TypedArray ( buffer [ , byteOffset [ , length ] ] ) +JSTaggedValue TypedArrayHelper::CreateFromArrayBuffer(EcmaRuntimeCallInfo *argv, const JSHandle &obj, + const JSHandle &constructorName) +{ + ASSERT(argv); + JSThread *thread = argv->GetThread(); + [[maybe_unused]] EcmaHandleScope handleScope(thread); + // 5. Let elementSize be the Element Size value specified in Table 61 for constructorName. + // 6. Let offset be ? ToIndex(byteOffset). + int32_t elementSize = TypedArrayHelper::GetSizeFromName(thread, constructorName); + JSHandle byteOffset = BuiltinsBase::GetCallArg(argv, 1); + JSTaggedNumber index = JSTaggedValue::ToIndex(thread, byteOffset); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + auto offset = static_cast(index.GetNumber()); + // 7. If offset modulo elementSize ≠ 0, throw a RangeError exception. + if (offset % elementSize != 0) { + THROW_RANGE_ERROR_AND_RETURN(thread, "The offset cannot be an integral multiple of elementSize.", + JSTaggedValue::Exception()); + } + // 8. If length is not undefined, then + // a. Let newLength be ? ToIndex(length). + JSHandle length = BuiltinsBase::GetCallArg(argv, BuiltinsBase::ArgsPosition::THIRD); + int32_t newLength = 0; + if (!length->IsUndefined()) { + index = JSTaggedValue::ToIndex(thread, length); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + newLength = static_cast(index.GetNumber()); + } + // 9. If IsDetachedBuffer(buffer) is true, throw a TypeError exception. + JSHandle buffer = BuiltinsBase::GetCallArg(argv, 0); + if (BuiltinsArrayBuffer::IsDetachedBuffer(buffer.GetTaggedValue())) { + THROW_TYPE_ERROR_AND_RETURN(thread, "The srcData is detached buffer.", JSTaggedValue::Exception()); + } + // 10. Let bufferByteLength be buffer.[[ArrayBufferByteLength]]. + JSTaggedNumber newLengthNum = + JSTaggedNumber::FromIntOrDouble(thread, JSHandle(buffer)->GetArrayBufferByteLength()); + int32_t bufferByteLength = newLengthNum.ToInt32(); + // 11. If length is undefined, then + // a. If bufferByteLength modulo elementSize ≠ 0, throw a RangeError exception. + // b. Let newByteLength be bufferByteLength - offset. + // c. If newByteLength < 0, throw a RangeError exception. + int32_t newByteLength; + if (length->IsUndefined()) { + if (bufferByteLength % elementSize != 0) { + THROW_RANGE_ERROR_AND_RETURN(thread, "The bufferByteLength cannot be an integral multiple of elementSize.", + JSTaggedValue::Exception()); + } + newByteLength = bufferByteLength - offset; + if (newByteLength < 0) { + THROW_RANGE_ERROR_AND_RETURN(thread, "The newByteLength is less than 0.", JSTaggedValue::Exception()); + } + } else { + // 12. Else, + // a. Let newByteLength be newLength × elementSize. + // b. If offset + newByteLength > bufferByteLength, throw a RangeError exception. + newByteLength = newLength * elementSize; + if (offset + newByteLength > bufferByteLength) { + THROW_RANGE_ERROR_AND_RETURN(thread, "The newByteLength is out of range.", JSTaggedValue::Exception()); + } + } + // 13. Set O.[[ViewedArrayBuffer]] to buffer. + // 14. Set O.[[ByteLength]] to newByteLength. + // 15. Set O.[[ByteOffset]] to offset. + // 16. Set O.[[ArrayLength]] to newByteLength / elementSize. + JSTypedArray::Cast(*obj)->SetViewedArrayBuffer(thread, buffer); + JSTypedArray::Cast(*obj)->SetByteLength(thread, JSTaggedValue(newByteLength)); + JSTypedArray::Cast(*obj)->SetByteOffset(thread, JSTaggedValue(offset)); + JSTypedArray::Cast(*obj)->SetArrayLength(thread, JSTaggedValue(newByteLength / elementSize)); + // 17. Return O. + return obj.GetTaggedValue(); +} + +// es11 22.2.4.2.1 Runtime Semantics: AllocateTypedArray ( constructorName, newTarget, defaultProto ) +JSHandle TypedArrayHelper::AllocateTypedArray(ObjectFactory *factory, EcmaVM *ecmaVm, + const JSHandle &constructorName, + const JSHandle &newTarget) +{ + JSThread *thread = ecmaVm->GetJSThread(); + // 1. Let proto be ? GetPrototypeFromConstructor(newTarget, defaultProto). + // 2. Let obj be ! IntegerIndexedObjectCreate(proto). + RETURN_VALUE_IF_ABRUPT_COMPLETION(thread, JSHandle(thread, JSTaggedValue::Exception())); + JSHandle typedArrayFunc = TypedArrayHelper::GetConstructorFromName(thread, constructorName); + JSHandle obj = factory->NewJSObjectByConstructor(typedArrayFunc, newTarget); + RETURN_VALUE_IF_ABRUPT_COMPLETION(thread, JSHandle(thread, JSTaggedValue::Exception())); + // 3. Assert: obj.[[ViewedArrayBuffer]] is undefined. + // 4. Set obj.[[TypedArrayName]] to constructorName. + // 7. If length is not present, then + // a. Set obj.[[ByteLength]] to 0. + // b. Set obj.[[ByteOffset]] to 0. + // c. Set obj.[[ArrayLength]] to 0. + JSTypedArray::Cast(*obj)->SetTypedArrayName(thread, constructorName); + JSTypedArray::Cast(*obj)->SetByteLength(thread, JSTaggedValue(0)); + JSTypedArray::Cast(*obj)->SetByteOffset(thread, JSTaggedValue(0)); + JSTypedArray::Cast(*obj)->SetArrayLength(thread, JSTaggedValue(0)); + // 9. Return obj. + return obj; +} // namespace panda::ecmascript::base + +// es11 22.2.4.2.1 Runtime Semantics: AllocateTypedArray ( constructorName, newTarget, defaultProto, length ) +JSHandle TypedArrayHelper::AllocateTypedArray(ObjectFactory *factory, EcmaVM *ecmaVm, + const JSHandle &constructorName, + const JSHandle &newTarget, int32_t length) +{ + JSThread *thread = ecmaVm->GetJSThread(); + // 1. Let proto be ? GetPrototypeFromConstructor(newTarget, defaultProto). + // 2. Let obj be ! IntegerIndexedObjectCreate(proto). + RETURN_VALUE_IF_ABRUPT_COMPLETION(thread, JSHandle(thread, JSTaggedValue::Exception())); + JSHandle typedArrayFunc = TypedArrayHelper::GetConstructorFromName(thread, constructorName); + JSHandle obj = factory->NewJSObjectByConstructor(typedArrayFunc, newTarget); + RETURN_VALUE_IF_ABRUPT_COMPLETION(thread, JSHandle(thread, JSTaggedValue::Exception())); + // 3. Assert: obj.[[ViewedArrayBuffer]] is undefined. + // 4. Set obj.[[TypedArrayName]] to constructorName. + JSTypedArray::Cast(*obj)->SetTypedArrayName(thread, constructorName); + // 7. If length is not present, then + // 8. Else, + // a. Perform ? AllocateTypedArrayBuffer(obj, length). + TypedArrayHelper::AllocateTypedArrayBuffer(thread, ecmaVm, obj, length); + RETURN_VALUE_IF_ABRUPT_COMPLETION(thread, JSHandle(thread, JSTaggedValue::Exception())); + // 9. Return obj. + return obj; +} + +// es11 22.2.4.2.2 Runtime Semantics: AllocateTypedArrayBuffer ( O, length ) +JSHandle TypedArrayHelper::AllocateTypedArrayBuffer(JSThread *thread, EcmaVM *ecmaVm, + const JSHandle &obj, double length) +{ + JSHandle env = ecmaVm->GetGlobalEnv(); + // 1. Assert: O is an Object that has a [[ViewedArrayBuffer]] internal slot. + // 2. Assert: O.[[ViewedArrayBuffer]] is undefined. + // 3. Assert: ! IsNonNegativeInteger(length) is true. + ASSERT(JSTaggedValue(length).IsInteger()); + ASSERT(length >= 0); + JSHandle exception(thread, JSTaggedValue::Exception()); + if (length > JSTypedArray::MAX_TYPED_ARRAY_INDEX) { + THROW_RANGE_ERROR_AND_RETURN(thread, "array length must less than 2^32 - 1", exception); + } + // 4. Let constructorName be the String value of O.[[TypedArrayName]]. + JSHandle constructorName(thread, JSTypedArray::Cast(*obj)->GetTypedArrayName()); + // 5. Let elementSize be the Element Size value specified in Table 61 for constructorName. + int32_t elementSize = TypedArrayHelper::GetSizeFromName(thread, constructorName); + // 6. Let byteLength be elementSize × length. + double byteLength = elementSize * length; + // 7. Let data be ? AllocateArrayBuffer(%ArrayBuffer%, byteLength). + JSHandle constructor = env->GetArrayBufferFunction(); + JSTaggedValue data = BuiltinsArrayBuffer::AllocateArrayBuffer(thread, constructor, byteLength); + RETURN_VALUE_IF_ABRUPT_COMPLETION(thread, exception); + // 8. Set O.[[ViewedArrayBuffer]] to data. + // 9. Set O.[[ByteLength]] to byteLength. + // 10. Set O.[[ByteOffset]] to 0. + // 11. Set O.[[ArrayLength]] to length. + JSTypedArray::Cast(*obj)->SetViewedArrayBuffer(thread, data); + JSTypedArray::Cast(*obj)->SetByteLength(thread, JSTaggedValue(byteLength)); + JSTypedArray::Cast(*obj)->SetByteOffset(thread, JSTaggedValue(0)); + JSTypedArray::Cast(*obj)->SetArrayLength(thread, JSTaggedValue(length)); + // 12. Return O. + return obj; +} + +// es11 22.2.4.7 TypedArraySpeciesCreate ( exemplar, argumentList ) +JSHandle TypedArrayHelper::TypedArraySpeciesCreate( + JSThread *thread, const JSHandle &obj, uint32_t argc, + const JSTaggedType argv[] // NOLINT(modernize-avoid-c-arrays) +) +{ + // 1. Assert: exemplar is an Object that has [[TypedArrayName]] and [[ContentType]] internal slots. + // 2. Let defaultConstructor be the intrinsic object listed in column one of Table 61 for + // exemplar.[[TypedArrayName]]. + JSHandle defaultConstructor = TypedArrayHelper::GetConstructor(thread, JSHandle(obj)); + // 3. Let constructor be ? SpeciesConstructor(exemplar, defaultConstructor). + JSHandle thisConstructor = JSObject::SpeciesConstructor(thread, obj, defaultConstructor); + RETURN_VALUE_IF_ABRUPT_COMPLETION(thread, JSHandle(thread, JSTaggedValue::Exception())); + // 4. Let result be ? TypedArrayCreate(constructor, argumentList). + return TypedArrayHelper::TypedArrayCreate(thread, thisConstructor, argc, argv); +} + +// es11 22.2.4.6 TypedArrayCreate ( constructor, argumentList ) +JSHandle TypedArrayHelper::TypedArrayCreate(JSThread *thread, const JSHandle &constructor, + uint32_t argc, + const JSTaggedType argv[] // NOLINT(modernize-avoid-c-arrays) +) +{ + // 1. Let newTypedArray be ? Construct(constructor, argumentList). + JSTaggedValue taggedArray = JSFunction::Construct(thread, constructor, argc, argv, + JSHandle(thread, JSTaggedValue::Undefined())); + RETURN_VALUE_IF_ABRUPT_COMPLETION(thread, JSHandle(thread, JSTaggedValue::Exception())); + if (!taggedArray.IsECMAObject()) { + THROW_TYPE_ERROR_AND_RETURN(thread, "Failed to construct the Typedarray.", + JSHandle(thread, JSTaggedValue::Exception())); + } + JSHandle taggedArrayHandle(thread, taggedArray); + // 2. Perform ? ValidateTypedArray(newTypedArray). + TypedArrayHelper::ValidateTypedArray(thread, taggedArrayHandle); + RETURN_VALUE_IF_ABRUPT_COMPLETION(thread, JSHandle(thread, JSTaggedValue::Exception())); + JSHandle newTypedArray(taggedArrayHandle); + // 3. If argumentList is a List of a single Number, then + // a. If newTypedArray.[[ArrayLength]] < argumentList[0], throw a TypeError exception. + if (argc == 1) { + if (TypedArrayHelper::GetArrayLength(thread, newTypedArray) < + // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-pointer-arithmetic) + JSTaggedValue::ToInt32(thread, JSHandle(thread, JSTaggedValue(argv[0])))) { + THROW_TYPE_ERROR_AND_RETURN(thread, "the length of newTypedArray is not a correct value.", + JSHandle(thread, JSTaggedValue::Exception())); + } + } + // 4. Return newTypedArray. + return newTypedArray; +} + +// es11 22.2.3.5.1 Runtime Semantics: ValidateTypedArray ( O ) +JSTaggedValue TypedArrayHelper::ValidateTypedArray(JSThread *thread, const JSHandle &value) +{ + // 1. Perform ? RequireInternalSlot(O, [[TypedArrayName]]). + // 2. Assert: O has a [[ViewedArrayBuffer]] internal slot. + if (!value->IsTypedArray()) { + THROW_TYPE_ERROR_AND_RETURN(thread, "The O is not a TypedArray.", JSTaggedValue::Exception()); + } + // 3. Let buffer be O.[[ViewedArrayBuffer]]. + // 4. If IsDetachedBuffer(buffer) is true, throw a TypeError exception. + JSTaggedValue buffer = JSHandle::Cast(value)->GetViewedArrayBuffer(); + if (BuiltinsArrayBuffer::IsDetachedBuffer(buffer)) { + THROW_TYPE_ERROR_AND_RETURN(thread, "The ViewedArrayBuffer of O is detached buffer.", + JSTaggedValue::Exception()); + } + // 5. Return buffer. + return buffer; +} + +int32_t TypedArrayHelper::SortCompare(JSThread *thread, const JSHandle &callbackfnHandle, + const JSHandle &buffer, const JSHandle &firstValue, + const JSHandle &secondValue) +{ + const GlobalEnvConstants *globalConst = thread->GlobalConstants(); + // 1. Assert: Both Type(x) and Type(y) is Number. + ASSERT(firstValue->IsNumber() && secondValue->IsNumber()); + // 2. If the argument comparefn is not undefined, then + // a. Let v be Call(comparefn, undefined, «x, y»). + // b. ReturnIfAbrupt(v). + // c. If IsDetachedBuffer(buffer) is true, throw a TypeError exception. + // d. If v is NaN, return +0. + // e. Return v. + if (!callbackfnHandle->IsUndefined()) { + JSHandle thisArgHandle = globalConst->GetHandledUndefined(); + InternalCallParams *arguments = thread->GetInternalCallParams(); + arguments->MakeArgv(firstValue, secondValue); + JSTaggedValue callResult = + JSFunction::Call(thread, callbackfnHandle, thisArgHandle, 2, arguments->GetArgv()); // 2: two args + RETURN_VALUE_IF_ABRUPT_COMPLETION(thread, 0); + if (BuiltinsArrayBuffer::IsDetachedBuffer(buffer.GetTaggedValue())) { + THROW_TYPE_ERROR_AND_RETURN(thread, "The buffer is detached buffer.", 0); + } + JSHandle testResult(thread, callResult); + JSTaggedNumber v = JSTaggedValue::ToNumber(thread, testResult); + RETURN_VALUE_IF_ABRUPT_COMPLETION(thread, 0); + double value = v.GetNumber(); + if (std::isnan(value)) { + return +0; + } + return value; + } + // 3. If x and y are both NaN, return +0. + if (NumberHelper::IsNaN(firstValue.GetTaggedValue())) { + if (NumberHelper::IsNaN(secondValue.GetTaggedValue())) { + return +0; + } + // 4. If x is NaN, return 1. + return 1; + } + // 5. If y is NaN, return -1. + if (NumberHelper::IsNaN(secondValue.GetTaggedValue())) { + return -1; + } + ComparisonResult compareResult = JSTaggedValue::Compare(thread, firstValue, secondValue); + // 6. If x < y, return -1. + // 7. If x > y, return 1. + // 8. If x is -0 and y is +0, return -1. + // 9. If x is +0 and y is -0, return 1. + // 10. Return +0. + if (compareResult == ComparisonResult::LESS) { + return -1; + } + if (compareResult == ComparisonResult::GREAT) { + return 1; + } + JSTaggedNumber xNumber = JSTaggedValue::ToNumber(thread, firstValue); + JSTaggedNumber yNumber = JSTaggedValue::ToNumber(thread, secondValue); + double eZeroTemp = -0.0; + auto eZero = JSTaggedNumber(eZeroTemp); + double pZeroTemp = +0.0; + auto pZero = JSTaggedNumber(pZeroTemp); + if (JSTaggedNumber::SameValue(xNumber, eZero) && JSTaggedNumber::SameValue(yNumber, pZero)) { + return -1; + } + if (JSTaggedNumber::SameValue(xNumber, pZero) && JSTaggedNumber::SameValue(yNumber, eZero)) { + return 1; + } + return +0; +} +} // namespace panda::ecmascript::base diff --git a/runtime/base/typed_array_helper.h b/runtime/base/typed_array_helper.h new file mode 100644 index 000000000..3bc635611 --- /dev/null +++ b/runtime/base/typed_array_helper.h @@ -0,0 +1,69 @@ +/* + * Copyright (c) 2021-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_BASE_TYPED_ARRAY_HELPER_H +#define ECMASCRIPT_BASE_TYPED_ARRAY_HELPER_H + +#include "plugins/ecmascript/runtime/base/builtins_base.h" +#include "plugins/ecmascript/runtime/js_dataview.h" +#include +#include + +namespace panda::ecmascript::base { +enum ElementSize : uint8_t { ONE = 1, TWO = 2, FOUR = 4, EIGHT = 8 }; + +class TypedArrayHelper { +public: + static JSTaggedValue TypedArrayConstructor(EcmaRuntimeCallInfo *argv, + const JSHandle &constructorName); + static JSHandle AllocateTypedArray(ObjectFactory *factory, EcmaVM *ecmaVm, + const JSHandle &constructorName, + const JSHandle &newTarget); + static JSHandle AllocateTypedArray(ObjectFactory *factory, EcmaVM *ecmaVm, + const JSHandle &constructorName, + const JSHandle &newTarget, int32_t length); + static JSHandle TypedArraySpeciesCreate(JSThread *thread, const JSHandle &obj, uint32_t argc, + const JSTaggedType argv[] // NOLINT(modernize-avoid-c-arrays) + ); + static JSHandle TypedArrayCreate(JSThread *thread, const JSHandle &constructor, + uint32_t argc, + const JSTaggedType argv[] // NOLINT(modernize-avoid-c-arrays) + ); + static JSTaggedValue ValidateTypedArray(JSThread *thread, const JSHandle &value); + inline static DataViewType GetType(const JSHandle &obj); + inline static int32_t GetElementSize(const JSHandle &obj); + inline static DataViewType GetTypeFromName(JSThread *thread, const JSHandle &typeName); + inline static JSHandle GetConstructor(JSThread *thread, const JSHandle &obj); + inline static JSHandle GetConstructorFromName(JSThread *thread, + const JSHandle &typeName); + inline static int32_t GetSizeFromName(JSThread *thread, const JSHandle &typeName); + inline static int32_t GetByteOffset(JSThread *thread, const JSHandle &obj); + inline static int32_t GetArrayLength(JSThread *thread, const JSHandle &obj); + static int32_t SortCompare(JSThread *thread, const JSHandle &callbackfnHandle, + const JSHandle &buffer, const JSHandle &firstValue, + const JSHandle &secondValue); + +private: + static JSTaggedValue CreateFromOrdinaryObject(EcmaRuntimeCallInfo *argv, const JSHandle &obj); + static JSTaggedValue CreateFromTypedArray(EcmaRuntimeCallInfo *argv, const JSHandle &obj, + const JSHandle &constructorName); + static JSTaggedValue CreateFromArrayBuffer(EcmaRuntimeCallInfo *argv, const JSHandle &obj, + const JSHandle &constructorName); + static JSHandle AllocateTypedArrayBuffer(JSThread *thread, EcmaVM *ecmaVm, const JSHandle &obj, + double length); +}; +} // namespace panda::ecmascript::base + +#endif // ECMASCRIPT_BASE_TYPED_ARRAY_HELPER_H diff --git a/runtime/base/utf_helper.cpp b/runtime/base/utf_helper.cpp new file mode 100644 index 000000000..a287adc62 --- /dev/null +++ b/runtime/base/utf_helper.cpp @@ -0,0 +1,229 @@ +/* + * Copyright (c) 2021-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 "plugins/ecmascript/runtime/base/utf_helper.h" + +// NOLINTNEXTLINE(cppcoreguidelines-macro-usage, hicpp-signed-bitwise) +static constexpr int32_t U16_SURROGATE_OFFSET = (0xd800 << 10UL) + 0xdc00 - 0x10000; +// NOLINTNEXTLINE(cppcoreguidelines-macro-usage) +#define U16_GET_SUPPLEMENTARY(lead, trail) \ + ((static_cast(lead) << 10UL) + static_cast(trail) - U16_SURROGATE_OFFSET) + +namespace panda::ecmascript::base::utf_helper { +uint32_t UTF16Decode(uint16_t lead, uint16_t trail) +{ + ASSERT((lead >= DECODE_LEAD_LOW && lead <= DECODE_LEAD_HIGH) && + (trail >= DECODE_TRAIL_LOW && trail <= DECODE_TRAIL_HIGH)); + uint32_t cp = (lead - DECODE_LEAD_LOW) * DECODE_FIRST_FACTOR + (trail - DECODE_TRAIL_LOW) + DECODE_SECOND_FACTOR; + return cp; +} + +bool IsValidUTF8(const std::vector &data) +{ + uint32_t length = data.size(); + switch (length) { + case UtfLength::ONE: + if (data.at(0) >= BIT_MASK_1) { + return false; + } + break; + case UtfLength::TWO: + if ((data.at(0) & BIT_MASK_3) != BIT_MASK_2) { + return false; + } + break; + case UtfLength::THREE: + if ((data.at(0) & BIT_MASK_4) != BIT_MASK_3) { + return false; + } + break; + case UtfLength::FOUR: + if ((data.at(0) & BIT_MASK_5) != BIT_MASK_4) { + return false; + } + break; + default: + UNREACHABLE(); + break; + } + + for (uint32_t i = 1; i < length; i++) { + if ((data.at(i) & BIT_MASK_2) != BIT_MASK_1) { + return false; + } + } + return true; +} + +Utf8Char ConvertUtf16ToUtf8(uint16_t d0, uint16_t d1, bool modify) +{ + // when first utf16 code is in 0xd800-0xdfff and second utf16 code is 0, + // means that is a single code point, it needs to be represented by three UTF8 code. + if (d1 == 0 && d0 >= utf::HI_SURROGATE_MIN && d0 <= utf::LO_SURROGATE_MAX) { + auto ch0 = static_cast(UTF8_3B_FIRST | static_cast(d0 >> UtfOffset::TWELVE)); + auto ch1 = static_cast(UTF8_3B_SECOND | (static_cast(d0 >> UtfOffset::SIX) & utf::MASK_6BIT)); + auto ch2 = static_cast(UTF8_3B_THIRD | (d0 & utf::MASK_6BIT)); + return {UtfLength::THREE, {ch0, ch1, ch2}}; + } + + if (d0 == 0) { + if (modify) { + // special case for \u0000 ==> C080 - 1100'0000 1000'0000 + return {UtfLength::TWO, {UTF8_2B_FIRST, UTF8_2B_SECOND}}; + } + // For print string, just skip '\u0000' + return {0, {0x00U}}; + } + if (d0 <= UTF8_1B_MAX) { + return {UtfLength::ONE, {static_cast(d0)}}; + } + if (d0 <= UTF8_2B_MAX) { + auto ch0 = static_cast(UTF8_2B_FIRST | static_cast(d0 >> UtfOffset::SIX)); + auto ch1 = static_cast(UTF8_2B_SECOND | (d0 & utf::MASK_6BIT)); + return {UtfLength::TWO, {ch0, ch1}}; + } + if (d0 < utf::HI_SURROGATE_MIN || d0 > utf::HI_SURROGATE_MAX) { + auto ch0 = static_cast(UTF8_3B_FIRST | static_cast(d0 >> UtfOffset::TWELVE)); + auto ch1 = static_cast(UTF8_3B_SECOND | (static_cast(d0 >> UtfOffset::SIX) & utf::MASK_6BIT)); + auto ch2 = static_cast(UTF8_3B_THIRD | (d0 & utf::MASK_6BIT)); + return {UtfLength::THREE, {ch0, ch1, ch2}}; + } + if (d1 < utf::LO_SURROGATE_MIN || d1 > utf::LO_SURROGATE_MAX) { + // Bad sequence + UNREACHABLE(); + } + + uint32_t codePoint = CombineTwoU16(d0, d1); + + auto ch0 = static_cast((codePoint >> UtfOffset::EIGHTEEN) | UTF8_4B_FIRST); + auto ch1 = static_cast(((codePoint >> UtfOffset::TWELVE) & utf::MASK_6BIT) | utf::MASK1); + auto ch2 = static_cast(((codePoint >> UtfOffset::SIX) & utf::MASK_6BIT) | utf::MASK1); + auto ch3 = static_cast((codePoint & utf::MASK_6BIT) | utf::MASK1); + return {UtfLength::FOUR, {ch0, ch1, ch2, ch3}}; +} + +size_t Utf16ToUtf8Size(const uint16_t *utf16, uint32_t length, bool modify) +{ + size_t res = 1; // zero byte + // when utf16 data length is only 1 and code in 0xd800-0xdfff, + // means that is a single code point, it needs to be represented by three UTF8 code. + if (length == 1 && utf16[0] >= utf::HI_SURROGATE_MIN && // NOLINT(cppcoreguidelines-pro-bounds-pointer-arithmetic) + utf16[0] <= utf::LO_SURROGATE_MAX) { // NOLINT(cppcoreguidelines-pro-bounds-pointer-arithmetic) + res += UtfLength::THREE; + return res; + } + + for (uint32_t i = 0; i < length; ++i) { + if (utf16[i] == 0) { // NOLINT(cppcoreguidelines-pro-bounds-pointer-arithmetic) + if (modify) { + res += UtfLength::TWO; // special case for U+0000 => C0 80 + } + } else if (utf16[i] <= UTF8_1B_MAX) { // NOLINT(cppcoreguidelines-pro-bounds-pointer-arithmetic) + res += 1; + } else if (utf16[i] <= UTF8_2B_MAX) { // NOLINT(cppcoreguidelines-pro-bounds-pointer-arithmetic) + res += UtfLength::TWO; + // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-pointer-arithmetic) + } else if (utf16[i] < utf::HI_SURROGATE_MIN || utf16[i] > utf::HI_SURROGATE_MAX) { + res += UtfLength::THREE; + } else { + if (i < length - 1 && + utf16[i + 1] >= utf::LO_SURROGATE_MIN && // NOLINT(cppcoreguidelines-pro-bounds-pointer-arithmetic) + utf16[i + 1] <= utf::LO_SURROGATE_MAX) { // NOLINT(cppcoreguidelines-pro-bounds-pointer-arithmetic) + res += UtfLength::FOUR; + ++i; + } else { + res += UtfLength::THREE; + } + } + } + return res; +} + +size_t ConvertRegionUtf16ToUtf8(const uint16_t *utf16In, uint8_t *utf8Out, size_t utf16Len, size_t utf8Len, + size_t start, bool modify) +{ + size_t utf8Pos = 0; + if (utf16In == nullptr || utf8Out == nullptr || utf8Len == 0) { + return 0; + } + size_t end = start + utf16Len; + for (size_t i = start; i < end; ++i) { + uint16_t next16Code = 0; + // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-pointer-arithmetic) + if ((i + 1) != end && utf::IsAvailableNextUtf16Code(utf16In[i + 1])) { + next16Code = utf16In[i + 1]; // NOLINT(cppcoreguidelines-pro-bounds-pointer-arithmetic) + } + // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-pointer-arithmetic) + Utf8Char ch = ConvertUtf16ToUtf8(utf16In[i], next16Code, modify); + if (utf8Pos + ch.n > utf8Len) { + break; + } + for (size_t c = 0; c < ch.n; ++c) { + utf8Out[utf8Pos++] = ch.ch[c]; // NOLINT(cppcoreguidelines-pro-bounds-pointer-arithmetic) + } + if (ch.n == UtfLength::FOUR) { // Two UTF-16 chars are used + ++i; + } + } + return utf8Pos; +} + +std::pair ConvertUtf8ToUtf16Pair(const uint8_t *data, bool combine) +{ + uint8_t d0 = data[0]; // NOLINT(cppcoreguidelines-pro-bounds-pointer-arithmetic) + if ((d0 & utf::MASK1) == 0) { + return {d0, 1}; + } + + uint8_t d1 = data[1]; // NOLINT(cppcoreguidelines-pro-bounds-pointer-arithmetic) + if ((d0 & utf::MASK2) == 0) { + return {((d0 & utf::MASK_5BIT) << utf::DATA_WIDTH) | (d1 & utf::MASK_6BIT), UtfLength::TWO}; + } + + uint8_t d2 = data[UtfLength::TWO]; // NOLINT(cppcoreguidelines-pro-bounds-pointer-arithmetic) + if ((d0 & utf::MASK3) == 0) { + return {((d0 & utf::MASK_4BIT) << UtfOffset::TWELVE) | ((d1 & utf::MASK_6BIT) << utf::DATA_WIDTH) | + (d2 & utf::MASK_6BIT), + UtfLength::THREE}; + } + + uint8_t d3 = data[UtfLength::THREE]; // NOLINT(cppcoreguidelines-pro-bounds-pointer-arithmetic) + uint32_t codePoint = ((d0 & utf::MASK_4BIT) << UtfOffset::EIGHTEEN) | ((d1 & utf::MASK_6BIT) << UtfOffset::TWELVE) | + ((d2 & utf::MASK_6BIT) << utf::DATA_WIDTH) | (d3 & utf::MASK_6BIT); + + uint32_t pair = 0; + if (combine) { + uint32_t lead = ((codePoint >> (utf::PAIR_ELEMENT_WIDTH - utf::DATA_WIDTH)) + utf::U16_LEAD); + uint32_t tail = ((codePoint & utf::MASK_10BIT) + utf::U16_TAIL) & utf::MASK_16BIT; + pair = U16_GET_SUPPLEMENTARY(lead, tail); // NOLINTNEXTLINE(hicpp-signed-bitwise) + } else { + pair |= ((codePoint >> (utf::PAIR_ELEMENT_WIDTH - utf::DATA_WIDTH)) + utf::U16_LEAD) << utf::PAIR_ELEMENT_WIDTH; + pair |= ((codePoint & utf::MASK_10BIT) + utf::U16_TAIL) & utf::MASK_16BIT; + } + + return {pair, UtfLength::FOUR}; +} + +size_t Utf8ToUtf16Size(const uint8_t *utf8, size_t utf8Len) +{ + return utf::MUtf8ToUtf16Size(utf8, utf8Len); +} + +size_t ConvertRegionUtf8ToUtf16(const uint8_t *utf8In, uint16_t *utf16Out, size_t utf8Len, size_t utf16Len, + size_t start) +{ + return utf::ConvertRegionMUtf8ToUtf16(utf8In, utf16Out, utf8Len, utf16Len, start); +} +} // namespace panda::ecmascript::base::utf_helper diff --git a/runtime/base/utf_helper.h b/runtime/base/utf_helper.h new file mode 100644 index 000000000..48728f726 --- /dev/null +++ b/runtime/base/utf_helper.h @@ -0,0 +1,87 @@ +/* + * Copyright (c) 2021-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_BASE_UTF_HELPER_H +#define ECMASCRIPT_BASE_UTF_HELPER_H + +#include +#include + +#include "libpandabase/utils/utf.h" + +namespace panda::ecmascript::base::utf_helper { +static constexpr uint16_t DECODE_LEAD_LOW = 0xD800; +static constexpr uint16_t DECODE_LEAD_HIGH = 0xDBFF; +static constexpr uint16_t DECODE_TRAIL_LOW = 0xDC00; +static constexpr uint16_t DECODE_TRAIL_HIGH = 0xDFFF; +static constexpr uint32_t DECODE_FIRST_FACTOR = 0x400; +static constexpr uint32_t DECODE_SECOND_FACTOR = 0x10000; + +static constexpr uint8_t BIT_MASK_1 = 0x80; +static constexpr uint8_t BIT_MASK_2 = 0xC0; +static constexpr uint8_t BIT_MASK_3 = 0xE0; +static constexpr uint8_t BIT_MASK_4 = 0xF0; +static constexpr uint8_t BIT_MASK_5 = 0xF8; + +static constexpr uint8_t UTF8_1B_MAX = 0x7f; + +static constexpr uint16_t UTF8_2B_MAX = 0x7ff; +static constexpr uint8_t UTF8_2B_FIRST = 0xc0; +static constexpr uint8_t UTF8_2B_SECOND = 0x80; + +static constexpr uint8_t UTF8_3B_FIRST = 0xe0; +static constexpr uint8_t UTF8_3B_SECOND = 0x80; +static constexpr uint8_t UTF8_3B_THIRD = 0x80; + +static constexpr uint8_t UTF8_4B_FIRST = 0xf0; + +enum UtfLength : uint8_t { ONE = 1, TWO = 2, THREE = 3, FOUR = 4 }; +enum UtfOffset : uint8_t { SIX = 6, TEN = 10, TWELVE = 12, EIGHTEEN = 18 }; + +static constexpr size_t MAX_BYTES = 4; +struct Utf8Char { + size_t n; + std::array ch; +}; + +uint32_t UTF16Decode(uint16_t lead, uint16_t trail); + +bool IsValidUTF8(const std::vector &data); + +Utf8Char ConvertUtf16ToUtf8(uint16_t d0, uint16_t d1, bool modify); + +size_t Utf16ToUtf8Size(const uint16_t *utf16, uint32_t length, bool modify = true); + +size_t ConvertRegionUtf16ToUtf8(const uint16_t *utf16In, uint8_t *utf8Out, size_t utf16Len, size_t utf8Len, + size_t start, bool modify = true); + +std::pair ConvertUtf8ToUtf16Pair(const uint8_t *data, bool combine = false); + +size_t Utf8ToUtf16Size(const uint8_t *utf8, size_t utf8Len); + +size_t ConvertRegionUtf8ToUtf16(const uint8_t *utf8In, uint16_t *utf16Out, size_t utf8Len, size_t utf16Len, + size_t start); + +static inline uint32_t CombineTwoU16(uint16_t d0, uint16_t d1) +{ + uint32_t codePoint = d0 - utf::HI_SURROGATE_MIN; + codePoint <<= UtfOffset::TEN; + codePoint |= d1 - utf::LO_SURROGATE_MIN; + codePoint += utf::LO_SUPPLEMENTS_MIN; + return codePoint; +} +} // namespace panda::ecmascript::base::utf_helper + +#endif // ECMASCRIPT_BASE_UTF_HELPER_H \ No newline at end of file diff --git a/runtime/bridge/arch/aarch64/handle_call_ecma_N_v8_aarch64.S b/runtime/bridge/arch/aarch64/handle_call_ecma_N_v8_aarch64.S new file mode 100644 index 000000000..0d95ea155 --- /dev/null +++ b/runtime/bridge/arch/aarch64/handle_call_ecma_N_v8_aarch64.S @@ -0,0 +1,72 @@ +/** + * 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. + */ + +// uint64_t EcmaResolveSecondArgument(uint64_t function_tagged_value); +.extern EcmaResolveSecondArgument + +// handle ecma.call0dyn, ecma.call1dyn, ecma.call2dyn, ecma.call3dyn +// regs set as follow +// x9 - frame.vregs, x10 - insn_ptr, x12 - method, x15 - number of passed arguments, x13, x14 - temp + + ldrb w13, [x10], #1 + add x13, x9, x13, lsl #3 // log2(FRAME_VREGISTER_SIZE) + ldr x13, [x13] + + // Compute second argument in C++ code + stp x9, x10, [sp, #-16]! + stp x12, x13, [sp, #-16]! + stp x14, x15, [sp, #-16]! + mov x0, x13 + bl EcmaResolveSecondArgument + ldp x14, x15, [sp], #16 + ldp x12, x13, [sp], #16 + ldp x9, x10, [sp], #16 + + // ABI arg reg 1 (x1) <- number of arguments + add x1, x15, #2 // + new.target + calculated argument + // ABI arg reg 2 (x2) <- functional object + mov x2, x13 + sub w15, w15, #1 + // ABI arg reg 3 (x3) <- new.target (undefined for now) + mov x3, #TAGGED_VALUE_UNDEFINED + // ABI arg reg 4 (x4) <- computed + mov x4, x0 + // ABI arg reg 0 (x0) <- panda::Method* + mov x0, x12 + cbz w15, .Linvoke + + // ABI arg reg 5 (x5) <- boxed arg0 from user code + ldrb w13, [x10], #1 + add x13, x9, x13, lsl #3 // log2(FRAME_VREGISTER_SIZE) + ldr x5, [x13] + sub w15, w15, #1 + cbz w15, .Linvoke + + // ABI arg reg 6 (x6) <- boxed arg1 from user code + ldrb w13, [x10], #1 + add x13, x9, x13, lsl #3 // log2(FRAME_VREGISTER_SIZE) + ldr x6, [x13] + sub w15, w15, #1 + cbz w15, .Linvoke + + // ABI arg reg 7 (x7) <- boxed arg2 from user code + ldrb w13, [x10], #1 + add x13, x9, x13, lsl #3 // log2(FRAME_VREGISTER_SIZE) + ldr x7, [x13] + sub w15, w15, #1 + cbz w15, .Linvoke + + // Unreachable, other handler will be used in case of larger number of argument + b .Linvoke diff --git a/runtime/bridge/arch/aarch64/handle_call_ecma_callirangedyn_pref_imm16_v8_aarch64.S b/runtime/bridge/arch/aarch64/handle_call_ecma_callirangedyn_pref_imm16_v8_aarch64.S new file mode 100644 index 000000000..e04684d37 --- /dev/null +++ b/runtime/bridge/arch/aarch64/handle_call_ecma_callirangedyn_pref_imm16_v8_aarch64.S @@ -0,0 +1,75 @@ +/** + * 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. + */ + +// uint64_t EcmaResolveSecondArgument(uint64_t function_tagged_value); +.extern EcmaResolveSecondArgument + +// handle ecma.callirangedyn +// regs set as follow +// x9 - frame.vregs, x10 - insn_ptr, x12 - method, x13-x15 - temp + + ldrh w14, [x10], #2 + + ldrb w15, [x10], #1 + add x9, x9, x15, lsl #3 // log2(FRAME_VREGISTER_SIZE) + ldr x13, [x9], #FRAME_VREGISTER_SIZE + + // Compute second argument in C++ code + stp x9, x10, [sp, #-16]! + stp x12, x13, [sp, #-16]! + stp x14, x15, [sp, #-16]! + mov x0, x13 + bl EcmaResolveSecondArgument + ldp x14, x15, [sp], #16 + ldp x12, x13, [sp], #16 + ldp x9, x10, [sp], #16 + + // ABI arg reg 1 (x1) <- number of arguments + add x1, x14, #3 // + functional object + new.target + calculated argument + // ABI arg reg 2 (x2) <- functional object + mov x2, x13 + // ABI arg reg 3 (x3) <- new.target (undefined for now) + mov x3, #TAGGED_VALUE_UNDEFINED + // ABI arg reg 4 (x4) <- computed + mov x4, x0 + // ABI arg reg 0 (x0) <- panda::Method* + mov x0, x12 + + // There's at least 4 remaining arguments (number is in w14) + // ABI arg reg 5 (x5) <- boxed arg0 from user code + ldr x5, [x9], #FRAME_VREGISTER_SIZE + sub w14, w14, #1 + + // ABI arg reg 6 (x6) <- boxed arg1 from user code + ldr x6, [x9], #FRAME_VREGISTER_SIZE + sub w14, w14, #1 + + // ABI arg reg 7 (x7) <- boxed arg2 from user code + ldr x7, [x9], #FRAME_VREGISTER_SIZE + sub w14, w14, #1 + + // setup stack args + // reserve stack space + // x13 - stack pointer + sub x13, sp, x14, lsl #4 + mov sp, x13 + // copy arguments to the stack +.Larg_loop_: + ldr x10, [x9], #FRAME_VREGISTER_SIZE + str x10, [x13], #FRAME_VREGISTER_SIZE + sub w14, w14, #1 + cbnz w14, .Larg_loop_ + + b .Linvoke diff --git a/runtime/bridge/arch/aarch64/handle_call_ecma_callithisrangedyn_pref_imm16_v8_aarch64.S b/runtime/bridge/arch/aarch64/handle_call_ecma_callithisrangedyn_pref_imm16_v8_aarch64.S new file mode 100644 index 000000000..4bd364436 --- /dev/null +++ b/runtime/bridge/arch/aarch64/handle_call_ecma_callithisrangedyn_pref_imm16_v8_aarch64.S @@ -0,0 +1,67 @@ +/** + * 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. + */ + +// handle ecma.callithisrangedyn +// regs set as follow +// x9 - frame.vregs, x10 - insn_ptr, x12 - method, x13-x15 - temp + + // ABI arg reg 0 (x0) <- panda::Method* + mov x0, x12 + + ldrh w14, [x10], #2 + // ABI arg reg 1 (x1) <- number of arguments + add x1, x14, #2 // + functional object + new.target + + // ABI arg reg 2 (x2) <- functional object + ldrb w15, [x10], #1 + add x9, x9, x15, lsl #3 // log2(FRAME_VREGISTER_SIZE) + ldr x2, [x9], #FRAME_VREGISTER_SIZE + + // ABI arg reg 3 (x3) <- new.target (undefined for now) + mov x3, #TAGGED_VALUE_UNDEFINED + + // ABI arg reg 4 (x4) <- this + ldr x4, [x9], #FRAME_VREGISTER_SIZE + sub w14, w14, #1 + cbz w14, .Linvoke + + // ABI arg reg 5 (x5) <- boxed arg0 from user code + ldr x5, [x9], #FRAME_VREGISTER_SIZE + sub w14, w14, #1 + cbz w14, .Linvoke + + // ABI arg reg 6 (x6) <- boxed arg1 from user code + ldr x6, [x9], #FRAME_VREGISTER_SIZE + sub w14, w14, #1 + cbz w14, .Linvoke + + // ABI arg reg 7 (x7) <- boxed arg2 from user code + ldr x7, [x9], #FRAME_VREGISTER_SIZE + sub w14, w14, #1 + cbz w14, .Linvoke + + // setup stack args + // reserve stack space + // x13 - stack pointer + sub x13, sp, x14, lsl #4 + mov sp, x13 + // copy arguments to the stack +.Larg_loop: + ldr x10, [x9], #FRAME_VREGISTER_SIZE + str x10, [x13], #FRAME_VREGISTER_SIZE + sub w14, w14, #1 + cbnz w14, .Larg_loop + + b .Linvoke diff --git a/runtime/bridge/arch/amd64/handle_call_ecma_N_v8_amd64.S b/runtime/bridge/arch/amd64/handle_call_ecma_N_v8_amd64.S new file mode 100644 index 000000000..18fed7d08 --- /dev/null +++ b/runtime/bridge/arch/amd64/handle_call_ecma_N_v8_amd64.S @@ -0,0 +1,89 @@ +/** + * 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. + */ + +// uint64_t EcmaResolveSecondArgument(uint64_t function_tagged_value); +.extern EcmaResolveSecondArgument + +// handle ecma.call0dyn, ecma.call1dyn, ecma.call2dyn, ecma.call3dyn +// regs set as follow +// %rax - insn_ptr, %rbx - frame.vregs, %r12 - method, %r15 - number of passed arguments, %r13, %r14 - temp + + movzbl (%rax), %r13d + addq $1, %rax + shlq $3, %r13 // log2(FRAME_VREGISTER_SIZE) + movq (%rbx,%r13), %r13 + + movq %rax, %r14 + movq %r13, %rdi + callq EcmaResolveSecondArgument@plt + + // ABI arg reg 0 (rdi) <- panda::Method* + movq %r12, %rdi + + // ABI arg reg 1 (rsi) <- number of arguments + movq %r15, %rsi + addq $2, %rsi // + new.target + calculated argument + + // ABI arg reg 2 (rdx) <- functional object + movq %r13, %rdx + subq $1, %r15 + + // ABI arg reg 3 (rcx) <- new.target (undefined for now) + movq $TAGGED_VALUE_UNDEFINED, %rcx + + // ABI arg reg 4 (r8) <- computed + movq %rax, %r8 + cmpq $0, %r15 + je .Linvoke + + movq %r14, %rax // restore + + // ABI arg reg 5 (r9) <- boxed arg0 from user code + movzbl (%rax), %r13d + addq $1, %rax + shlq $3, %r13 // log2(FRAME_VREGISTER_SIZE) + movq (%rbx,%r13), %r9 + subq $1, %r15 + jz .Linvoke + + // setup stack args + // reserve stack space + // %r12 - stack pointer + movq %r15, %r12 + shlq $4, %r12 + subq %r12, %rsp + movq %rsp, %r12 + + // ABI arg stack <- boxed arg1 from user code + movzbl (%rax), %r13d + addq $1, %rax + shlq $3, %r13 // log2(FRAME_VREGISTER_SIZE) + movq (%rbx,%r13), %r13 + movq %r13, (%r12) + subq $1, %r15 + jz .Linvoke + + // ABI arg stack <- boxed arg2 from user code + addq $FRAME_VREGISTER_SIZE, %r12 + movzbl (%rax), %r13d + addq $1, %rax + shlq $3, %r13 // log2(FRAME_VREGISTER_SIZE) + movq (%rbx,%r13), %r13 + movq %r13, (%r12) + subq $1, %r15 + jz .Linvoke + + // Unreachable, other handler will be used in case of larger number of argument + jmp .Linvoke diff --git a/runtime/bridge/arch/amd64/handle_call_ecma_callirangedyn_pref_imm16_v8_amd64.S b/runtime/bridge/arch/amd64/handle_call_ecma_callirangedyn_pref_imm16_v8_amd64.S new file mode 100644 index 000000000..3ddb0b27e --- /dev/null +++ b/runtime/bridge/arch/amd64/handle_call_ecma_callirangedyn_pref_imm16_v8_amd64.S @@ -0,0 +1,73 @@ +/** + * 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. + */ + +// uint64_t EcmaResolveSecondArgument(uint64_t function_tagged_value); +.extern EcmaResolveSecondArgument + +// handle ecma.callirangedyn +// regs set as follow +// %rax - insn_ptr, %rbx - frame.vregs, %r12 - method, %r13, %r14 - temp + + movzwl (%rax), %r14d + addq $2, %rax + + movzbl (%rax), %r13d + addq $1, %rax + shlq $3, %r13 // log2(FRAME_VREGISTER_SIZE) + addq %r13, %rbx + movq (%rbx), %r13 + addq $FRAME_VREGISTER_SIZE, %rbx + + // Compute second argument in C++ code + movq %r13, %rdi + callq EcmaResolveSecondArgument@plt + + // ABI arg reg 0 (rdi) <- panda::Method* + movq %r12, %rdi + // ABI arg reg 1 (rsi) <- number of arguments + movq %r14, %rsi + addq $3, %rsi // + functional object + new.target + calculated argument + + // ABI arg reg 2 (rdx) <- functional object + movq %r13, %rdx + + // ABI arg reg 3 (rcx) <- new.target (undefined for now) + movq $TAGGED_VALUE_UNDEFINED, %rcx + + // ABI arg reg 4 (r8) <- computed + movq %rax, %r8 + + // There's at least 4 remaining arguments (number is in %r14d) + movq (%rbx), %r9 + addq $FRAME_VREGISTER_SIZE, %rbx + subl $1, %r14d + + // setup stack args + // reserve stack space + // %r12 - stack pointer + movl %r14d, %r12d + shll $4, %r12d + subq %r12, %rsp + movq %rsp, %r12 + +.Larg_loop_: + movq (%rbx), %r13 + movq %r13, (%r12) + addq $FRAME_VREGISTER_SIZE, %rbx + addq $FRAME_VREGISTER_SIZE, %r12 + subl $1, %r14d + ja .Larg_loop_ + + jmp .Linvoke diff --git a/runtime/bridge/arch/amd64/handle_call_ecma_callithisrangedyn_pref_imm16_v8_amd64.S b/runtime/bridge/arch/amd64/handle_call_ecma_callithisrangedyn_pref_imm16_v8_amd64.S new file mode 100644 index 000000000..9327c0640 --- /dev/null +++ b/runtime/bridge/arch/amd64/handle_call_ecma_callithisrangedyn_pref_imm16_v8_amd64.S @@ -0,0 +1,69 @@ +/** + * 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. + */ + +// handle ecma.callithisrangedyn +// regs set as follow +// %rax - insn_ptr, %rbx - frame.vregs, %r12 - method, %r13, %r14, %r15 - temp + + // ABI arg reg 0 (rdi) <- panda::Method* + movq %r12, %rdi + + movzwl (%rax), %r14d + addq $2, %rax + + // ABI arg reg 1 (rsi) <- number of arguments + movq %r14, %rsi + addq $2, %rsi // + functional object + new.target + + // ABI arg reg 2 (rdx) <- functional object + movzbl (%rax), %r13d + addq $1, %rax + shlq $3, %r13 // log2(FRAME_VREGISTER_SIZE) + addq %r13, %rbx + movq (%rbx), %rdx + addq $FRAME_VREGISTER_SIZE, %rbx + + // ABI arg reg 3 (rcx) <- new.target (undefined for now) + movq $TAGGED_VALUE_UNDEFINED, %rcx + + // ABI arg reg 4 (r8) <- this + movq (%rbx), %r8 + addq $FRAME_VREGISTER_SIZE, %rbx + subl $1, %r14d + jz .Linvoke + + // ABI arg reg 5 (r9) <- boxed arg0 from user code + movq (%rbx), %r9 + addq $FRAME_VREGISTER_SIZE, %rbx + subl $1, %r14d + jz .Linvoke + + // setup stack args + // reserve stack space + // %r12 - stack pointer + movl %r14d, %r12d + shll $4, %r12d + subq %r12, %rsp + movq %rsp, %r12 + + .Larg_loop: + movq (%rbx), %r13 + movq %r13, (%r12) + addq $FRAME_VREGISTER_SIZE, %r12 + addq $FRAME_VREGISTER_SIZE, %rbx + subl $1, %r14d + ja .Larg_loop + + jmp .Linvoke diff --git a/runtime/bridge/arch/arm/handle_call_ecma_N_v8_arm.S b/runtime/bridge/arch/arm/handle_call_ecma_N_v8_arm.S new file mode 100644 index 000000000..93b017e51 --- /dev/null +++ b/runtime/bridge/arch/arm/handle_call_ecma_N_v8_arm.S @@ -0,0 +1,16 @@ +/** + * 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. + */ + +// The bridge is not supported currently diff --git a/runtime/bridge/arch/arm/handle_call_ecma_callirangedyn_pref_imm16_v8_arm.S b/runtime/bridge/arch/arm/handle_call_ecma_callirangedyn_pref_imm16_v8_arm.S new file mode 100644 index 000000000..93b017e51 --- /dev/null +++ b/runtime/bridge/arch/arm/handle_call_ecma_callirangedyn_pref_imm16_v8_arm.S @@ -0,0 +1,16 @@ +/** + * 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. + */ + +// The bridge is not supported currently diff --git a/runtime/bridge/arch/arm/handle_call_ecma_callithisrangedyn_pref_imm16_v8_arm.S b/runtime/bridge/arch/arm/handle_call_ecma_callithisrangedyn_pref_imm16_v8_arm.S new file mode 100644 index 000000000..93b017e51 --- /dev/null +++ b/runtime/bridge/arch/arm/handle_call_ecma_callithisrangedyn_pref_imm16_v8_arm.S @@ -0,0 +1,16 @@ +/** + * 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. + */ + +// The bridge is not supported currently diff --git a/runtime/bridge/ecma_bridge_helpers.cpp b/runtime/bridge/ecma_bridge_helpers.cpp new file mode 100644 index 000000000..210e3fb92 --- /dev/null +++ b/runtime/bridge/ecma_bridge_helpers.cpp @@ -0,0 +1,17 @@ +/** + * Copyright (c) Huawei Technologies Co., Ltd. 2019-2021. All rights reserved. + */ + +#include "plugins/ecmascript/runtime/global_env.h" +#include "plugins/ecmascript/compiler/ecmascript_extensions/thread_environment_api.h" + +namespace panda::ecmascript { +extern "C" uint64_t EcmaResolveSecondArgument(uint64_t function_tagged_value) +{ + JSTaggedValue js_func(function_tagged_value); + if (js_func.IsJSFunction() && !JSFunction::Cast(js_func.GetHeapObject())->IsStrict()) { + return JSThread::GetCurrentRaw()->GetEcmaVM()->GetGlobalEnv()->GetGlobalObject().GetRawData(); + } + return JSTaggedValue::Undefined().GetRawData(); +} +} // namespace panda::ecmascript diff --git a/runtime/builtins.cpp b/runtime/builtins.cpp new file mode 100644 index 000000000..123b8456d --- /dev/null +++ b/runtime/builtins.cpp @@ -0,0 +1,3054 @@ +/* + * Copyright (c) 2021-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 "plugins/ecmascript/runtime/builtins.h" + +#include "ecma_vm.h" +#include "plugins/ecmascript/runtime/base/error_type.h" +#include "plugins/ecmascript/runtime/base/number_helper.h" +#include "plugins/ecmascript/runtime/builtins/builtins_ark_tools.h" +#include "plugins/ecmascript/runtime/builtins/builtins_array.h" +#include "plugins/ecmascript/runtime/builtins/builtins_arraybuffer.h" +#include "plugins/ecmascript/runtime/builtins/builtins_async_from_sync_iterator.h" +#include "plugins/ecmascript/runtime/builtins/builtins_async_function.h" +#include "plugins/ecmascript/runtime/builtins/builtins_async_generator.h" +#include "plugins/ecmascript/runtime/builtins/builtins_async_iterator.h" +#include "plugins/ecmascript/runtime/builtins/builtins_boolean.h" +#include "plugins/ecmascript/runtime/builtins/builtins_collator.h" +#include "plugins/ecmascript/runtime/builtins/builtins_dataview.h" +#include "plugins/ecmascript/runtime/builtins/builtins_date.h" +#include "plugins/ecmascript/runtime/builtins/builtins_date_time_format.h" +#include "plugins/ecmascript/runtime/builtins/builtins_errors.h" +#include "plugins/ecmascript/runtime/builtins/builtins_function.h" +#include "plugins/ecmascript/runtime/builtins/builtins_generator.h" +#include "plugins/ecmascript/runtime/builtins/builtins_global.h" +#include "plugins/ecmascript/runtime/builtins/builtins_intl.h" +#include "plugins/ecmascript/runtime/builtins/builtins_iterator.h" +#include "plugins/ecmascript/runtime/builtins/builtins_json.h" +#include "plugins/ecmascript/runtime/builtins/builtins_locale.h" +#include "plugins/ecmascript/runtime/builtins/builtins_map.h" +#include "plugins/ecmascript/runtime/builtins/builtins_math.h" +#include "plugins/ecmascript/runtime/builtins/builtins_number.h" +#include "plugins/ecmascript/runtime/builtins/builtins_number_format.h" +#include "plugins/ecmascript/runtime/builtins/builtins_object.h" +#include "plugins/ecmascript/runtime/builtins/builtins_plural_rules.h" +#include "plugins/ecmascript/runtime/builtins/builtins_promise.h" +#include "plugins/ecmascript/runtime/builtins/builtins_promise_handler.h" +#include "plugins/ecmascript/runtime/builtins/builtins_promise_job.h" +#include "plugins/ecmascript/runtime/builtins/builtins_proxy.h" +#include "plugins/ecmascript/runtime/builtins/builtins_reflect.h" +#include "plugins/ecmascript/runtime/builtins/builtins_regexp.h" +#include "plugins/ecmascript/runtime/builtins/builtins_relative_time_format.h" +#include "plugins/ecmascript/runtime/builtins/builtins_set.h" +#include "plugins/ecmascript/runtime/builtins/builtins_string.h" +#include "plugins/ecmascript/runtime/builtins/builtins_string_iterator.h" +#include "plugins/ecmascript/runtime/builtins/builtins_symbol.h" +#include "plugins/ecmascript/runtime/builtins/builtins_typedarray.h" +#include "plugins/ecmascript/runtime/builtins/builtins_weak_map.h" +#include "plugins/ecmascript/runtime/builtins/builtins_weak_set.h" +#include "plugins/ecmascript/runtime/containers/containers_private.h" +#include "plugins/ecmascript/runtime/ecma_runtime_call_info.h" +#include "plugins/ecmascript/runtime/js_array.h" +#include "plugins/ecmascript/runtime/js_arraybuffer.h" +#include "plugins/ecmascript/runtime/js_arraylist.h" +#include "plugins/ecmascript/runtime/js_array_iterator.h" +#include "plugins/ecmascript/runtime/js_async_from_sync_iterator_object.h" +#include "plugins/ecmascript/runtime/js_async_function.h" +#include "plugins/ecmascript/runtime/js_async_generator_object.h" +#include "plugins/ecmascript/runtime/js_collator.h" +#include "plugins/ecmascript/runtime/js_dataview.h" +#include "plugins/ecmascript/runtime/js_date_time_format.h" +#include "plugins/ecmascript/runtime/js_for_in_iterator.h" +#include "plugins/ecmascript/runtime/js_function.h" +#include "plugins/ecmascript/runtime/js_handle.h" +#include "plugins/ecmascript/runtime/js_hclass.h" +#include "plugins/ecmascript/runtime/js_locale.h" +#include "plugins/ecmascript/runtime/js_map.h" +#include "plugins/ecmascript/runtime/js_map_iterator.h" +#include "plugins/ecmascript/runtime/js_number_format.h" +#include "plugins/ecmascript/runtime/js_plural_rules.h" +#include "plugins/ecmascript/runtime/js_primitive_ref.h" +#include "plugins/ecmascript/runtime/js_promise.h" +#include "plugins/ecmascript/runtime/js_regexp.h" +#include "plugins/ecmascript/runtime/js_relative_time_format.h" +#include "plugins/ecmascript/runtime/js_runtime_options.h" +#include "plugins/ecmascript/runtime/js_set.h" +#include "plugins/ecmascript/runtime/js_set_iterator.h" +#include "plugins/ecmascript/runtime/js_string_iterator.h" +#include "plugins/ecmascript/runtime/js_tagged_value.h" +#include "plugins/ecmascript/runtime/js_typed_array.h" +#include "plugins/ecmascript/runtime/js_weak_container.h" +#include "plugins/ecmascript/runtime/mem/mem.h" +#include "plugins/ecmascript/runtime/object_factory.h" +#include "ohos/init_data.h" + +namespace panda::ecmascript { +using Number = builtins::BuiltinsNumber; +using Object = builtins::BuiltinsObject; +using Date = builtins::BuiltinsDate; +using Symbol = builtins::BuiltinsSymbol; +using Boolean = builtins::BuiltinsBoolean; +using BuiltinsMap = builtins::BuiltinsMap; +using BuiltinsSet = builtins::BuiltinsSet; +using BuiltinsWeakMap = builtins::BuiltinsWeakMap; +using BuiltinsWeakSet = builtins::BuiltinsWeakSet; +using BuiltinsArray = builtins::BuiltinsArray; +using BuiltinsTypedArray = builtins::BuiltinsTypedArray; +using BuiltinsIterator = builtins::BuiltinsIterator; +using BuiltinsAsyncIterator = builtins::BuiltinsAsyncIterator; + +using Error = builtins::BuiltinsError; +using RangeError = builtins::BuiltinsRangeError; +using ReferenceError = builtins::BuiltinsReferenceError; +using TypeError = builtins::BuiltinsTypeError; +using URIError = builtins::BuiltinsURIError; +using SyntaxError = builtins::BuiltinsSyntaxError; +using EvalError = builtins::BuiltinsEvalError; +using ErrorType = base::ErrorType; +using Global = builtins::BuiltinsGlobal; +using BuiltinsString = builtins::BuiltinsString; +using StringIterator = builtins::BuiltinsStringIterator; +using RegExp = builtins::BuiltinsRegExp; +using Function = builtins::BuiltinsFunction; +using Math = builtins::BuiltinsMath; +using ArrayBuffer = builtins::BuiltinsArrayBuffer; +using Json = builtins::BuiltinsJson; +using Proxy = builtins::BuiltinsProxy; +using Reflect = builtins::BuiltinsReflect; +using AsyncFunction = builtins::BuiltinsAsyncFunction; +using GeneratorObject = builtins::BuiltinsGenerator; +using AsyncGeneratorObject = builtins::BuiltinsAsyncGenerator; +using AsyncFromSyncIterator = builtins::BuiltinsAsyncFromSyncIterator; +using Promise = builtins::BuiltinsPromise; +using BuiltinsPromiseHandler = builtins::BuiltinsPromiseHandler; +using BuiltinsPromiseJob = builtins::BuiltinsPromiseJob; +using ErrorType = base::ErrorType; +using DataView = builtins::BuiltinsDataView; +using Intl = builtins::BuiltinsIntl; +using Locale = builtins::BuiltinsLocale; +using DateTimeFormat = builtins::BuiltinsDateTimeFormat; +using RelativeTimeFormat = builtins::BuiltinsRelativeTimeFormat; +using NumberFormat = builtins::BuiltinsNumberFormat; +using Collator = builtins::BuiltinsCollator; +using PluralRules = builtins::BuiltinsPluralRules; +using ContainersPrivate = containers::ContainersPrivate; + +void Builtins::Initialize(const JSHandle &env, JSThread *thread) +{ + thread_ = thread; + vm_ = thread->GetEcmaVM(); + factory_ = vm_->GetFactory(); + [[maybe_unused]] EcmaHandleScope scope(thread_); + JSHandle nullHandle(thread, JSTaggedValue::Null()); + + // Object.prototype[dynclass] + JSHandle objPrototypeDynclass = factory_->NewEcmaDynClass(JSObject::SIZE, JSType::JS_OBJECT, nullHandle); + + // Object.prototype + JSHandle objFuncPrototype = factory_->NewJSObject(objPrototypeDynclass); + JSHandle objFuncPrototypeVal(objFuncPrototype); + + // Object.prototype_or_dynclass + JSHandle objFuncDynclass = + factory_->NewEcmaDynClass(JSObject::SIZE, JSType::JS_OBJECT, objFuncPrototypeVal); + + // GLobalObject.prototype_or_dynclass + JSHandle globalObjFuncDynclass = factory_->NewEcmaDynClass(JSObject::SIZE, JSType::JS_GLOBAL_OBJECT, 0); + globalObjFuncDynclass->SetPrototype(thread_, objFuncPrototypeVal.GetTaggedValue()); + globalObjFuncDynclass->SetIsDictionaryMode(true); + // Function.prototype_or_dynclass + JSHandle emptyFuncDynclass( + factory_->NewEcmaDynClass(JSFunction::SIZE, JSType::JS_FUNCTION, objFuncPrototypeVal, HClass::IS_CALLABLE)); + emptyFuncDynclass->GetHClass()->MarkFieldAsNative(JSFunction::METHOD_OFFSET); + emptyFuncDynclass->SetExtensible(true); + + // PrimitiveRef.prototype_or_dynclass + JSHandle primRefObjDynclass = + factory_->NewEcmaDynClass(JSPrimitiveRef::SIZE, JSType::JS_PRIMITIVE_REF, objFuncPrototypeVal); + + // init global object + JSHandle globalObject = factory_->NewNonMovableJSObject(globalObjFuncDynclass); + JSHandle newGlobalDynclass = JSHClass::Clone(thread_, globalObjFuncDynclass); + globalObject->SetClass(newGlobalDynclass); + env->SetJSGlobalObject(thread_, globalObject); + + // initialize Function, forbidden change order + InitializeFunction(env, emptyFuncDynclass); + + JSHandle objFuncInstancePrototype = factory_->NewJSObject(objFuncDynclass); + JSHandle objFuncInstancePrototypeValue(objFuncInstancePrototype); + JSHandle asyncFuncClass = factory_->CreateFunctionClass( + FunctionKind::ASYNC_FUNCTION, JSAsyncFunction::SIZE, JSType::JS_ASYNC_FUNCTION, objFuncInstancePrototypeValue); + asyncFuncClass->SetExtensible(true); + env->SetAsyncFunctionClass(thread_, asyncFuncClass); + + JSHandle asyncAwaitStatusFuncClass = + factory_->CreateFunctionClass(FunctionKind::NORMAL_FUNCTION, JSAsyncAwaitStatusFunction::SIZE, + JSType::JS_ASYNC_AWAIT_STATUS_FUNCTION, env->GetFunctionPrototype()); + env->SetAsyncAwaitStatusFunctionClass(thread_, asyncAwaitStatusFuncClass); + + JSHandle asyncGeneratorResolveNextFuncClass = factory_->NewEcmaDynClass( + JSAsyncGeneratorResolveNextFunction::SIZE, JSType::JS_ASYNC_GENERATOR_RESOLVE_NEXT_FUNCTION, + env->GetFunctionPrototype(), HClass::IS_CALLABLE); + asyncGeneratorResolveNextFuncClass->SetExtensible(true); + asyncGeneratorResolveNextFuncClass->GetHClass()->MarkFieldAsNative( + JSAsyncGeneratorResolveNextFunction::METHOD_OFFSET); + env->SetAsyncGeneratorResolveNextFunctionClass(thread_, asyncGeneratorResolveNextFuncClass); + + JSHandle asyncFromSyncIteratorValueUnwrapFuncClass = factory_->NewEcmaDynClass( + JSAsyncFromSyncIteratorValueUnwrapFunction::SIZE, JSType::JS_ASYNC_FROM_SYNC_ITERATOR_VALUE_UNWRAP_FUNCTION, + env->GetFunctionPrototype(), HClass::IS_CALLABLE); + asyncFromSyncIteratorValueUnwrapFuncClass->SetExtensible(true); + asyncFromSyncIteratorValueUnwrapFuncClass->GetHClass()->MarkFieldAsNative( + JSAsyncFromSyncIteratorValueUnwrapFunction::METHOD_OFFSET); + env->SetAsyncFromSyncIteratorValueUnwrapFunctionClass(thread_, asyncFromSyncIteratorValueUnwrapFuncClass); + + JSHandle promiseReactionFuncClass = + factory_->NewEcmaDynClass(JSPromiseReactionsFunction::SIZE, JSType::JS_PROMISE_REACTIONS_FUNCTION, + env->GetFunctionPrototype(), HClass::IS_CALLABLE); + promiseReactionFuncClass->SetExtensible(true); + promiseReactionFuncClass->GetHClass()->MarkFieldAsNative(JSPromiseReactionsFunction::METHOD_OFFSET); + env->SetPromiseReactionFunctionClass(thread_, promiseReactionFuncClass); + + JSHandle promiseExecutorFuncClass = + factory_->NewEcmaDynClass(JSPromiseExecutorFunction::SIZE, JSType::JS_PROMISE_EXECUTOR_FUNCTION, + env->GetFunctionPrototype(), HClass::IS_CALLABLE); + promiseExecutorFuncClass->SetExtensible(true); + promiseExecutorFuncClass->GetHClass()->MarkFieldAsNative(JSPromiseExecutorFunction::METHOD_OFFSET); + env->SetPromiseExecutorFunctionClass(thread_, promiseExecutorFuncClass); + + JSHandle promiseAllResolveElementFunctionClass = factory_->NewEcmaDynClass( + JSPromiseAllResolveElementFunction::SIZE, JSType::JS_PROMISE_ALL_RESOLVE_ELEMENT_FUNCTION, + env->GetFunctionPrototype(), HClass::IS_CALLABLE); + promiseAllResolveElementFunctionClass->SetExtensible(true); + promiseAllResolveElementFunctionClass->GetHClass()->MarkFieldAsNative( + JSPromiseAllResolveElementFunction::METHOD_OFFSET); + env->SetPromiseAllResolveElementFunctionClass(thread_, promiseAllResolveElementFunctionClass); + + JSHandle proxyRevocFuncClass = factory_->NewEcmaDynClass( + JSProxyRevocFunction::SIZE, JSType::JS_PROXY_REVOC_FUNCTION, env->GetFunctionPrototype(), HClass::IS_CALLABLE); + proxyRevocFuncClass->SetExtensible(true); + proxyRevocFuncClass->GetHClass()->MarkFieldAsNative(JSProxyRevocFunction::METHOD_OFFSET); + env->SetProxyRevocFunctionClass(thread_, proxyRevocFuncClass); + + // Object = new Function() + JSHandle objectFunction( + NewBuiltinConstructor(env, objFuncPrototype, Object::ObjectConstructor, "Object", FunctionLength::ONE)); + ASSERT(objectFunction->IsBuiltinsConstructor()); + objectFunction.GetObject()->SetFunctionPrototype(thread_, objFuncDynclass.GetTaggedValue()); + // initialize object method. + env->SetObjectFunction(thread_, objectFunction); + env->SetObjectFunctionPrototype(thread_, objFuncPrototype); + + JSHandle functionClass = factory_->CreateFunctionClass(FunctionKind::BASE_CONSTRUCTOR, JSFunction::SIZE, + JSType::JS_FUNCTION, env->GetFunctionPrototype()); + env->SetFunctionClassWithProto(thread_, functionClass); + functionClass = factory_->CreateFunctionClass(FunctionKind::NORMAL_FUNCTION, JSFunction::SIZE, JSType::JS_FUNCTION, + env->GetFunctionPrototype()); + env->SetFunctionClassWithoutProto(thread_, functionClass); + functionClass = factory_->CreateFunctionClass(FunctionKind::CLASS_CONSTRUCTOR, JSFunction::SIZE, + JSType::JS_FUNCTION, env->GetFunctionPrototype()); + env->SetFunctionClassWithoutName(thread_, functionClass); + + if (env == vm_->GetGlobalEnv()) { + InitializeAllTypeError(env, objFuncDynclass); + InitializeSymbol(env, primRefObjDynclass); + } else { + // error and symbol need to be shared when initialize realm + InitializeAllTypeErrorWithRealm(env); + InitializeSymbolWithRealm(env, primRefObjDynclass); + } + + InitializeNumber(env, globalObject, primRefObjDynclass); + InitializeDate(env, objFuncDynclass); + InitializeObject(env, objFuncPrototype, objectFunction); + InitializeBoolean(env, primRefObjDynclass); + + InitializeRegExp(env); + InitializeSet(env, objFuncDynclass); + InitializeMap(env, objFuncDynclass); + InitializeWeakMap(env, objFuncDynclass); + InitializeWeakSet(env, objFuncDynclass); + InitializeArray(env, objFuncPrototypeVal); + InitializeTypedArray(env, objFuncDynclass); + InitializeString(env, primRefObjDynclass); + InitializeArrayBuffer(env, objFuncDynclass); + InitializeDataView(env, objFuncDynclass); + + JSHandle argumentsDynclass = factory_->CreateJSArguments(); + env->SetArgumentsClass(thread_, argumentsDynclass); + SetArgumentsSharedAccessor(env); + + InitializeGlobalObject(env, globalObject); + InitializeMath(env, objFuncPrototypeVal); + InitializeJson(env, objFuncPrototypeVal); + InitializeIterator(env, objFuncDynclass); + InitializeAsyncIterator(env, objFuncDynclass); + InitializeProxy(env); + InitializeReflect(env, objFuncPrototypeVal); + InitializeAsyncFunction(env, objFuncDynclass); + InitializeGenerator(env, objFuncDynclass); + InitializeGeneratorFunction(env, objFuncDynclass); + InitializeAsyncGenerator(env, objFuncDynclass); + InitializeAsyncGeneratorFunction(env, objFuncDynclass); + InitializeAsyncFromSyncIteratorPrototypeObject(env, objFuncDynclass); + InitializePromise(env, objFuncDynclass); + InitializePromiseJob(env); + + JSRuntimeOptions options = JSRuntimeOptions::Cast(vm_->GetOptions()); + std::string icuPath = options.GetIcuDataPath(); + if (icuPath == "default") { + SetHwIcuDirectory(); + } else { + u_setDataDirectory(icuPath.c_str()); + } + InitializeIntl(env, objFuncPrototypeVal); + InitializeLocale(env); + InitializeDateTimeFormat(env); + InitializeNumberFormat(env); + InitializeRelativeTimeFormat(env); + InitializeCollator(env); + InitializePluralRules(env); + + JSHandle generatorFuncClass = + factory_->CreateFunctionClass(FunctionKind::GENERATOR_FUNCTION, JSFunction::SIZE, JSType::JS_GENERATOR_FUNCTION, + env->GetGeneratorFunctionPrototype()); + env->SetGeneratorFunctionClass(thread_, generatorFuncClass); + + JSHandle asyncGeneratorFuncClass = + factory_->CreateFunctionClass(FunctionKind::ASYNC_GENERATOR_FUNCTION, JSFunction::SIZE, + JSType::JS_ASYNC_GENERATOR_FUNCTION, env->GetAsyncGeneratorFunctionPrototype()); + env->SetAsyncGeneratorFunctionClass(thread_, asyncGeneratorFuncClass); + + env->SetObjectFunctionPrototypeClass(thread_, JSTaggedValue(objFuncPrototype->GetClass())); + thread_->ResetGuardians(); + +#ifdef FUZZING_FUZZILLI_BUILTIN + InitializeFuzzilli(env, objFuncDynclass); +#endif +} + +void Builtins::InitializeGlobalObject(const JSHandle &env, const JSHandle &globalObject) +{ + [[maybe_unused]] EcmaHandleScope scope(thread_); + + // Global object test + SetFunction(env, globalObject, "print", Global::PrintEntrypoint, 0); +#if ECMASCRIPT_ENABLE_RUNTIME_STAT + SetFunction(env, globalObject, "startRuntimeStat", Global::StartRuntimeStat, 0); + SetFunction(env, globalObject, "stopRuntimeStat", Global::StopRuntimeStat, 0); +#endif + + JSRuntimeOptions options = JSRuntimeOptions::Cast(vm_->GetOptions()); + if (options.IsEnableArkTools()) { + JSHandle arkTools(InitializeArkTools(env)); + SetConstantObject(globalObject, "ArkTools", arkTools); + } + +#if ECMASCRIPT_ENABLE_ARK_CONTAINER + // Set ArkPrivate + JSHandle arkPrivate(InitializeArkPrivate(env)); + SetConstantObject(globalObject, "ArkPrivate", arkPrivate); +#endif + + // Global object function + SetFunction(env, globalObject, "eval", Global::NotSupportEval, FunctionLength::ONE); + SetFunction(env, globalObject, "isFinite", Global::IsFinite, FunctionLength::ONE); + SetFunction(env, globalObject, "isNaN", Global::IsNaN, FunctionLength::ONE); + SetFunction(env, globalObject, "decodeURI", Global::DecodeURI, FunctionLength::ONE); + SetFunction(env, globalObject, "encodeURI", Global::EncodeURI, FunctionLength::ONE); + SetFunction(env, globalObject, "decodeURIComponent", Global::DecodeURIComponent, FunctionLength::ONE); + SetFunction(env, globalObject, "encodeURIComponent", Global::EncodeURIComponent, FunctionLength::ONE); + + // Global object property + SetGlobalThis(globalObject, "globalThis", JSHandle::Cast(globalObject)); + SetConstant(globalObject, "Infinity", JSTaggedValue(base::POSITIVE_INFINITY)); + SetConstant(globalObject, "NaN", JSTaggedValue(base::NAN_VALUE)); + SetConstant(globalObject, "undefined", JSTaggedValue::Undefined()); +} + +void Builtins::InitializeFunction(const JSHandle &env, const JSHandle &emptyFuncDynclass) const +{ + [[maybe_unused]] EcmaHandleScope scope(thread_); + // Initialize Function.prototype + JSMethod *invokeSelf = + vm_->GetMethodForNativeFunction(reinterpret_cast(Function::FunctionPrototypeInvokeSelf)); + JSHandle funcFuncPrototype = factory_->NewJSFunctionByDynClass(invokeSelf, emptyFuncDynclass); + // ecma 19.2.3 The value of the name property of the Function prototype object is the empty String. + JSHandle emptyString(thread_->GlobalConstants()->GetHandledEmptyString()); + JSHandle undefinedString(thread_, JSTaggedValue::Undefined()); + JSFunction::SetFunctionName(thread_, JSHandle(funcFuncPrototype), emptyString, undefinedString); + // ecma 19.2.3 The value of the length property of the Function prototype object is 0. + JSFunction::SetFunctionLength(thread_, funcFuncPrototype, JSTaggedValue(FunctionLength::ZERO)); + + JSHandle funcFuncPrototypeValue(funcFuncPrototype); + // Function.prototype_or_dynclass + JSHandle funcFuncIntanceDynclass = factory_->NewEcmaDynClass( + JSFunction::SIZE, JSType::JS_FUNCTION, funcFuncPrototypeValue, HClass::IS_CALLABLE | HClass::IS_BUILTINS_CTOR); + funcFuncIntanceDynclass->SetConstructor(true); + funcFuncIntanceDynclass->GetHClass()->MarkFieldAsNative(JSFunction::METHOD_OFFSET); + + // Function = new Function() (forbidden use NewBuiltinConstructor) + JSMethod *ctor = vm_->GetMethodForNativeFunction(reinterpret_cast(Function::FunctionConstructor)); + JSHandle funcFunc = + factory_->NewJSFunctionByDynClass(ctor, funcFuncIntanceDynclass, FunctionKind::BUILTIN_CONSTRUCTOR); + + auto funcFuncPrototypeObj = JSHandle(funcFuncPrototype); + InitializeCtor(env, funcFuncPrototypeObj, funcFunc, "Function", FunctionLength::ONE); + + funcFunc->SetFunctionPrototype(thread_, funcFuncIntanceDynclass.GetTaggedValue()); + env->SetFunctionFunction(thread_, funcFunc); + env->SetFunctionPrototype(thread_, funcFuncPrototype); + + JSHandle normalFuncClass = factory_->NewEcmaDynClass(JSFunction::SIZE, JSType::JS_FUNCTION, + env->GetFunctionPrototype(), HClass::IS_CALLABLE); + normalFuncClass->GetHClass()->MarkFieldAsNative(JSFunction::METHOD_OFFSET); + env->SetNormalFunctionClass(thread_, normalFuncClass); + + JSHandle jSIntlBoundFunctionClass = + factory_->CreateFunctionClass(FunctionKind::NORMAL_FUNCTION, JSIntlBoundFunction::SIZE, + JSType::JS_INTL_BOUND_FUNCTION, env->GetFunctionPrototype()); + jSIntlBoundFunctionClass->GetHClass()->MarkFieldAsNative(JSFunction::METHOD_OFFSET); + env->SetJSIntlBoundFunctionClass(thread_, jSIntlBoundFunctionClass); + + JSHandle constructorFunctionClass = + factory_->NewEcmaDynClass(JSFunction::SIZE, JSType::JS_FUNCTION, env->GetFunctionPrototype(), + HClass::IS_CALLABLE | HClass::IS_BUILTINS_CTOR); + constructorFunctionClass->SetConstructor(true); + constructorFunctionClass->GetHClass()->MarkFieldAsNative(JSFunction::METHOD_OFFSET); + env->SetConstructorFunctionClass(thread_, constructorFunctionClass); + + StrictModeForbiddenAccessCallerArguments(env, funcFuncPrototypeObj); + + // Function.prototype method + // 19.2.3.1 Function.prototype.apply ( thisArg, argArray ) + SetFunction(env, funcFuncPrototypeObj, "apply", Function::FunctionPrototypeApply, FunctionLength::TWO); + // 19.2.3.2 Function.prototype.bind ( thisArg , ...args) + SetFunction(env, funcFuncPrototypeObj, "bind", Function::FunctionPrototypeBind, FunctionLength::ONE); + // 19.2.3.3 Function.prototype.call (thisArg , ...args) + SetFunction(env, funcFuncPrototypeObj, "call", Function::FunctionPrototypeCall, FunctionLength::ONE); + // 19.2.3.5 Function.prototype.toString ( ) + SetFunction(env, funcFuncPrototypeObj, thread_->GlobalConstants()->GetHandledToStringString(), + Function::FunctionPrototypeToString, FunctionLength::ZERO); +} + +void Builtins::InitializeObject(const JSHandle &env, const JSHandle &objFuncPrototype, + const JSHandle &objFunc) +{ + [[maybe_unused]] EcmaHandleScope scope(thread_); + // Object method. + // 19.1.2.1Object.assign ( target, ...sources ) + SetFunction(env, objFunc, "assign", Object::Assign, FunctionLength::TWO); + // 19.1.2.2Object.create ( O [ , Properties ] ) + SetFunction(env, objFunc, "create", Object::Create, FunctionLength::TWO); + // 19.1.2.3Object.defineProperties ( O, Properties ) + SetFunction(env, objFunc, "defineProperties", Object::DefineProperties, FunctionLength::TWO); + // 19.1.2.4Object.defineProperty ( O, P, Attributes ) + SetFunction(env, objFunc, "defineProperty", Object::DefineProperty, FunctionLength::THREE); + // 19.1.2.5Object.freeze ( O ) + SetFunction(env, objFunc, "freeze", Object::Freeze, FunctionLength::ONE); + // ES2021 20.1.2.7Object.fromEntries ( iterable ) + SetFunction(env, objFunc, "fromEntries", Object::FromEntries, FunctionLength::ONE); + // 19.1.2.6Object.getOwnPropertyDescriptor ( O, P ) + SetFunction(env, objFunc, "getOwnPropertyDescriptor", Object::GetOwnPropertyDesciptor, FunctionLength::TWO); + // ES2021 20.1.2.9 Object.getOwnPropertyDescriptors ( O ) + SetFunction(env, objFunc, "getOwnPropertyDescriptors", Object::GetOwnPropertyDesciptors, FunctionLength::ONE); + // 19.1.2.7Object.getOwnPropertyNames ( O ) + SetFunction(env, objFunc, "getOwnPropertyNames", Object::GetOwnPropertyNames, FunctionLength::ONE); + // 19.1.2.8Object.getOwnPropertySymbols ( O ) + SetFunction(env, objFunc, "getOwnPropertySymbols", Object::GetOwnPropertySymbols, FunctionLength::ONE); + // 19.1.2.9Object.getPrototypeOf ( O ) + SetFunction(env, objFunc, "getPrototypeOf", Object::GetPrototypeOf, FunctionLength::ONE); + // 19.1.2.10Object.is ( value1, value2 ) + SetFunction(env, objFunc, "is", Object::Is, 2); + // 19.1.2.11Object.isExtensible ( O ) + SetFunction(env, objFunc, "isExtensible", Object::IsExtensible, FunctionLength::ONE); + // 19.1.2.12Object.isFrozen ( O ) + SetFunction(env, objFunc, "isFrozen", Object::IsFrozen, FunctionLength::ONE); + // 19.1.2.13Object.isSealed ( O ) + SetFunction(env, objFunc, "isSealed", Object::IsSealed, FunctionLength::ONE); + // 19.1.2.14 Object.keys(O) + SetFunction(env, objFunc, "keys", Object::Keys, FunctionLength::ONE); + // 19.1.2.15 Object.preventExtensions(O) + SetFunction(env, objFunc, "preventExtensions", Object::PreventExtensions, FunctionLength::ONE); + // 19.1.2.17 Object.seal(O) + SetFunction(env, objFunc, "seal", Object::Seal, FunctionLength::ONE); + // 19.1.2.18 Object.setPrototypeOf(O, proto) + SetFunction(env, objFunc, "setPrototypeOf", Object::SetPrototypeOf, FunctionLength::TWO); + // 20.1.2.5 Object.entries ( O ) + SetFunction(env, objFunc, "entries", Object::Entries, FunctionLength::ONE); + // ES2021 20.1.2.22 Object.values ( O ) + SetFunction(env, objFunc, "values", Object::Values, FunctionLength::ONE); + + // Object.property method + // 19.1.3.2 Object.prototype.hasOwnProperty(V) + SetFunction(env, objFuncPrototype, "hasOwnProperty", Object::HasOwnProperty, FunctionLength::ONE); + // 19.1.3.3 Object.prototype.isPrototypeOf(V) + SetFunction(env, objFuncPrototype, "isPrototypeOf", Object::IsPrototypeOf, FunctionLength::ONE); + // 19.1.3.4 Object.prototype.propertyIsEnumerable(V) + SetFunction(env, objFuncPrototype, "propertyIsEnumerable", Object::PropertyIsEnumerable, FunctionLength::ONE); + // 19.1.3.5 Object.prototype.toLocaleString([reserved1[, reserved2]]) + SetFunction(env, objFuncPrototype, "toLocaleString", Object::ToLocaleString, FunctionLength::ZERO); + // 19.1.3.6 Object.prototype.toString() + SetFunction(env, objFuncPrototype, thread_->GlobalConstants()->GetHandledToStringString(), Object::ToString, + FunctionLength::ZERO); + // 19.1.3.7 Object.prototype.valueOf() + SetFunction(env, objFuncPrototype, thread_->GlobalConstants()->GetHandledValueOfString(), Object::ValueOf, + FunctionLength::ZERO); + + SetFunction(env, objFuncPrototype, "createRealm", Object::CreateRealm, FunctionLength::ZERO); + + // B.2.2.1 Object.prototype.__proto__ + JSHandle protoKey(factory_->NewFromCanBeCompressString("__proto__")); + JSHandle protoGetter = CreateGetter(env, Object::ProtoGetter, "__proto__", FunctionLength::ZERO); + JSHandle protoSetter = CreateSetter(env, Object::ProtoSetter, "__proto__", FunctionLength::ONE); + SetAccessor(objFuncPrototype, protoKey, protoGetter, protoSetter); + // B.2.2.2 Object.prototype.__defineGetter__ ( P, getter ) + SetFunction(env, objFuncPrototype, "__defineGetter__", Object::DefineGetter, FunctionLength::TWO); + // B.2.2.3 Object.prototype.__defineSetter__ ( P, setter ) + SetFunction(env, objFuncPrototype, "__defineSetter__", Object::DefineSetter, FunctionLength::TWO); + // B.2.2.4 Object.prototype.__lookupGetter__ ( P ) + SetFunction(env, objFuncPrototype, "__lookupGetter__", Object::LookupGetter, FunctionLength::ONE); + // B.2.2.5 Object.prototype.__lookupSetter__ ( P ) + SetFunction(env, objFuncPrototype, "__lookupSetter__", Object::LookupSetter, FunctionLength::ONE); +} + +void Builtins::InitializeSymbol(const JSHandle &env, const JSHandle &objFuncDynclass) const +{ + [[maybe_unused]] EcmaHandleScope scope(thread_); + const GlobalEnvConstants *globalConst = thread_->GlobalConstants(); + // Symbol.prototype + JSHandle symbolFuncPrototype = factory_->NewJSObject(objFuncDynclass); + JSHandle symbolFuncPrototypeValue(symbolFuncPrototype); + + // Symbol.prototype_or_dynclass + JSHandle symbolFuncInstanceDynclass = + factory_->NewEcmaDynClass(JSPrimitiveRef::SIZE, JSType::JS_PRIMITIVE_REF, symbolFuncPrototypeValue); + + // Symbol = new Function() + JSHandle symbolFunction( + NewBuiltinConstructor(env, symbolFuncPrototype, Symbol::SymbolConstructor, "Symbol", FunctionLength::ZERO)); + JSHandle(symbolFunction)->SetFunctionPrototype(thread_, symbolFuncInstanceDynclass.GetTaggedValue()); + + // "constructor" property on the prototype + JSHandle constructorKey = globalConst->GetHandledConstructorString(); + PropertyDescriptor descriptor(thread_, JSHandle::Cast(symbolFunction), true, false, true); + JSObject::DefineOwnProperty(thread_, symbolFuncPrototype, constructorKey, descriptor); + + SetFunction(env, symbolFunction, "for", Symbol::For, FunctionLength::ONE); + SetFunction(env, symbolFunction, "keyFor", Symbol::KeyFor, FunctionLength::ONE); + + // Symbol attribute + JSHandle hasInstanceSymbol(factory_->NewWellKnownSymbolWithChar("Symbol.hasInstance")); + SetNoneAttributeProperty(symbolFunction, "hasInstance", hasInstanceSymbol); + JSHandle isConcatSpreadableSymbol(factory_->NewWellKnownSymbolWithChar("Symbol.isConcatSpreadable")); + SetNoneAttributeProperty(symbolFunction, "isConcatSpreadable", isConcatSpreadableSymbol); + JSHandle toStringTagSymbol(factory_->NewWellKnownSymbolWithChar("Symbol.toStringTag")); + SetNoneAttributeProperty(symbolFunction, "toStringTag", toStringTagSymbol); + JSHandle iteratorSymbol(factory_->NewPublicSymbolWithChar("Symbol.iterator")); + SetNoneAttributeProperty(symbolFunction, "iterator", iteratorSymbol); + JSHandle asyncIteratorSymbol(factory_->NewPublicSymbolWithChar("Symbol.asyncIterator")); + SetNoneAttributeProperty(symbolFunction, "asyncIterator", asyncIteratorSymbol); + JSHandle matchSymbol(factory_->NewPublicSymbolWithChar("Symbol.match")); + SetNoneAttributeProperty(symbolFunction, "match", matchSymbol); + JSHandle replaceSymbol(factory_->NewPublicSymbolWithChar("Symbol.replace")); + SetNoneAttributeProperty(symbolFunction, "replace", replaceSymbol); + JSHandle searchSymbol(factory_->NewPublicSymbolWithChar("Symbol.search")); + SetNoneAttributeProperty(symbolFunction, "search", searchSymbol); + JSHandle speciesSymbol(factory_->NewPublicSymbolWithChar("Symbol.species")); + SetNoneAttributeProperty(symbolFunction, "species", speciesSymbol); + JSHandle splitSymbol(factory_->NewPublicSymbolWithChar("Symbol.split")); + SetNoneAttributeProperty(symbolFunction, "split", splitSymbol); + JSHandle toPrimitiveSymbol(factory_->NewPublicSymbolWithChar("Symbol.toPrimitive")); + SetNoneAttributeProperty(symbolFunction, "toPrimitive", toPrimitiveSymbol); + JSHandle unscopablesSymbol(factory_->NewPublicSymbolWithChar("Symbol.unscopables")); + SetNoneAttributeProperty(symbolFunction, "unscopables", unscopablesSymbol); + + // symbol.prototype.description + PropertyDescriptor descriptionDesc(thread_); + JSHandle getterKey(factory_->NewFromCanBeCompressString("description")); + JSHandle getter(factory_->NewJSFunction(env, reinterpret_cast(Symbol::DescriptionGetter))); + SetGetter(symbolFuncPrototype, getterKey, getter); + + // Setup symbol.prototype[@@toPrimitive] + SetFunctionAtSymbol( + env, symbolFuncPrototype, toPrimitiveSymbol, "[Symbol.toPrimitive]", Symbol::ToPrimitive, FunctionLength::ONE); + // install the Symbol.prototype methods + SetFunction(env, symbolFuncPrototype, thread_->GlobalConstants()->GetHandledToStringString(), Symbol::ToString, + FunctionLength::ZERO); + SetFunction(env, symbolFuncPrototype, thread_->GlobalConstants()->GetHandledValueOfString(), Symbol::ValueOf, + FunctionLength::ZERO); + + env->SetSymbolFunction(thread_, symbolFunction); + env->SetHasInstanceSymbol(thread_, hasInstanceSymbol); + env->SetIsConcatSpreadableSymbol(thread_, isConcatSpreadableSymbol); + env->SetToStringTagSymbol(thread_, toStringTagSymbol); + env->SetIteratorSymbol(thread_, iteratorSymbol); + env->SetAsyncIteratorSymbol(thread_, asyncIteratorSymbol); + env->SetMatchSymbol(thread_, matchSymbol); + env->SetReplaceSymbol(thread_, replaceSymbol); + env->SetSearchSymbol(thread_, searchSymbol); + env->SetSpeciesSymbol(thread_, speciesSymbol); + env->SetSplitSymbol(thread_, splitSymbol); + env->SetToPrimitiveSymbol(thread_, toPrimitiveSymbol); + env->SetUnscopablesSymbol(thread_, unscopablesSymbol); + + // Setup %SymbolPrototype% + SetStringTagSymbol(env, symbolFuncPrototype, "Symbol"); + + JSHandle holeySymbol(factory_->NewPrivateNameSymbolWithChar("holey")); + env->SetHoleySymbol(thread_, holeySymbol.GetTaggedValue()); + JSHandle elementIcSymbol(factory_->NewPrivateNameSymbolWithChar("element-ic")); + env->SetElementICSymbol(thread_, elementIcSymbol.GetTaggedValue()); + + // ecma 19.2.3.6 Function.prototype[@@hasInstance] ( V ) + JSHandle funcFuncPrototypeObj = JSHandle(env->GetFunctionPrototype()); + SetFunctionAtSymbol( + env, funcFuncPrototypeObj, env->GetHasInstanceSymbol(), "[Symbol.hasInstance]", + Function::FunctionPrototypeHasInstance, FunctionLength::ONE); +} + +void Builtins::InitializeSymbolWithRealm(const JSHandle &realm, + const JSHandle &objFuncInstanceDynclass) +{ + [[maybe_unused]] EcmaHandleScope scope(thread_); + JSHandle env = vm_->GetGlobalEnv(); + // Symbol.prototype + JSHandle symbolFuncPrototype = factory_->NewJSObject(objFuncInstanceDynclass); + JSHandle symbolFuncPrototypeValue(symbolFuncPrototype); + + // Symbol.prototype_or_dynclass + JSHandle symbolFuncInstanceDynclass = + factory_->NewEcmaDynClass(JSPrimitiveRef::SIZE, JSType::JS_PRIMITIVE_REF, symbolFuncPrototypeValue); + + // Symbol = new Function() + JSHandle symbolFunction( + NewBuiltinConstructor(realm, symbolFuncPrototype, Symbol::SymbolConstructor, "Symbol", FunctionLength::ZERO)); + JSHandle(symbolFunction)->SetFunctionPrototype(thread_, symbolFuncInstanceDynclass.GetTaggedValue()); + + // "constructor" property on the prototype + JSHandle constructorKey = thread_->GlobalConstants()->GetHandledConstructorString(); + PropertyDescriptor descriptor(thread_, JSHandle::Cast(symbolFunction), true, false, true); + JSObject::DefineOwnProperty(thread_, symbolFuncPrototype, constructorKey, descriptor); + + SetFunction(realm, symbolFunction, "for", Symbol::For, FunctionLength::ONE); + SetFunction(realm, symbolFunction, "keyFor", Symbol::KeyFor, FunctionLength::ONE); + + // Symbol attribute + SetNoneAttributeProperty(symbolFunction, "hasInstance", env->GetHasInstanceSymbol()); + SetNoneAttributeProperty(symbolFunction, "isConcatSpreadable", env->GetIsConcatSpreadableSymbol()); + SetNoneAttributeProperty(symbolFunction, "toStringTag", env->GetToStringTagSymbol()); + SetNoneAttributeProperty(symbolFunction, "iterator", env->GetIteratorSymbol()); + SetNoneAttributeProperty(symbolFunction, "asyncIterator", env->GetAsyncIteratorSymbol()); + SetNoneAttributeProperty(symbolFunction, "match", env->GetMatchSymbol()); + SetNoneAttributeProperty(symbolFunction, "replace", env->GetReplaceSymbol()); + SetNoneAttributeProperty(symbolFunction, "search", env->GetSearchSymbol()); + SetNoneAttributeProperty(symbolFunction, "species", env->GetSpeciesSymbol()); + SetNoneAttributeProperty(symbolFunction, "split", env->GetSplitSymbol()); + SetNoneAttributeProperty(symbolFunction, "toPrimitive", env->GetToPrimitiveSymbol()); + SetNoneAttributeProperty(symbolFunction, "unscopables", env->GetUnscopablesSymbol()); + + // symbol.prototype.description + PropertyDescriptor descriptionDesc(thread_); + JSHandle getterKey(factory_->NewFromCanBeCompressString("description")); + JSHandle getter(factory_->NewJSFunction(realm, reinterpret_cast(Symbol::DescriptionGetter))); + SetGetter(symbolFuncPrototype, getterKey, getter); + + // Setup symbol.prototype[@@toPrimitive] + SetFunctionAtSymbol(realm, symbolFuncPrototype, env->GetToPrimitiveSymbol(), + "[Symbol.toPrimitive]", Symbol::ToPrimitive, + FunctionLength::ONE); + // install the Symbol.prototype methods + SetFunction(realm, symbolFuncPrototype, thread_->GlobalConstants()->GetHandledToStringString(), Symbol::ToString, + FunctionLength::ZERO); + SetFunction(realm, symbolFuncPrototype, thread_->GlobalConstants()->GetHandledValueOfString(), Symbol::ValueOf, + FunctionLength::ZERO); + + realm->SetSymbolFunction(thread_, symbolFunction); + realm->SetHasInstanceSymbol(thread_, env->GetHasInstanceSymbol()); + realm->SetIsConcatSpreadableSymbol(thread_, env->GetIsConcatSpreadableSymbol()); + realm->SetToStringTagSymbol(thread_, env->GetToStringTagSymbol()); + realm->SetIteratorSymbol(thread_, env->GetIteratorSymbol()); + realm->SetAsyncIteratorSymbol(thread_, env->GetAsyncIteratorSymbol()); + realm->SetMatchSymbol(thread_, env->GetMatchSymbol()); + realm->SetReplaceSymbol(thread_, env->GetReplaceSymbol()); + realm->SetSearchSymbol(thread_, env->GetSearchSymbol()); + realm->SetSpeciesSymbol(thread_, env->GetSpeciesSymbol()); + realm->SetSplitSymbol(thread_, env->GetSplitSymbol()); + realm->SetToPrimitiveSymbol(thread_, env->GetToPrimitiveSymbol()); + realm->SetUnscopablesSymbol(thread_, env->GetUnscopablesSymbol()); + + // Setup %SymbolPrototype% + SetStringTagSymbol(realm, symbolFuncPrototype, "Symbol"); + + JSHandle holeySymbol(factory_->NewPrivateNameSymbolWithChar("holey")); + realm->SetHoleySymbol(thread_, holeySymbol.GetTaggedValue()); + JSHandle elementIcSymbol(factory_->NewPrivateNameSymbolWithChar("element-ic")); + realm->SetElementICSymbol(thread_, elementIcSymbol.GetTaggedValue()); + + // ecma 19.2.3.6 Function.prototype[@@hasInstance] ( V ) + JSHandle funcFuncPrototypeObj = JSHandle(realm->GetFunctionPrototype()); + SetFunctionAtSymbol( + realm, funcFuncPrototypeObj, realm->GetHasInstanceSymbol(), "[Symbol.hasInstance]", + Function::FunctionPrototypeHasInstance, FunctionLength::ONE); +} + +void Builtins::InitializeNumber(const JSHandle &env, const JSHandle &globalObject, + const JSHandle &primRefObjDynclass) +{ + [[maybe_unused]] EcmaHandleScope scope(thread_); + // Number.prototype + JSHandle toObject(thread_, JSTaggedValue(FunctionLength::ZERO)); + JSHandle numFuncPrototype = + JSHandle::Cast(factory_->NewJSPrimitiveRef(primRefObjDynclass, toObject)); + JSHandle numFuncPrototypeValue(numFuncPrototype); + + // Number.prototype_or_dynclass + JSHandle numFuncInstanceClass = + factory_->NewEcmaDynClass(JSPrimitiveRef::SIZE, JSType::JS_PRIMITIVE_REF, numFuncPrototypeValue); + + // Number = new Function() + JSHandle numFunction( + NewBuiltinConstructor(env, numFuncPrototype, Number::NumberConstructor, "Number", FunctionLength::ONE)); + numFunction.GetObject()->SetFunctionPrototype(thread_, numFuncInstanceClass.GetTaggedValue()); + + // Number.prototype method + SetFunction(env, numFuncPrototype, "toExponential", Number::ToExponential, FunctionLength::ONE); + SetFunction(env, numFuncPrototype, "toFixed", Number::ToFixed, FunctionLength::ONE); + SetFunction(env, numFuncPrototype, "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); + + // Number method + SetFunction(env, numFunction, "isFinite", Number::IsFinite, FunctionLength::ONE); + SetFunction(env, numFunction, "isInteger", Number::IsInteger, FunctionLength::ONE); + SetFunction(env, numFunction, "isNaN", Number::IsNaN, FunctionLength::ONE); + SetFunction(env, numFunction, "isSafeInteger", Number::IsSafeInteger, FunctionLength::ONE); + SetFuncToObjAndGlobal(env, globalObject, numFunction, "parseFloat", Number::ParseFloat, FunctionLength::ONE); + SetFuncToObjAndGlobal(env, globalObject, numFunction, "parseInt", Number::ParseInt, FunctionLength::TWO); + + // Number constant + const double epsilon = 2.220446049250313e-16; + const double maxSafeInteger = 9007199254740991; + const double maxValue = 1.7976931348623157e+308; + const double minValue = 5e-324; + const double positiveInfinity = std::numeric_limits::infinity(); + SetConstant(numFunction, "MAX_VALUE", JSTaggedValue(maxValue)); + SetConstant(numFunction, "MIN_VALUE", JSTaggedValue(minValue)); + SetConstant(numFunction, "NaN", JSTaggedValue(NAN)); + SetConstant(numFunction, "NEGATIVE_INFINITY", JSTaggedValue(-positiveInfinity)); + SetConstant(numFunction, "POSITIVE_INFINITY", JSTaggedValue(positiveInfinity)); + SetConstant(numFunction, "MAX_SAFE_INTEGER", JSTaggedValue(maxSafeInteger)); + SetConstant(numFunction, "MIN_SAFE_INTEGER", JSTaggedValue(-maxSafeInteger)); + SetConstant(numFunction, "EPSILON", JSTaggedValue(epsilon)); + + env->SetNumberFunction(thread_, numFunction); +} + +void Builtins::InitializeDate(const JSHandle &env, const JSHandle &objFuncDynclass) const +{ + [[maybe_unused]] EcmaHandleScope scope(thread_); + const int utcLength = 7; + // Date.prototype + JSHandle dateFuncPrototype = factory_->NewJSObject(objFuncDynclass); + JSHandle dateFuncPrototypeValue(dateFuncPrototype); + + // Date.prototype_or_dynclass + JSHandle dateFuncInstanceDynclass = + factory_->NewEcmaDynClass(JSDate::SIZE, JSType::JS_DATE, dateFuncPrototypeValue); + + // Date = new Function() + JSHandle dateFunction( + NewBuiltinConstructor(env, dateFuncPrototype, Date::DateConstructor, "Date", FunctionLength::ONE)); + JSHandle(dateFunction)->SetFunctionPrototype(thread_, dateFuncInstanceDynclass.GetTaggedValue()); + + // Date.prototype method + SetFunction(env, dateFuncPrototype, "getDate", Date::GetDate, FunctionLength::ZERO); + SetFunction(env, dateFuncPrototype, "getDay", Date::GetDay, FunctionLength::ZERO); + SetFunction(env, dateFuncPrototype, "getFullYear", Date::GetFullYear, FunctionLength::ZERO); + SetFunction(env, dateFuncPrototype, "getHours", Date::GetHours, FunctionLength::ZERO); + SetFunction(env, dateFuncPrototype, "getMilliseconds", Date::GetMilliseconds, FunctionLength::ZERO); + SetFunction(env, dateFuncPrototype, "getMinutes", Date::GetMinutes, FunctionLength::ZERO); + SetFunction(env, dateFuncPrototype, "getMonth", Date::GetMonth, FunctionLength::ZERO); + SetFunction(env, dateFuncPrototype, "getSeconds", Date::GetSeconds, FunctionLength::ZERO); + SetFunction(env, dateFuncPrototype, "getTime", Date::GetTime, FunctionLength::ZERO); + SetFunction(env, dateFuncPrototype, "getTimezoneOffset", Date::GetTimezoneOffset, FunctionLength::ZERO); + SetFunction(env, dateFuncPrototype, "getUTCDate", Date::GetUTCDate, FunctionLength::ZERO); + SetFunction(env, dateFuncPrototype, "getUTCDay", Date::GetUTCDay, FunctionLength::ZERO); + SetFunction(env, dateFuncPrototype, "getUTCFullYear", Date::GetUTCFullYear, FunctionLength::ZERO); + SetFunction(env, dateFuncPrototype, "getUTCHours", Date::GetUTCHours, FunctionLength::ZERO); + SetFunction(env, dateFuncPrototype, "getUTCMilliseconds", Date::GetUTCMilliseconds, FunctionLength::ZERO); + SetFunction(env, dateFuncPrototype, "getUTCMinutes", Date::GetUTCMinutes, FunctionLength::ZERO); + SetFunction(env, dateFuncPrototype, "getUTCMonth", Date::GetUTCMonth, FunctionLength::ZERO); + SetFunction(env, dateFuncPrototype, "getUTCSeconds", Date::GetUTCSeconds, FunctionLength::ZERO); + + SetFunction(env, dateFuncPrototype, "setDate", Date::SetDate, FunctionLength::ONE); + SetFunction(env, dateFuncPrototype, "setFullYear", Date::SetFullYear, FunctionLength::THREE); + SetFunction(env, dateFuncPrototype, "setHours", Date::SetHours, FunctionLength::FOUR); + SetFunction(env, dateFuncPrototype, "setMilliseconds", Date::SetMilliseconds, FunctionLength::ONE); + SetFunction(env, dateFuncPrototype, "setMinutes", Date::SetMinutes, FunctionLength::THREE); + SetFunction(env, dateFuncPrototype, "setMonth", Date::SetMonth, FunctionLength::TWO); + SetFunction(env, dateFuncPrototype, "setSeconds", Date::SetSeconds, FunctionLength::TWO); + SetFunction(env, dateFuncPrototype, "setTime", Date::SetTime, FunctionLength::ONE); + SetFunction(env, dateFuncPrototype, "setUTCDate", Date::SetUTCDate, FunctionLength::ONE); + SetFunction(env, dateFuncPrototype, "setUTCFullYear", Date::SetUTCFullYear, FunctionLength::THREE); + SetFunction(env, dateFuncPrototype, "setUTCHours", Date::SetUTCHours, FunctionLength::FOUR); + SetFunction(env, dateFuncPrototype, "setUTCMilliseconds", Date::SetUTCMilliseconds, FunctionLength::ONE); + SetFunction(env, dateFuncPrototype, "setUTCMinutes", Date::SetUTCMinutes, FunctionLength::THREE); + SetFunction(env, dateFuncPrototype, "setUTCMonth", Date::SetUTCMonth, FunctionLength::TWO); + SetFunction(env, dateFuncPrototype, "setUTCSeconds", Date::SetUTCSeconds, FunctionLength::TWO); + + SetFunction(env, dateFuncPrototype, "toDateString", Date::ToDateString, FunctionLength::ZERO); + SetFunction(env, dateFuncPrototype, "toISOString", Date::ToISOString, FunctionLength::ZERO); + SetFunction(env, dateFuncPrototype, "toJSON", Date::ToJSON, FunctionLength::ONE); + SetFunction(env, dateFuncPrototype, "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); + + SetFunctionAtSymbol(env, dateFuncPrototype, env->GetToPrimitiveSymbol(), "[Symbol.toPrimitive]", Date::ToPrimitive, + FunctionLength::ONE); + + // Date method + SetFunction(env, dateFunction, "now", Date::Now, FunctionLength::ZERO); + SetFunction(env, dateFunction, "parse", Date::Parse, FunctionLength::ONE); + SetFunction(env, dateFunction, "UTC", Date::UTC, utcLength); + + // Date.length + SetConstant(dateFunction, "length", JSTaggedValue(utcLength)); + + env->SetDateFunction(thread_, dateFunction); +} + +void Builtins::InitializeBoolean(const JSHandle &env, const JSHandle &primRefObjDynclass) const +{ + [[maybe_unused]] EcmaHandleScope scope(thread_); + // Boolean.prototype + JSHandle toObject(thread_, JSTaggedValue::False()); + JSHandle booleanFuncPrototype = + JSHandle::Cast(factory_->NewJSPrimitiveRef(primRefObjDynclass, toObject)); + JSHandle booleanFuncPrototypeValue(booleanFuncPrototype); + + // Boolean.prototype_or_dynclass + JSHandle booleanFuncInstanceDynclass = + factory_->NewEcmaDynClass(JSPrimitiveRef::SIZE, JSType::JS_PRIMITIVE_REF, booleanFuncPrototypeValue); + + // new Boolean Function() + JSHandle booleanFunction( + NewBuiltinConstructor(env, booleanFuncPrototype, Boolean::BooleanConstructor, "Boolean", FunctionLength::ONE)); + booleanFunction->SetFunctionPrototype(thread_, booleanFuncInstanceDynclass.GetTaggedValue()); + + // Boolean.prototype method + SetFunction(env, booleanFuncPrototype, thread_->GlobalConstants()->GetHandledToStringString(), + Boolean::BooleanPrototypeToString, FunctionLength::ZERO); + SetFunction(env, booleanFuncPrototype, thread_->GlobalConstants()->GetHandledValueOfString(), + Boolean::BooleanPrototypeValueOf, FunctionLength::ZERO); + + env->SetBooleanFunction(thread_, booleanFunction); +} + +void Builtins::InitializeProxy(const JSHandle &env) +{ + JSHandle proxyFunction(InitializeExoticConstructor(env, Proxy::ProxyConstructor, "Proxy", 2)); + + // Proxy method + SetFunction(env, proxyFunction, "revocable", Proxy::Revocable, FunctionLength::TWO); + env->SetProxyFunction(thread_, proxyFunction); +} + +JSHandle Builtins::InitializeExoticConstructor(const JSHandle &env, EcmaEntrypoint ctorFunc, + const char *name, int length) +{ + JSHandle ctor = + factory_->NewJSFunction(env, reinterpret_cast(ctorFunc), FunctionKind::BUILTIN_PROXY_CONSTRUCTOR); + + JSFunction::SetFunctionLength(thread_, ctor, JSTaggedValue(length)); + JSHandle nameString(factory_->NewFromString(name)); + JSFunction::SetFunctionName(thread_, JSHandle(ctor), nameString, + JSHandle(thread_, JSTaggedValue::Undefined())); + + JSHandle globalObject(thread_, env->GetGlobalObject()); + PropertyDescriptor descriptor(thread_, JSHandle(ctor), true, false, true); + JSObject::DefineOwnProperty(thread_, globalObject, nameString, descriptor); + return ctor; +} + +void Builtins::InitializeAsyncFunction(const JSHandle &env, const JSHandle &objFuncDynclass) const +{ + [[maybe_unused]] EcmaHandleScope scope(thread_); + const GlobalEnvConstants *globalConst = thread_->GlobalConstants(); + // AsyncFunction.prototype + JSHandle asyncFuncPrototype = factory_->NewJSObject(objFuncDynclass); + JSObject::SetPrototype(thread_, asyncFuncPrototype, env->GetFunctionPrototype()); + JSHandle async_func_prototype_value(asyncFuncPrototype); + + // AsyncFunction.prototype_or_dynclass + JSHandle asyncFuncInstanceDynclass = + factory_->NewEcmaDynClass(JSAsyncFunction::SIZE, JSType::JS_ASYNC_FUNCTION, async_func_prototype_value); + asyncFuncInstanceDynclass->GetHClass()->MarkFieldAsNative(JSAsyncFunction::METHOD_OFFSET); + + // AsyncFunction = new Function() + JSHandle asyncFunction = NewBuiltinConstructor( + env, asyncFuncPrototype, AsyncFunction::AsyncFunctionConstructor, "AsyncFunction", FunctionLength::ONE); + JSObject::SetPrototype(thread_, JSHandle::Cast(asyncFunction), env->GetFunctionFunction()); + JSHandle constructorKey = globalConst->GetHandledConstructorString(); + PropertyDescriptor asyncDesc(thread_, JSHandle::Cast(asyncFunction), false, false, true); + JSObject::DefineOwnProperty(thread_, asyncFuncPrototype, constructorKey, asyncDesc); + asyncFunction->SetProtoOrDynClass(thread_, asyncFuncInstanceDynclass.GetTaggedValue()); + + // AsyncFunction.prototype property + SetStringTagSymbol(env, asyncFuncPrototype, "AsyncFunction"); + env->SetAsyncFunction(thread_, asyncFunction); + env->SetAsyncFunctionPrototype(thread_, asyncFuncPrototype); +} + +void Builtins::InitializeAllTypeError(const JSHandle &env, const JSHandle &objFuncDynclass) const +{ + // Error.prototype + JSHandle errorFuncPrototype = factory_->NewJSObject(objFuncDynclass); + JSHandle errorFuncPrototypeValue(errorFuncPrototype); + // Error.prototype_or_dynclass + JSHandle errorFuncInstanceDynclass = + factory_->NewEcmaDynClass(JSObject::SIZE, JSType::JS_ERROR, errorFuncPrototypeValue); + // Error() = new Function() + JSHandle errorFunction( + NewBuiltinConstructor(env, errorFuncPrototype, Error::ErrorConstructor, "Error", FunctionLength::ONE)); + errorFunction->SetFunctionPrototype(thread_, errorFuncInstanceDynclass.GetTaggedValue()); + + // Error.prototype method + SetFunction(env, errorFuncPrototype, thread_->GlobalConstants()->GetHandledToStringString(), Error::ToString, + FunctionLength::ZERO); + + // Error.prototype Attribute + SetAttribute(errorFuncPrototype, "name", "Error"); + SetAttribute(errorFuncPrototype, "message", ""); + env->SetErrorFunction(thread_, errorFunction); + + JSHandle nativeErrorFuncClass = factory_->NewEcmaDynClass( + JSFunction::SIZE, JSType::JS_FUNCTION, env->GetErrorFunction(), HClass::IS_CALLABLE | HClass::IS_BUILTINS_CTOR); + nativeErrorFuncClass->SetConstructor(true); + nativeErrorFuncClass->GetHClass()->MarkFieldAsNative(JSFunction::METHOD_OFFSET); + env->SetNativeErrorFunctionClass(thread_, nativeErrorFuncClass); + + JSHandle errorNativeFuncInstanceDynclass = + factory_->NewEcmaDynClass(JSObject::SIZE, JSType::JS_OBJECT, errorFuncPrototypeValue); + InitializeError(env, errorNativeFuncInstanceDynclass, JSType::JS_RANGE_ERROR); + InitializeError(env, errorNativeFuncInstanceDynclass, JSType::JS_REFERENCE_ERROR); + InitializeError(env, errorNativeFuncInstanceDynclass, JSType::JS_TYPE_ERROR); + InitializeError(env, errorNativeFuncInstanceDynclass, JSType::JS_URI_ERROR); + InitializeError(env, errorNativeFuncInstanceDynclass, JSType::JS_SYNTAX_ERROR); + InitializeError(env, errorNativeFuncInstanceDynclass, JSType::JS_EVAL_ERROR); +} + +void Builtins::InitializeAllTypeErrorWithRealm(const JSHandle &realm) const +{ + JSHandle env = vm_->GetGlobalEnv(); + + realm->SetErrorFunction(thread_, env->GetErrorFunction()); + realm->SetNativeErrorFunctionClass(thread_, env->GetNativeErrorFunctionClass()); + + SetErrorWithRealm(realm, JSType::JS_RANGE_ERROR); + SetErrorWithRealm(realm, JSType::JS_REFERENCE_ERROR); + SetErrorWithRealm(realm, JSType::JS_TYPE_ERROR); + SetErrorWithRealm(realm, JSType::JS_URI_ERROR); + SetErrorWithRealm(realm, JSType::JS_SYNTAX_ERROR); + SetErrorWithRealm(realm, JSType::JS_EVAL_ERROR); +} + +void Builtins::SetErrorWithRealm(const JSHandle &realm, const JSType &errorTag) const +{ + JSHandle env = vm_->GetGlobalEnv(); + JSHandle globalObject(thread_, realm->GetGlobalObject()); + JSHandle nameString; + JSHandle nativeErrorFunction; + switch (errorTag) { + case JSType::JS_RANGE_ERROR: + nativeErrorFunction = env->GetRangeErrorFunction(); + nameString = JSHandle(thread_->GlobalConstants()->GetHandledRangeErrorString()); + realm->SetRangeErrorFunction(thread_, nativeErrorFunction); + break; + case JSType::JS_EVAL_ERROR: + nativeErrorFunction = env->GetEvalErrorFunction(); + nameString = JSHandle(thread_->GlobalConstants()->GetHandledEvalErrorString()); + realm->SetEvalErrorFunction(thread_, nativeErrorFunction); + break; + case JSType::JS_REFERENCE_ERROR: + nativeErrorFunction = env->GetReferenceErrorFunction(); + nameString = JSHandle(thread_->GlobalConstants()->GetHandledReferenceErrorString()); + realm->SetReferenceErrorFunction(thread_, nativeErrorFunction); + break; + case JSType::JS_TYPE_ERROR: + nativeErrorFunction = env->GetTypeErrorFunction(); + nameString = JSHandle(thread_->GlobalConstants()->GetHandledTypeErrorString()); + realm->SetTypeErrorFunction(thread_, nativeErrorFunction); + realm->SetThrowTypeError(thread_, env->GetThrowTypeError()); + break; + case JSType::JS_URI_ERROR: + nativeErrorFunction = env->GetURIErrorFunction(); + nameString = JSHandle(thread_->GlobalConstants()->GetHandledURIErrorString()); + realm->SetURIErrorFunction(thread_, nativeErrorFunction); + break; + case JSType::JS_SYNTAX_ERROR: + nativeErrorFunction = env->GetSyntaxErrorFunction(); + nameString = JSHandle(thread_->GlobalConstants()->GetHandledSyntaxErrorString()); + realm->SetSyntaxErrorFunction(thread_, nativeErrorFunction); + break; + default: + break; + } + PropertyDescriptor descriptor(thread_, nativeErrorFunction, true, false, true); + JSObject::DefineOwnProperty(thread_, globalObject, nameString, descriptor); +} + +void Builtins::GeneralUpdateError(ErrorParameter *error, EcmaEntrypoint constructor, EcmaEntrypoint method, + const char *name, JSType type) const +{ + error->nativeConstructor = constructor; + error->nativeMethod = method; + error->nativePropertyName = name; + error->nativeJstype = type; +} + +void Builtins::InitializeError(const JSHandle &env, const JSHandle &objFuncDynclass, + const JSType &errorTag) const +{ + // NativeError.prototype + JSHandle nativeErrorFuncPrototype = factory_->NewJSObject(objFuncDynclass); + JSHandle nativeErrorFuncPrototypeValue(nativeErrorFuncPrototype); + + ErrorParameter errorParameter {RangeError::RangeErrorConstructor, RangeError::ToString, "RangeError", + JSType::JS_RANGE_ERROR}; + switch (errorTag) { + case JSType::JS_RANGE_ERROR: + GeneralUpdateError(&errorParameter, RangeError::RangeErrorConstructor, RangeError::ToString, "RangeError", + JSType::JS_RANGE_ERROR); + break; + case JSType::JS_EVAL_ERROR: + GeneralUpdateError(&errorParameter, EvalError::EvalErrorConstructor, EvalError::ToString, "EvalError", + JSType::JS_EVAL_ERROR); + break; + case JSType::JS_REFERENCE_ERROR: + GeneralUpdateError(&errorParameter, ReferenceError::ReferenceErrorConstructor, ReferenceError::ToString, + "ReferenceError", JSType::JS_REFERENCE_ERROR); + break; + case JSType::JS_TYPE_ERROR: + GeneralUpdateError(&errorParameter, TypeError::TypeErrorConstructor, TypeError::ToString, "TypeError", + JSType::JS_TYPE_ERROR); + break; + case JSType::JS_URI_ERROR: + GeneralUpdateError(&errorParameter, URIError::URIErrorConstructor, URIError::ToString, "URIError", + JSType::JS_URI_ERROR); + break; + case JSType::JS_SYNTAX_ERROR: + GeneralUpdateError(&errorParameter, SyntaxError::SyntaxErrorConstructor, SyntaxError::ToString, + "SyntaxError", JSType::JS_SYNTAX_ERROR); + break; + default: + break; + } + + // NativeError.prototype_or_dynclass + JSHandle nativeErrorFuncInstanceDynclass = + factory_->NewEcmaDynClass(JSObject::SIZE, errorParameter.nativeJstype, nativeErrorFuncPrototypeValue); + + // NativeError() = new Error() + JSHandle nativeErrorFunction = + factory_->NewJSNativeErrorFunction(env, reinterpret_cast(errorParameter.nativeConstructor)); + InitializeCtor(env, nativeErrorFuncPrototype, nativeErrorFunction, errorParameter.nativePropertyName, + FunctionLength::ONE); + + nativeErrorFunction->SetFunctionPrototype(thread_, nativeErrorFuncInstanceDynclass.GetTaggedValue()); + + // NativeError.prototype method + SetFunction(env, nativeErrorFuncPrototype, thread_->GlobalConstants()->GetHandledToStringString(), + errorParameter.nativeMethod, FunctionLength::ZERO); + + // Error.prototype Attribute + SetAttribute(nativeErrorFuncPrototype, "name", errorParameter.nativePropertyName); + SetAttribute(nativeErrorFuncPrototype, "message", ""); + + if (errorTag == JSType::JS_RANGE_ERROR) { + env->SetRangeErrorFunction(thread_, nativeErrorFunction); + } else if (errorTag == JSType::JS_REFERENCE_ERROR) { + env->SetReferenceErrorFunction(thread_, nativeErrorFunction); + } else if (errorTag == JSType::JS_TYPE_ERROR) { + env->SetTypeErrorFunction(thread_, nativeErrorFunction); + JSHandle throwTypeErrorFunction = + factory_->NewJSFunction(env, reinterpret_cast(TypeError::ThrowTypeError)); + JSFunction::SetFunctionLength(thread_, throwTypeErrorFunction, JSTaggedValue(1), false); + JSObject::PreventExtensions(thread_, JSHandle::Cast(throwTypeErrorFunction)); + env->SetThrowTypeError(thread_, throwTypeErrorFunction); + } else if (errorTag == JSType::JS_URI_ERROR) { + env->SetURIErrorFunction(thread_, nativeErrorFunction); + } else if (errorTag == JSType::JS_SYNTAX_ERROR) { + env->SetSyntaxErrorFunction(thread_, nativeErrorFunction); + } else { + env->SetEvalErrorFunction(thread_, nativeErrorFunction); + } +} // namespace panda::ecmascript + +void Builtins::InitializeCtor(const JSHandle &env, const JSHandle &prototype, + const JSHandle &ctor, const char *name, int length) const +{ + const GlobalEnvConstants *globalConst = thread_->GlobalConstants(); + JSFunction::SetFunctionLength(thread_, ctor, JSTaggedValue(length)); + JSHandle nameString(factory_->NewFromString(name)); + JSFunction::SetFunctionName(thread_, JSHandle(ctor), nameString, + JSHandle(thread_, JSTaggedValue::Undefined())); + JSHandle constructorKey = globalConst->GetHandledConstructorString(); + PropertyDescriptor descriptor1(thread_, JSHandle::Cast(ctor), true, false, true); + JSObject::DefineOwnProperty(thread_, prototype, constructorKey, descriptor1); + + /* set "prototype" in constructor */ + ctor->SetFunctionPrototype(thread_, prototype.GetTaggedValue()); + + if (!JSTaggedValue::SameValue(nameString, thread_->GlobalConstants()->GetHandledAsyncFunctionString())) { + JSHandle globalObject(thread_, env->GetGlobalObject()); + PropertyDescriptor descriptor2(thread_, JSHandle::Cast(ctor), true, false, true); + JSObject::DefineOwnProperty(thread_, globalObject, nameString, descriptor2); + } +} + +void Builtins::InitializeSet(const JSHandle &env, const JSHandle &objFuncDynclass) const +{ + [[maybe_unused]] EcmaHandleScope scope(thread_); + const GlobalEnvConstants *globalConst = thread_->GlobalConstants(); + // Set.prototype + JSHandle setFuncPrototype = factory_->NewJSObject(objFuncDynclass); + JSHandle setFuncPrototypeValue(setFuncPrototype); + // Set.prototype_or_dynclass + JSHandle setFuncInstanceDynclass = + factory_->NewEcmaDynClass(JSSet::SIZE, JSType::JS_SET, setFuncPrototypeValue); + // Set() = new Function() + JSHandle setFunction( + NewBuiltinConstructor(env, setFuncPrototype, BuiltinsSet::SetConstructor, "Set", FunctionLength::ZERO)); + JSHandle(setFunction)->SetFunctionPrototype(thread_, setFuncInstanceDynclass.GetTaggedValue()); + + // "constructor" property on the prototype + JSHandle constructorKey = globalConst->GetHandledConstructorString(); + JSObject::SetProperty(thread_, JSHandle(setFuncPrototype), constructorKey, setFunction); + // set.prototype.add() + SetFunction(env, setFuncPrototype, "add", BuiltinsSet::Add, FunctionLength::ONE); + // set.prototype.clear() + SetFunction(env, setFuncPrototype, "clear", BuiltinsSet::Clear, FunctionLength::ZERO); + // set.prototype.delete() + SetFunction(env, setFuncPrototype, "delete", BuiltinsSet::Delete, FunctionLength::ONE); + // set.prototype.has() + SetFunction(env, setFuncPrototype, "has", BuiltinsSet::Has, FunctionLength::ONE); + // set.prototype.forEach() + SetFunction(env, setFuncPrototype, "forEach", BuiltinsSet::ForEach, FunctionLength::ONE); + // set.prototype.entries() + SetFunction(env, setFuncPrototype, "entries", BuiltinsSet::Entries, FunctionLength::ZERO); + // set.prototype.keys() + SetFunction(env, setFuncPrototype, "values", BuiltinsSet::Values, FunctionLength::ZERO); + // set.prototype.values() + JSHandle keys(factory_->NewFromCanBeCompressString("keys")); + JSHandle values(factory_->NewFromCanBeCompressString("values")); + JSHandle valuesFunc = + JSObject::GetMethod(thread_, JSHandle::Cast(setFuncPrototype), values); + PropertyDescriptor descriptor(thread_, valuesFunc, true, false, true); + JSObject::DefineOwnProperty(thread_, setFuncPrototype, keys, descriptor); + + // @@ToStringTag + SetStringTagSymbol(env, setFuncPrototype, "Set"); + + // 23.1.3.10get Set.prototype.size + JSHandle sizeGetter = CreateGetter(env, BuiltinsSet::GetSize, "size", FunctionLength::ZERO); + JSHandle sizeKey(factory_->NewFromCanBeCompressString("size")); + SetGetter(setFuncPrototype, sizeKey, sizeGetter); + + // 23.1.2.2get Set [ @@species ] + JSHandle speciesSymbol = env->GetSpeciesSymbol(); + JSHandle speciesGetter = + CreateGetter(env, BuiltinsSet::Species, "[Symbol.species]", FunctionLength::ZERO); + SetGetter(JSHandle(setFunction), speciesSymbol, speciesGetter); + + // %SetPrototype% [ @@iterator ] + JSHandle iteratorSymbol = env->GetIteratorSymbol(); + JSObject::DefineOwnProperty(thread_, setFuncPrototype, iteratorSymbol, descriptor); + + env->SetBuiltinsSetFunction(thread_, setFunction); +} + +void Builtins::InitializeMap(const JSHandle &env, const JSHandle &objFuncDynclass) const +{ + [[maybe_unused]] EcmaHandleScope scope(thread_); + const GlobalEnvConstants *globalConst = thread_->GlobalConstants(); + // Map.prototype + JSHandle mapFuncPrototype = factory_->NewJSObject(objFuncDynclass); + JSHandle mapFuncPrototypeValue(mapFuncPrototype); + // Map.prototype_or_dynclass + JSHandle mapFuncInstanceDynclass = + factory_->NewEcmaDynClass(JSMap::SIZE, JSType::JS_MAP, mapFuncPrototypeValue); + // Map() = new Function() + JSHandle mapFunction( + NewBuiltinConstructor(env, mapFuncPrototype, BuiltinsMap::MapConstructor, "Map", FunctionLength::ZERO)); + // Map().prototype = Map.Prototype & Map.prototype.constructor = Map() + JSFunction::Cast(mapFunction->GetTaggedObject()) + ->SetFunctionPrototype(thread_, mapFuncInstanceDynclass.GetTaggedValue()); + + // "constructor" property on the prototype + JSHandle constructorKey = globalConst->GetHandledConstructorString(); + JSObject::SetProperty(thread_, JSHandle(mapFuncPrototype), constructorKey, mapFunction); + // map.prototype.set() + SetFunction(env, mapFuncPrototype, globalConst->GetHandledSetString(), BuiltinsMap::Set, FunctionLength::TWO); + // map.prototype.clear() + SetFunction(env, mapFuncPrototype, "clear", BuiltinsMap::Clear, FunctionLength::ZERO); + // map.prototype.delete() + SetFunction(env, mapFuncPrototype, "delete", BuiltinsMap::Delete, FunctionLength::ONE); + // map.prototype.has() + SetFunction(env, mapFuncPrototype, "has", BuiltinsMap::Has, FunctionLength::ONE); + // map.prototype.get() + SetFunction(env, mapFuncPrototype, thread_->GlobalConstants()->GetHandledGetString(), BuiltinsMap::Get, + FunctionLength::ONE); + // map.prototype.forEach() + SetFunction(env, mapFuncPrototype, "forEach", BuiltinsMap::ForEach, FunctionLength::ONE); + // map.prototype.keys() + SetFunction(env, mapFuncPrototype, "keys", BuiltinsMap::Keys, FunctionLength::ZERO); + // map.prototype.values() + SetFunction(env, mapFuncPrototype, "values", BuiltinsMap::Values, FunctionLength::ZERO); + // map.prototype.entries() + SetFunction(env, mapFuncPrototype, "entries", BuiltinsMap::Entries, FunctionLength::ZERO); + // @@ToStringTag + SetStringTagSymbol(env, mapFuncPrototype, "Map"); + + // 23.1.3.10get Map.prototype.size + JSHandle sizeGetter = CreateGetter(env, BuiltinsMap::GetSize, "size", FunctionLength::ZERO); + JSHandle sizeKey(factory_->NewFromCanBeCompressString("size")); + SetGetter(mapFuncPrototype, sizeKey, sizeGetter); + + // 23.1.2.2get Map [ @@species ] + JSHandle speciesSymbol = env->GetSpeciesSymbol(); + JSHandle speciesGetter = + CreateGetter(env, BuiltinsMap::Species, "[Symbol.species]", FunctionLength::ZERO); + SetGetter(JSHandle(mapFunction), speciesSymbol, speciesGetter); + + // %MapPrototype% [ @@iterator ] + JSHandle iteratorSymbol = env->GetIteratorSymbol(); + JSHandle entries(factory_->NewFromCanBeCompressString("entries")); + JSHandle entriesFunc = + JSObject::GetMethod(thread_, JSHandle::Cast(mapFuncPrototype), entries); + PropertyDescriptor descriptor(thread_, entriesFunc, true, false, true); + JSObject::DefineOwnProperty(thread_, mapFuncPrototype, iteratorSymbol, descriptor); + + env->SetBuiltinsMapFunction(thread_, mapFunction); + env->SetMapPrototype(thread_, mapFuncPrototype); +} + +void Builtins::InitializeWeakMap(const JSHandle &env, const JSHandle &objFuncDynclass) const +{ + [[maybe_unused]] EcmaHandleScope scope(thread_); + const GlobalEnvConstants *globalConst = thread_->GlobalConstants(); + // WeakMap.prototype + JSHandle weakMapFuncPrototype = factory_->NewJSObject(objFuncDynclass); + JSHandle weakMapFuncPrototypeValue(weakMapFuncPrototype); + // WeakMap.prototype_or_dynclass + JSHandle weakMapFuncInstanceDynclass = + factory_->NewEcmaDynClass(JSWeakMap::SIZE, JSType::JS_WEAK_MAP, weakMapFuncPrototypeValue); + // WeakMap() = new Function() + JSHandle weakMapFunction(NewBuiltinConstructor( + env, weakMapFuncPrototype, BuiltinsWeakMap::WeakMapConstructor, "WeakMap", FunctionLength::ZERO)); + // WeakMap().prototype = WeakMap.Prototype & WeakMap.prototype.constructor = WeakMap() + JSFunction::Cast(weakMapFunction->GetTaggedObject()) + ->SetProtoOrDynClass(thread_, weakMapFuncInstanceDynclass.GetTaggedValue()); + + // "constructor" property on the prototype + JSHandle constructorKey = globalConst->GetHandledConstructorString(); + JSObject::SetProperty(thread_, JSHandle(weakMapFuncPrototype), constructorKey, weakMapFunction); + // weakmap.prototype.set() + SetFunction(env, weakMapFuncPrototype, globalConst->GetHandledSetString(), BuiltinsWeakMap::Set, + FunctionLength::TWO); + // weakmap.prototype.delete() + SetFunction(env, weakMapFuncPrototype, "delete", BuiltinsWeakMap::Delete, FunctionLength::ONE); + // weakmap.prototype.has() + SetFunction(env, weakMapFuncPrototype, "has", BuiltinsWeakMap::Has, FunctionLength::ONE); + // weakmap.prototype.get() + SetFunction(env, weakMapFuncPrototype, thread_->GlobalConstants()->GetHandledGetString(), BuiltinsWeakMap::Get, + FunctionLength::ONE); + // @@ToStringTag + SetStringTagSymbol(env, weakMapFuncPrototype, "WeakMap"); + + env->SetBuiltinsWeakMapFunction(thread_, weakMapFunction); +} + +void Builtins::InitializeWeakSet(const JSHandle &env, const JSHandle &objFuncDynclass) const +{ + [[maybe_unused]] EcmaHandleScope scope(thread_); + const GlobalEnvConstants *globalConst = thread_->GlobalConstants(); + // Set.prototype + JSHandle weakSetFuncPrototype = factory_->NewJSObject(objFuncDynclass); + JSHandle weakSetFuncPrototypeValue(weakSetFuncPrototype); + // Set.prototype_or_dynclass + JSHandle weakSetFuncInstanceDynclass = + factory_->NewEcmaDynClass(JSWeakSet::SIZE, JSType::JS_WEAK_SET, weakSetFuncPrototypeValue); + // Set() = new Function() + JSHandle weakSetFunction(NewBuiltinConstructor( + env, weakSetFuncPrototype, BuiltinsWeakSet::WeakSetConstructor, "WeakSet", FunctionLength::ZERO)); + JSHandle(weakSetFunction)->SetProtoOrDynClass(thread_, weakSetFuncInstanceDynclass.GetTaggedValue()); + + // "constructor" property on the prototype + JSHandle constructorKey = globalConst->GetHandledConstructorString(); + JSObject::SetProperty(thread_, JSHandle(weakSetFuncPrototype), constructorKey, weakSetFunction); + // set.prototype.add() + SetFunction(env, weakSetFuncPrototype, "add", BuiltinsWeakSet::Add, FunctionLength::ONE); + // set.prototype.delete() + SetFunction(env, weakSetFuncPrototype, "delete", BuiltinsWeakSet::Delete, FunctionLength::ONE); + // set.prototype.has() + SetFunction(env, weakSetFuncPrototype, "has", BuiltinsWeakSet::Has, FunctionLength::ONE); + + // @@ToStringTag + SetStringTagSymbol(env, weakSetFuncPrototype, "WeakSet"); + + env->SetBuiltinsWeakSetFunction(thread_, weakSetFunction); +} + +void Builtins::InitializeMath(const JSHandle &env, const JSHandle &objFuncPrototypeVal) const +{ + [[maybe_unused]] EcmaHandleScope scope(thread_); + JSHandle mathDynclass = factory_->NewEcmaDynClass(JSObject::SIZE, JSType::JS_OBJECT, objFuncPrototypeVal); + JSHandle mathObject = factory_->NewJSObject(mathDynclass); + SetFunction(env, mathObject, "abs", Math::Abs, FunctionLength::ONE); + SetFunction(env, mathObject, "acos", Math::Acos, FunctionLength::ONE); + SetFunction(env, mathObject, "acosh", Math::Acosh, FunctionLength::ONE); + SetFunction(env, mathObject, "asin", Math::Asin, FunctionLength::ONE); + SetFunction(env, mathObject, "asinh", Math::Asinh, FunctionLength::ONE); + SetFunction(env, mathObject, "atan", Math::Atan, FunctionLength::ONE); + SetFunction(env, mathObject, "atanh", Math::Atanh, FunctionLength::ONE); + SetFunction(env, mathObject, "atan2", Math::Atan2, FunctionLength::TWO); + SetFunction(env, mathObject, "cbrt", Math::Cbrt, FunctionLength::ONE); + SetFunction(env, mathObject, "ceil", Math::Ceil, FunctionLength::ONE); + SetFunction(env, mathObject, "clz32", Math::Clz32, FunctionLength::ONE); + SetFunction(env, mathObject, "cos", Math::Cos, FunctionLength::ONE); + SetFunction(env, mathObject, "cosh", Math::Cosh, FunctionLength::ONE); + SetFunction(env, mathObject, "exp", Math::Exp, FunctionLength::ONE); + SetFunction(env, mathObject, "expm1", Math::Expm1, FunctionLength::ONE); + SetFunction(env, mathObject, "floor", Math::Floor, FunctionLength::ONE); + SetFunction(env, mathObject, "fround", Math::Fround, FunctionLength::ONE); + SetFunction(env, mathObject, "hypot", Math::Hypot, FunctionLength::TWO); + SetFunction(env, mathObject, "imul", Math::Imul, FunctionLength::TWO); + SetFunction(env, mathObject, "log", Math::Log, FunctionLength::ONE); + SetFunction(env, mathObject, "log1p", Math::Log1p, FunctionLength::ONE); + SetFunction(env, mathObject, "log10", Math::Log10, FunctionLength::ONE); + SetFunction(env, mathObject, "log2", Math::Log2, FunctionLength::ONE); + SetFunction(env, mathObject, "max", Math::Max, FunctionLength::TWO); + SetFunction(env, mathObject, "min", Math::Min, FunctionLength::TWO); + SetFunction(env, mathObject, "pow", Math::Pow, FunctionLength::TWO); + SetFunction(env, mathObject, "random", Math::Random, FunctionLength::ZERO); + SetFunction(env, mathObject, "round", Math::Round, FunctionLength::ONE); + SetFunction(env, mathObject, "sign", Math::Sign, FunctionLength::ONE); + SetFunction(env, mathObject, "sin", Math::Sin, FunctionLength::ONE); + SetFunction(env, mathObject, "sinh", Math::Sinh, FunctionLength::ONE); + SetFunction(env, mathObject, "sqrt", Math::Sqrt, FunctionLength::ONE); + SetFunction(env, mathObject, "tan", Math::Tan, FunctionLength::ONE); + SetFunction(env, mathObject, "tanh", Math::Tanh, FunctionLength::ONE); + SetFunction(env, mathObject, "trunc", Math::Trunc, FunctionLength::ONE); + + SetConstant(mathObject, "E", JSTaggedValue(Math::E)); + SetConstant(mathObject, "LN10", JSTaggedValue(Math::LN10)); + SetConstant(mathObject, "LN2", JSTaggedValue(Math::LN2)); + SetConstant(mathObject, "LOG10E", JSTaggedValue(Math::LOG10E)); + SetConstant(mathObject, "LOG2E", JSTaggedValue(Math::LOG2E)); + SetConstant(mathObject, "PI", JSTaggedValue(Math::PI)); + SetConstant(mathObject, "SQRT1_2", JSTaggedValue(Math::SQRT1_2)); + SetConstant(mathObject, "SQRT2", JSTaggedValue(Math::SQRT2)); + + JSHandle mathString(factory_->NewFromCanBeCompressString("Math")); + JSHandle globalObject(thread_, env->GetGlobalObject()); + PropertyDescriptor mathDesc(thread_, JSHandle::Cast(mathObject), true, false, true); + JSObject::DefineOwnProperty(thread_, globalObject, mathString, mathDesc); + // @@ToStringTag + SetStringTagSymbol(env, mathObject, "Math"); + env->SetMathFunction(thread_, mathObject); +} + +void Builtins::InitializeJson(const JSHandle &env, const JSHandle &objFuncPrototypeVal) const +{ + [[maybe_unused]] EcmaHandleScope scope(thread_); + JSHandle jsonDynclass = factory_->NewEcmaDynClass(JSObject::SIZE, JSType::JS_OBJECT, objFuncPrototypeVal); + JSHandle jsonObject = factory_->NewJSObject(jsonDynclass); + + SetFunction(env, jsonObject, "parse", Json::Parse, FunctionLength::TWO); + SetFunction(env, jsonObject, "stringify", Json::Stringify, FunctionLength::THREE); + + PropertyDescriptor jsonDesc(thread_, JSHandle::Cast(jsonObject), true, false, true); + JSHandle jsonString(factory_->NewFromCanBeCompressString("JSON")); + JSHandle globalObject(thread_, env->GetGlobalObject()); + JSObject::DefineOwnProperty(thread_, globalObject, jsonString, jsonDesc); + // @@ToStringTag + SetStringTagSymbol(env, jsonObject, "JSON"); + env->SetJsonFunction(thread_, jsonObject); +} + +void Builtins::InitializeString(const JSHandle &env, const JSHandle &primRefObjDynclass) const +{ + [[maybe_unused]] EcmaHandleScope scope(thread_); + // String.prototype + JSHandle toObject(factory_->GetEmptyString()); + JSHandle stringFuncPrototype = + JSHandle::Cast(factory_->NewJSPrimitiveRef(primRefObjDynclass, toObject)); + JSHandle stringFuncPrototypeValue(stringFuncPrototype); + + // String.prototype_or_dynclass + JSHandle stringFuncInstanceDynclass = + factory_->NewEcmaDynClass(JSPrimitiveRef::SIZE, JSType::JS_PRIMITIVE_REF, stringFuncPrototypeValue); + + // String = new Function() + JSHandle stringFunction(NewBuiltinConstructor(env, stringFuncPrototype, BuiltinsString::StringConstructor, + "String", FunctionLength::ONE)); + stringFunction.GetObject()->SetFunctionPrototype(thread_, stringFuncInstanceDynclass.GetTaggedValue()); + + // String.prototype method + SetFunction(env, stringFuncPrototype, "charAt", BuiltinsString::CharAt, FunctionLength::ONE); + SetFunction(env, stringFuncPrototype, "charCodeAt", BuiltinsString::CharCodeAt, FunctionLength::ONE); + SetFunction(env, stringFuncPrototype, "codePointAt", BuiltinsString::CodePointAt, FunctionLength::ONE); + SetFunction(env, stringFuncPrototype, "concat", BuiltinsString::Concat, FunctionLength::ONE); + SetFunction(env, stringFuncPrototype, "endsWith", BuiltinsString::EndsWith, FunctionLength::ONE); + SetFunction(env, stringFuncPrototype, "includes", BuiltinsString::Includes, FunctionLength::ONE); + SetFunction(env, stringFuncPrototype, "indexOf", BuiltinsString::IndexOf, FunctionLength::ONE); + SetFunction(env, stringFuncPrototype, "lastIndexOf", BuiltinsString::LastIndexOf, FunctionLength::ONE); + SetFunction(env, stringFuncPrototype, "localeCompare", BuiltinsString::LocaleCompare, FunctionLength::ONE); + SetFunction(env, stringFuncPrototype, "match", BuiltinsString::Match, FunctionLength::ONE); + SetFunction(env, stringFuncPrototype, "normalize", BuiltinsString::Normalize, FunctionLength::ZERO); + SetFunction(env, stringFuncPrototype, "padEnd", BuiltinsString::padEnd, FunctionLength::ONE); + SetFunction(env, stringFuncPrototype, "padStart", BuiltinsString::padStart, FunctionLength::ONE); + SetFunction(env, stringFuncPrototype, "repeat", BuiltinsString::Repeat, 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); + SetFunction(env, stringFuncPrototype, "substr", BuiltinsString::SubStr, FunctionLength::TWO); + 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, "trimEnd", BuiltinsString::TrimEnd, FunctionLength::ZERO); + SetFunction(env, stringFuncPrototype, "trimStart", BuiltinsString::TrimStart, FunctionLength::ZERO); + + // trimRight + JSHandle trimRight(factory_->NewFromString("trimRight")); + JSHandle trimEnd(factory_->NewFromString("trimEnd")); + JSHandle trimEndFunc = + JSObject::GetMethod(thread_, JSHandle::Cast(stringFuncPrototype), trimEnd); + PropertyDescriptor trimRightDescriptor(thread_, trimEndFunc, true, false, true); + JSObject::DefineOwnProperty(thread_, stringFuncPrototype, trimRight, trimRightDescriptor); + + // trimLeft + JSHandle trimLeft(factory_->NewFromString("trimLeft")); + JSHandle trimStart(factory_->NewFromString("trimStart")); + JSHandle trimStartFunc = + JSObject::GetMethod(thread_, JSHandle::Cast(stringFuncPrototype), trimStart); + PropertyDescriptor trimLeftdescriptor(thread_, trimStartFunc, true, false, true); + JSObject::DefineOwnProperty(thread_, stringFuncPrototype, trimLeft, trimLeftdescriptor); + + SetFunction(env, stringFuncPrototype, thread_->GlobalConstants()->GetHandledValueOfString(), + BuiltinsString::ValueOf, FunctionLength::ZERO); + SetFunctionAtSymbol(env, stringFuncPrototype, env->GetIteratorSymbol(), "[Symbol.iterator]", + BuiltinsString::GetStringIterator, FunctionLength::ZERO); + + // String method + SetFunction(env, stringFunction, "fromCharCode", BuiltinsString::FromCharCode, FunctionLength::ONE); + SetFunction(env, stringFunction, "fromCodePoint", BuiltinsString::FromCodePoint, FunctionLength::ONE); + SetFunction(env, stringFunction, "raw", BuiltinsString::Raw, FunctionLength::ONE); + + // String.prototype.length + JSHandle lengthGetter = CreateGetter(env, BuiltinsString::GetLength, "length", FunctionLength::ZERO); + JSHandle lengthKey(factory_->NewFromCanBeCompressString("length")); + SetGetter(stringFuncPrototype, lengthKey, lengthGetter); + + env->SetStringFunction(thread_, stringFunction); +} + +void Builtins::InitializeStringIterator(const JSHandle &env, + const JSHandle &iteratorFuncDynclass) const +{ + // StringIterator.prototype + JSHandle strIterPrototype(factory_->NewJSObject(iteratorFuncDynclass)); + + // StringIterator.prototype_or_dynclass + JSHandle strIterFuncInstanceDynclass = factory_->NewEcmaDynClass( + JSStringIterator::SIZE, JSType::JS_STRING_ITERATOR, JSHandle(strIterPrototype)); + + JSHandle strIterFunction( + factory_->NewJSFunction(env, static_cast(nullptr), FunctionKind::BASE_CONSTRUCTOR)); + strIterFunction->SetFunctionPrototype(thread_, strIterFuncInstanceDynclass.GetTaggedValue()); + + SetFunction(env, strIterPrototype, "next", StringIterator::Next, FunctionLength::ZERO); + SetStringTagSymbol(env, strIterPrototype, "String Iterator"); + + env->SetStringIterator(thread_, strIterFunction); + env->SetStringIteratorPrototype(thread_, strIterPrototype); +} + +void Builtins::InitializeIterator(const JSHandle &env, const JSHandle &objFuncDynclass) const +{ + [[maybe_unused]] EcmaHandleScope scope(thread_); + // Iterator.prototype + JSHandle iteratorPrototype = factory_->NewJSObject(objFuncDynclass); + // Iterator.prototype.next() + SetFunction(env, iteratorPrototype, "next", BuiltinsIterator::Next, FunctionLength::ONE); + // Iterator.prototype.return() + SetFunction(env, iteratorPrototype, "return", BuiltinsIterator::Return, FunctionLength::ONE); + // Iterator.prototype.throw() + SetFunction(env, iteratorPrototype, "throw", BuiltinsIterator::Throw, FunctionLength::ONE); + // %IteratorPrototype% [ @@iterator ] + SetFunctionAtSymbol(env, iteratorPrototype, env->GetIteratorSymbol(), "[Symbol.iterator]", + BuiltinsIterator::GetIteratorObj, FunctionLength::ZERO); + env->SetIteratorPrototype(thread_, iteratorPrototype); + + // Iterator.dynclass + JSHandle iteratorFuncDynclass = + factory_->NewEcmaDynClass(JSObject::SIZE, JSType::JS_ITERATOR, JSHandle(iteratorPrototype)); + + InitializeForinIterator(env, iteratorFuncDynclass); + InitializeSetIterator(env, iteratorFuncDynclass); + InitializeMapIterator(env, iteratorFuncDynclass); + InitializeArrayIterator(env, iteratorFuncDynclass); + InitializeStringIterator(env, iteratorFuncDynclass); +} + +void Builtins::InitializeAsyncIterator(const JSHandle &env, const JSHandle &objFuncDynclass) const +{ + [[maybe_unused]] EcmaHandleScope scope(thread_); + // AsyncIterator.prototype + JSHandle asyncIteratorPrototype = factory_->NewJSObject(objFuncDynclass); + // %AsyncIteratorPrototype% [ @@asyncIterator ] + SetFunctionAtSymbol(env, asyncIteratorPrototype, env->GetAsyncIteratorSymbol(), "[Symbol.asyncIterator]", + BuiltinsAsyncIterator::GetAsyncIteratorObj, FunctionLength::ZERO); + env->SetAsyncIteratorPrototype(thread_, asyncIteratorPrototype); +} + +void Builtins::InitializeForinIterator(const JSHandle &env, + const JSHandle &iteratorFuncDynclass) const +{ + [[maybe_unused]] EcmaHandleScope scope(thread_); + // Iterator.prototype + JSHandle forinIteratorPrototype = factory_->NewJSObject(iteratorFuncDynclass); + JSHandle dynclass = factory_->NewEcmaDynClass(JSForInIterator::SIZE, JSType::JS_FORIN_ITERATOR, + JSHandle(forinIteratorPrototype)); + + // Iterator.prototype.next() + SetFunction(env, forinIteratorPrototype, "next", JSForInIterator::Next, FunctionLength::ONE); + env->SetForinIteratorPrototype(thread_, forinIteratorPrototype); + env->SetForinIteratorClass(thread_, dynclass); +} + +void Builtins::InitializeSetIterator(const JSHandle &env, + const JSHandle &iteratorFuncDynclass) const +{ + // SetIterator.prototype + JSHandle setIteratorPrototype(factory_->NewJSObject(iteratorFuncDynclass)); + // Iterator.prototype.next() + SetFunction(env, setIteratorPrototype, "next", JSSetIterator::Next, FunctionLength::ZERO); + SetStringTagSymbol(env, setIteratorPrototype, "Set Iterator"); + env->SetSetIteratorPrototype(thread_, setIteratorPrototype); +} + +void Builtins::InitializeMapIterator(const JSHandle &env, + const JSHandle &iteratorFuncDynclass) const +{ + // MapIterator.prototype + JSHandle mapIteratorPrototype(factory_->NewJSObject(iteratorFuncDynclass)); + // Iterator.prototype.next() + SetFunction(env, mapIteratorPrototype, "next", JSMapIterator::Next, FunctionLength::ZERO); + SetStringTagSymbol(env, mapIteratorPrototype, "Map Iterator"); + env->SetMapIteratorPrototype(thread_, mapIteratorPrototype); +} +void Builtins::InitializeArrayIterator(const JSHandle &env, + const JSHandle &iteratorFuncDynclass) const +{ + // ArrayIterator.prototype + JSHandle arrayIteratorPrototype(factory_->NewJSObject(iteratorFuncDynclass)); + // Iterator.prototype.next() + SetFunction(env, arrayIteratorPrototype, "next", JSArrayIterator::Next, FunctionLength::ZERO); + SetStringTagSymbol(env, arrayIteratorPrototype, "Array Iterator"); + env->SetArrayIteratorPrototype(thread_, arrayIteratorPrototype); +} + +void Builtins::InitializeRegExp(const JSHandle &env) +{ + [[maybe_unused]] EcmaHandleScope scope(thread_); + // RegExp.prototype + JSHandle objFun = env->GetObjectFunction(); + JSHandle regPrototype = factory_->NewJSObjectByConstructor(JSHandle(objFun), objFun); + JSHandle regPrototypeValue(regPrototype); + + // RegExp.prototype_or_dynclass + JSHandle regexpFuncInstanceDynclass = factory_->CreateJSRegExpInstanceClass(regPrototypeValue); + + // RegExp = new Function() + JSHandle regexpFunction( + NewBuiltinConstructor(env, regPrototype, RegExp::RegExpConstructor, "RegExp", FunctionLength::TWO)); + + JSHandle(regexpFunction)->SetFunctionPrototype(thread_, regexpFuncInstanceDynclass.GetTaggedValue()); + + // RegExp.prototype method + SetFunction(env, regPrototype, "exec", RegExp::Exec, FunctionLength::ONE); + SetFunction(env, regPrototype, "test", RegExp::Test, FunctionLength::ONE); + SetFunction(env, regPrototype, thread_->GlobalConstants()->GetHandledToStringString(), RegExp::ToString, + FunctionLength::ZERO); + + JSHandle flagsGetter = CreateGetter(env, RegExp::GetFlags, "flags", FunctionLength::ZERO); + JSHandle flagsKey(factory_->NewFromCanBeCompressString("flags")); + SetGetter(regPrototype, flagsKey, flagsGetter); + + JSHandle sourceGetter = CreateGetter(env, RegExp::GetSource, "source", FunctionLength::ZERO); + JSHandle sourceKey(factory_->NewFromCanBeCompressString("source")); + SetGetter(regPrototype, sourceKey, sourceGetter); + + JSHandle globalGetter = CreateGetter(env, RegExp::GetGlobal, "global", FunctionLength::ZERO); + JSHandle globalKey(factory_->NewFromCanBeCompressString("global")); + SetGetter(regPrototype, globalKey, globalGetter); + + JSHandle ignoreCaseGetter = + CreateGetter(env, RegExp::GetIgnoreCase, "ignoreCase", FunctionLength::ZERO); + JSHandle ignoreCaseKey(factory_->NewFromCanBeCompressString("ignoreCase")); + SetGetter(regPrototype, ignoreCaseKey, ignoreCaseGetter); + + JSHandle multilineGetter = + CreateGetter(env, RegExp::GetMultiline, "multiline", FunctionLength::ZERO); + JSHandle multilineKey(factory_->NewFromCanBeCompressString("multiline")); + SetGetter(regPrototype, multilineKey, multilineGetter); + + JSHandle dotAllGetter = CreateGetter(env, RegExp::GetDotAll, "dotAll", FunctionLength::ZERO); + JSHandle dotAllKey(factory_->NewFromCanBeCompressString("dotAll")); + SetGetter(regPrototype, dotAllKey, dotAllGetter); + + JSHandle stickyGetter = CreateGetter(env, RegExp::GetSticky, "sticky", FunctionLength::ZERO); + JSHandle stickyKey(factory_->NewFromCanBeCompressString("sticky")); + SetGetter(regPrototype, stickyKey, stickyGetter); + + JSHandle unicodeGetter = CreateGetter(env, RegExp::GetUnicode, "unicode", FunctionLength::ZERO); + JSHandle unicodeKey(factory_->NewFromCanBeCompressString("unicode")); + SetGetter(regPrototype, unicodeKey, unicodeGetter); + + // Set RegExp [ @@species ] + JSHandle speciesSymbol = env->GetSpeciesSymbol(); + JSHandle speciesGetter = + CreateGetter(env, BuiltinsMap::Species, "[Symbol.species]", FunctionLength::ZERO); + SetGetter(JSHandle(regexpFunction), speciesSymbol, speciesGetter); + + // Set RegExp.prototype[@@split] + SetFunctionAtSymbol(env, regPrototype, env->GetSplitSymbol(), "[Symbol.split]", RegExp::Split, FunctionLength::TWO); + // Set RegExp.prototype[@@search] + SetFunctionAtSymbol(env, regPrototype, env->GetSearchSymbol(), "[Symbol.search]", RegExp::Search, + FunctionLength::ONE); + // Set RegExp.prototype[@@match] + SetFunctionAtSymbol(env, regPrototype, env->GetMatchSymbol(), "[Symbol.match]", RegExp::Match, FunctionLength::ONE); + // Set RegExp.prototype[@@replace] + SetFunctionAtSymbol(env, regPrototype, env->GetReplaceSymbol(), "[Symbol.replace]", RegExp::Replace, + FunctionLength::TWO); + + env->SetRegExpFunction(thread_, regexpFunction); + auto globalConst = const_cast(thread_->GlobalConstants()); + globalConst->SetConstant(ConstantIndex::JS_REGEXP_CLASS_INDEX, regexpFuncInstanceDynclass.GetTaggedValue()); +} + +void Builtins::InitializeArray(const JSHandle &env, const JSHandle &objFuncPrototypeVal) const +{ + [[maybe_unused]] EcmaHandleScope scope(thread_); + // Arraybase.prototype + JSHandle arrBaseFuncInstanceDynclass = factory_->CreateJSArrayInstanceClass(objFuncPrototypeVal); + + // Array.prototype + JSHandle arrFuncPrototype = factory_->NewJSObject(arrBaseFuncInstanceDynclass); + JSHandle::Cast(arrFuncPrototype)->SetLength(thread_, JSTaggedValue(FunctionLength::ZERO)); + auto accessor = thread_->GlobalConstants()->GetArrayLengthAccessor(); + JSArray::Cast(*arrFuncPrototype)->SetPropertyInlinedProps(thread_, JSArray::LENGTH_INLINE_PROPERTY_INDEX, accessor); + JSHandle arrFuncPrototypeValue(arrFuncPrototype); + + // Array.prototype_or_dynclass + JSHandle arrFuncInstanceDynclass = factory_->CreateJSArrayInstanceClass(arrFuncPrototypeValue); + + // Array = new Function() + JSHandle arrayFunction( + NewBuiltinConstructor(env, arrFuncPrototype, BuiltinsArray::ArrayConstructor, "Array", FunctionLength::ONE)); + JSHandle arrayFuncFunction(arrayFunction); + + // Set the [[Realm]] internal slot of F to the running execution context's Realm + JSHandle lexicalEnv = factory_->NewLexicalEnv(0); + lexicalEnv->SetParentEnv(thread_, env.GetTaggedValue()); + arrayFuncFunction->SetLexicalEnv(thread_, lexicalEnv.GetTaggedValue()); + + arrayFuncFunction->SetFunctionPrototype(thread_, arrFuncInstanceDynclass.GetTaggedValue()); + + // Array.prototype method + SetFunction(env, arrFuncPrototype, "concat", BuiltinsArray::Concat, FunctionLength::ONE); + SetFunction(env, arrFuncPrototype, "copyWithin", BuiltinsArray::CopyWithin, FunctionLength::TWO); + SetFunction(env, arrFuncPrototype, "entries", BuiltinsArray::Entries, FunctionLength::ZERO); + SetFunction(env, arrFuncPrototype, "every", BuiltinsArray::Every, FunctionLength::ONE); + SetFunction(env, arrFuncPrototype, "flat", BuiltinsArray::Flat, FunctionLength::ZERO); + SetFunction(env, arrFuncPrototype, "flatMap", BuiltinsArray::FlatMap, 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, "includes", BuiltinsArray::Includes, FunctionLength::ONE); + SetFunction(env, arrFuncPrototype, "indexOf", BuiltinsArray::IndexOf, FunctionLength::ONE); + SetFunction(env, arrFuncPrototype, "join", BuiltinsArray::Join, FunctionLength::ONE); + SetFunction(env, arrFuncPrototype, "keys", BuiltinsArray::Keys, FunctionLength::ZERO); + SetFunction(env, arrFuncPrototype, "lastIndexOf", BuiltinsArray::LastIndexOf, FunctionLength::ONE); + SetFunction(env, arrFuncPrototype, "map", BuiltinsArray::Map, FunctionLength::ONE); + SetFunction(env, arrFuncPrototype, "pop", BuiltinsArray::Pop, FunctionLength::ZERO); + SetFunction(env, arrFuncPrototype, "push", BuiltinsArray::Push, FunctionLength::ONE); + SetFunction(env, arrFuncPrototype, "reduce", BuiltinsArray::Reduce, FunctionLength::ONE); + SetFunction(env, arrFuncPrototype, "reduceRight", BuiltinsArray::ReduceRight, FunctionLength::ONE); + SetFunction(env, arrFuncPrototype, "reverse", BuiltinsArray::Reverse, FunctionLength::ZERO); + SetFunction(env, arrFuncPrototype, "shift", BuiltinsArray::Shift, FunctionLength::ZERO); + SetFunction(env, arrFuncPrototype, "slice", BuiltinsArray::Slice, FunctionLength::TWO); + SetFunction(env, arrFuncPrototype, "some", BuiltinsArray::Some, FunctionLength::ONE); + SetFunction(env, arrFuncPrototype, "sort", BuiltinsArray::Sort, FunctionLength::ONE); + SetFunction(env, arrFuncPrototype, "splice", BuiltinsArray::Splice, FunctionLength::TWO); + SetFunction(env, arrFuncPrototype, thread_->GlobalConstants()->GetHandledToLocaleStringString(), + BuiltinsArray::ToLocaleString, FunctionLength::ZERO); + SetFunction(env, arrFuncPrototype, thread_->GlobalConstants()->GetHandledToStringString(), BuiltinsArray::ToString, + FunctionLength::ZERO); + SetFunction(env, arrFuncPrototype, "unshift", BuiltinsArray::Unshift, FunctionLength::ONE); + SetFunction(env, arrFuncPrototype, "values", BuiltinsArray::Values, FunctionLength::ZERO); + + // %ArrayPrototype% [ @@iterator ] + JSHandle values(factory_->NewFromCanBeCompressString("values")); + JSHandle iteratorSymbol = env->GetIteratorSymbol(); + JSHandle valuesFunc = + JSObject::GetMethod(thread_, JSHandle::Cast(arrFuncPrototype), values); + PropertyDescriptor iteartorDesc(thread_, valuesFunc, true, false, true); + JSObject::DefineOwnProperty(thread_, arrFuncPrototype, iteratorSymbol, iteartorDesc); + + // Array method + SetFunction(env, arrayFunction, "from", BuiltinsArray::From, FunctionLength::ONE); + SetFunction(env, arrayFunction, "isArray", BuiltinsArray::IsArray, FunctionLength::ONE); + SetFunction(env, arrayFunction, "of", BuiltinsArray::Of, FunctionLength::ZERO); + + // 22.1.2.5 get %Array% [ @@species ] + JSHandle speciesSymbol = env->GetSpeciesSymbol(); + JSHandle speciesGetter = + CreateGetter(env, BuiltinsArray::Species, "[Symbol.species]", FunctionLength::ZERO); + SetGetter(JSHandle(arrayFunction), speciesSymbol, speciesGetter); + + const int arrProtoLen = 0; + JSHandle key_string = thread_->GlobalConstants()->GetHandledLengthString(); + PropertyDescriptor descriptor(thread_, JSHandle(thread_, JSTaggedValue(arrProtoLen)), true, false, + false); + JSObject::DefineOwnProperty(thread_, arrFuncPrototype, key_string, descriptor); + + JSHandle valuesKey(factory_->NewFromCanBeCompressString("values")); + PropertyDescriptor desc(thread_); + JSObject::GetOwnProperty(thread_, arrFuncPrototype, valuesKey, desc); + + // Array.prototype [ @@unscopables ] + JSHandle unscopablesSymbol = env->GetUnscopablesSymbol(); + JSHandle unscopablesGetter = + CreateGetter(env, BuiltinsArray::Unscopables, "[Symbol.unscopables]", FunctionLength::ZERO); + SetGetter(JSHandle(arrFuncPrototype), unscopablesSymbol, unscopablesGetter); + + env->SetArrayProtoValuesFunction(thread_, desc.GetValue()); + env->SetArrayFunction(thread_, arrayFunction); + env->SetArrayPrototype(thread_, arrFuncPrototype); +} + +void Builtins::InitializeTypedArray(const JSHandle &env, const JSHandle &objFuncDynclass) const +{ + [[maybe_unused]] EcmaHandleScope scope(thread_); + // TypedArray.prototype + JSHandle typedArrFuncPrototype = factory_->NewJSObject(objFuncDynclass); + JSHandle typedArrFuncPrototypeValue(typedArrFuncPrototype); + + // TypedArray.prototype_or_dynclass + JSHandle typedArrFuncInstanceDynclass = factory_->NewEcmaDynClass( + panda::ecmascript::JSTypedArray::SIZE, JSType::JS_TYPED_ARRAY, typedArrFuncPrototypeValue); + + // TypedArray = new Function() + JSHandle typedArrayFunction(NewBuiltinConstructor( + env, typedArrFuncPrototype, BuiltinsTypedArray::TypedArrayBaseConstructor, "TypedArray", FunctionLength::ZERO)); + + JSHandle(typedArrayFunction) + ->SetProtoOrDynClass(thread_, typedArrFuncInstanceDynclass.GetTaggedValue()); + + // TypedArray.prototype method + SetFunction(env, typedArrFuncPrototype, "copyWithin", BuiltinsTypedArray::CopyWithin, FunctionLength::TWO); + SetFunction(env, typedArrFuncPrototype, "entries", BuiltinsTypedArray::Entries, FunctionLength::ZERO); + SetFunction(env, typedArrFuncPrototype, "every", BuiltinsTypedArray::Every, FunctionLength::ONE); + SetFunction(env, typedArrFuncPrototype, "fill", BuiltinsTypedArray::Fill, FunctionLength::ONE); + SetFunction(env, typedArrFuncPrototype, "filter", BuiltinsTypedArray::Filter, FunctionLength::ONE); + SetFunction(env, typedArrFuncPrototype, "find", BuiltinsTypedArray::Find, FunctionLength::ONE); + SetFunction(env, typedArrFuncPrototype, "findIndex", BuiltinsTypedArray::FindIndex, FunctionLength::ONE); + SetFunction(env, typedArrFuncPrototype, "forEach", BuiltinsTypedArray::ForEach, FunctionLength::ONE); + SetFunction(env, typedArrFuncPrototype, "includes", BuiltinsTypedArray::Includes, FunctionLength::ONE); + SetFunction(env, typedArrFuncPrototype, "indexOf", BuiltinsTypedArray::IndexOf, FunctionLength::ONE); + SetFunction(env, typedArrFuncPrototype, "join", BuiltinsTypedArray::Join, FunctionLength::ONE); + SetFunction(env, typedArrFuncPrototype, "keys", BuiltinsTypedArray::Keys, FunctionLength::ZERO); + SetFunction(env, typedArrFuncPrototype, "lastIndexOf", BuiltinsTypedArray::LastIndexOf, FunctionLength::ONE); + SetFunction(env, typedArrFuncPrototype, "map", BuiltinsTypedArray::Map, FunctionLength::ONE); + SetFunction(env, typedArrFuncPrototype, "reduce", BuiltinsTypedArray::Reduce, FunctionLength::ONE); + SetFunction(env, typedArrFuncPrototype, "reduceRight", BuiltinsTypedArray::ReduceRight, FunctionLength::ONE); + SetFunction(env, typedArrFuncPrototype, "reverse", BuiltinsTypedArray::Reverse, FunctionLength::ZERO); + SetFunction(env, typedArrFuncPrototype, "set", BuiltinsTypedArray::Set, FunctionLength::ONE); + SetFunction(env, typedArrFuncPrototype, "slice", BuiltinsTypedArray::Slice, FunctionLength::TWO); + SetFunction(env, typedArrFuncPrototype, "some", BuiltinsTypedArray::Some, FunctionLength::ONE); + SetFunction(env, typedArrFuncPrototype, "sort", BuiltinsTypedArray::Sort, FunctionLength::ONE); + SetFunction(env, typedArrFuncPrototype, "subarray", BuiltinsTypedArray::Subarray, FunctionLength::TWO); + SetFunction(env, typedArrFuncPrototype, thread_->GlobalConstants()->GetHandledToLocaleStringString(), + BuiltinsTypedArray::ToLocaleString, FunctionLength::ZERO); + SetFunction(env, typedArrFuncPrototype, "values", BuiltinsTypedArray::Values, FunctionLength::ZERO); + + JSHandle bufferGetter = + CreateGetter(env, BuiltinsTypedArray::GetBuffer, "buffer", FunctionLength::ZERO); + JSHandle bufferKey(factory_->NewFromCanBeCompressString("buffer")); + SetGetter(typedArrFuncPrototype, bufferKey, bufferGetter); + + JSHandle byteLengthGetter = + CreateGetter(env, BuiltinsTypedArray::GetByteLength, "byteLength", FunctionLength::ZERO); + JSHandle byteLengthKey(factory_->NewFromCanBeCompressString("byteLength")); + SetGetter(typedArrFuncPrototype, byteLengthKey, byteLengthGetter); + + JSHandle byteOffsetGetter = + CreateGetter(env, BuiltinsTypedArray::GetByteOffset, "byteOffset", FunctionLength::ZERO); + JSHandle byteOffsetKey(factory_->NewFromCanBeCompressString("byteOffset")); + SetGetter(typedArrFuncPrototype, byteOffsetKey, byteOffsetGetter); + + JSHandle lengthGetter = + CreateGetter(env, BuiltinsTypedArray::GetLength, "length", FunctionLength::ZERO); + JSHandle lengthKey(factory_->NewFromCanBeCompressString("length")); + SetGetter(typedArrFuncPrototype, lengthKey, lengthGetter); + + // %TypedArray%.prototype.toString() + JSHandle arrFuncPrototype = env->GetArrayPrototype(); + JSHandle toStringFunc = + JSObject::GetMethod(thread_, arrFuncPrototype, thread_->GlobalConstants()->GetHandledToStringString()); + PropertyDescriptor toStringDesc(thread_, toStringFunc, true, false, true); + JSObject::DefineOwnProperty(thread_, typedArrFuncPrototype, thread_->GlobalConstants()->GetHandledToStringString(), + toStringDesc); + + // %TypedArray%.prototype [ @@iterator ] ( ) + JSHandle values(factory_->NewFromCanBeCompressString("values")); + JSHandle iteratorSymbol = env->GetIteratorSymbol(); + JSHandle valuesFunc = + JSObject::GetMethod(thread_, JSHandle::Cast(typedArrFuncPrototype), values); + PropertyDescriptor iteartorDesc(thread_, valuesFunc, true, false, true); + JSObject::DefineOwnProperty(thread_, typedArrFuncPrototype, iteratorSymbol, iteartorDesc); + + // 22.2.3.31 get %TypedArray%.prototype [ @@toStringTag ] + JSHandle toStringTagSymbol = env->GetToStringTagSymbol(); + JSHandle toStringTagGetter = + CreateGetter(env, BuiltinsTypedArray::ToStringTag, "[Symbol.toStringTag]", FunctionLength::ZERO); + SetGetter(typedArrFuncPrototype, toStringTagSymbol, toStringTagGetter); + + // TypedArray method + SetFunction(env, typedArrayFunction, "from", BuiltinsTypedArray::From, FunctionLength::ONE); + SetFunction(env, typedArrayFunction, "of", BuiltinsTypedArray::Of, FunctionLength::ZERO); + + // 22.2.2.4 get %TypedArray% [ @@species ] + JSHandle speciesSymbol = env->GetSpeciesSymbol(); + JSHandle speciesGetter = + CreateGetter(env, BuiltinsTypedArray::Species, "[Symbol.species]", FunctionLength::ZERO); + SetGetter(JSHandle(typedArrayFunction), speciesSymbol, speciesGetter); + + env->SetTypedArrayFunction(thread_, typedArrayFunction.GetTaggedValue()); + env->SetTypedArrayPrototype(thread_, typedArrFuncPrototype); + + JSHandle specificTypedArrayFuncClass = + factory_->NewEcmaDynClass(JSFunction::SIZE, JSType::JS_FUNCTION, env->GetTypedArrayFunction(), + HClass::IS_CALLABLE | HClass::IS_BUILTINS_CTOR); + specificTypedArrayFuncClass->SetConstructor(true); + specificTypedArrayFuncClass->GetHClass()->MarkFieldAsNative(JSFunction::METHOD_OFFSET); + env->SetSpecificTypedArrayFunctionClass(thread_, specificTypedArrayFuncClass); + + InitializeInt8Array(env, typedArrFuncInstanceDynclass); + InitializeUint8Array(env, typedArrFuncInstanceDynclass); + InitializeUint8ClampedArray(env, typedArrFuncInstanceDynclass); + InitializeInt16Array(env, typedArrFuncInstanceDynclass); + InitializeUint16Array(env, typedArrFuncInstanceDynclass); + InitializeInt32Array(env, typedArrFuncInstanceDynclass); + InitializeUint32Array(env, typedArrFuncInstanceDynclass); + InitializeFloat32Array(env, typedArrFuncInstanceDynclass); + InitializeFloat64Array(env, typedArrFuncInstanceDynclass); +} + +void Builtins::InitializeInt8Array(const JSHandle &env, const JSHandle &objFuncDynclass) const +{ + [[maybe_unused]] EcmaHandleScope scope(thread_); + // Int8Array.prototype + JSHandle int8ArrFuncPrototype = factory_->NewJSObject(objFuncDynclass); + JSHandle int8ArrFuncPrototypeValue(int8ArrFuncPrototype); + + // Int8Array.prototype_or_dynclass + JSHandle int8ArrFuncInstanceDynclass = factory_->NewEcmaDynClass( + panda::ecmascript::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->SetProtoOrDynClass(thread_, int8ArrFuncInstanceDynclass.GetTaggedValue()); + + const int bytesPerElement = 1; + SetConstant(int8ArrFuncPrototype, "BYTES_PER_ELEMENT", JSTaggedValue(bytesPerElement)); + SetConstant(JSHandle(int8ArrayFunction), "BYTES_PER_ELEMENT", JSTaggedValue(bytesPerElement)); + env->SetInt8ArrayFunction(thread_, int8ArrayFunction); +} + +void Builtins::InitializeUint8Array(const JSHandle &env, const JSHandle &objFuncDynclass) const +{ + [[maybe_unused]] EcmaHandleScope scope(thread_); + // Uint8Array.prototype + JSHandle uint8ArrFuncPrototype = factory_->NewJSObject(objFuncDynclass); + JSHandle uint8ArrFuncPrototypeValue(uint8ArrFuncPrototype); + + // Uint8Array.prototype_or_dynclass + JSHandle uint8ArrFuncInstanceDynclass = factory_->NewEcmaDynClass( + panda::ecmascript::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->SetProtoOrDynClass(thread_, uint8ArrFuncInstanceDynclass.GetTaggedValue()); + + const int bytesPerElement = 1; + SetConstant(uint8ArrFuncPrototype, "BYTES_PER_ELEMENT", JSTaggedValue(bytesPerElement)); + SetConstant(JSHandle(uint8ArrayFunction), "BYTES_PER_ELEMENT", JSTaggedValue(bytesPerElement)); + env->SetUint8ArrayFunction(thread_, uint8ArrayFunction); +} + +void Builtins::InitializeUint8ClampedArray(const JSHandle &env, + const JSHandle &objFuncDynclass) const +{ + [[maybe_unused]] EcmaHandleScope scope(thread_); + // Uint8ClampedArray.prototype + JSHandle uint8ClampedArrFuncPrototype = factory_->NewJSObject(objFuncDynclass); + JSHandle uint8ClampedArrFuncPrototypeValue(uint8ClampedArrFuncPrototype); + + // Uint8ClampedArray.prototype_or_dynclass + JSHandle uint8ClampedArrFuncInstanceDynclass = factory_->NewEcmaDynClass( + panda::ecmascript::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->SetProtoOrDynClass(thread_, uint8ClampedArrFuncInstanceDynclass.GetTaggedValue()); + + const int bytesPerElement = 1; + SetConstant(uint8ClampedArrFuncPrototype, "BYTES_PER_ELEMENT", JSTaggedValue(bytesPerElement)); + SetConstant(JSHandle(uint8ClampedArrayFunction), "BYTES_PER_ELEMENT", JSTaggedValue(bytesPerElement)); + env->SetUint8ClampedArrayFunction(thread_, uint8ClampedArrayFunction); +} + +void Builtins::InitializeInt16Array(const JSHandle &env, const JSHandle &objFuncDynclass) const +{ + [[maybe_unused]] EcmaHandleScope scope(thread_); + // Int16Array.prototype + JSHandle int16ArrFuncPrototype = factory_->NewJSObject(objFuncDynclass); + JSHandle int16ArrFuncPrototypeValue(int16ArrFuncPrototype); + + // Int16Array.prototype_or_dynclass + JSHandle int16ArrFuncInstanceDynclass = factory_->NewEcmaDynClass( + panda::ecmascript::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->SetProtoOrDynClass(thread_, int16ArrFuncInstanceDynclass.GetTaggedValue()); + + const int bytesPerElement = 2; + SetConstant(int16ArrFuncPrototype, "BYTES_PER_ELEMENT", JSTaggedValue(bytesPerElement)); + SetConstant(JSHandle(int16ArrayFunction), "BYTES_PER_ELEMENT", JSTaggedValue(bytesPerElement)); + env->SetInt16ArrayFunction(thread_, int16ArrayFunction); +} + +void Builtins::InitializeUint16Array(const JSHandle &env, const JSHandle &objFuncDynclass) const +{ + [[maybe_unused]] EcmaHandleScope scope(thread_); + // Uint16Array.prototype + JSHandle uint16ArrFuncPrototype = factory_->NewJSObject(objFuncDynclass); + JSHandle uint16ArrFuncPrototypeValue(uint16ArrFuncPrototype); + + // Uint16Array.prototype_or_dynclass + JSHandle uint16ArrFuncInstanceDynclass = factory_->NewEcmaDynClass( + panda::ecmascript::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->SetProtoOrDynClass(thread_, uint16ArrFuncInstanceDynclass.GetTaggedValue()); + + const int bytesPerElement = 2; + SetConstant(uint16ArrFuncPrototype, "BYTES_PER_ELEMENT", JSTaggedValue(bytesPerElement)); + SetConstant(JSHandle(uint16ArrayFunction), "BYTES_PER_ELEMENT", JSTaggedValue(bytesPerElement)); + env->SetUint16ArrayFunction(thread_, uint16ArrayFunction); +} + +void Builtins::InitializeInt32Array(const JSHandle &env, const JSHandle &objFuncDynclass) const +{ + [[maybe_unused]] EcmaHandleScope scope(thread_); + // Int32Array.prototype + JSHandle int32ArrFuncPrototype = factory_->NewJSObject(objFuncDynclass); + JSHandle int32ArrFuncPrototypeValue(int32ArrFuncPrototype); + + // Int32Array.prototype_or_dynclass + JSHandle int32ArrFuncInstanceDynclass = factory_->NewEcmaDynClass( + panda::ecmascript::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->SetProtoOrDynClass(thread_, int32ArrFuncInstanceDynclass.GetTaggedValue()); + + const int bytesPerElement = 4; + SetConstant(int32ArrFuncPrototype, "BYTES_PER_ELEMENT", JSTaggedValue(bytesPerElement)); + SetConstant(JSHandle(int32ArrayFunction), "BYTES_PER_ELEMENT", JSTaggedValue(bytesPerElement)); + env->SetInt32ArrayFunction(thread_, int32ArrayFunction); +} + +void Builtins::InitializeUint32Array(const JSHandle &env, const JSHandle &objFuncDynclass) const +{ + [[maybe_unused]] EcmaHandleScope scope(thread_); + // Uint32Array.prototype + JSHandle uint32ArrFuncPrototype = factory_->NewJSObject(objFuncDynclass); + JSHandle uint32ArrFuncPrototypeValue(uint32ArrFuncPrototype); + + // Uint32Array.prototype_or_dynclass + JSHandle uint32ArrFuncInstanceDynclass = factory_->NewEcmaDynClass( + panda::ecmascript::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->SetProtoOrDynClass(thread_, uint32ArrFuncInstanceDynclass.GetTaggedValue()); + + const int bytesPerElement = 4; + SetConstant(uint32ArrFuncPrototype, "BYTES_PER_ELEMENT", JSTaggedValue(bytesPerElement)); + SetConstant(JSHandle(uint32ArrayFunction), "BYTES_PER_ELEMENT", JSTaggedValue(bytesPerElement)); + env->SetUint32ArrayFunction(thread_, uint32ArrayFunction); +} + +void Builtins::InitializeFloat32Array(const JSHandle &env, const JSHandle &objFuncDynclass) const +{ + [[maybe_unused]] EcmaHandleScope scope(thread_); + // Float32Array.prototype + JSHandle float32ArrFuncPrototype = factory_->NewJSObject(objFuncDynclass); + JSHandle float32ArrFuncPrototypeValue(float32ArrFuncPrototype); + + // Float32Array.prototype_or_dynclass + JSHandle float32ArrFuncInstanceDynclass = factory_->NewEcmaDynClass( + panda::ecmascript::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->SetProtoOrDynClass(thread_, float32ArrFuncInstanceDynclass.GetTaggedValue()); + + const int bytesPerElement = 4; + SetConstant(float32ArrFuncPrototype, "BYTES_PER_ELEMENT", JSTaggedValue(bytesPerElement)); + SetConstant(JSHandle(float32ArrayFunction), "BYTES_PER_ELEMENT", JSTaggedValue(bytesPerElement)); + env->SetFloat32ArrayFunction(thread_, float32ArrayFunction); +} + +void Builtins::InitializeFloat64Array(const JSHandle &env, const JSHandle &objFuncDynclass) const +{ + [[maybe_unused]] EcmaHandleScope scope(thread_); + // Float64Array.prototype + JSHandle float64ArrFuncPrototype = factory_->NewJSObject(objFuncDynclass); + JSHandle float64ArrFuncPrototypeValue(float64ArrFuncPrototype); + + // Float64Array.prototype_or_dynclass + JSHandle float64ArrFuncInstanceDynclass = factory_->NewEcmaDynClass( + panda::ecmascript::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->SetProtoOrDynClass(thread_, float64ArrFuncInstanceDynclass.GetTaggedValue()); + + const int bytesPerElement = 8; + SetConstant(float64ArrFuncPrototype, "BYTES_PER_ELEMENT", JSTaggedValue(bytesPerElement)); + SetConstant(JSHandle(float64ArrayFunction), "BYTES_PER_ELEMENT", JSTaggedValue(bytesPerElement)); + env->SetFloat64ArrayFunction(thread_, float64ArrayFunction); +} + +void Builtins::InitializeArrayBuffer(const JSHandle &env, const JSHandle &objFuncDynclass) const +{ + [[maybe_unused]] EcmaHandleScope scope(thread_); + // ArrayBuffer.prototype + JSHandle arrayBufferFuncPrototype = factory_->NewJSObject(objFuncDynclass); + JSHandle arrayBufferFuncPrototypeValue(arrayBufferFuncPrototype); + + // ArrayBuffer.prototype_or_dynclass + JSHandle arrayBufferFuncInstanceDynclass = + factory_->NewEcmaDynClass(JSArrayBuffer::SIZE, JSType::JS_ARRAY_BUFFER, arrayBufferFuncPrototypeValue); + + // ArrayBuffer = new Function() + JSHandle arrayBufferFunction(NewBuiltinConstructor( + env, arrayBufferFuncPrototype, ArrayBuffer::ArrayBufferConstructor, "ArrayBuffer", FunctionLength::ONE)); + + JSHandle(arrayBufferFunction) + ->SetFunctionPrototype(thread_, arrayBufferFuncInstanceDynclass.GetTaggedValue()); + + // ArrayBuffer prototype method + SetFunction(env, arrayBufferFuncPrototype, "slice", ArrayBuffer::Slice, FunctionLength::TWO); + + // ArrayBuffer method + SetFunction(env, arrayBufferFunction, "isView", ArrayBuffer::IsView, FunctionLength::ONE); + + // 24.1.3.3 get ArrayBuffer[@@species] + JSHandle speciesSymbol = env->GetSpeciesSymbol(); + JSHandle speciesGetter = + CreateGetter(env, ArrayBuffer::Species, "[Symbol.species]", FunctionLength::ZERO); + SetGetter(JSHandle(arrayBufferFunction), speciesSymbol, speciesGetter); + + // 24.1.4.1 get ArrayBuffer.prototype.byteLength + JSHandle lengthGetter = + CreateGetter(env, ArrayBuffer::GetByteLength, "byteLength", FunctionLength::ZERO); + JSHandle lengthKey(factory_->NewFromCanBeCompressString("byteLength")); + SetGetter(arrayBufferFuncPrototype, lengthKey, lengthGetter); + + // 24.1.4.4 ArrayBuffer.prototype[@@toStringTag] + SetStringTagSymbol(env, arrayBufferFuncPrototype, "ArrayBuffer"); + + env->SetArrayBufferFunction(thread_, arrayBufferFunction.GetTaggedValue()); +} + +void Builtins::InitializeReflect(const JSHandle &env, + const JSHandle &objFuncPrototypeVal) const +{ + [[maybe_unused]] EcmaHandleScope scope(thread_); + JSHandle reflectDynclass = + factory_->NewEcmaDynClass(JSObject::SIZE, JSType::JS_OBJECT, objFuncPrototypeVal); + JSHandle reflectObject = factory_->NewJSObject(reflectDynclass); + + SetFunction(env, reflectObject, "apply", Reflect::ReflectApply, FunctionLength::THREE); + SetFunction(env, reflectObject, "construct", Reflect::ReflectConstruct, FunctionLength::TWO); + SetFunction(env, reflectObject, "defineProperty", Reflect::ReflectDefineProperty, FunctionLength::THREE); + SetFunction(env, reflectObject, "deleteProperty", Reflect::ReflectDeleteProperty, FunctionLength::TWO); + SetFunction(env, reflectObject, "get", Reflect::ReflectGet, FunctionLength::TWO); + SetFunction(env, reflectObject, "getOwnPropertyDescriptor", Reflect::ReflectGetOwnPropertyDescriptor, + FunctionLength::TWO); + SetFunction(env, reflectObject, "getPrototypeOf", Reflect::ReflectGetPrototypeOf, FunctionLength::ONE); + SetFunction(env, reflectObject, "has", Reflect::ReflectHas, FunctionLength::TWO); + SetFunction(env, reflectObject, "isExtensible", Reflect::ReflectIsExtensible, FunctionLength::ONE); + SetFunction(env, reflectObject, "ownKeys", Reflect::ReflectOwnKeys, FunctionLength::ONE); + SetFunction(env, reflectObject, "preventExtensions", Reflect::ReflectPreventExtensions, FunctionLength::ONE); + SetFunction(env, reflectObject, "set", Reflect::ReflectSet, FunctionLength::THREE); + SetFunction(env, reflectObject, "setPrototypeOf", Reflect::ReflectSetPrototypeOf, FunctionLength::TWO); + + JSHandle reflectString(factory_->NewFromCanBeCompressString("Reflect")); + JSHandle globalObject(thread_, env->GetGlobalObject()); + PropertyDescriptor reflectDesc(thread_, JSHandle::Cast(reflectObject), true, false, true); + JSObject::DefineOwnProperty(thread_, globalObject, reflectString, reflectDesc); + + // @@ToStringTag + SetStringTagSymbol(env, reflectObject, "Reflect"); + + env->SetReflectFunction(thread_, reflectObject.GetTaggedValue()); +} + +void Builtins::InitializePromise(const JSHandle &env, const JSHandle &promiseFuncDynclass) +{ + [[maybe_unused]] EcmaHandleScope scope(thread_); + // Promise.prototype + JSHandle promiseFuncPrototype = factory_->NewJSObject(promiseFuncDynclass); + JSHandle promiseFuncPrototypeValue(promiseFuncPrototype); + // Promise.prototype_or_dynclass + JSHandle promiseFuncInstanceDynclass = + factory_->NewEcmaDynClass(JSPromise::SIZE, JSType::JS_PROMISE, promiseFuncPrototypeValue); + // Promise() = new Function() + JSHandle promiseFunction( + NewBuiltinConstructor(env, promiseFuncPrototype, Promise::PromiseConstructor, "Promise", FunctionLength::ONE)); + JSHandle(promiseFunction)->SetFunctionPrototype(thread_, promiseFuncInstanceDynclass.GetTaggedValue()); + + // Promise method + SetFunction(env, promiseFunction, "all", Promise::All, FunctionLength::ONE); + SetFunction(env, promiseFunction, "race", Promise::Race, FunctionLength::ONE); + SetFunction(env, promiseFunction, "resolve", Promise::Resolve, FunctionLength::ONE); + SetFunction(env, promiseFunction, "reject", Promise::Reject, FunctionLength::ONE); + + // promise.prototype method + SetFunction(env, promiseFuncPrototype, "catch", Promise::Catch, FunctionLength::ONE); + SetFunction(env, promiseFuncPrototype, "then", Promise::Then, FunctionLength::TWO); + + // Promise.prototype [ @@toStringTag ] + SetStringTagSymbol(env, promiseFuncPrototype, "Promise"); + + // Set Promise [@@species] + JSHandle speciesSymbol(env->GetSpeciesSymbol()); + JSHandle speciesGetter = + CreateGetter(env, Promise::GetSpecies, "[Symbol.species]", FunctionLength::ZERO); + SetGetter(promiseFunction, speciesSymbol, speciesGetter); + + env->SetPromiseFunction(thread_, promiseFunction); +} + +void Builtins::InitializePromiseJob(const JSHandle &env) +{ + JSHandle keyString(thread_->GlobalConstants()->GetHandledEmptyString()); + auto func = NewFunction(env, keyString, BuiltinsPromiseJob::PromiseReactionJob, FunctionLength::TWO); + env->SetPromiseReactionJob(thread_, func); + func = NewFunction(env, keyString, BuiltinsPromiseJob::PromiseResolveThenableJob, FunctionLength::THREE); + env->SetPromiseResolveThenableJob(thread_, func); +} + +void Builtins::InitializeDataView(const JSHandle &env, const JSHandle &objFuncDynclass) const +{ + [[maybe_unused]] EcmaHandleScope scope(thread_); + // ArrayBuffer.prototype + JSHandle dataViewFuncPrototype = factory_->NewJSObject(objFuncDynclass); + JSHandle dataViewFuncPrototypeValue(dataViewFuncPrototype); + + // ArrayBuffer.prototype_or_dynclass + JSHandle dataViewFuncInstanceDynclass = + factory_->NewEcmaDynClass(JSDataView::SIZE, JSType::JS_DATA_VIEW, dataViewFuncPrototypeValue); + + // ArrayBuffer = new Function() + JSHandle dataViewFunction(NewBuiltinConstructor(env, dataViewFuncPrototype, DataView::DataViewConstructor, + "DataView", FunctionLength::ONE)); + + JSHandle(dataViewFunction)->SetProtoOrDynClass(thread_, dataViewFuncInstanceDynclass.GetTaggedValue()); + // DataView.prototype method + SetFunction(env, dataViewFuncPrototype, "getFloat32", DataView::GetFloat32, FunctionLength::ONE); + SetFunction(env, dataViewFuncPrototype, "getFloat64", DataView::GetFloat64, FunctionLength::ONE); + SetFunction(env, dataViewFuncPrototype, "getInt8", DataView::GetInt8, FunctionLength::ONE); + SetFunction(env, dataViewFuncPrototype, "getInt16", DataView::GetInt16, FunctionLength::ONE); + SetFunction(env, dataViewFuncPrototype, "getInt32", DataView::GetInt32, FunctionLength::ONE); + SetFunction(env, dataViewFuncPrototype, "getUint8", DataView::GetUint8, FunctionLength::ONE); + SetFunction(env, dataViewFuncPrototype, "getUint16", DataView::GetUint16, FunctionLength::ONE); + SetFunction(env, dataViewFuncPrototype, "getUint32", DataView::GetUint32, FunctionLength::ONE); + SetFunction(env, dataViewFuncPrototype, "setFloat32", DataView::SetFloat32, FunctionLength::TWO); + SetFunction(env, dataViewFuncPrototype, "setFloat64", DataView::SetFloat64, FunctionLength::TWO); + SetFunction(env, dataViewFuncPrototype, "setInt8", DataView::SetInt8, FunctionLength::TWO); + SetFunction(env, dataViewFuncPrototype, "setInt16", DataView::SetInt16, FunctionLength::TWO); + SetFunction(env, dataViewFuncPrototype, "setInt32", DataView::SetInt32, FunctionLength::TWO); + SetFunction(env, dataViewFuncPrototype, "setUint8", DataView::SetUint8, FunctionLength::TWO); + SetFunction(env, dataViewFuncPrototype, "setUint16", DataView::SetUint16, FunctionLength::TWO); + SetFunction(env, dataViewFuncPrototype, "setUint32", DataView::SetUint32, FunctionLength::TWO); + + // 24.2.4.1 get DataView.prototype.buffer + JSHandle bufferGetter = CreateGetter(env, DataView::GetBuffer, "buffer", FunctionLength::ZERO); + JSHandle bufferKey(factory_->NewFromCanBeCompressString("buffer")); + SetGetter(dataViewFuncPrototype, bufferKey, bufferGetter); + + // 24.2.4.2 get DataView.prototype.byteLength + JSHandle lengthGetter = + CreateGetter(env, DataView::GetByteLength, "byteLength", FunctionLength::ZERO); + JSHandle lengthKey(factory_->NewFromCanBeCompressString("byteLength")); + SetGetter(dataViewFuncPrototype, lengthKey, lengthGetter); + + // 24.2.4.3 get DataView.prototype.byteOffset + JSHandle offsetGetter = CreateGetter(env, DataView::GetOffset, "byteOffset", FunctionLength::ZERO); + JSHandle offsetKey(factory_->NewFromCanBeCompressString("byteOffset")); + SetGetter(dataViewFuncPrototype, offsetKey, offsetGetter); + + // 24.2.4.21 DataView.prototype[ @@toStringTag ] + SetStringTagSymbol(env, dataViewFuncPrototype, "DataView"); + env->SetDataViewFunction(thread_, dataViewFunction.GetTaggedValue()); +} + +JSHandle Builtins::NewBuiltinConstructor(const JSHandle &env, + const JSHandle &prototype, EcmaEntrypoint ctorFunc, + const char *name, int length) const +{ + JSHandle ctor = + factory_->NewJSFunction(env, reinterpret_cast(ctorFunc), FunctionKind::BUILTIN_CONSTRUCTOR); + InitializeCtor(env, prototype, ctor, name, length); + return ctor; +} + +JSHandle Builtins::NewFunction(const JSHandle &env, const JSHandle &key, + EcmaEntrypoint func, int length) const +{ + JSHandle function = factory_->NewJSFunction(env, reinterpret_cast(func)); + JSFunction::SetFunctionLength(thread_, function, JSTaggedValue(length)); + JSHandle baseFunction(function); + JSHandle handleUndefine(thread_, JSTaggedValue::Undefined()); + JSFunction::SetFunctionName(thread_, baseFunction, key, handleUndefine); + return function; +} + +void Builtins::SetFunction(const JSHandle &env, const JSHandle &obj, const char *key, + EcmaEntrypoint func, int length) const +{ + JSHandle keyString(factory_->NewFromString(key)); + SetFunction(env, obj, keyString, func, length); +} + +void Builtins::SetFunction(const JSHandle &env, const JSHandle &obj, + const JSHandle &key, EcmaEntrypoint func, int length) const +{ + JSHandle function(NewFunction(env, key, func, length)); + PropertyDescriptor descriptor(thread_, JSHandle(function), true, false, true); + JSObject::DefineOwnProperty(thread_, obj, key, descriptor); +} + +void Builtins::SetFrozenFunction(const JSHandle &env, const JSHandle &obj, const char *key, + EcmaEntrypoint func, int length) const +{ + JSHandle keyString(factory_->NewFromString(key)); + JSHandle function = NewFunction(env, keyString, func, length); + PropertyDescriptor descriptor(thread_, JSHandle(function), false, false, false); + JSObject::DefineOwnProperty(thread_, obj, keyString, descriptor); +} + +template +void Builtins::SetFunctionAtSymbol(const JSHandle &env, const JSHandle &obj, + const JSHandle &symbol, const char *name, EcmaEntrypoint func, + int length) const +{ + JSHandle function = factory_->NewJSFunction(env, reinterpret_cast(func)); + JSFunction::SetFunctionLength(thread_, function, JSTaggedValue(length)); + JSHandle nameString(factory_->NewFromString(name)); + JSHandle baseFunction(function); + JSHandle handleUndefine(thread_, JSTaggedValue::Undefined()); + JSFunction::SetFunctionName(thread_, baseFunction, nameString, handleUndefine); + // NOLINTNEXTLINE(readability-braces-around-statements, bugprone-suspicious-semicolon) + if constexpr (flag == JSSymbol::SYMBOL_TO_PRIMITIVE_TYPE) { + PropertyDescriptor descriptor(thread_, JSHandle::Cast(function), false, false, true); + JSObject::DefineOwnProperty(thread_, obj, symbol, descriptor); + return; + } + if constexpr (flag == JSSymbol::SYMBOL_HAS_INSTANCE_TYPE) { // NOLINTE(readability-braces-around-statements) + // ecma 19.2.3.6 Function.prototype[@@hasInstance] has the attributes + // { [[Writable]]: false, [[Enumerable]]: false, [[Configurable]]: false }. + PropertyDescriptor descriptor(thread_, JSHandle::Cast(function), false, false, false); + JSObject::DefineOwnProperty(thread_, obj, symbol, descriptor); + return; + } + PropertyDescriptor descriptor(thread_, JSHandle::Cast(function), true, false, true); + JSObject::DefineOwnProperty(thread_, obj, symbol, descriptor); +} + +void Builtins::SetStringTagSymbol(const JSHandle &env, const JSHandle &obj, const char *key) const +{ + JSHandle tag(factory_->NewFromString(key)); + JSHandle symbol = env->GetToStringTagSymbol(); + PropertyDescriptor desc(thread_, tag, false, false, true); + JSObject::DefineOwnProperty(thread_, obj, symbol, desc); +} + +JSHandle Builtins::CreateGetter(const JSHandle &env, EcmaEntrypoint func, const char *name, + int length) const +{ + JSHandle function = factory_->NewJSFunction(env, reinterpret_cast(func)); + JSFunction::SetFunctionLength(thread_, function, JSTaggedValue(length)); + JSHandle funcName(factory_->NewFromString(name)); + JSHandle prefix = thread_->GlobalConstants()->GetHandledGetString(); + JSFunction::SetFunctionName(thread_, JSHandle(function), funcName, prefix); + return JSHandle(function); +} + +JSHandle Builtins::CreateSetter(const JSHandle &env, EcmaEntrypoint func, const char *name, + int length) +{ + JSHandle function = factory_->NewJSFunction(env, reinterpret_cast(func)); + JSFunction::SetFunctionLength(thread_, function, JSTaggedValue(length)); + JSHandle funcName(factory_->NewFromString(name)); + JSHandle prefix = thread_->GlobalConstants()->GetHandledSetString(); + JSFunction::SetFunctionName(thread_, JSHandle(function), funcName, prefix); + return JSHandle(function); +} + +void Builtins::SetConstant(const JSHandle &obj, const char *key, JSTaggedValue value) const +{ + JSHandle keyString(factory_->NewFromString(key)); + PropertyDescriptor descriptor(thread_, JSHandle(thread_, value), false, false, false); + JSObject::DefineOwnProperty(thread_, obj, keyString, descriptor); +} + +void Builtins::SetConstantObject(const JSHandle &obj, const char *key, JSHandle &value) const +{ + JSHandle keyString(factory_->NewFromString(key)); + PropertyDescriptor descriptor(thread_, value, false, false, false); + JSObject::DefineOwnProperty(thread_, obj, keyString, descriptor); +} + +void Builtins::SetGlobalThis(const JSHandle &obj, const char *key, const JSHandle &globalValue) +{ + JSHandle keyString(factory_->NewFromString(key)); + PropertyDescriptor descriptor(thread_, globalValue, true, false, true); + JSObject::DefineOwnProperty(thread_, obj, keyString, descriptor); +} + +void Builtins::SetAttribute(const JSHandle &obj, const char *key, const char *value) const +{ + JSHandle keyString(factory_->NewFromString(key)); + PropertyDescriptor descriptor(thread_, JSHandle(factory_->NewFromString(value)), true, false, true); + JSObject::DefineOwnProperty(thread_, obj, keyString, descriptor); +} + +void Builtins::SetNoneAttributeProperty(const JSHandle &obj, const char *key, + const JSHandle &value) const +{ + JSHandle keyString(factory_->NewFromString(key)); + PropertyDescriptor des(thread_, value, false, false, false); + JSObject::DefineOwnProperty(thread_, obj, keyString, des); +} + +void Builtins::SetFuncToObjAndGlobal(const JSHandle &env, const JSHandle &globalObject, + const JSHandle &obj, const char *key, EcmaEntrypoint func, int length) +{ + JSHandle function = factory_->NewJSFunction(env, reinterpret_cast(func)); + JSFunction::SetFunctionLength(thread_, function, JSTaggedValue(length)); + JSHandle keyString(factory_->NewFromString(key)); + JSHandle baseFunction(function); + JSHandle handleUndefine(thread_, JSTaggedValue::Undefined()); + JSFunction::SetFunctionName(thread_, baseFunction, keyString, handleUndefine); + PropertyDescriptor descriptor(thread_, JSHandle::Cast(function), true, false, true); + JSObject::DefineOwnProperty(thread_, obj, keyString, descriptor); + JSObject::DefineOwnProperty(thread_, globalObject, keyString, descriptor); +} + +void Builtins::StrictModeForbiddenAccessCallerArguments(const JSHandle &env, + const JSHandle &prototype) const +{ + JSHandle function = + factory_->NewJSFunction(env, reinterpret_cast(JSFunction::AccessCallerArgumentsThrowTypeError)); + + JSHandle caller(factory_->NewFromCanBeCompressString("caller")); + SetAccessor(prototype, caller, JSHandle::Cast(function), JSHandle::Cast(function)); + + JSHandle arguments(factory_->NewFromCanBeCompressString("arguments")); + SetAccessor(prototype, arguments, JSHandle::Cast(function), JSHandle::Cast(function)); +} + +void Builtins::InitializeGeneratorFunction(const JSHandle &env, + const JSHandle &objFuncDynclass) const +{ + [[maybe_unused]] EcmaHandleScope scope(thread_); + const GlobalEnvConstants *globalConst = thread_->GlobalConstants(); + JSHandle generatorFuncPrototype = factory_->NewJSObject(objFuncDynclass); + JSHandle generatorFuncPrototypeValue(generatorFuncPrototype); + + // 26.3.3.1 GeneratorFunction.prototype.constructor + // GeneratorFunction.prototype_or_dynclass + JSHandle generatorFuncInstanceDynclass = factory_->NewEcmaDynClass( + JSFunction::SIZE, JSType::JS_GENERATOR_FUNCTION, generatorFuncPrototypeValue, HClass::IS_CALLABLE); + generatorFuncInstanceDynclass->SetExtensible(true); + generatorFuncInstanceDynclass->GetHClass()->MarkFieldAsNative(JSFunction::METHOD_OFFSET); + // GeneratorFunction = new GeneratorFunction() + JSHandle generatorFunction = + NewBuiltinConstructor(env, generatorFuncPrototype, GeneratorObject::GeneratorFunctionConstructor, + "GeneratorFunction", FunctionLength::ONE); + JSHandle constructorKey = globalConst->GetHandledConstructorString(); + PropertyDescriptor generatorDesc(thread_, JSHandle::Cast(generatorFunction), false, false, true); + JSObject::DefineOwnProperty(thread_, generatorFuncPrototype, constructorKey, generatorDesc); + generatorFunction->SetProtoOrDynClass(thread_, generatorFuncInstanceDynclass.GetTaggedValue()); + env->SetGeneratorFunctionFunction(thread_, generatorFunction); + + // 26.3.3.2 GeneratorFunction.prototype.prototype -> Generator prototype object. + PropertyDescriptor descriptor(thread_, env->GetGeneratorPrototype(), false, false, true); + JSObject::DefineOwnProperty(thread_, generatorFuncPrototype, globalConst->GetHandledPrototypeString(), descriptor); + + // 26.3.3.3 GeneratorFunction.prototype[@@toStringTag] + SetStringTagSymbol(env, generatorFuncPrototype, "GeneratorFunction"); + + // GeneratorFunction prototype __proto__ -> Function. + JSObject::SetPrototype(thread_, generatorFuncPrototype, env->GetFunctionPrototype()); + + // 26.5.1.1 Generator.prototype.constructor -> %GeneratorFunction.prototype%. + PropertyDescriptor generatorObjDesc(thread_, generatorFuncPrototypeValue, false, false, true); + JSObject::DefineOwnProperty(thread_, JSHandle(env->GetInitialGenerator()), + globalConst->GetHandledConstructorString(), generatorObjDesc); + + // Generator instances prototype -> GeneratorFunction.prototype.prototype + PropertyDescriptor generatorObjProtoDesc(thread_, generatorFuncPrototypeValue, true, false, false); + JSObject::DefineOwnProperty(thread_, JSHandle(env->GetInitialGenerator()), + globalConst->GetHandledPrototypeString(), generatorObjProtoDesc); + + env->SetGeneratorFunctionPrototype(thread_, generatorFuncPrototype); +} + +void Builtins::InitializeGenerator(const JSHandle &env, const JSHandle &objFuncDynclass) const +{ + [[maybe_unused]] EcmaHandleScope scope(thread_); + const GlobalEnvConstants *globalConst = thread_->GlobalConstants(); + JSHandle generatorFuncPrototype = factory_->NewJSObject(objFuncDynclass); + + // GeneratorObject.prototype method + // 26.5.1.2 Generator.prototype.next(value) + SetFunction(env, generatorFuncPrototype, "next", GeneratorObject::GeneratorPrototypeNext, FunctionLength::ONE); + // 26.5.1.3 Generator.prototype.return(value) + SetFunction(env, generatorFuncPrototype, "return", GeneratorObject::GeneratorPrototypeReturn, FunctionLength::ONE); + // 26.5.1.4 Generator.prototype.throw(exception) + SetFunction(env, generatorFuncPrototype, "throw", GeneratorObject::GeneratorPrototypeThrow, FunctionLength::ONE); + + // 26.5.1.5 Generator.prototype[@@toStringTag] + SetStringTagSymbol(env, generatorFuncPrototype, "Generator"); + + // Generator with constructor, symbolTag, next/return/throw etc. + PropertyDescriptor descriptor(thread_, env->GetIteratorPrototype(), true, false, false); + JSObject::DefineOwnProperty(thread_, generatorFuncPrototype, globalConst->GetHandledPrototypeString(), descriptor); + env->SetGeneratorPrototype(thread_, generatorFuncPrototype); + JSObject::SetPrototype(thread_, generatorFuncPrototype, env->GetIteratorPrototype()); + + // Generator {} + JSHandle initialGeneratorFuncPrototype = factory_->NewJSObject(objFuncDynclass); + JSObject::SetPrototype(thread_, initialGeneratorFuncPrototype, JSHandle(generatorFuncPrototype)); + env->SetInitialGenerator(thread_, initialGeneratorFuncPrototype); +} + +void Builtins::InitializeAsyncGeneratorFunction(const JSHandle &env, + const JSHandle &objFuncDynclass) const +{ + [[maybe_unused]] EcmaHandleScope scope(thread_); + const GlobalEnvConstants *globalConst = thread_->GlobalConstants(); + JSHandle asyncGeneratorFuncPrototype = factory_->NewJSObject(objFuncDynclass); + JSHandle asyncGeneratorFuncPrototypeValue(asyncGeneratorFuncPrototype); + + // 26.3.3.1 AsyncGeneratorFunction.prototype.constructor + // AsyncGeneratorFunction.prototype_or_dynclass + JSHandle asyncGeneratorFuncInstanceDynclass = + factory_->NewEcmaDynClass(JSFunction::SIZE, JSType::JS_ASYNC_GENERATOR_FUNCTION, + asyncGeneratorFuncPrototypeValue, HClass::IS_CALLABLE); + asyncGeneratorFuncInstanceDynclass->SetExtensible(true); + asyncGeneratorFuncInstanceDynclass->GetHClass()->MarkFieldAsNative(JSFunction::METHOD_OFFSET); + + // AsyncGeneratorFunction = new AsyncGeneratorFunction() + JSHandle asyncGeneratorFunction = + NewBuiltinConstructor(env, asyncGeneratorFuncPrototype, AsyncGeneratorObject::AsyncGeneratorFunctionConstructor, + "AsyncGeneratorFunction", FunctionLength::ONE); + JSHandle constructorKey = globalConst->GetHandledConstructorString(); + PropertyDescriptor asyncGeneratorDesc(thread_, JSHandle::Cast(asyncGeneratorFunction), false, false, + true); + JSObject::DefineOwnProperty(thread_, asyncGeneratorFuncPrototype, constructorKey, asyncGeneratorDesc); + asyncGeneratorFunction->SetProtoOrDynClass(thread_, asyncGeneratorFuncInstanceDynclass.GetTaggedValue()); + env->SetAsyncGeneratorFunctionFunction(thread_, asyncGeneratorFunction); + + // 26.3.3.2 AsyncGeneratorFunction.prototype.prototype -> AsyncGenerator prototype object. + PropertyDescriptor descriptor(thread_, env->GetAsyncGeneratorPrototype(), false, false, true); + JSObject::DefineOwnProperty(thread_, asyncGeneratorFuncPrototype, globalConst->GetHandledPrototypeString(), + descriptor); + + // 26.3.3.3 AsyncGeneratorFunction.prototype[@@toStringTag] + SetStringTagSymbol(env, asyncGeneratorFuncPrototype, "AsyncGeneratorFunction"); + + // AsyncGeneratorFunction prototype __proto__ -> Function. + JSObject::SetPrototype(thread_, asyncGeneratorFuncPrototype, env->GetFunctionPrototype()); + + // 26.5.1.1 AsyncGenerator.prototype.constructor -> %AsyncGeneratorFunction.prototype%. + PropertyDescriptor asyncGeneratorObjDesc(thread_, asyncGeneratorFuncPrototypeValue, false, false, true); + JSObject::DefineOwnProperty(thread_, JSHandle(env->GetInitialAsyncGenerator()), + globalConst->GetHandledConstructorString(), asyncGeneratorObjDesc); + + // AsyncGenerator instances prototype -> AsyncGeneratorFunction.prototype.prototype + PropertyDescriptor asyncGeneratorObjProtoDesc(thread_, asyncGeneratorFuncPrototypeValue, true, false, false); + JSObject::DefineOwnProperty(thread_, JSHandle(env->GetInitialAsyncGenerator()), + globalConst->GetHandledPrototypeString(), asyncGeneratorObjProtoDesc); + + env->SetAsyncGeneratorFunctionPrototype(thread_, asyncGeneratorFuncPrototype); +} + +void Builtins::InitializeAsyncGenerator(const JSHandle &env, const JSHandle &objFuncDynclass) const +{ + [[maybe_unused]] EcmaHandleScope scope(thread_); + const GlobalEnvConstants *globalConst = thread_->GlobalConstants(); + JSHandle asyncGeneratorFuncPrototype = factory_->NewJSObject(objFuncDynclass); + + // AsyncGeneratorObject.prototype method + // 27.6.1.2 AsyncGenerator.prototype.next(value) + SetFunction(env, asyncGeneratorFuncPrototype, "next", AsyncGeneratorObject::AsyncGeneratorPrototypeNext, + FunctionLength::ONE); + // 27.6.1.3 AsyncGenerator.prototype.return(value) + SetFunction(env, asyncGeneratorFuncPrototype, "return", AsyncGeneratorObject::AsyncGeneratorPrototypeReturn, + FunctionLength::ONE); + // 27.6.1.4 AsyncGenerator.prototype.throw(exception) + SetFunction(env, asyncGeneratorFuncPrototype, "throw", AsyncGeneratorObject::AsyncGeneratorPrototypeThrow, + FunctionLength::ONE); + + // 27.6.1.5 AsyncGenerator.prototype[@@toStringTag] + SetStringTagSymbol(env, asyncGeneratorFuncPrototype, "AsyncGenerator"); + + PropertyDescriptor descriptor(thread_, env->GetAsyncIteratorPrototype(), true, false, false); + JSObject::DefineOwnProperty(thread_, asyncGeneratorFuncPrototype, globalConst->GetHandledPrototypeString(), + descriptor); + env->SetAsyncGeneratorPrototype(thread_, asyncGeneratorFuncPrototype); + JSObject::SetPrototype(thread_, asyncGeneratorFuncPrototype, env->GetAsyncIteratorPrototype()); + + // AsyncGenerator {} + JSHandle initialAsyncGeneratorFuncPrototype = factory_->NewJSObject(objFuncDynclass); + JSObject::SetPrototype(thread_, initialAsyncGeneratorFuncPrototype, + JSHandle(asyncGeneratorFuncPrototype)); + env->SetInitialAsyncGenerator(thread_, initialAsyncGeneratorFuncPrototype); +} + +void Builtins::InitializeAsyncFromSyncIteratorPrototypeObject(const JSHandle &env, + const JSHandle &objFuncDynclass) const +{ + [[maybe_unused]] EcmaHandleScope scope(thread_); + JSHandle asyncFromSyncIteratorPrototype = factory_->NewJSObject(objFuncDynclass); + + // 27.1.4.2.1 %AsyncFromSyncIteratorPrototype%.next + SetFunction(env, asyncFromSyncIteratorPrototype, "next", AsyncFromSyncIterator::AsyncFromSyncIteratorPrototypeNext, + FunctionLength::ONE); + // 27.1.4.2.2 %AsyncFromSyncIteratorPrototype%.return + SetFunction(env, asyncFromSyncIteratorPrototype, "return", + AsyncFromSyncIterator::AsyncFromSyncIteratorPrototypeReturn, FunctionLength::ONE); + // 27.1.4.2.3 %AsyncFromSyncIteratorPrototype%.throw + SetFunction(env, asyncFromSyncIteratorPrototype, "throw", + AsyncFromSyncIterator::AsyncFromSyncIteratorPrototypeThrow, FunctionLength::ONE); + + JSObject::SetPrototype(thread_, asyncFromSyncIteratorPrototype, env->GetAsyncIteratorPrototype()); + env->SetAsyncFromSyncIteratorPrototype(thread_, asyncFromSyncIteratorPrototype); +} + +void Builtins::SetArgumentsSharedAccessor(const JSHandle &env) +{ + JSHandle throwFunction = env->GetThrowTypeError(); + + JSHandle accessor = factory_->NewAccessorData(); + accessor->SetGetter(thread_, throwFunction); + accessor->SetSetter(thread_, throwFunction); + env->SetArgumentsCallerAccessor(thread_, accessor); + + accessor = factory_->NewAccessorData(); + accessor->SetGetter(thread_, throwFunction); + accessor->SetSetter(thread_, throwFunction); + env->SetArgumentsCalleeAccessor(thread_, accessor); +} + +void Builtins::SetAccessor(const JSHandle &obj, const JSHandle &key, + const JSHandle &getter, const JSHandle &setter) const +{ + JSHandle accessor = factory_->NewAccessorData(); + accessor->SetGetter(thread_, getter); + accessor->SetSetter(thread_, setter); + PropertyAttributes attr = PropertyAttributes::DefaultAccessor(false, false, true); + JSObject::AddAccessor(thread_, JSHandle::Cast(obj), key, accessor, attr); +} + +void Builtins::SetGetter(const JSHandle &obj, const JSHandle &key, + const JSHandle &getter) const +{ + JSHandle accessor = factory_->NewAccessorData(); + accessor->SetGetter(thread_, getter); + PropertyAttributes attr = PropertyAttributes::DefaultAccessor(false, false, true); + JSObject::AddAccessor(thread_, JSHandle::Cast(obj), key, accessor, attr); +} + +JSHandle Builtins::NewIntlConstructor(const JSHandle &env, const JSHandle &prototype, + EcmaEntrypoint ctorFunc, const char *name, int length) +{ + JSHandle ctor = + factory_->NewJSFunction(env, reinterpret_cast(ctorFunc), FunctionKind::BUILTIN_CONSTRUCTOR); + InitializeIntlCtor(env, prototype, ctor, name, length); + return ctor; +} + +void Builtins::InitializeIntlCtor(const JSHandle &env, const JSHandle &prototype, + const JSHandle &ctor, const char *name, int length) +{ + const GlobalEnvConstants *globalConst = thread_->GlobalConstants(); + JSFunction::SetFunctionLength(thread_, ctor, JSTaggedValue(length)); + JSHandle nameString(factory_->NewFromString(name)); + JSFunction::SetFunctionName(thread_, JSHandle(ctor), nameString, + JSHandle(thread_, JSTaggedValue::Undefined())); + JSHandle constructorKey = globalConst->GetHandledConstructorString(); + PropertyDescriptor descriptor1(thread_, JSHandle::Cast(ctor), true, false, true); + JSObject::DefineOwnProperty(thread_, prototype, constructorKey, descriptor1); + + // set "prototype" in constructor. + ctor->SetFunctionPrototype(thread_, prototype.GetTaggedValue()); + + if (!JSTaggedValue::SameValue(nameString, thread_->GlobalConstants()->GetHandledAsyncFunctionString())) { + JSHandle intlObject(thread_, env->GetIntlFunction().GetTaggedValue()); + PropertyDescriptor descriptor2(thread_, JSHandle::Cast(ctor), true, false, true); + JSObject::DefineOwnProperty(thread_, intlObject, nameString, descriptor2); + } +} + +void Builtins::InitializeIntl(const JSHandle &env, const JSHandle &objFuncPrototypeValue) +{ + [[maybe_unused]] EcmaHandleScope scope(thread_); + JSHandle intlDynclass = factory_->NewEcmaDynClass(JSObject::SIZE, JSType::JS_INTL, objFuncPrototypeValue); + JSHandle intlObject = factory_->NewJSObject(intlDynclass); + + JSHandle initIntlSymbol(factory_->NewPublicSymbolWithChar("Symbol.IntlLegacyConstructedSymbol")); + SetNoneAttributeProperty(intlObject, "fallbackSymbol", initIntlSymbol); + + SetFunction(env, intlObject, "getCanonicalLocales", Intl::GetCanonicalLocales, FunctionLength::ONE); + + // initial value of the "Intl" property of the global object. + JSHandle intlString(factory_->NewFromString("Intl")); + JSHandle globalObject(thread_, env->GetGlobalObject()); + PropertyDescriptor intlDesc(thread_, JSHandle::Cast(intlObject), true, false, true); + JSObject::DefineOwnProperty(thread_, globalObject, intlString, intlDesc); + + SetStringTagSymbol(env, intlObject, "Intl"); + + env->SetIntlFunction(thread_, intlObject); +} + +void Builtins::InitializeDateTimeFormat(const JSHandle &env) +{ + [[maybe_unused]] EcmaHandleScope scope(thread_); + // DateTimeFormat.prototype + JSHandle objFun = env->GetObjectFunction(); + JSHandle dtfPrototype = factory_->NewJSObjectByConstructor(JSHandle(objFun), objFun); + JSHandle dtfPrototypeValue(dtfPrototype); + + // DateTimeFormat.prototype_or_dynclass + JSHandle dtfFuncInstanceDynclass = + factory_->NewEcmaDynClass(JSDateTimeFormat::SIZE, JSType::JS_DATE_TIME_FORMAT, dtfPrototypeValue); + + // DateTimeFormat = new Function() + // 13.4.1 Intl.DateTimeFormat.prototype.constructor + JSHandle dtfFunction(NewIntlConstructor(env, dtfPrototype, DateTimeFormat::DateTimeFormatConstructor, + "DateTimeFormat", FunctionLength::ZERO)); + JSHandle(dtfFunction)->SetFunctionPrototype(thread_, JSTaggedValue(*dtfFuncInstanceDynclass)); + + // 13.3.2 Intl.DateTimeFormat.supportedLocalesOf ( locales [ , options ] ) + SetFunction(env, dtfFunction, "supportedLocalesOf", DateTimeFormat::SupportedLocalesOf, FunctionLength::ONE); + + // DateTimeFormat.prototype method + // 13.4.2 Intl.DateTimeFormat.prototype [ @@toStringTag ] + SetStringTagSymbol(env, dtfPrototype, "Intl.DateTimeFormat"); + env->SetDateTimeFormatFunction(thread_, dtfFunction); + + // 13.4.3 get Intl.DateTimeFormat.prototype.format + JSHandle formatGetter = CreateGetter(env, DateTimeFormat::Format, "format", FunctionLength::ZERO); + JSHandle formatSetter(thread_, JSTaggedValue::Undefined()); + SetAccessor(dtfPrototype, thread_->GlobalConstants()->GetHandledFormatString(), formatGetter, formatSetter); + + // 13.4.4 Intl.DateTimeFormat.prototype.formatToParts ( date ) + SetFunction(env, dtfPrototype, "formatToParts", DateTimeFormat::FormatToParts, FunctionLength::ONE); + + // 13.4.5 Intl.DateTimeFormat.prototype.resolvedOptions () + SetFunction(env, dtfPrototype, "resolvedOptions", DateTimeFormat::ResolvedOptions, FunctionLength::ZERO); + + SetFunction(env, dtfPrototype, "formatRange", DateTimeFormat::FormatRange, FunctionLength::TWO); + + SetFunction(env, dtfPrototype, "formatRangeToParts", DateTimeFormat::FormatRangeToParts, FunctionLength::TWO); +} + +void Builtins::InitializeRelativeTimeFormat(const JSHandle &env) +{ + [[maybe_unused]] EcmaHandleScope scope(thread_); + // RelativeTimeFormat.prototype + JSHandle objFun = env->GetObjectFunction(); + JSHandle rtfPrototype = factory_->NewJSObjectByConstructor(JSHandle(objFun), objFun); + JSHandle rtfPrototypeValue(rtfPrototype); + + // RelativeTimeFormat.prototype_or_dynclass + JSHandle rtfFuncInstanceDynclass = + factory_->NewEcmaDynClass(JSRelativeTimeFormat::SIZE, JSType::JS_RELATIVE_TIME_FORMAT, rtfPrototypeValue); + + // RelativeTimeFormat = new Function() + // 14.2.1 Intl.RelativeTimeFormat.prototype.constructor + JSHandle rtfFunction(NewIntlConstructor(env, rtfPrototype, + RelativeTimeFormat::RelativeTimeFormatConstructor, + "RelativeTimeFormat", FunctionLength::ZERO)); + JSHandle(rtfFunction)->SetFunctionPrototype(thread_, JSTaggedValue(*rtfFuncInstanceDynclass)); + + // 14.3.2 Intl.RelativeTimeFormat.supportedLocalesOf ( locales [ , options ] ) + SetFunction(env, rtfFunction, "supportedLocalesOf", RelativeTimeFormat::SupportedLocalesOf, FunctionLength::ONE); + + // RelativeTimeFormat.prototype method + // 14.4.2 Intl.RelativeTimeFormat.prototype [ @@toStringTag ] + SetStringTagSymbol(env, rtfPrototype, "Intl.RelativeTimeFormat"); + env->SetRelativeTimeFormatFunction(thread_, rtfFunction); + + // 14.4.3 get Intl.RelativeTimeFormat.prototype.format + SetFunction(env, rtfPrototype, "format", RelativeTimeFormat::Format, FunctionLength::TWO); + + // 14.4.4 Intl.RelativeTimeFormat.prototype.formatToParts( value, unit ) + SetFunction(env, rtfPrototype, "formatToParts", RelativeTimeFormat::FormatToParts, FunctionLength::TWO); + + // 14.4.5 Intl.RelativeTimeFormat.prototype.resolvedOptions () + SetFunction(env, rtfPrototype, "resolvedOptions", RelativeTimeFormat::ResolvedOptions, FunctionLength::ZERO); +} + +void Builtins::InitializeNumberFormat(const JSHandle &env) +{ + [[maybe_unused]] EcmaHandleScope scope(thread_); + // NumberFormat.prototype + JSHandle objFun = env->GetObjectFunction(); + JSHandle nfPrototype = factory_->NewJSObjectByConstructor(JSHandle(objFun), objFun); + JSHandle nfPrototypeValue(nfPrototype); + + // NumberFormat.prototype_or_dynclass + JSHandle nfFuncInstanceDynclass = + factory_->NewEcmaDynClass(JSNumberFormat::SIZE, JSType::JS_NUMBER_FORMAT, nfPrototypeValue); + + // NumberFormat = new Function() + // 12.4.1 Intl.NumberFormat.prototype.constructor + JSHandle nfFunction(NewIntlConstructor(env, nfPrototype, NumberFormat::NumberFormatConstructor, + "NumberFormat", FunctionLength::ZERO)); + JSHandle(nfFunction)->SetFunctionPrototype(thread_, JSTaggedValue(*nfFuncInstanceDynclass)); + + // 12.3.2 Intl.NumberFormat.supportedLocalesOf ( locales [ , options ] ) + SetFunction(env, nfFunction, "supportedLocalesOf", NumberFormat::SupportedLocalesOf, FunctionLength::ONE); + + // NumberFormat.prototype method + // 12.4.2 Intl.NumberFormat.prototype [ @@toStringTag ] + SetStringTagSymbol(env, nfPrototype, "Intl.NumberFormat"); + env->SetNumberFormatFunction(thread_, nfFunction); + + // 12.4.3 get Intl.NumberFormat.prototype.format + JSHandle formatGetter = CreateGetter(env, NumberFormat::Format, "format", FunctionLength::ZERO); + JSHandle formatSetter(thread_, JSTaggedValue::Undefined()); + SetAccessor(nfPrototype, thread_->GlobalConstants()->GetHandledFormatString(), formatGetter, formatSetter); + + // 12.4.4 Intl.NumberFormat.prototype.formatToParts ( date ) + SetFunction(env, nfPrototype, "formatToParts", NumberFormat::FormatToParts, FunctionLength::ONE); + + // 12.4.5 Intl.NumberFormat.prototype.resolvedOptions () + SetFunction(env, nfPrototype, "resolvedOptions", NumberFormat::ResolvedOptions, FunctionLength::ZERO); +} + +void Builtins::InitializeLocale(const JSHandle &env) +{ + [[maybe_unused]] EcmaHandleScope scope(thread_); + // Locale.prototype + JSHandle objFun = env->GetObjectFunction(); + JSHandle localePrototype = factory_->NewJSObjectByConstructor(JSHandle(objFun), objFun); + JSHandle localePrototypeValue(localePrototype); + + // Locale.prototype_or_dynclass + JSHandle localeFuncInstanceDynclass = + factory_->NewEcmaDynClass(JSLocale::SIZE, JSType::JS_LOCALE, localePrototypeValue); + + // Locale = new Function() + JSHandle localeFunction( + NewIntlConstructor(env, localePrototype, Locale::LocaleConstructor, "Locale", FunctionLength::ONE)); + JSHandle(localeFunction)->SetFunctionPrototype(thread_, JSTaggedValue(*localeFuncInstanceDynclass)); + + // Locale.prototype method + SetFunction(env, localePrototype, "maximize", Locale::Maximize, FunctionLength::ZERO); + SetFunction(env, localePrototype, "minimize", Locale::Minimize, FunctionLength::ZERO); + SetFunction(env, localePrototype, "toString", Locale::ToString, FunctionLength::ZERO); + + JSHandle baseNameGetter = CreateGetter(env, Locale::GetBaseName, "baseName", FunctionLength::ZERO); + SetGetter(localePrototype, thread_->GlobalConstants()->GetHandledBaseNameString(), baseNameGetter); + + JSHandle calendarGetter = CreateGetter(env, Locale::GetCalendar, "calendar", FunctionLength::ZERO); + SetGetter(localePrototype, thread_->GlobalConstants()->GetHandledCalendarString(), calendarGetter); + + JSHandle caseFirstGetter = + CreateGetter(env, Locale::GetCaseFirst, "caseFirst", FunctionLength::ZERO); + SetGetter(localePrototype, thread_->GlobalConstants()->GetHandledCaseFirstString(), caseFirstGetter); + + JSHandle collationGetter = + CreateGetter(env, Locale::GetCollation, "collation", FunctionLength::ZERO); + SetGetter(localePrototype, thread_->GlobalConstants()->GetHandledCollationString(), collationGetter); + + JSHandle hourCycleGetter = + CreateGetter(env, Locale::GetHourCycle, "hourCycle", FunctionLength::ZERO); + SetGetter(localePrototype, thread_->GlobalConstants()->GetHandledHourCycleString(), hourCycleGetter); + + JSHandle numericGetter = CreateGetter(env, Locale::GetNumeric, "numeric", FunctionLength::ZERO); + SetGetter(localePrototype, thread_->GlobalConstants()->GetHandledNumericString(), numericGetter); + + JSHandle numberingSystemGetter = + CreateGetter(env, Locale::GetNumberingSystem, "numberingSystem", FunctionLength::ZERO); + SetGetter(localePrototype, thread_->GlobalConstants()->GetHandledNumberingSystemString(), numberingSystemGetter); + + JSHandle languageGetter = CreateGetter(env, Locale::GetLanguage, "language", FunctionLength::ZERO); + SetGetter(localePrototype, thread_->GlobalConstants()->GetHandledLanguageString(), languageGetter); + + JSHandle scriptGetter = CreateGetter(env, Locale::GetScript, "script", FunctionLength::ZERO); + SetGetter(localePrototype, thread_->GlobalConstants()->GetHandledScriptString(), scriptGetter); + + JSHandle regionGetter = CreateGetter(env, Locale::GetRegion, "region", FunctionLength::ZERO); + SetGetter(localePrototype, thread_->GlobalConstants()->GetHandledRegionString(), regionGetter); + + // 10.3.2 Intl.Locale.prototype[ @@toStringTag ] + SetStringTagSymbol(env, localePrototype, "Intl.Locale"); + env->SetLocaleFunction(thread_, localeFunction); +} + +void Builtins::InitializeCollator(const JSHandle &env) +{ + [[maybe_unused]] EcmaHandleScope scope(thread_); + // Collator.prototype + JSHandle objFun = env->GetObjectFunction(); + JSHandle collatorPrototype = factory_->NewJSObjectByConstructor(JSHandle(objFun), objFun); + JSHandle collatorPrototypeValue(collatorPrototype); + + // Collator.prototype_or_dynclass + JSHandle collatorFuncInstanceDynclass = + factory_->NewEcmaDynClass(JSCollator::SIZE, JSType::JS_COLLATOR, collatorPrototypeValue); + + // Collator = new Function() + // 11.1.2 Intl.Collator.prototype.constructor + JSHandle collatorFunction( + NewIntlConstructor(env, collatorPrototype, Collator::CollatorConstructor, "Collator", FunctionLength::ZERO)); + JSHandle(collatorFunction)->SetFunctionPrototype(thread_, JSTaggedValue(*collatorFuncInstanceDynclass)); + + // 11.2.2 Intl.Collator.supportedLocalesOf ( locales [ , options ] ) + SetFunction(env, collatorFunction, "supportedLocalesOf", Collator::SupportedLocalesOf, FunctionLength::ONE); + + // Collator.prototype method + // 11.3.2 Intl.Collator.prototype [ @@toStringTag ] + SetStringTagSymbol(env, collatorPrototype, "Intl.Collator"); + env->SetCollatorFunction(thread_, collatorFunction); + + // 11.3.3 get Intl.Collator.prototype.compare + JSHandle compareGetter = CreateGetter(env, Collator::Compare, "compare", FunctionLength::ZERO); + JSHandle compareSetter(thread_, JSTaggedValue::Undefined()); + SetAccessor(collatorPrototype, thread_->GlobalConstants()->GetHandledCompareString(), compareGetter, compareSetter); + + // 11.3.4 Intl.Collator.prototype.resolvedOptions () + SetFunction(env, collatorPrototype, "resolvedOptions", Collator::ResolvedOptions, FunctionLength::ZERO); +} + +void Builtins::InitializePluralRules(const JSHandle &env) +{ + [[maybe_unused]] EcmaHandleScope scope(thread_); + // PluralRules.prototype + JSHandle objFun(env->GetObjectFunction()); + JSHandle prPrototype = factory_->NewJSObjectByConstructor(JSHandle(objFun), objFun); + JSHandle prPrototypeValue(prPrototype); + + // PluralRules.prototype_or_dynclass + JSHandle prFuncInstanceDynclass = + factory_->NewEcmaDynClass(JSPluralRules::SIZE, JSType::JS_PLURAL_RULES, prPrototypeValue); + + // PluralRules = new Function() + // 15.2.1 Intl.PluralRules.prototype.constructor + JSHandle prFunction( + NewIntlConstructor(env, prPrototype, PluralRules::PluralRulesConstructor, "PluralRules", FunctionLength::ZERO)); + JSHandle(prFunction)->SetFunctionPrototype(thread_, JSTaggedValue(*prFuncInstanceDynclass)); + + // 15.3.2 Intl.PluralRules.supportedLocalesOf ( locales [ , options ] ) + SetFunction(env, prFunction, "supportedLocalesOf", PluralRules::SupportedLocalesOf, FunctionLength::ONE); + + // PluralRules.prototype method + // 15.4.2 Intl.PluralRules.prototype [ @@toStringTag ] + SetStringTagSymbol(env, prPrototype, "Intl.PluralRules"); + env->SetPluralRulesFunction(thread_, prFunction); + + // 15.4.3 get Intl.PluralRules.prototype.select + SetFunction(env, prPrototype, "select", PluralRules::Select, FunctionLength::ONE); + + // 15.4.5 Intl.PluralRules.prototype.resolvedOptions () + SetFunction(env, prPrototype, "resolvedOptions", PluralRules::ResolvedOptions, FunctionLength::ZERO); +} + +JSHandle Builtins::InitializeArkTools(const JSHandle &env) const +{ + JSHandle tools = factory_->NewEmptyJSObject(); + SetFunction(env, tools, "print", builtins::BuiltinsArkTools::ObjectDump, FunctionLength::ZERO); + return tools; +} + +JSHandle Builtins::InitializeArkPrivate(const JSHandle &env) const +{ + JSHandle arkPrivate = factory_->NewEmptyJSObject(); + SetFrozenFunction(env, arkPrivate, "Load", ContainersPrivate::Load, FunctionLength::ZERO); + SetConstant(arkPrivate, "ArrayList", JSTaggedValue(static_cast(containers::ContainerTag::ArrayList))); + SetConstant(arkPrivate, "Queue", JSTaggedValue(static_cast(containers::ContainerTag::Queue))); + SetConstant(arkPrivate, "Deque", JSTaggedValue(static_cast(containers::ContainerTag::Deque))); + SetConstant(arkPrivate, "Stack", JSTaggedValue(static_cast(containers::ContainerTag::Stack))); + SetConstant(arkPrivate, "Vector", JSTaggedValue(static_cast(containers::ContainerTag::Vector))); + SetConstant(arkPrivate, "List", JSTaggedValue(static_cast(containers::ContainerTag::List))); + SetConstant(arkPrivate, "LinkedList", JSTaggedValue(static_cast(containers::ContainerTag::LinkedList))); + SetConstant(arkPrivate, "TreeMap", JSTaggedValue(static_cast(containers::ContainerTag::TreeMap))); + SetConstant(arkPrivate, "TreeSet", JSTaggedValue(static_cast(containers::ContainerTag::TreeSet))); + SetConstant(arkPrivate, "HashMap", JSTaggedValue(static_cast(containers::ContainerTag::HashMap))); + SetConstant(arkPrivate, "HashSet", JSTaggedValue(static_cast(containers::ContainerTag::HashSet))); + SetConstant(arkPrivate, "LightWightMap", JSTaggedValue(static_cast(containers::ContainerTag::LightWightMap))); + SetConstant(arkPrivate, "LightWightSet", JSTaggedValue(static_cast(containers::ContainerTag::LightWightSet))); + SetConstant(arkPrivate, "PlainArray", JSTaggedValue(static_cast(containers::ContainerTag::PlainArray))); + return arkPrivate; +} + +#ifdef FUZZING_FUZZILLI_BUILTIN +void Builtins::InitializeFuzzilli(const JSHandle &env, const JSHandle &objFuncDynclass) +{ + class BuiltinsFuzzilli : public ecmascript::base::BuiltinsBase { + public: + static JSTaggedValue FuzzilliConstructor(EcmaRuntimeCallInfo *argv) + { + ASSERT(argv); + BUILTINS_API_TRACE(argv->GetThread(), Object, Constructor); + JSThread *thread = argv->GetThread(); + [[maybe_unused]] EcmaHandleScope handleScope(thread); + + auto value = GetCallArg(argv, 0); + JSHandle command = JSTaggedValue::ToString(thread, value); + auto commandVal = base::StringHelper::ToStdString(*command); + if (commandVal == "FUZZILLI_CRASH") { + JSTaggedNumber num = JSTaggedValue::ToNumber(thread, GetCallArg(argv, 1)); + auto numVal = base::NumberHelper::DoubleInRangeInt32(num.GetNumber()); + switch (numVal) { + case 0: + *((int *)0x41414141) = 0x1337; + break; + case 1: + ASSERT(false); + break; + default: + ASSERT(false); + break; + } + } else if (commandVal == "FUZZILLI_PRINT") { + JSHandle arg = JSTaggedValue::ToString(thread, GetCallArg(argv, 1)); + auto stringVal = base::StringHelper::ToStdString(*arg); + // REPRL_DWFD for fuzzilli + FILE *fzliout = fdopen(103, "w"); + if (!fzliout) { + fzliout = stderr; + fprintf(fzliout, "Fuzzer output channel not available, printing to stderr instead\n"); + } + + fprintf(fzliout, "%s\n", stringVal.c_str()); + + fflush(fzliout); + } + + return JSTaggedValue::ToObject(thread, value).GetTaggedValue(); + } + }; + + JSHandle fuzzilliFuncPrototype = factory_->NewJSObject(objFuncDynclass); + JSHandle fuzzilliFuncPrototypeValue(fuzzilliFuncPrototype); + + JSHandle fuzzilliFuncInstanceDynclass = + factory_->NewEcmaDynClass(JSDate::SIZE, JSType::JS_FUNCTION, fuzzilliFuncPrototypeValue); + + JSHandle fuzzilliFunction(NewBuiltinConstructor( + env, fuzzilliFuncPrototype, BuiltinsFuzzilli::FuzzilliConstructor, "fuzzilli", FunctionLength::THREE)); + JSHandle(fuzzilliFunction) + ->SetFunctionPrototype(thread_, fuzzilliFuncInstanceDynclass.GetTaggedValue()); +} +#endif +} // namespace panda::ecmascript diff --git a/runtime/builtins.h b/runtime/builtins.h new file mode 100644 index 000000000..d1431c42c --- /dev/null +++ b/runtime/builtins.h @@ -0,0 +1,231 @@ +/* + * Copyright (c) 2021-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_BUILTINS_H +#define ECMASCRIPT_BUILTINS_H + +#include "plugins/ecmascript/runtime/global_env.h" +#include "plugins/ecmascript/runtime/js_function.h" +#include "plugins/ecmascript/runtime/js_handle.h" +#include "plugins/ecmascript/runtime/js_tagged_value.h" +#include "plugins/ecmascript/runtime/js_thread.h" +#include "object_factory.h" +#include "plugins/ecmascript/runtime/object_factory.h" + +namespace panda::ecmascript { +struct ErrorParameter { + EcmaEntrypoint nativeConstructor {nullptr}; + EcmaEntrypoint nativeMethod {nullptr}; + const char *nativePropertyName {nullptr}; + JSType nativeJstype {JSType::INVALID}; +}; + +enum FunctionLength : uint8_t { ZERO = 0, ONE, TWO, THREE, FOUR }; + +class Builtins { +public: + explicit Builtins() = default; + ~Builtins() = default; + NO_COPY_SEMANTIC(Builtins); + NO_MOVE_SEMANTIC(Builtins); + + void Initialize(const JSHandle &env, JSThread *thread); + +private: + JSThread *thread_ {nullptr}; + ObjectFactory *factory_ {nullptr}; + EcmaVM *vm_ {nullptr}; + + JSHandle NewBuiltinConstructor(const JSHandle &env, const JSHandle &prototype, + EcmaEntrypoint ctorFunc, const char *name, int length) const; + + JSHandle NewFunction(const JSHandle &env, const JSHandle &key, + EcmaEntrypoint func, int length) const; + + void InitializeCtor(const JSHandle &env, const JSHandle &prototype, + const JSHandle &ctor, const char *name, int length) const; + + void InitializeGlobalObject(const JSHandle &env, const JSHandle &globalObject); + + void InitializeFunction(const JSHandle &env, const JSHandle &emptyFuncDynclass) const; + + void InitializeObject(const JSHandle &env, const JSHandle &objFuncPrototype, + const JSHandle &objFunc); + + void InitializeNumber(const JSHandle &env, const JSHandle &globalObject, + const JSHandle &primRefObjDynclass); + + void InitializeDate(const JSHandle &env, const JSHandle &objFuncDynclass) const; + + void InitializeBoolean(const JSHandle &env, const JSHandle &primRefObjDynclass) const; + + void InitializeSymbol(const JSHandle &env, const JSHandle &objFuncDynclass) const; + + void InitializeSymbolWithRealm(const JSHandle &realm, const JSHandle &objFuncInstanceDynclass); + + void InitializeArray(const JSHandle &env, const JSHandle &objFuncPrototypeVal) const; + + void InitializeTypedArray(const JSHandle &env, const JSHandle &objFuncDynclass) const; + + void InitializeInt8Array(const JSHandle &env, const JSHandle &objFuncDynclass) const; + + void InitializeUint8Array(const JSHandle &env, const JSHandle &objFuncDynclass) const; + + void InitializeUint8ClampedArray(const JSHandle &env, const JSHandle &objFuncDynclass) const; + + void InitializeInt16Array(const JSHandle &env, const JSHandle &objFuncDynclass) const; + + void InitializeUint16Array(const JSHandle &env, const JSHandle &objFuncDynclass) const; + + void InitializeInt32Array(const JSHandle &env, const JSHandle &objFuncDynclass) const; + + void InitializeUint32Array(const JSHandle &env, const JSHandle &objFuncDynclass) const; + + void InitializeFloat32Array(const JSHandle &env, const JSHandle &objFuncDynclass) const; + + void InitializeFloat64Array(const JSHandle &env, const JSHandle &objFuncDynclass) const; + + void InitializeAllTypeError(const JSHandle &env, const JSHandle &objFuncDynclass) const; + + void InitializeAllTypeErrorWithRealm(const JSHandle &realm) const; + + void InitializeError(const JSHandle &env, const JSHandle &objFuncDynclass, + const JSType &errorTag) const; + + void SetErrorWithRealm(const JSHandle &realm, const JSType &errorTag) const; + + void InitializeRegExp(const JSHandle &env); + + // for Intl. + JSHandle NewIntlConstructor(const JSHandle &env, const JSHandle &prototype, + EcmaEntrypoint ctorFunc, const char *name, int length); + void InitializeIntlCtor(const JSHandle &env, const JSHandle &prototype, + const JSHandle &ctor, const char *name, int length); + void InitializeIntl(const JSHandle &env, const JSHandle &objFuncPrototypeValue); + void InitializeLocale(const JSHandle &env); + void InitializeDateTimeFormat(const JSHandle &env); + void InitializeRelativeTimeFormat(const JSHandle &env); + void InitializeNumberFormat(const JSHandle &env); + void InitializeCollator(const JSHandle &env); + void InitializePluralRules(const JSHandle &env); + + void GeneralUpdateError(ErrorParameter *error, EcmaEntrypoint constructor, EcmaEntrypoint method, const char *name, + JSType type) const; + + void InitializeSet(const JSHandle &env, const JSHandle &objFuncDynclass) const; + + void InitializeMap(const JSHandle &env, const JSHandle &objFuncDynclass) const; + + void InitializeWeakMap(const JSHandle &env, const JSHandle &objFuncDynclass) const; + + void InitializeWeakSet(const JSHandle &env, const JSHandle &objFuncDynclass) const; + + void InitializeMath(const JSHandle &env, const JSHandle &objFuncPrototypeVal) const; + + void InitializeJson(const JSHandle &env, const JSHandle &objFuncPrototypeVal) const; + + void InitializeString(const JSHandle &env, const JSHandle &primRefObjDynclass) const; + + void InitializeIterator(const JSHandle &env, const JSHandle &objFuncDynclass) const; + + void InitializeAsyncIterator(const JSHandle &env, const JSHandle &objFuncDynclass) const; + + void InitializeStringIterator(const JSHandle &env, const JSHandle &iteratorFuncDynclass) const; + + void InitializeForinIterator(const JSHandle &env, const JSHandle &iteratorFuncDynclass) const; + + void InitializeMapIterator(const JSHandle &env, const JSHandle &iteratorFuncDynclass) const; + + void InitializeSetIterator(const JSHandle &env, const JSHandle &iteratorFuncDynclass) const; + + void InitializeArrayIterator(const JSHandle &env, const JSHandle &iteratorFuncDynclass) const; + + void InitializeArrayBuffer(const JSHandle &env, const JSHandle &objFuncDynclass) const; + + void InitializeDataView(const JSHandle &env, const JSHandle &objFuncDynclass) const; + + void InitializeProxy(const JSHandle &env); + + void InitializeReflect(const JSHandle &env, const JSHandle &objFuncPrototypeVal) const; + + void InitializeAsyncFunction(const JSHandle &env, const JSHandle &objFuncDynclass) const; + + void InitializeGeneratorFunction(const JSHandle &env, const JSHandle &objFuncDynclass) const; + + void InitializeGenerator(const JSHandle &env, const JSHandle &objFuncDynclass) const; + + void InitializeAsyncGeneratorFunction(const JSHandle &env, + const JSHandle &objFuncDynclass) const; + + void InitializeAsyncGenerator(const JSHandle &env, const JSHandle &objFuncDynclass) const; + + void InitializeAsyncFromSyncIteratorPrototypeObject(const JSHandle &env, + const JSHandle &objFuncDynclass) const; + + JSHandle InitializeExoticConstructor(const JSHandle &env, EcmaEntrypoint ctorFunc, + const char *name, int length); + + void InitializePromise(const JSHandle &env, const JSHandle &promiseFuncDynclass); + + void InitializePromiseJob(const JSHandle &env); + + void SetFunction(const JSHandle &env, const JSHandle &obj, const char *key, + EcmaEntrypoint func, int length) const; + + void SetFunction(const JSHandle &env, const JSHandle &obj, const JSHandle &key, + EcmaEntrypoint func, int length) const; + + void SetFuncToObjAndGlobal(const JSHandle &env, const JSHandle &globalObject, + const JSHandle &obj, const char *key, EcmaEntrypoint func, int length); + + template + void SetFunctionAtSymbol(const JSHandle &env, const JSHandle &obj, + const JSHandle &symbol, const char *name, EcmaEntrypoint func, + int length) const; + + void SetStringTagSymbol(const JSHandle &env, const JSHandle &obj, const char *key) const; + JSHandle CreateGetter(const JSHandle &env, EcmaEntrypoint func, const char *name, + int length) const; + + void SetConstant(const JSHandle &obj, const char *key, JSTaggedValue value) const; + + void SetGlobalThis(const JSHandle &obj, const char *key, const JSHandle &globalValue); + + void SetAttribute(const JSHandle &obj, const char *key, const char *value) const; + + void SetNoneAttributeProperty(const JSHandle &obj, const char *key, + const JSHandle &value) const; + + void StrictModeForbiddenAccessCallerArguments(const JSHandle &env, + const JSHandle &prototype) const; + + JSHandle CreateSetter(const JSHandle &env, EcmaEntrypoint func, const char *name, + int length); + void SetArgumentsSharedAccessor(const JSHandle &env); + void SetAccessor(const JSHandle &obj, const JSHandle &key, + const JSHandle &getter, const JSHandle &setter) const; + void SetGetter(const JSHandle &obj, const JSHandle &key, + const JSHandle &getter) const; + JSHandle InitializeArkTools(const JSHandle &env) const; + 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, + EcmaEntrypoint func, int length) const; +#ifdef FUZZING_FUZZILLI_BUILTIN + void InitializeFuzzilli(const JSHandle &env, const JSHandle &objFuncDynclass); +#endif +}; +} // namespace panda::ecmascript +#endif // ECMASCRIPT_BUILTINS_H diff --git a/runtime/builtins/builtins_ark_tools.cpp b/runtime/builtins/builtins_ark_tools.cpp new file mode 100644 index 000000000..d0b7563fd --- /dev/null +++ b/runtime/builtins/builtins_ark_tools.cpp @@ -0,0 +1,44 @@ +/* + * Copyright (c) 2021-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 "plugins/ecmascript/runtime/builtins/builtins_ark_tools.h" +#include "plugins/ecmascript/runtime/base/string_helper.h" + +namespace panda::ecmascript::builtins { +using StringHelper = base::StringHelper; + +JSTaggedValue BuiltinsArkTools::ObjectDump(EcmaRuntimeCallInfo *msg) +{ + ASSERT(msg); + JSThread *thread = msg->GetThread(); + [[maybe_unused]] EcmaHandleScope handleScope(thread); + + JSHandle str = JSTaggedValue::ToString(thread, GetCallArg(msg, 0)); + // The default log level of ace_engine and js_runtime is error + LOG(ERROR, RUNTIME) << ": " << base::StringHelper::ToStdString(*str); + + uint32_t numArgs = msg->GetArgsNumber(); + for (uint32_t i = 1; i < numArgs; i++) { + JSHandle obj = GetCallArg(msg, i); + std::ostringstream oss; + obj->Dump(thread, oss); + + // The default log level of ace_engine and js_runtime is error + LOG(ERROR, RUNTIME) << ": " << oss.str(); + } + + return JSTaggedValue::Undefined(); +} +} // namespace panda::ecmascript::builtins diff --git a/runtime/builtins/builtins_ark_tools.h b/runtime/builtins/builtins_ark_tools.h new file mode 100644 index 000000000..ada9ebed8 --- /dev/null +++ b/runtime/builtins/builtins_ark_tools.h @@ -0,0 +1,31 @@ +/* + * Copyright (c) 2021-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_BUILTINS_BUILTINS_ARK_TOOLS_H +#define ECMASCRIPT_BUILTINS_BUILTINS_ARK_TOOLS_H + +#include "plugins/ecmascript/runtime/base/builtins_base.h" +#include "plugins/ecmascript/runtime/js_thread.h" + +namespace panda::ecmascript::builtins { +class BuiltinsArkTools : public base::BuiltinsBase { +public: + // Make sure the ECMASCRIPT_OBJECT_DUMP in config.h has been opened before use it + // Use through ArkTools.print(msg, [obj1, obj2, ... objn]) in js + static JSTaggedValue ObjectDump(EcmaRuntimeCallInfo *msg); +}; +} // namespace panda::ecmascript::builtins + +#endif // ECMASCRIPT_BUILTINS_BUILTINS_ARK_TOOLS_H diff --git a/runtime/builtins/builtins_array.cpp b/runtime/builtins/builtins_array.cpp new file mode 100644 index 000000000..1ef008251 --- /dev/null +++ b/runtime/builtins/builtins_array.cpp @@ -0,0 +1,2870 @@ +/* + * Copyright (c) 2021-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 "plugins/ecmascript/runtime/builtins/builtins_array.h" + +#include + +#include "plugins/ecmascript/runtime/base/array_helper.h" +#include "plugins/ecmascript/runtime/base/number_helper.h" +#include "plugins/ecmascript/runtime/base/typed_array_helper-inl.h" +#include "plugins/ecmascript/runtime/base/typed_array_helper.h" +#include "plugins/ecmascript/runtime/ecma_runtime_call_info.h" +#include "plugins/ecmascript/runtime/ecma_string.h" +#include "plugins/ecmascript/runtime/global_env.h" +#include "plugins/ecmascript/runtime/internal_call_params.h" +#include "plugins/ecmascript/runtime/interpreter/fast_runtime_stub-inl.h" +#include "plugins/ecmascript/runtime/js_array.h" +#include "plugins/ecmascript/runtime/js_array_iterator.h" +#include "plugins/ecmascript/runtime/js_function.h" +#include "plugins/ecmascript/runtime/js_handle.h" +#include "plugins/ecmascript/runtime/js_stable_array.h" +#include "plugins/ecmascript/runtime/js_tagged_number.h" +#include "plugins/ecmascript/runtime/object_factory.h" +#include "plugins/ecmascript/runtime/tagged_array-inl.h" + +namespace panda::ecmascript::builtins { +using ArrayHelper = ecmascript::base::ArrayHelper; +using TypedArrayHelper = ecmascript::base::TypedArrayHelper; + +// 22.1.1 +JSTaggedValue BuiltinsArray::ArrayConstructor(EcmaRuntimeCallInfo *argv) +{ + ASSERT(argv); + BUILTINS_API_TRACE(argv->GetThread(), Array, Constructor); + JSThread *thread = argv->GetThread(); + [[maybe_unused]] EcmaHandleScope handleScope(thread); + ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); + + // 1. Let numberOfArgs be the number of arguments passed to this function call. + uint32_t argc = argv->GetArgsNumber(); + + // 3. If NewTarget is undefined, let newTarget be the active function object, else let newTarget be NewTarget. + JSHandle constructor = GetConstructor(argv); + JSHandle newTarget = GetNewTarget(argv); + if (newTarget->IsUndefined()) { + newTarget = constructor; + } + + // 4. Let proto be GetPrototypeFromConstructor(newTarget, "%ArrayPrototype%"). + // In NewJSObjectByConstructor(), will get prototype. + // 5. ReturnIfAbrupt(proto). + + // 22.1.1.1 Array ( ) + if (argc == 0) { + // 6. Return ArrayCreate(0, proto). + return JSTaggedValue(JSArray::ArrayCreate(thread, JSTaggedNumber(0), newTarget).GetObject()); + } + + // 22.1.1.2 Array(len) + if (argc == 1) { + // 6. Let array be ArrayCreate(0, proto). + uint32_t newLen = 0; + JSHandle newArrayHandle(JSArray::ArrayCreate(thread, JSTaggedNumber(newLen), newTarget)); + JSHandle len = GetCallArg(argv, 0); + // 7. If Type(len) is not Number, then + // a. Let defineStatus be CreateDataProperty(array, "0", len). + // b. Assert: defineStatus is true. + // c. Let intLen be 1. + // 8. Else, + // a. Let intLen be ToUint32(len). + // b. If intLen ≠ len, throw a RangeError exception. + // 9. Let setStatus be Set(array, "length", intLen, true). + // 10. Assert: setStatus is not an abrupt completion. + if (!len->IsNumber()) { + JSHandle key0(factory->NewFromCanBeCompressString("0")); + JSObject::CreateDataProperty(thread, newArrayHandle, key0, len); + newLen = 1; + } else { + newLen = JSTaggedValue::ToUint32(thread, len); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + if (JSTaggedNumber(len.GetTaggedValue()).GetNumber() != newLen) { + THROW_RANGE_ERROR_AND_RETURN(thread, "The length is out of range.", JSTaggedValue::Exception()); + } + } + JSArray::Cast(*newArrayHandle)->SetArrayLength(thread, newLen); + + // 11. Return array. + return newArrayHandle.GetTaggedValue(); + } + + // 22.1.1.3 Array(...items ) + JSTaggedValue newArray = JSArray::ArrayCreate(thread, JSTaggedNumber(argc), newTarget).GetTaggedValue(); + if (!newArray.IsArray(thread)) { + THROW_TYPE_ERROR_AND_RETURN(thread, "Failed to create array.", JSTaggedValue::Exception()); + } + JSHandle newArrayHandle(thread, newArray); + + // 8. Let k be 0. + // 9. Let items be a zero-origined List containing the argument items in order. + // 10. Repeat, while k < numberOfArgs + // a. Let Pk be ToString(k). + // b. Let itemK be items[k]. + // c. Let defineStatus be CreateDataProperty(array, Pk, itemK). + // d. Assert: defineStatus is true. + // e. Increase k by 1. + JSMutableHandle key(thread, JSTaggedValue::Undefined()); + for (uint32_t k = 0; k < argc; k++) { + key.Update(JSTaggedValue(k)); + JSHandle itemK = GetCallArg(argv, k); + JSObject::CreateDataProperty(thread, newArrayHandle, key, itemK); + } + + // 11. Assert: the value of array’s length property is numberOfArgs. + // 12. Return array. + JSArray::Cast(*newArrayHandle)->SetArrayLength(thread, argc); + return newArrayHandle.GetTaggedValue(); +} + +// 22.1.2.1 Array.from ( items [ , mapfn [ , thisArg ] ] ) +// NOLINTNEXTLINE(readability-function-size) +JSTaggedValue BuiltinsArray::From(EcmaRuntimeCallInfo *argv) +{ + ASSERT(argv); + BUILTINS_API_TRACE(argv->GetThread(), Array, From); + JSThread *thread = argv->GetThread(); + [[maybe_unused]] EcmaHandleScope handleScope(thread); + InternalCallParams *arguments = thread->GetInternalCallParams(); + JSHandle lengthKey = thread->GlobalConstants()->GetHandledLengthString(); + // 1. Let C be the this value. + JSHandle thisHandle = GetThis(argv); + // 2. If mapfn is undefined, let mapping be false. + bool mapping = false; + // 3. else + // a. If IsCallable(mapfn) is false, throw a TypeError exception. + // b. If thisArg was supplied, let T be thisArg; else let T be undefined. + // c. Let mapping be true + JSHandle thisArgHandle = GetCallArg(argv, INDEX_TWO); + JSHandle mapfn = GetCallArg(argv, 1); + if (!mapfn->IsUndefined()) { + if (!mapfn->IsCallable()) { + THROW_TYPE_ERROR_AND_RETURN(thread, "the mapfn is not callable.", JSTaggedValue::Exception()); + } + mapping = true; + } + // 4. Let usingIterator be GetMethod(items, @@iterator). + JSHandle items = GetCallArg(argv, 0); + if (items->IsNull()) { + THROW_TYPE_ERROR_AND_RETURN(thread, "The items is null.", JSTaggedValue::Exception()); + } + JSHandle env = thread->GetEcmaVM()->GetGlobalEnv(); + JSHandle iteratorSymbol = env->GetIteratorSymbol(); + JSHandle usingIterator = JSObject::GetMethod(thread, items, iteratorSymbol); + // 5. ReturnIfAbrupt(usingIterator). + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + // 6. If usingIterator is not undefined, then + if (!usingIterator->IsUndefined()) { + // a. If IsConstructor(C) is true, then + // i. Let A be Construct(C). + // b. Else, + // i. Let A be ArrayCreate(0). + // c. ReturnIfAbrupt(A). + JSTaggedValue newArray; + if (thisHandle->IsConstructor()) { + newArray = JSFunction::Construct(thread, thisHandle, 0, nullptr, + JSHandle(thread, JSTaggedValue::Undefined())); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + } else { + newArray = JSArray::ArrayCreate(thread, JSTaggedNumber(0)).GetTaggedValue(); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + } + if (!newArray.IsECMAObject()) { + THROW_TYPE_ERROR_AND_RETURN(thread, "Failed to construct the array.", JSTaggedValue::Exception()); + } + JSHandle newArrayHandle(thread, newArray); + // d. Let iterator be GetIterator(items, usingIterator). + JSHandle iterator = JSIterator::GetIterator(thread, items, usingIterator); + // e. ReturnIfAbrupt(iterator). + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + // f. Let k be 0. + int k = 0; + // g. Repeat + JSMutableHandle key(thread, JSTaggedValue::Undefined()); + JSMutableHandle mapValue(thread, JSTaggedValue::Undefined()); + while (true) { + key.Update(JSTaggedValue(k)); + // i. Let Pk be ToString(k). + // ii. Let next be IteratorStep(iterator). + JSHandle next = JSIterator::IteratorStep(thread, iterator); + // iii. ReturnIfAbrupt(next). + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + // iv. If next is false, then + // 1. Let setStatus be Set(A, "length", k, true). + // 2. ReturnIfAbrupt(setStatus). + // 3. Return A. + if (next->IsFalse()) { + JSTaggedValue::SetProperty(thread, JSHandle::Cast(newArrayHandle), lengthKey, key, true); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + return JSTaggedValue(newArrayHandle.GetTaggedValue()); + } + // v. Let nextValue be IteratorValue(next). + JSHandle nextValue = JSIterator::IteratorValue(thread, next); + // vi. ReturnIfAbrupt(nextValue). + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + // vii. If mapping is true, then + // 1. Let mappedValue be Call(mapfn, T, «nextValue, k»). + // 2. If mappedValue is an abrupt completion, return IteratorClose(iterator, mappedValue). + // 3. Let mappedValue be mappedValue.[[value]]. + // viii. Else, let mappedValue be nextValue. + if (mapping) { + arguments->MakeArgv(nextValue, key); + JSTaggedValue callResult = + JSFunction::Call(thread, mapfn, thisArgHandle, 2, arguments->GetArgv()); // 2: two args + mapValue.Update(callResult); + JSTaggedValue mapResult = JSIterator::IteratorClose(thread, iterator, mapValue).GetTaggedValue(); + RETURN_VALUE_IF_ABRUPT_COMPLETION(thread, JSTaggedValue(mapResult)); + } else { + mapValue.Update(nextValue.GetTaggedValue()); + } + // ix. Let defineStatus be CreateDataPropertyOrThrow(A, Pk, mappedValue). + // x. If defineStatus is an abrupt completion, return IteratorClose(iterator, defineStatus). + // xi. Increase k by 1. + JSHandle defineStatus( + thread, JSTaggedValue(JSObject::CreateDataPropertyOrThrow(thread, newArrayHandle, key, mapValue))); + JSTaggedValue defineResult = JSIterator::IteratorClose(thread, iterator, defineStatus).GetTaggedValue(); + RETURN_VALUE_IF_ABRUPT_COMPLETION(thread, JSTaggedValue(defineResult)); + k++; + } + } + // 7. Assert: items is not an Iterable so assume it is an array-like object. + // 8. Let arrayLike be ToObject(items). + JSHandle arrayLikeObj = JSTaggedValue::ToObject(thread, items); + // 9. ReturnIfAbrupt(arrayLike). + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + JSHandle arrayLike(arrayLikeObj); + // 10. Let len be ToLength(Get(arrayLike, "length")). + double len = ArrayHelper::GetArrayLength(thread, arrayLike); + // 11. ReturnIfAbrupt(len). + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + // 12. If IsConstructor(C) is true, then + // a. Let A be Construct(C, «len»). + // 13. Else, + // a. Let A be ArrayCreate(len). + // 14. ReturnIfAbrupt(A). + JSTaggedValue newArray; + if (thisHandle->IsConstructor()) { + arguments->MakeArgv(JSTaggedValue(len)); + newArray = JSFunction::Construct(thread, thisHandle, 1, arguments->GetArgv(), + JSHandle(thread, JSTaggedValue::Undefined())); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + } else { + newArray = JSArray::ArrayCreate(thread, JSTaggedNumber(len)).GetTaggedValue(); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + } + if (!newArray.IsECMAObject()) { + THROW_TYPE_ERROR_AND_RETURN(thread, "Failed to construct the array.", JSTaggedValue::Exception()); + } + JSHandle newArrayHandle(thread, newArray); + // 15. Let k be 0. + // 16. Repeat, while k < len + // a. Let Pk be ToString(k). + // b. Let kValue be Get(arrayLike, Pk). + // d. If mapping is true, then + // i. Let mappedValue be Call(mapfn, T, «kValue, k»). + // e. Else, let mappedValue be kValue. + // f. Let defineStatus be CreateDataPropertyOrThrow(A, Pk, mappedValue). + JSMutableHandle key(thread, JSTaggedValue::Undefined()); + JSMutableHandle mapValue(thread, JSTaggedValue::Undefined()); + double k = 0; + while (k < len) { + JSHandle kValue = JSArray::FastGetPropertyByValue(thread, arrayLike, k); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + if (mapping) { + key.Update(JSTaggedValue(k)); + arguments->MakeArgv(kValue, key); + JSTaggedValue callResult = + JSFunction::Call(thread, mapfn, thisArgHandle, 2, arguments->GetArgv()); // 2: two args + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + mapValue.Update(callResult); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + } else { + mapValue.Update(kValue.GetTaggedValue()); + } + JSObject::CreateDataPropertyOrThrow(thread, newArrayHandle, k, mapValue); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + k++; + } + // 17. Let setStatus be Set(A, "length", len, true). + JSHandle lenHandle(thread, JSTaggedValue(len)); + JSTaggedValue::SetProperty(thread, JSHandle::Cast(newArrayHandle), lengthKey, lenHandle, true); + // 18. ReturnIfAbrupt(setStatus). + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + // 19. Return A. + return newArrayHandle.GetTaggedValue(); +} + +// 22.1.2.2 Array.isArray ( arg ) +JSTaggedValue BuiltinsArray::IsArray(EcmaRuntimeCallInfo *argv) +{ + ASSERT(argv); + BUILTINS_API_TRACE(argv->GetThread(), Array, IsArray); + // 1. Return IsArray(arg). + if (GetCallArg(argv, 0)->IsArray(argv->GetThread())) { + return GetTaggedBoolean(true); + } + return GetTaggedBoolean(false); +} + +// 22.1.2.3 Array.of ( ...items ) +JSTaggedValue BuiltinsArray::Of(EcmaRuntimeCallInfo *argv) +{ + ASSERT(argv); + BUILTINS_API_TRACE(argv->GetThread(), Array, Of); + JSThread *thread = argv->GetThread(); + [[maybe_unused]] EcmaHandleScope handleScope(thread); + const GlobalEnvConstants *globalConst = thread->GlobalConstants(); + JSHandle lengthKey = globalConst->GetHandledLengthString(); + + // 1. Let len be the actual number of arguments passed to this function. + uint32_t argc = argv->GetArgsNumber(); + + // 3. Let C be the this value. + JSHandle thisHandle = GetThis(argv); + // 4. If IsConstructor(C) is true, then + // a. Let A be Construct(C, «len»). + // 5. Else, + // a. Let A be ArrayCreate(len). + // 6. ReturnIfAbrupt(A). + JSHandle newArray; + if (thisHandle->IsConstructor()) { + JSHandle undefined = globalConst->GetHandledUndefined(); + InternalCallParams *arguments = thread->GetInternalCallParams(); + arguments->MakeArgv(JSTaggedValue(argc)); + JSTaggedValue taggedArray = JSFunction::Construct(thread, thisHandle, 1, arguments->GetArgv(), undefined); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + newArray = JSHandle(thread, taggedArray); + } else { + newArray = JSArray::ArrayCreate(thread, JSTaggedNumber(argc)); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + } + if (!newArray->IsECMAObject()) { + THROW_TYPE_ERROR_AND_RETURN(thread, "Failed to create Object.", JSTaggedValue::Exception()); + } + JSHandle newArrayHandle(newArray); + + // 7. Let k be 0. + // 8. Repeat, while k < len + // a. Let kValue be items[k]. + // b. Let Pk be ToString(k). + // c. Let defineStatus be CreateDataPropertyOrThrow(A,Pk, kValue). + // d. ReturnIfAbrupt(defineStatus). + // e. Increase k by 1. + JSMutableHandle key(thread, JSTaggedValue::Undefined()); + for (uint32_t k = 0; k < argc; k++) { + key.Update(JSTaggedValue(k)); + JSHandle kValue = GetCallArg(argv, k); + JSObject::CreateDataPropertyOrThrow(thread, newArrayHandle, key, kValue); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + } + // 9. Let setStatus be Set(A, "length", len, true). + JSHandle lenHandle(thread, JSTaggedValue(argc)); + JSTaggedValue::SetProperty(thread, JSHandle::Cast(newArrayHandle), lengthKey, lenHandle, true); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + // 11. Return A. + return newArrayHandle.GetTaggedValue(); +} + +// 22.1.2.5 get Array [ @@species ] +JSTaggedValue BuiltinsArray::Species([[maybe_unused]] EcmaRuntimeCallInfo *argv) +{ + ASSERT(argv); + // 1. Return the this value. + return GetThis(argv).GetTaggedValue(); +} + +// 22.1.3.1 Array.prototype.concat ( ...arguments ) +JSTaggedValue BuiltinsArray::Concat(EcmaRuntimeCallInfo *argv) +{ + ASSERT(argv); + BUILTINS_API_TRACE(argv->GetThread(), Array, Concat); + JSThread *thread = argv->GetThread(); + [[maybe_unused]] EcmaHandleScope handleScope(thread); + uint32_t argc = argv->GetArgsNumber(); + + // 1. Let O be ToObject(this value). + JSHandle thisHandle = GetThis(argv); + JSHandle thisObjHandle = JSTaggedValue::ToObject(thread, thisHandle); + // 2. ReturnIfAbrupt(O). + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + JSHandle thisObjVal(thisObjHandle); + + // 3. Let A be ArraySpeciesCreate(O, 0). + uint32_t arrayLen = 0; + JSTaggedValue newArray = JSArray::ArraySpeciesCreate(thread, thisObjHandle, JSTaggedNumber(arrayLen)); + // 4. ReturnIfAbrupt(A). + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + JSHandle newArrayHandle(thread, newArray); + + // 5. Let n be 0. + double n = 0; + JSMutableHandle fromKey(thread, JSTaggedValue::Undefined()); + JSMutableHandle toKey(thread, JSTaggedValue::Undefined()); + bool isSpreadable = ArrayHelper::IsConcatSpreadable(thread, thisHandle); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + if (isSpreadable) { + double thisLen = ArrayHelper::GetArrayLength(thread, thisObjVal); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + if (n + thisLen > ecmascript::base::MAX_SAFE_INTEGER) { + THROW_TYPE_ERROR_AND_RETURN(thread, "out of range.", JSTaggedValue::Exception()); + } + double k = 0; + while (k < thisLen) { + fromKey.Update(JSTaggedValue(k)); + toKey.Update(JSTaggedValue(n)); + bool exists = JSTaggedValue::HasProperty(thread, thisObjVal, fromKey); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + if (exists) { + JSHandle fromValHandle = JSArray::FastGetPropertyByValue(thread, thisObjVal, fromKey); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + JSObject::CreateDataPropertyOrThrow(thread, newArrayHandle, toKey, fromValHandle); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + } + n++; + k++; + } + } else { + if (n >= ecmascript::base::MAX_SAFE_INTEGER) { + THROW_TYPE_ERROR_AND_RETURN(thread, "out of range.", JSTaggedValue::Exception()); + } + JSObject::CreateDataPropertyOrThrow(thread, newArrayHandle, n, thisObjVal); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + n++; + } + // 7. Repeat, while items is not empty + for (uint32_t i = 0; i < argc; i++) { + // a. Remove the first element from items and let E be the value of the element + JSHandle addHandle = GetCallArg(argv, i); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + JSHandle addObjHandle(addHandle); + + // b. Let spreadable be IsConcatSpreadable(E). + isSpreadable = ArrayHelper::IsConcatSpreadable(thread, addHandle); + // c. ReturnIfAbrupt(spreadable). + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + // d. If spreadable is true, then + if (isSpreadable) { + // ii. Let len be ToLength(Get(E, "length")). + double len = ArrayHelper::GetArrayLength(thread, JSHandle::Cast(addObjHandle)); + // iii. ReturnIfAbrupt(len). + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + // iv. If n + len > 253-1, throw a TypeError exception. + if (n + len > ecmascript::base::MAX_SAFE_INTEGER) { + THROW_TYPE_ERROR_AND_RETURN(thread, "out of range.", JSTaggedValue::Exception()); + } + double k = 0; + // v. Repeat, while k < len + while (k < len) { + fromKey.Update(JSTaggedValue(k)); + toKey.Update(JSTaggedValue(n)); + // 1. Let P be ToString(k). + // 2. Let exists be HasProperty(E, P). + // 4. If exists is true, then + bool exists = JSTaggedValue::HasProperty(thread, JSHandle::Cast(addObjHandle), fromKey); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + if (exists) { + // a. Let subElement be Get(E, P). + JSHandle fromValHandle = JSArray::FastGetPropertyByValue(thread, addHandle, fromKey); + // b. ReturnIfAbrupt(subElement). + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + JSObject::CreateDataPropertyOrThrow(thread, newArrayHandle, toKey, fromValHandle); + // d. ReturnIfAbrupt(status). + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + } + // 5. Increase n by 1. + // 6. Increase k by 1. + n++; + k++; + } + } else { // e. Else E is added as a single item rather than spread, + // i. If n≥253-1, throw a TypeError exception. + if (n >= ecmascript::base::MAX_SAFE_INTEGER) { + THROW_TYPE_ERROR_AND_RETURN(thread, "out of range.", JSTaggedValue::Exception()); + } + // ii. Let status be CreateDataPropertyOrThrow (A, ToString(n), E). + JSObject::CreateDataPropertyOrThrow(thread, newArrayHandle, n, addHandle); + // iii. ReturnIfAbrupt(status). + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + + // iv. Increase n by 1. + n++; + } + } + // 8. Let setStatus be Set(A, "length", n, true). + JSHandle lengthKey = thread->GlobalConstants()->GetHandledLengthString(); + JSHandle lenHandle(thread, JSTaggedValue(n)); + JSTaggedValue::SetProperty(thread, JSHandle::Cast(newArrayHandle), lengthKey, lenHandle, true); + // 9. ReturnIfAbrupt(setStatus). + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + + // 10. Return A. + return newArrayHandle.GetTaggedValue(); +} + +// 22.1.3.3 Array.prototype.copyWithin (target, start [ , end ] ) +JSTaggedValue BuiltinsArray::CopyWithin(EcmaRuntimeCallInfo *argv) +{ + ASSERT(argv); + BUILTINS_API_TRACE(argv->GetThread(), Array, CopyWithin); + JSThread *thread = argv->GetThread(); + [[maybe_unused]] EcmaHandleScope handleScope(thread); + + // 1. Let O be ToObject(this value). + JSHandle thisObjHandle = JSTaggedValue::ToObject(thread, GetThis(argv)); + // 2. ReturnIfAbrupt(O). + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + JSHandle thisObjVal(thisObjHandle); + + // 3. Let len be ToLength(Get(O, "length")). + double len = ArrayHelper::GetLength(thread, thisObjVal); + // 4. ReturnIfAbrupt(len). + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + + double copyTo; + double copyFrom; + double copyEnd; + + // 5. Let relativeTarget be ToInteger(target). + JSTaggedNumber targetTemp = JSTaggedValue::ToInteger(thread, GetCallArg(argv, 0)); + // 6. ReturnIfAbrupt(relativeTarget). + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + double target = targetTemp.GetNumber(); + // 7. If relativeTarget < 0, let to be max((len + relativeTarget),0); else let to be min(relativeTarget, len). + if (target < 0) { + copyTo = target + len > 0 ? target + len : 0; + } else { + copyTo = target < len ? target : len; + } + + // 8. Let relativeStart be ToInteger(start). + JSTaggedNumber start_t = JSTaggedValue::ToInteger(thread, GetCallArg(argv, 1)); + // 9. ReturnIfAbrupt(relativeStart). + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + double start = start_t.GetNumber(); + // 10. If relativeStart < 0, let from be max((len + relativeStart),0); else let from be min(relativeStart, len). + if (start < 0) { + copyFrom = start + len > 0 ? start + len : 0; + } else { + copyFrom = start < len ? start : len; + } + + // 11. If end is undefined, let relativeEnd be len; else let relativeEnd be ToInteger(end). + double end = len; + JSHandle msg3 = GetCallArg(argv, INDEX_TWO); + if (!msg3->IsUndefined()) { + JSTaggedNumber temp = JSTaggedValue::ToInteger(thread, msg3); + // 12. ReturnIfAbrupt(relativeEnd). + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + end = temp.GetNumber(); + } + + // 13. If relativeEnd < 0, let final be max((len + relativeEnd),0); else let final be min(relativeEnd, len). + if (end < 0) { + copyEnd = end + len > 0 ? end + len : 0; + } else { + copyEnd = end < len ? end : len; + } + + // 14. Let count be min(final-from, len-to). + double count = (copyEnd - copyFrom < len - copyTo) ? (copyEnd - copyFrom) : (len - copyTo); + + // 15. If from 0 + // a. Let fromKey be ToString(from). + // b. Let toKey be ToString(to). + // c. Let fromPresent be HasProperty(O, fromKey). + // d. ReturnIfAbrupt(fromPresent). + // e. If fromPresent is true, then + // i. Let fromVal be Get(O, fromKey). + // ii. ReturnIfAbrupt(fromVal). + // iii. Let setStatus be Set(O, toKey, fromVal, true). + // iv. ReturnIfAbrupt(setStatus). + // f. Else fromPresent is false, + // i. Let deleteStatus be DeletePropertyOrThrow(O, toKey). + // ii. ReturnIfAbrupt(deleteStatus). + // g. Let from be from + direction. + // h. Let to be to + direction. + // i. Let count be count − 1. + JSMutableHandle fromKey(thread, JSTaggedValue::Undefined()); + JSMutableHandle toKey(thread, JSTaggedValue::Undefined()); + while (count > 0) { + fromKey.Update(JSTaggedValue(copyFrom)); + toKey.Update(JSTaggedValue(copyTo)); + bool exists = (thisObjVal->IsTypedArray() || JSTaggedValue::HasProperty(thread, thisObjVal, fromKey)); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + if (exists) { + JSHandle fromValHandle = JSArray::FastGetPropertyByValue(thread, thisObjVal, fromKey); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + JSArray::FastSetPropertyByValue(thread, thisObjVal, toKey, fromValHandle); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + } else { + if (thisObjVal->IsJSProxy()) { + toKey.Update(JSTaggedValue::ToString(thread, toKey).GetTaggedValue()); + } + JSTaggedValue::DeletePropertyOrThrow(thread, thisObjVal, toKey); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + } + copyFrom = copyFrom + direction; + copyTo = copyTo + direction; + count--; + } + + // 18. Return O. + return thisObjHandle.GetTaggedValue(); +} + +// 22.1.3.4 Array.prototype.entries ( ) +JSTaggedValue BuiltinsArray::Entries(EcmaRuntimeCallInfo *argv) +{ + ASSERT(argv); + BUILTINS_API_TRACE(argv->GetThread(), Array, Entries); + JSThread *thread = argv->GetThread(); + [[maybe_unused]] EcmaHandleScope handleScope(thread); + ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); + // 1. Let O be ToObject(this value). + // 2. ReturnIfAbrupt(O). + JSHandle self = JSTaggedValue::ToObject(thread, GetThis(argv)); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + // 3. Return CreateArrayIterator(O, "key+value"). + JSHandle iter(factory->NewJSArrayIterator(self, IterationKind::KEY_AND_VALUE)); + return iter.GetTaggedValue(); +} + +// 22.1.3.5 Array.prototype.every ( callbackfn [ , thisArg] ) +JSTaggedValue BuiltinsArray::Every(EcmaRuntimeCallInfo *argv) +{ + ASSERT(argv); + JSThread *thread = argv->GetThread(); + [[maybe_unused]] EcmaHandleScope handleScope(thread); + + // 1. Let O be ToObject(this value). + JSHandle thisHandle = GetThis(argv); + JSHandle thisObjHandle = JSTaggedValue::ToObject(thread, thisHandle); + // 2. ReturnIfAbrupt(O). + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + JSHandle thisObjVal(thisObjHandle); + + // 3. Let len be ToLength(Get(O, "length")). + double len = ArrayHelper::GetArrayLength(thread, thisObjVal); + // 4. ReturnIfAbrupt(len). + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + + // 5. If IsCallable(callbackfn) is false, throw a TypeError exception. + JSHandle callbackFnHandle = GetCallArg(argv, 0); + if (!callbackFnHandle->IsCallable()) { + THROW_TYPE_ERROR_AND_RETURN(thread, "the callbackfun is not callable.", JSTaggedValue::Exception()); + } + + // 6. If thisArg was supplied, let T be thisArg; else let T be undefined. + JSHandle thisArgHandle = GetCallArg(argv, 1); + + // 7. Let k be 0. + // 8. Repeat, while k < len + // a. Let Pk be ToString(k). + // b. Let kPresent be HasProperty(O, Pk). + // c. ReturnIfAbrupt(kPresent). + // d. If kPresent is true, then + // i. Let kValue be Get(O, Pk). + // ii. ReturnIfAbrupt(kValue). + // iii. Let testResult be ToBoolean(Call(callbackfn, T, «kValue, k, O»)). + // iv. ReturnIfAbrupt(testResult). + // v. If testResult is false, return false. + // e. Increase k by 1. + InternalCallParams *arguments = thread->GetInternalCallParams(); + JSMutableHandle key(thread, JSTaggedValue::Undefined()); + uint32_t k = 0; + while (k < len) { + bool exists = JSTaggedValue::HasProperty(thread, thisObjVal, k); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + if (exists) { + JSHandle kValue = JSArray::FastGetPropertyByValue(thread, thisObjVal, k); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + key.Update(JSTaggedValue(k)); + arguments->MakeArgv(kValue, key, thisObjVal); + JSTaggedValue callResult = + JSFunction::Call(thread, callbackFnHandle, thisArgHandle, 3, arguments->GetArgv()); // 3: three args + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + bool boolResult = callResult.ToBoolean(); + if (!boolResult) { + return GetTaggedBoolean(false); + } + } + k++; + } + + // 9. Return true. + return GetTaggedBoolean(true); +} + +// ES2019 22.1.3.10.1 FlattenIntoArray(target, source, sourceLen, start, depth, [, mapperFunction, thisArg]) +static JSTaggedValue FlattenIntoArray(JSThread *thread, const JSHandle &target, + const JSHandle &source, double sourceLen, double start, + double depth, const JSHandle &mapperFunc, + const JSHandle &thisArg) +{ + JSTaggedValue targetIdx(start); + // 1. - 3. + for (uint32_t srcIdx = 0; srcIdx < sourceLen; ++srcIdx) { + // a. Let P be ! ToString(sourceIndex). + JSTaggedValue prop = + JSTaggedValue::ToString(thread, JSHandle(thread, JSTaggedValue(srcIdx))).GetTaggedValue(); + JSHandle propHandle(thread, prop); + + // b. Let exists be ? HasProperty(source, P). + // c. If exists is true, then + bool isExists = JSTaggedValue::HasProperty(thread, source, propHandle); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + + if (!isExists) { + continue; + } + + // i. Let element be ? Get(source, P). + // ii. If mapperFunction is present, then + // 1. Assert: thisArg is present. + // 2. Set element to ? Call(mapperFunction, thisArg , « element, sourceIndex, source »). + JSHandle element(JSTaggedValue::GetProperty(thread, source, propHandle).GetValue()); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + if (!mapperFunc->IsHole()) { + ASSERT(!thisArg->IsHole()); + InternalCallParams *arguments = thread->GetInternalCallParams(); + arguments->MakeArgv(element.GetTaggedValue(), JSTaggedNumber(srcIdx), source.GetTaggedValue()); + JSTaggedValue res = + JSFunction::Call(thread, mapperFunc, thisArg, 3, arguments->GetArgv()); // 3: three args + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + element = JSHandle(thread, res); + } + + // iii. Let shouldFlatten be false. + // iv. If depth > 0, then + // 1. Set shouldFlatten to ? IsArray(element). + // v. If shouldFlatten is true, then + // 1. Let elementLen be ? ToLength(? Get(element, "length")). + // 2. Set targetIndex to ? FlattenIntoArray(target, element, elementLen, targetIndex, depth - 1). + // vi. Else, + // 1. If targetIndex ≥ 2^53-1, throw a TypeError exception. + // 2. Perform ? CreateDataPropertyOrThrow(target, ! ToString(targetIndex), element). + // 3. Increase targetIndex by 1. + if ((depth > 0) && element->IsJSArray()) { + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + double elementLen = ArrayHelper::GetLength(thread, element); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + + targetIdx = FlattenIntoArray(thread, target, element, elementLen, targetIdx.GetNumber(), depth - 1, + JSHandle(thread, JSTaggedValue::Hole()), + JSHandle(thread, JSTaggedValue::Hole())); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + } else { + if (targetIdx.GetNumber() >= ecmascript::base::MAX_SAFE_INTEGER) { + THROW_TYPE_ERROR_AND_RETURN(thread, "out of range", JSTaggedValue::Exception()); + } + JSTaggedValue str = + JSTaggedValue::ToString(thread, JSHandle(thread, targetIdx)).GetTaggedValue(); + JSHandle strHandle(thread, str); + JSObject::CreateDataPropertyOrThrow(thread, target, strHandle, element); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + targetIdx = JSTaggedValue(targetIdx.GetNumber() + 1); + } + } + // 4. Return targetIndex. + return targetIdx; +} + +// ES2019 22.1.3.10 Array.prototype.flat( [ depth ] ) +JSTaggedValue BuiltinsArray::Flat(EcmaRuntimeCallInfo *argv) +{ + ASSERT(argv); + BUILTINS_API_TRACE(argv->GetThread(), Array, Flat); + JSThread *thread = argv->GetThread(); + [[maybe_unused]] EcmaHandleScope handleScope(thread); + + // 1. Let O be ? ToObject(this value). + JSHandle thisHandle = GetThis(argv); + JSHandle thisObjHandle = JSTaggedValue::ToObject(thread, thisHandle); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + + // 2. Let sourceLen be ? ToLength(? Get(O, "length")). + double sourceLen = ArrayHelper::GetLength(thread, thisHandle); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + + // 3. Let depthNum be 1. + // 4. If depth is not undefined, then + // a. Set depthNum to ? ToInteger(depth) + double depthNum = 1; + JSHandle msg1 = GetCallArg(argv, 0); + if (!msg1->IsUndefined()) { + JSTaggedValue depth = JSTaggedValue::ToInteger(thread, msg1); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + depthNum = depth.GetNumber(); + } + + // 5. Let A be ? ArraySpeciesCreate(O, 0) + JSTaggedValue newArray = JSArray::ArraySpeciesCreate(thread, thisObjHandle, JSTaggedNumber(0)); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + JSHandle newArrayHandle(thread, newArray); + + // 6. Perform FlattenIntoArray(target, source, sourceLen, start, depth) + FlattenIntoArray(thread, newArrayHandle, thisHandle, sourceLen, 0, depthNum, + JSHandle(thread, JSTaggedValue::Hole()), + JSHandle(thread, JSTaggedValue::Hole())); + + return newArrayHandle.GetTaggedValue(); +} + +// ES2019 22.1.3.11 Array.prototype.flatMap(mapperFunction [ , thisArg ]]) +JSTaggedValue BuiltinsArray::FlatMap(EcmaRuntimeCallInfo *argv) +{ + ASSERT(argv); + BUILTINS_API_TRACE(argv->GetThread(), Array, FlatMap); + JSThread *thread = argv->GetThread(); + [[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); + + // 2. Let sourceLen be ? ToLength(? Get(O, "length")). + double sourceLen = ArrayHelper::GetLength(thread, thisHandle); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + + // 3. If IsCallable(mapperFunction) is false, throw a TypeError exception. + JSHandle mapperFunction = GetCallArg(argv, 0); + if (!mapperFunction->IsCallable()) { + THROW_TYPE_ERROR_AND_RETURN(thread, "Object is not callable", JSTaggedValue::Exception()); + } + // 4. If thisArg is present, let T be thisArg; else let T be undefined. + JSMutableHandle thisArg(thread, JSTaggedValue::Undefined()); + if (argc > 1) { + thisArg.Update(GetCallArg(argv, 1).GetTaggedValue()); + } + // 5. Let A be ? ArraySpeciesCreate(O, 0). + JSTaggedValue newArray = JSArray::ArraySpeciesCreate(thread, thisObjHandle, JSTaggedNumber(0)); + JSHandle newArrayHandle(thread, newArray); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + + // 6. Perform ? FlattenIntoArray(A, O, sourceLen, 0, 1, mapperFunction, T). + + FlattenIntoArray(thread, newArrayHandle, thisHandle, sourceLen, 0, 1, mapperFunction, thisArg); + + // 7. Return A. + return newArrayHandle.GetTaggedValue(); +} + +// 22.1.3.6 Array.prototype.fill (value [ , start [ , end ] ] ) +JSTaggedValue BuiltinsArray::Fill(EcmaRuntimeCallInfo *argv) +{ + ASSERT(argv); + BUILTINS_API_TRACE(argv->GetThread(), Array, Fill); + JSThread *thread = argv->GetThread(); + [[maybe_unused]] EcmaHandleScope handleScope(thread); + + // 1. Let O be ToObject(this value). + JSHandle thisHandle = GetThis(argv); + JSHandle thisObjHandle = JSTaggedValue::ToObject(thread, thisHandle); + // 2. ReturnIfAbrupt(O). + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + JSHandle thisObjVal(thisObjHandle); + + JSHandle value = GetCallArg(argv, 0); + if (thisHandle->IsTypedArray()) { + JSTaggedNumber number = JSTaggedValue::ToNumber(thread, value); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + value = JSHandle(thread, JSTaggedValue(number.GetNumber())); + } + + // 3. Let len be ToLength(Get(O, "length")). + double len = ArrayHelper::GetLength(thread, thisObjVal); + // 4. ReturnIfAbrupt(len). + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + + // 5. Let relativeStart be ToInteger(start). + double start; + JSHandle msg1 = GetCallArg(argv, 1); + JSTaggedNumber argStartTemp = JSTaggedValue::ToInteger(thread, msg1); + // 6. ReturnIfAbrupt(relativeStart). + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + double argStart = argStartTemp.GetNumber(); + // 7. If relativeStart < 0, let k be max((len + relativeStart),0); else let k be min(relativeStart, len). + if (argStart < 0) { + start = argStart + len > 0 ? argStart + len : 0; + } else { + start = argStart < len ? argStart : len; + } + + // 8. If end is undefined, let relativeEnd be len; else let relativeEnd be ToInteger(end). + double argEnd = len; + JSHandle msg2 = GetCallArg(argv, INDEX_TWO); + if (!msg2->IsUndefined()) { + JSTaggedNumber argEndTemp = JSTaggedValue::ToInteger(thread, msg2); + // 9. ReturnIfAbrupt(relativeEnd). + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + argEnd = argEndTemp.GetNumber(); + } + + // 10. If relativeEnd < 0, let final be max((len + relativeEnd),0); else let final be min(relativeEnd, len). + double end; + if (argEnd < 0) { + end = argEnd + len > 0 ? argEnd + len : 0; + } else { + end = argEnd < len ? argEnd : len; + } + + // 11. Repeat, while k < final + // a. Let Pk be ToString(k). + // b. Let setStatus be Set(O, Pk, value, true). + // c. ReturnIfAbrupt(setStatus). + // d. Increase k by 1. + JSMutableHandle key(thread, JSTaggedValue::Undefined()); + double k = start; + while (k < end) { + key.Update(JSTaggedValue(k)); + JSArray::FastSetPropertyByValue(thread, thisObjVal, key, value); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + k++; + } + + // 12. Return O. + return thisObjHandle.GetTaggedValue(); +} + +// 22.1.3.7 Array.prototype.filter ( callbackfn [ , thisArg ] ) +JSTaggedValue BuiltinsArray::Filter(EcmaRuntimeCallInfo *argv) +{ + ASSERT(argv); + JSThread *thread = argv->GetThread(); + [[maybe_unused]] EcmaHandleScope handleScope(thread); + + // 1. Let O be ToObject(this value). + JSHandle thisHandle = GetThis(argv); + JSHandle thisObjHandle = JSTaggedValue::ToObject(thread, thisHandle); + // 2. ReturnIfAbrupt(O). + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + JSHandle thisObjVal(thisObjHandle); + + // 3. Let len be ToLength(Get(O, "length")). + double len = ArrayHelper::GetArrayLength(thread, thisObjVal); + // 4. ReturnIfAbrupt(len). + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + + // 5. If IsCallable(callbackfn) is false, throw a TypeError exception. + JSHandle callbackFnHandle = GetCallArg(argv, 0); + if (!callbackFnHandle->IsCallable()) { + THROW_TYPE_ERROR_AND_RETURN(thread, "the callbackfun is not callable.", JSTaggedValue::Exception()); + } + + // 6. If thisArg was supplied, let T be thisArg; else let T be undefined. + JSHandle thisArgHandle = GetCallArg(argv, 1); + + // 7. Let A be ArraySpeciesCreate(O, 0). + int32_t arrayLen = 0; + JSTaggedValue newArray = JSArray::ArraySpeciesCreate(thread, thisObjHandle, JSTaggedNumber(arrayLen)); + // 8. ReturnIfAbrupt(A). + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + JSHandle newArrayHandle(thread, newArray); + + // 9. Let k be 0. + // 10. Let to be 0. + // 11. Repeat, while k < len + // a. Let Pk be ToString(k). + // b. Let kPresent be HasProperty(O, Pk). + // c. ReturnIfAbrupt(kPresent). + // d. If kPresent is true, then + // i. Let kValue be Get(O, Pk). + // ii. ReturnIfAbrupt(kValue). + // iii. Let selected be ToBoolean(Call(callbackfn, T, «kValue, k, O»)). + // iv. ReturnIfAbrupt(selected). + // v. If selected is true, then + // 1. Let status be CreateDataPropertyOrThrow (A, ToString(to), kValue). + // 2. ReturnIfAbrupt(status). + // 3. Increase to by 1. + // e. Increase k by 1. + double toIndex = 0; + JSMutableHandle key(thread, JSTaggedValue::Undefined()); + JSMutableHandle toIndexHandle(thread, JSTaggedValue::Undefined()); + InternalCallParams *arguments = thread->GetInternalCallParams(); + uint32_t k = 0; + while (k < len) { + bool exists = JSTaggedValue::HasProperty(thread, thisObjVal, k); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + if (exists) { + JSHandle kValue = JSArray::FastGetPropertyByValue(thread, thisObjVal, k); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + key.Update(JSTaggedValue(k)); + arguments->MakeArgv(kValue, key, thisObjVal); + JSTaggedValue callResult = + JSFunction::Call(thread, callbackFnHandle, thisArgHandle, 3, arguments->GetArgv()); // 3: three args + bool boolResult = callResult.ToBoolean(); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + if (boolResult) { + toIndexHandle.Update(JSTaggedValue(toIndex)); + JSObject::CreateDataPropertyOrThrow(thread, newArrayHandle, toIndexHandle, kValue); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + toIndex++; + } + } + k++; + } + + // 12. Return A. + return newArrayHandle.GetTaggedValue(); +} + +// 22.1.3.8 Array.prototype.find ( predicate [ , thisArg ] ) +JSTaggedValue BuiltinsArray::Find(EcmaRuntimeCallInfo *argv) +{ + ASSERT(argv); + BUILTINS_API_TRACE(argv->GetThread(), Array, Find); + JSThread *thread = argv->GetThread(); + [[maybe_unused]] EcmaHandleScope handleScope(thread); + + // 1. Let O be ToObject(this value). + JSHandle thisHandle = GetThis(argv); + JSHandle thisObjHandle = JSTaggedValue::ToObject(thread, thisHandle); + // 2. ReturnIfAbrupt(O). + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + JSHandle thisObjVal(thisObjHandle); + + // 3. Let len be ToLength(Get(O, "length")). + double len = ArrayHelper::GetLength(thread, thisObjVal); + // 4. ReturnIfAbrupt(len). + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + + // 5. If IsCallable(predicate) is false, throw a TypeError exception. + JSHandle callbackFnHandle = GetCallArg(argv, 0); + if (!callbackFnHandle->IsCallable()) { + THROW_TYPE_ERROR_AND_RETURN(thread, "the predicate is not callable.", JSTaggedValue::Exception()); + } + + // 6. If thisArg was supplied, let T be thisArg; else let T be undefined. + JSHandle thisArgHandle = GetCallArg(argv, 1); + + // 7. Let k be 0. + // 8. Repeat, while k < len + // a. Let Pk be ToString(k). + // b. Let kValue be Get(O, Pk). + // c. ReturnIfAbrupt(kValue). + // d. Let testResult be ToBoolean(Call(predicate, T, «kValue, k, O»)). + // e. ReturnIfAbrupt(testResult). + // f. If testResult is true, return kValue. + // g. Increase k by 1. + JSMutableHandle key(thread, JSTaggedValue::Undefined()); + InternalCallParams *arguments = thread->GetInternalCallParams(); + uint32_t k = 0; + while (k < len) { + JSHandle kValue = JSArray::FastGetPropertyByValue(thread, thisObjVal, k); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + key.Update(JSTaggedValue(k)); + arguments->MakeArgv(kValue, key, thisObjVal); + JSTaggedValue callResult = + JSFunction::Call(thread, callbackFnHandle, thisArgHandle, 3, arguments->GetArgv()); // 3: three args + bool boolResult = callResult.ToBoolean(); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + if (boolResult) { + return kValue.GetTaggedValue(); + } + k++; + } + + // 9. Return undefined. + return JSTaggedValue::Undefined(); +} + +// 22.1.3.9 Array.prototype.findIndex ( predicate [ , thisArg ] ) +JSTaggedValue BuiltinsArray::FindIndex(EcmaRuntimeCallInfo *argv) +{ + ASSERT(argv); + BUILTINS_API_TRACE(argv->GetThread(), Array, FindIndex); + JSThread *thread = argv->GetThread(); + [[maybe_unused]] EcmaHandleScope handleScope(thread); + + // 1. Let O be ToObject(this value). + JSHandle thisHandle = GetThis(argv); + JSHandle thisObjHandle = JSTaggedValue::ToObject(thread, thisHandle); + // 2. ReturnIfAbrupt(O). + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + JSHandle thisObjVal(thisObjHandle); + + // 3. Let len be ToLength(Get(O, "length")). + double len = ArrayHelper::GetLength(thread, thisObjVal); + // 4. ReturnIfAbrupt(len). + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + + // 5. If IsCallable(predicate) is false, throw a TypeError exception. + JSHandle callbackFnHandle = GetCallArg(argv, 0); + if (!callbackFnHandle->IsCallable()) { + THROW_TYPE_ERROR_AND_RETURN(thread, "the predicate is not callable.", JSTaggedValue::Exception()); + } + + // 6. If thisArg was supplied, let T be thisArg; else let T be undefined. + JSHandle thisArgHandle = GetCallArg(argv, 1); + + // 7. Let k be 0. + // 8. Repeat, while k < len + // a. Let Pk be ToString(k). + // b. Let kValue be Get(O, Pk). + // c. ReturnIfAbrupt(kValue). + // d. Let testResult be ToBoolean(Call(predicate, T, «kValue, k, O»)). + // e. ReturnIfAbrupt(testResult). + // f. If testResult is true, return k. + // g. Increase k by 1. + JSMutableHandle key(thread, JSTaggedValue::Undefined()); + InternalCallParams *arguments = thread->GetInternalCallParams(); + uint32_t k = 0; + while (k < len) { + JSHandle kValue = JSArray::FastGetPropertyByValue(thread, thisObjVal, k); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + key.Update(JSTaggedValue(k)); + arguments->MakeArgv(kValue, key, thisObjVal); + JSTaggedValue callResult = + JSFunction::Call(thread, callbackFnHandle, thisArgHandle, 3, arguments->GetArgv()); // 3: three args + bool boolResult = callResult.ToBoolean(); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + if (boolResult) { + return GetTaggedDouble(k); + } + k++; + } + + // 9. Return -1. + return GetTaggedDouble(-1); +} + +// 22.1.3.10 Array.prototype.forEach ( callbackfn [ , thisArg ] ) +JSTaggedValue BuiltinsArray::ForEach(EcmaRuntimeCallInfo *argv) +{ + ASSERT(argv); + JSThread *thread = argv->GetThread(); + [[maybe_unused]] EcmaHandleScope handleScope(thread); + + // 1. Let O be ToObject(this value). + JSHandle thisHandle = GetThis(argv); + JSHandle thisObjHandle = JSTaggedValue::ToObject(thread, thisHandle); + // 2. ReturnIfAbrupt(O). + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + JSHandle thisObjVal(thisObjHandle); + + // 3. Let len be ToLength(Get(O, "length")). + double len = ArrayHelper::GetArrayLength(thread, thisObjVal); + // 4. ReturnIfAbrupt(len). + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + + // 5. If IsCallable(callbackfn) is false, throw a TypeError exception. + JSHandle callbackFnHandle = GetCallArg(argv, 0); + if (!callbackFnHandle->IsCallable()) { + THROW_TYPE_ERROR_AND_RETURN(thread, "the callbackfun is not callable.", JSTaggedValue::Exception()); + } + + // 6. If thisArg was supplied, let T be thisArg; else let T be undefined. + JSHandle thisArgHandle = GetCallArg(argv, 1); + + // 7. Let k be 0. + // 8. Repeat, while k < len + // a. Let Pk be ToString(k). + // b. Let kPresent be HasProperty(O, Pk). + // c. ReturnIfAbrupt(kPresent). + // d. If kPresent is true, then + // i. Let kValue be Get(O, Pk). + // ii. ReturnIfAbrupt(kValue). + // iii. Let funcResult be Call(callbackfn, T, «kValue, k, O»). + // iv. ReturnIfAbrupt(funcResult). + // e. Increase k by 1. + JSMutableHandle key(thread, JSTaggedValue::Undefined()); + InternalCallParams *arguments = thread->GetInternalCallParams(); + uint32_t k = 0; + while (k < len) { + bool exists = JSTaggedValue::HasProperty(thread, thisObjVal, k); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + if (exists) { + JSHandle kValue = JSArray::FastGetPropertyByValue(thread, thisObjVal, k); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + key.Update(JSTaggedValue(k)); + arguments->MakeArgv(kValue, key, thisObjVal); + JSTaggedValue funcResult = + JSFunction::Call(thread, callbackFnHandle, thisArgHandle, 3, arguments->GetArgv()); // 3: three args + RETURN_VALUE_IF_ABRUPT_COMPLETION(thread, funcResult); + } + k++; + } + + // 9. Return undefined. + return JSTaggedValue::Undefined(); +} + +// ES2021 23.1.3.13 Array.prototype.includes ( searchElement [ , fromIndex ] ) +JSTaggedValue BuiltinsArray::Includes(EcmaRuntimeCallInfo *argv) +{ + ASSERT(argv); + BUILTINS_API_TRACE(argv->GetThread(), Array, Includes); + JSThread *thread = argv->GetThread(); + [[maybe_unused]] EcmaHandleScope handleScope(thread); + + // 1. Let O be ToObject(this value). + JSHandle thisHandle = GetThis(argv); + JSHandle thisObjHandle = JSTaggedValue::ToObject(thread, thisHandle); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + + JSHandle thisObjVal(thisObjHandle); + + // 2. Let len be ? LengthOfArrayLike(O). + double len = ArrayHelper::GetLength(thread, thisObjVal); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + + // 3. If len is 0, return false. + if (len == 0) { + return GetTaggedBoolean(false); + } + + array_size_t argc = argv->GetArgsNumber(); + double fromIndex = 0; + + if (argc > 1) { + // 4-5. If argument fromIndex was passed let n be ToInteger(fromIndex); else let n be 0. + JSHandle msg1 = GetCallArg(argv, 1); + JSTaggedNumber fromIndexTemp = JSTaggedValue::ToNumber(thread, msg1); + + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + fromIndex = ecmascript::base::NumberHelper::TruncateDouble(fromIndexTemp.GetNumber()); + + // 6. If n is positiveInfinity, return false. + if (JSTaggedValue::Equal(thread, msg1, + JSHandle(thread, JSTaggedValue(base::POSITIVE_INFINITY)))) { + return GetTaggedBoolean(false); + } + + // 7. Else if n is negativeInfinity, set n to 0. + if (JSTaggedValue::Equal(thread, msg1, + JSHandle(thread, JSTaggedValue(-base::POSITIVE_INFINITY)))) { + fromIndex = 0; + } + } + + // 8. If n ≥ 0, then + // a. Let k be n. + // 9. Else, + // a. Let k be len + n. + // b. If k < 0, set k to 0. + double from = (fromIndex >= 0) ? fromIndex : ((len + fromIndex) >= 0 ? len + fromIndex : 0); + + // 10. + const JSHandle searchElement = GetCallArg(argv, 0); + while (from < len) { + // a) + JSHandle indexHandle(thread, JSTaggedValue(from)); + JSHandle handle = + JSHandle(thread, JSTaggedValue::ToString(thread, indexHandle).GetTaggedValue()); + JSHandle element = JSTaggedValue::GetProperty(thread, thisObjVal, handle).GetValue(); + + // b) + if (JSTaggedValue::SameValueZero(searchElement.GetTaggedValue(), element.GetTaggedValue())) { + return GetTaggedBoolean(true); + } + + // c) + from++; + } + + return GetTaggedBoolean(false); +} + +// 22.1.3.11 Array.prototype.indexOf ( searchElement [ , fromIndex ] ) +JSTaggedValue BuiltinsArray::IndexOf(EcmaRuntimeCallInfo *argv) +{ + ASSERT(argv); + BUILTINS_API_TRACE(argv->GetThread(), Array, IndexOf); + JSThread *thread = argv->GetThread(); + [[maybe_unused]] EcmaHandleScope handleScope(thread); + + uint32_t argc = argv->GetArgsNumber(); + + // 1. Let O be ToObject(this value). + JSHandle thisHandle = GetThis(argv); + JSHandle thisObjHandle = JSTaggedValue::ToObject(thread, thisHandle); + // 2. ReturnIfAbrupt(O). + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + JSHandle thisObjVal(thisObjHandle); + + JSHandle searchElement = GetCallArg(argv, 0); + + // 3. Let len be ToLength(Get(O, "length")). + double len = ArrayHelper::GetLength(thread, thisObjVal); + // 4. ReturnIfAbrupt(len). + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + + // 5. If len is 0, return −1. + if (len == 0) { + return GetTaggedInt(-1); + } + + // 6. If argument fromIndex was passed let n be ToInteger(fromIndex); else let n be 0. + double fromIndex = 0; + if (argc > 1) { + JSHandle msg1 = GetCallArg(argv, 1); + JSTaggedNumber fromIndexTemp = JSTaggedValue::ToNumber(thread, msg1); + // 7. ReturnIfAbrupt(n). + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + fromIndex = ecmascript::base::NumberHelper::TruncateDouble(fromIndexTemp.GetNumber()); + } + + // 8. If n ≥ len, return −1. + if (fromIndex >= len) { + return GetTaggedInt(-1); + } + + // 9. If n ≥ 0, then + // a. Let k be n. + // 10. Else n<0, + // a. Let k be len - abs(n). + // b. If k < 0, let k be 0. + double from = (fromIndex >= 0) ? fromIndex : ((len + fromIndex) >= 0 ? len + fromIndex : 0); + + // 11. Repeat, while k key(thread, JSTaggedValue::Undefined()); + while (from < len) { + key.Update(JSTaggedValue(from)); + bool exists = (thisHandle->IsTypedArray() || JSTaggedValue::HasProperty(thread, thisObjVal, key)); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + if (exists) { + JSHandle kValueHandle = JSArray::FastGetPropertyByValue(thread, thisObjVal, key); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + if (JSTaggedValue::StrictEqual(thread, searchElement, kValueHandle)) { + return GetTaggedDouble(from); + } + } + from++; + } + + // 12. Return -1. + return GetTaggedInt(-1); +} + +// 22.1.3.12 Array.prototype.join (separator) +JSTaggedValue BuiltinsArray::Join(EcmaRuntimeCallInfo *argv) +{ + ASSERT(argv); + BUILTINS_API_TRACE(argv->GetThread(), Array, Join); + JSThread *thread = argv->GetThread(); + [[maybe_unused]] EcmaHandleScope handleScope(thread); + JSHandle thisHandle = GetThis(argv); + if (thisHandle->IsStableJSArray(thread)) { + return JSStableArray::Join(JSHandle::Cast(thisHandle), argv); + } + auto factory = thread->GetEcmaVM()->GetFactory(); + + // 1. Let O be ToObject(this value). + JSHandle thisObjHandle = JSTaggedValue::ToObject(thread, thisHandle); + // 2. ReturnIfAbrupt(O). + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + JSHandle thisObjVal(thisObjHandle); + + // 3. Let len be ToLength(Get(O, "length")). + double len = ArrayHelper::GetLength(thread, thisObjVal); + // 4. ReturnIfAbrupt(len). + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + + // 5. If separator is undefined, let separator be the single-element String ",". + // 6. Let sep be ToString(separator). + JSHandle sepHandle; + if ((GetCallArg(argv, 0)->IsUndefined())) { + sepHandle = JSHandle::Cast(factory->NewFromCanBeCompressString(",")); + } else { + sepHandle = GetCallArg(argv, 0); + } + + JSHandle sepStringHandle = JSTaggedValue::ToString(thread, sepHandle); + // 7. ReturnIfAbrupt(sep). + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + int32_t sepLen = sepStringHandle->GetLength(); + std::u16string sepStr; + if (sepStringHandle->IsUtf16()) { + sepStr = ecmascript::base::StringHelper::Utf16ToU16String(sepStringHandle->GetDataUtf16(), sepLen); + } else { + sepStr = ecmascript::base::StringHelper::Utf8ToU16String(sepStringHandle->GetDataUtf8(), sepLen); + } + + // 8. If len is zero, return the empty String. + if (len == 0) { + return GetTaggedString(thread, ""); + } + + // 9. Let element0 be Get(O, "0"). + // 10. If element0 is undefined or null, let R be the empty String; otherwise, let R be ToString(element0). + // 11. ReturnIfAbrupt(R). + // 12. Let k be 1. + // 13. Repeat, while k < len + // a. Let S be the String value produced by concatenating R and sep. + // b. Let element be Get(O, ToString(k)). + // c. If element is undefined or null, let next be the empty String; otherwise, let next be ToString(element). + // d. ReturnIfAbrupt(next). + // e. Let R be a String value produced by concatenating S and next. + // f. Increase k by 1. + std::u16string concatStr; + std::u16string concatStrNew; + for (int32_t k = 0; k < len; k++) { + std::u16string nextStr; + JSHandle element = JSArray::FastGetPropertyByValue(thread, thisObjVal, k); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + if (!element->IsUndefined() && !element->IsNull()) { + JSHandle nextStringHandle = JSTaggedValue::ToString(thread, element); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + int32_t nextLen = nextStringHandle->GetLength(); + if (nextStringHandle->IsUtf16()) { + nextStr = ecmascript::base::StringHelper::Utf16ToU16String(nextStringHandle->GetDataUtf16(), nextLen); + } else { + nextStr = ecmascript::base::StringHelper::Utf8ToU16String(nextStringHandle->GetDataUtf8(), nextLen); + } + } + if (k > 0) { + concatStrNew = ecmascript::base::StringHelper::Append(concatStr, sepStr); + concatStr = ecmascript::base::StringHelper::Append(concatStrNew, nextStr); + continue; + } + concatStr = ecmascript::base::StringHelper::Append(concatStr, nextStr); + } + + // 14. Return R. + const char16_t *constChar16tData = concatStr.data(); + auto *char16tData = const_cast(constChar16tData); + auto *uint16tData = reinterpret_cast(char16tData); + int32_t u16strSize = concatStr.size(); + return factory->NewFromUtf16Literal(uint16tData, u16strSize).GetTaggedValue(); +} + +// 22.1.3.13 Array.prototype.keys ( ) +JSTaggedValue BuiltinsArray::Keys(EcmaRuntimeCallInfo *argv) +{ + ASSERT(argv); + BUILTINS_API_TRACE(argv->GetThread(), Array, Keys); + JSThread *thread = argv->GetThread(); + [[maybe_unused]] EcmaHandleScope handleScope(thread); + ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); + // 1. Let O be ToObject(this value). + // 2. ReturnIfAbrupt(O). + JSHandle self = JSTaggedValue::ToObject(thread, GetThis(argv)); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + // 3. Return CreateArrayIterator(O, "key"). + JSHandle iter(factory->NewJSArrayIterator(self, IterationKind::KEY)); + return iter.GetTaggedValue(); +} + +// 22.1.3.14 Array.prototype.lastIndexOf ( searchElement [ , fromIndex ] ) +JSTaggedValue BuiltinsArray::LastIndexOf(EcmaRuntimeCallInfo *argv) +{ + ASSERT(argv); + BUILTINS_API_TRACE(argv->GetThread(), Array, LastIndexOf); + JSThread *thread = argv->GetThread(); + [[maybe_unused]] EcmaHandleScope handleScope(thread); + + uint32_t argc = argv->GetArgsNumber(); + + // 1. Let O be ToObject(this value). + JSHandle thisHandle = GetThis(argv); + JSHandle thisObjHandle = JSTaggedValue::ToObject(thread, thisHandle); + // 2. ReturnIfAbrupt(O). + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + JSHandle thisObjVal(thisObjHandle); + + JSHandle searchElement = GetCallArg(argv, 0); + + // 3. Let len be ToLength(Get(O, "length")). + double len = ArrayHelper::GetLength(thread, thisObjVal); + // 4. ReturnIfAbrupt(len). + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + + // 5. If len is 0, return −1. + if (len == 0) { + return GetTaggedInt(-1); + } + + // 6. If argument fromIndex was passed let n be ToInteger(fromIndex); else let n be len-1. + double fromIndex = len - 1; + if (argc > 1) { + JSHandle msg1 = GetCallArg(argv, 1); + JSTaggedNumber fromIndexTemp = JSTaggedValue::ToNumber(thread, msg1); + // 7. ReturnIfAbrupt(n). + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + fromIndex = ecmascript::base::NumberHelper::TruncateDouble(fromIndexTemp.GetNumber()); + } + + // 8. If n ≥ 0, let k be min(n, len – 1). + // 9. Else n < 0, + // a. Let k be len - abs(n). + double from = (fromIndex >= 0) ? ((len - 1) < fromIndex ? len - 1 : fromIndex) : len + fromIndex; + + // 10. Repeat, while k≥ 0 + // a. Let kPresent be HasProperty(O, ToString(k)). + // b. ReturnIfAbrupt(kPresent). + // c. If kPresent is true, then + // i. Let elementK be Get(O, ToString(k)). + // ii. ReturnIfAbrupt(elementK). + // iii. Let same be the result of performing Strict Equality Comparison searchElement === elementK. + // iv. If same is true, return k. + // d. Decrease k by 1. + JSMutableHandle key(thread, JSTaggedValue::Undefined()); + while (from >= 0) { + key.Update(JSTaggedValue(from)); + bool exists = (thisHandle->IsTypedArray() || JSTaggedValue::HasProperty(thread, thisObjVal, key)); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + if (exists) { + JSHandle kValueHandle = JSArray::FastGetPropertyByValue(thread, thisObjVal, key); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + if (JSTaggedValue::StrictEqual(thread, searchElement, kValueHandle)) { + return GetTaggedDouble(from); + } + } + from--; + } + + // 11. Return -1. + return GetTaggedInt(-1); +} + +// 22.1.3.15 Array.prototype.map ( callbackfn [ , thisArg ] ) +JSTaggedValue BuiltinsArray::Map(EcmaRuntimeCallInfo *argv) +{ + ASSERT(argv); + BUILTINS_API_TRACE(argv->GetThread(), Array, Map); + JSThread *thread = argv->GetThread(); + [[maybe_unused]] EcmaHandleScope handleScope(thread); + + // 1. Let O be ToObject(this value). + JSHandle thisHandle = GetThis(argv); + JSHandle thisObjHandle = JSTaggedValue::ToObject(thread, thisHandle); + // 2. ReturnIfAbrupt(O). + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + JSHandle thisObjVal(thisObjHandle); + + // 3. Let len be ToLength(Get(O, "length")). + double len = ArrayHelper::GetArrayLength(thread, thisObjVal); + // 4. ReturnIfAbrupt(len). + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + + // 5. If IsCallable(callbackfn) is false, throw a TypeError exception. + JSHandle callbackFnHandle = GetCallArg(argv, 0); + if (!callbackFnHandle->IsCallable()) { + THROW_TYPE_ERROR_AND_RETURN(thread, "the callbackfun is not callable.", JSTaggedValue::Exception()); + } + + // 6. If thisArg was supplied, let T be thisArg; else let T be undefined. + JSHandle thisArgHandle = GetCallArg(argv, 1); + + // 7. Let A be ArraySpeciesCreate(O, len). + JSTaggedValue newArray = JSArray::ArraySpeciesCreate(thread, thisObjHandle, JSTaggedNumber(len)); + // 8. ReturnIfAbrupt(A). + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + if (!newArray.IsECMAObject()) { + THROW_TYPE_ERROR_AND_RETURN(thread, "Failed to create Object.", JSTaggedValue::Exception()); + } + JSHandle newArrayHandle(thread, newArray); + + // 9. Let k be 0. + // 10. Repeat, while k < len + // a. Let Pk be ToString(k). + // b. Let kPresent be HasProperty(O, Pk). + // c. ReturnIfAbrupt(kPresent). + // d. If kPresent is true, then + // i. Let kValue be Get(O, Pk). + // ii. ReturnIfAbrupt(kValue). + // iii. Let mappedValue be Call(callbackfn, T, «kValue, k, O»). + // iv. ReturnIfAbrupt(mappedValue). + // v. Let status be CreateDataPropertyOrThrow (A, Pk, mappedValue). + // vi. ReturnIfAbrupt(status). + // e. Increase k by 1. + JSMutableHandle key(thread, JSTaggedValue::Undefined()); + JSMutableHandle mapResultHandle(thread, JSTaggedValue::Undefined()); + InternalCallParams *arguments = thread->GetInternalCallParams(); + uint32_t k = 0; + while (k < len) { + bool exists = JSTaggedValue::HasProperty(thread, thisObjVal, k); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + if (exists) { + JSHandle kValue = JSArray::FastGetPropertyByValue(thread, thisObjVal, k); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + key.Update(JSTaggedValue(k)); + arguments->MakeArgv(kValue, key, thisObjVal); + JSTaggedValue mapResult = + JSFunction::Call(thread, callbackFnHandle, thisArgHandle, 3, arguments->GetArgv()); // 3: three args + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + mapResultHandle.Update(mapResult); + JSObject::CreateDataPropertyOrThrow(thread, newArrayHandle, k, mapResultHandle); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + } + k++; + } + + // 11. Return A. + return newArrayHandle.GetTaggedValue(); +} + +// 22.1.3.16 Array.prototype.pop ( ) +JSTaggedValue BuiltinsArray::Pop(EcmaRuntimeCallInfo *argv) +{ + ASSERT(argv); + BUILTINS_API_TRACE(argv->GetThread(), Array, Pop); + JSThread *thread = argv->GetThread(); + [[maybe_unused]] EcmaHandleScope handleScope(thread); + + // 1. Let O be ToObject(this value). + JSHandle thisHandle = GetThis(argv); + if (thisHandle->IsStableJSArray(thread)) { + return JSStableArray::Pop(JSHandle::Cast(thisHandle), argv); + } + JSHandle thisObjHandle = JSTaggedValue::ToObject(thread, thisHandle); + // 2. ReturnIfAbrupt(O). + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + JSHandle thisObjVal(thisObjHandle); + + // 3. Let len be ToLength(Get(O, "length")). + double len = ArrayHelper::GetArrayLength(thread, thisObjVal); + // 4. ReturnIfAbrupt(len). + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + // 5. If len is zero, + // a. Let setStatus be Set(O, "length", 0, true). + // b. ReturnIfAbrupt(setStatus). + // c. Return undefined. + JSHandle lengthKey = thread->GlobalConstants()->GetHandledLengthString(); + if (len == 0) { + JSHandle lengthValue(thread, JSTaggedValue(0)); + JSTaggedValue::SetProperty(thread, thisObjVal, lengthKey, lengthValue, true); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + return JSTaggedValue::Undefined(); + } + + // 6. Else len > 0, + // a. Let newLen be len–1. + // b. Let indx be ToString(newLen). + // c. Let element be Get(O, indx). + // d. ReturnIfAbrupt(element). + // e. Let deleteStatus be DeletePropertyOrThrow(O, indx). + // f. ReturnIfAbrupt(deleteStatus). + // g. Let setStatus be Set(O, "length", newLen, true). + // h. ReturnIfAbrupt(setStatus). + // i. Return element. + double newLen = len - 1; + JSHandle indexHandle(thread, JSTaggedValue(newLen)); + JSHandle element = JSTaggedValue::GetProperty(thread, thisObjVal, indexHandle).GetValue(); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + JSTaggedValue::DeletePropertyOrThrow(thread, thisObjVal, indexHandle); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + JSTaggedValue::SetProperty(thread, thisObjVal, lengthKey, indexHandle, true); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + + return element.GetTaggedValue(); +} + +// 22.1.3.17 Array.prototype.push ( ...items ) +JSTaggedValue BuiltinsArray::Push(EcmaRuntimeCallInfo *argv) +{ + ASSERT(argv); + BUILTINS_API_TRACE(argv->GetThread(), Array, Push); + JSThread *thread = argv->GetThread(); + [[maybe_unused]] EcmaHandleScope handleScope(thread); + JSHandle thisHandle = GetThis(argv); + if (thisHandle->IsStableJSArray(thread)) { + return JSStableArray::Push(JSHandle::Cast(thisHandle), argv); + } + // 6. Let argCount be the number of elements in items. + uint32_t argc = argv->GetArgsNumber(); + + // 1. Let O be ToObject(this value). + JSHandle thisObjHandle = JSTaggedValue::ToObject(thread, thisHandle); + // 2. ReturnIfAbrupt(O). + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + JSHandle thisObjVal(thisObjHandle); + + // 3. Let len be ToLength(Get(O, "length")). + double len = ArrayHelper::GetArrayLength(thread, thisObjVal); + // 4. ReturnIfAbrupt(len). + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + // 7. If len + argCount > 253-1, throw a TypeError exception. + if (len + argc > ecmascript::base::MAX_SAFE_INTEGER) { + THROW_TYPE_ERROR_AND_RETURN(thread, "out of range.", JSTaggedValue::Exception()); + } + + // 8. Repeat, while items is not empty + // a. Remove the first element from items and let E be the value of the element. + // b. Let setStatus be Set(O, ToString(len), E, true). + // c. ReturnIfAbrupt(setStatus). + // d. Let len be len+1. + double k = 0; + JSMutableHandle key(thread, JSTaggedValue::Undefined()); + while (k < argc) { + key.Update(JSTaggedValue(len)); + JSHandle kValue = GetCallArg(argv, k); + JSArray::FastSetPropertyByValue(thread, thisObjVal, key, kValue); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + k++; + len++; + } + + // 9. Let setStatus be Set(O, "length", len, true). + JSHandle lengthKey = thread->GlobalConstants()->GetHandledLengthString(); + key.Update(JSTaggedValue(len)); + JSTaggedValue::SetProperty(thread, thisObjVal, lengthKey, key, true); + // 10. ReturnIfAbrupt(setStatus). + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + + // 11. Return len. + return GetTaggedDouble(len); +} + +// 22.1.3.18 Array.prototype.reduce ( callbackfn [ , initialValue ] ) +JSTaggedValue BuiltinsArray::Reduce(EcmaRuntimeCallInfo *argv) +{ + ASSERT(argv); + BUILTINS_API_TRACE(argv->GetThread(), Array, Reduce); + JSThread *thread = argv->GetThread(); + [[maybe_unused]] EcmaHandleScope handleScope(thread); + const GlobalEnvConstants *globalConst = thread->GlobalConstants(); + + uint32_t argc = argv->GetArgsNumber(); + // 1. Let O be ToObject(this value). + JSHandle thisHandle = GetThis(argv); + JSHandle thisObjHandle = JSTaggedValue::ToObject(thread, thisHandle); + // 2. ReturnIfAbrupt(O). + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + JSHandle thisObjVal(thisObjHandle); + + // 3. Let len be ToLength(Get(O, "length")). + double len = ArrayHelper::GetLength(thread, thisObjVal); + // 4. ReturnIfAbrupt(len). + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + + // 5. If IsCallable(callbackfn) is false, throw a TypeError exception. + JSHandle callbackFnHandle = GetCallArg(argv, 0); + if (!callbackFnHandle->IsCallable()) { + THROW_TYPE_ERROR_AND_RETURN(thread, "the callbackfun is not callable.", JSTaggedValue::Exception()); + } + + // 6. If len is 0 and initialValue is not present, throw a TypeError exception. + if (len == 0 && argc < 2) { // 2:2 means the number of parameters + THROW_TYPE_ERROR_AND_RETURN(thread, "out of range.", JSTaggedValue::Exception()); + } + + // 7. Let k be 0. + // 8. If initialValue is present, then + // a. Set accumulator to initialValue. + // 9. Else initialValue is not present, + // a. Let kPresent be false. + // b. Repeat, while kPresent is false and k < len + // i. Let Pk be ToString(k). + // ii. Let kPresent be HasProperty(O, Pk). + // iii. ReturnIfAbrupt(kPresent). + // iv. If kPresent is true, then + // 1. Let accumulator be Get(O, Pk). + // 2. ReturnIfAbrupt(accumulator). + // v. Increase k by 1. + // c. If kPresent is false, throw a TypeError exception. + uint32_t k = 0; + JSMutableHandle accumulator(thread, JSTaggedValue::Undefined()); + if (argc == 2) { // 2:2 means the number of parameters + accumulator.Update(GetCallArg(argv, 1).GetTaggedValue()); + } else { + bool kPresent = false; + while (!kPresent && k < len) { + kPresent = (thisHandle->IsTypedArray() || JSTaggedValue::HasProperty(thread, thisObjVal, k)); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + if (kPresent) { + accumulator.Update(JSArray::FastGetPropertyByValue(thread, thisObjVal, k).GetTaggedValue()); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + } + k++; + } + if (!kPresent) { + THROW_TYPE_ERROR_AND_RETURN(thread, "accumulator can't be initialized.", JSTaggedValue::Exception()); + } + } + + // 10. Repeat, while k < len + // a. Let Pk be ToString(k). + // b. Let kPresent be HasProperty(O, Pk). + // c. ReturnIfAbrupt(kPresent). + // d. If kPresent is true, then + // i. Let kValue be Get(O, Pk). + // ii. ReturnIfAbrupt(kValue). + // iii. Let accumulator be Call(callbackfn, undefined, «accumulator, kValue, k, O»). + // iv. ReturnIfAbrupt(accumulator). + // e. Increase k by 1. + JSTaggedValue callResult = JSTaggedValue::Undefined(); + JSMutableHandle key(thread, JSTaggedValue::Undefined()); + InternalCallParams *arguments = thread->GetInternalCallParams(); + while (k < len) { + bool exists = (thisHandle->IsTypedArray() || JSTaggedValue::HasProperty(thread, thisObjVal, k)); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + if (exists) { + JSHandle kValue = JSArray::FastGetPropertyByValue(thread, thisObjVal, k); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + key.Update(JSTaggedValue(k)); + arguments->MakeArgv(accumulator, kValue, key, thisObjVal); + JSHandle thisArgHandle = globalConst->GetHandledUndefined(); + callResult = + JSFunction::Call(thread, callbackFnHandle, thisArgHandle, 4, arguments->GetArgv()); // 4: four args + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + accumulator.Update(callResult); + } + k++; + } + + // 11. Return accumulator. + return accumulator.GetTaggedValue(); +} + +// 22.1.3.19 Array.prototype.reduceRight ( callbackfn [ , initialValue ] ) +JSTaggedValue BuiltinsArray::ReduceRight(EcmaRuntimeCallInfo *argv) +{ + ASSERT(argv); + BUILTINS_API_TRACE(argv->GetThread(), Array, ReduceRight); + JSThread *thread = argv->GetThread(); + [[maybe_unused]] EcmaHandleScope handleScope(thread); + const GlobalEnvConstants *globalConst = thread->GlobalConstants(); + + uint32_t argc = argv->GetArgsNumber(); + + // 1. Let O be ToObject(this value). + JSHandle thisHandle = GetThis(argv); + JSHandle thisObjHandle = JSTaggedValue::ToObject(thread, thisHandle); + // 2. ReturnIfAbrupt(O). + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + JSHandle thisObjVal(thisObjHandle); + + // 3. Let len be ToLength(Get(O, "length")). + double len = ArrayHelper::GetLength(thread, thisObjVal); + // 4. ReturnIfAbrupt(len). + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + + // 5. If IsCallable(callbackfn) is false, throw a TypeError exception. + JSHandle callbackFnHandle = GetCallArg(argv, 0); + if (!callbackFnHandle->IsCallable()) { + THROW_TYPE_ERROR_AND_RETURN(thread, "the callbackfun is not callable.", JSTaggedValue::Exception()); + } + + // 6. If len is 0 and initialValue is not present, throw a TypeError exception. + if (len == 0 && argc < 2) { // 2:2 means the number of parameters + THROW_TYPE_ERROR_AND_RETURN(thread, "out of range.", JSTaggedValue::Exception()); + } + + // 7. Let k be len-1. + double k = len - 1; + // 8. If initialValue is present, then + // a. Set accumulator to initialValue. + // 9. Else initialValue is not present, + // a. Let kPresent be false. + // b. Repeat, while kPresent is false and k ≥ 0 + // i. Let Pk be ToString(k). + // ii. Let kPresent be HasProperty(O, Pk). + // iii. ReturnIfAbrupt(kPresent). + // iv. If kPresent is true, then + // 1. Let accumulator be Get(O, Pk). + // 2. ReturnIfAbrupt(accumulator). + // v. Decrease k by 1. + // c. If kPresent is false, throw a TypeError exception. + JSMutableHandle accumulator(thread, JSTaggedValue::Undefined()); + if (argc == 2) { // 2:2 means the number of parameters + accumulator.Update(GetCallArg(argv, 1).GetTaggedValue()); + } else { + bool kPresent = false; + while (!kPresent && k >= 0) { + kPresent = (thisHandle->IsTypedArray() || JSTaggedValue::HasProperty(thread, thisObjVal, k)); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + if (kPresent) { + accumulator.Update(JSArray::FastGetPropertyByValue(thread, thisObjVal, k).GetTaggedValue()); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + } + k--; + } + if (!kPresent) { + THROW_TYPE_ERROR_AND_RETURN(thread, "accumulator can't be initialized.", JSTaggedValue::Exception()); + } + } + + // 10. Repeat, while k ≥ 0 + // a. Let Pk be ToString(k). + // b. Let kPresent be HasProperty(O, Pk). + // c. ReturnIfAbrupt(kPresent). + // d. If kPresent is true, then + // i. Let kValue be Get(O, Pk). + // ii. ReturnIfAbrupt(kValue). + // iii. Let accumulator be Call(callbackfn, undefined, «accumulator, kValue, k, O»). + // iv. ReturnIfAbrupt(accumulator). + // e. Decrease k by 1. + JSMutableHandle key(thread, JSTaggedValue::Undefined()); + JSTaggedValue callResult = JSTaggedValue::Undefined(); + InternalCallParams *arguments = thread->GetInternalCallParams(); + while (k >= 0) { + key.Update(JSTaggedValue(k)); + bool exists = (thisHandle->IsTypedArray() || JSTaggedValue::HasProperty(thread, thisObjVal, key)); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + if (exists) { + JSHandle kValue = JSArray::FastGetPropertyByValue(thread, thisObjVal, key); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + arguments->MakeArgv(accumulator, kValue, key, thisObjVal); + JSHandle thisArgHandle = globalConst->GetHandledUndefined(); + callResult = + JSFunction::Call(thread, callbackFnHandle, thisArgHandle, 4, arguments->GetArgv()); // 4: four args + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + accumulator.Update(callResult); + } + k--; + } + + // 11. Return accumulator. + return accumulator.GetTaggedValue(); +} + +// 22.1.3.20 Array.prototype.reverse ( ) +JSTaggedValue BuiltinsArray::Reverse(EcmaRuntimeCallInfo *argv) +{ + ASSERT(argv); + BUILTINS_API_TRACE(argv->GetThread(), Array, Reverse); + JSThread *thread = argv->GetThread(); + [[maybe_unused]] EcmaHandleScope handleScope(thread); + + // 1. Let O be ToObject(this value). + JSHandle thisHandle = GetThis(argv); + JSHandle thisObjHandle = JSTaggedValue::ToObject(thread, thisHandle); + // 2. ReturnIfAbrupt(O). + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + JSHandle thisObjVal(thisObjHandle); + + // 3. Let len be ToLength(Get(O, "length")). + double len = ArrayHelper::GetLength(thread, thisObjVal); + // 4. ReturnIfAbrupt(len). + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + + // 5. Let middle be floor(len/2). + double middle = std::floor(len / 2); + + // 6. Let lower be 0. + double lower = 0; + + // 7. Repeat, while lower != middle + // a. Let upper be len-lower-1. + // b. Let upperP be ToString(upper). + // c. Let lowerP be ToString(lower). + // d. Let lowerExists be HasProperty(O, lowerP). + // e. ReturnIfAbrupt(lowerExists). + // f. If lowerExists is true, then + // i. Let lowerValue be Get(O, lowerP). + // ii. ReturnIfAbrupt(lowerValue). + // g. Let upperExists be HasProperty(O, upperP). + // h. ReturnIfAbrupt(upperExists). + // i. If upperExists is true, then + // i. Let upperValue be Get(O, upperP). + // ii. ReturnIfAbrupt(upperValue). + // j. If lowerExists is true and upperExists is true, then + // i. Let setStatus be Set(O, lowerP, upperValue, true). + // ii. ReturnIfAbrupt(setStatus). + // iii. Let setStatus be Set(O, upperP, lowerValue, true). + // iv. ReturnIfAbrupt(setStatus). + // k. Else if lowerExists is false and upperExists is true, then + // i. Let setStatus be Set(O, lowerP, upperValue, true). + // ii. ReturnIfAbrupt(setStatus). + // iii. Let deleteStatus be DeletePropertyOrThrow (O, upperP). + // iv. ReturnIfAbrupt(deleteStatus). + // l. Else if lowerExists is true and upperExists is false, then + // i. Let deleteStatus be DeletePropertyOrThrow (O, lowerP). + // ii. ReturnIfAbrupt(deleteStatus). + // iii. Let setStatus be Set(O, upperP, lowerValue, true). + // iv. ReturnIfAbrupt(setStatus). + // m. Else both lowerExists and upperExists are false, + // i. No action is required. + // n. Increase lower by 1. + JSMutableHandle lowerP(thread, JSTaggedValue::Undefined()); + JSMutableHandle upperP(thread, JSTaggedValue::Undefined()); + JSHandle lowerValueHandle(thread, JSTaggedValue::Undefined()); + JSHandle upperValueHandle(thread, JSTaggedValue::Undefined()); + while (lower != middle) { + double upper = len - lower - 1; + lowerP.Update(JSTaggedValue(lower)); + upperP.Update(JSTaggedValue(upper)); + bool lowerExists = (thisHandle->IsTypedArray() || JSTaggedValue::HasProperty(thread, thisObjVal, lowerP)); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + if (lowerExists) { + lowerValueHandle = JSArray::FastGetPropertyByValue(thread, thisObjVal, lowerP); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + } + bool upperExists = (thisHandle->IsTypedArray() || JSTaggedValue::HasProperty(thread, thisObjVal, upperP)); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + if (upperExists) { + upperValueHandle = JSArray::FastGetPropertyByValue(thread, thisObjVal, upperP); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + } + if (lowerExists && upperExists) { + JSArray::FastSetPropertyByValue(thread, thisObjVal, lowerP, upperValueHandle); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + JSArray::FastSetPropertyByValue(thread, thisObjVal, upperP, lowerValueHandle); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + } else if (upperExists) { + JSArray::FastSetPropertyByValue(thread, thisObjVal, lowerP, upperValueHandle); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + JSTaggedValue::DeletePropertyOrThrow(thread, thisObjVal, upperP); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + } else if (lowerExists) { + JSTaggedValue::DeletePropertyOrThrow(thread, thisObjVal, lowerP); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + JSArray::FastSetPropertyByValue(thread, thisObjVal, upperP, lowerValueHandle); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + } else { + } + lower++; + } + + // 8. Return O . + return thisObjHandle.GetTaggedValue(); +} + +// 22.1.3.21 Array.prototype.shift ( ) +JSTaggedValue BuiltinsArray::Shift(EcmaRuntimeCallInfo *argv) +{ + ASSERT(argv); + BUILTINS_API_TRACE(argv->GetThread(), Array, Shift); + JSThread *thread = argv->GetThread(); + [[maybe_unused]] EcmaHandleScope handleScope(thread); + + // 1. Let O be ToObject(this value). + JSHandle thisHandle = GetThis(argv); + if (thisHandle->IsStableJSArray(thread)) { + return JSStableArray::Shift(JSHandle::Cast(thisHandle), argv); + } + JSHandle thisObjHandle = JSTaggedValue::ToObject(thread, thisHandle); + // 2. ReturnIfAbrupt(O). + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + JSHandle thisObjVal(thisObjHandle); + + // 3. Let len be ToLength(Get(O, "length")). + double len = ArrayHelper::GetArrayLength(thread, thisObjVal); + // 4. ReturnIfAbrupt(len). + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + // 5. If len is zero, then + // a. Let setStatus be Set(O, "length", 0, true). + // b. ReturnIfAbrupt(setStatus). + // c. Return undefined. + JSHandle lengthKey = thread->GlobalConstants()->GetHandledLengthString(); + if (len == 0) { + JSHandle zeroLenHandle(thread, JSTaggedValue(len)); + JSTaggedValue::SetProperty(thread, thisObjVal, lengthKey, zeroLenHandle, true); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + return JSTaggedValue::Undefined(); + } + + // 6. Let first be Get(O, "0"). + JSHandle firstKey(thread, JSTaggedValue(0)); + JSHandle firstValue = JSTaggedValue::GetProperty(thread, thisObjVal, firstKey).GetValue(); + // 7. ReturnIfAbrupt(first). + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + + // 8. Let k be 1. + // 9. Repeat, while k < len + // a. Let from be ToString(k). + // b. Let to be ToString(k–1). + // c. Let fromPresent be HasProperty(O, from). + // d. ReturnIfAbrupt(fromPresent). + // e. If fromPresent is true, then + // i. Let fromVal be Get(O, from). + // ii. ReturnIfAbrupt(fromVal). + // iii. Let setStatus be Set(O, to, fromVal, true). + // iv. ReturnIfAbrupt(setStatus). + // f. Else fromPresent is false, + // i. Let deleteStatus be DeletePropertyOrThrow(O, to). + // ii. ReturnIfAbrupt(deleteStatus). + // g. Increase k by 1. + JSMutableHandle toKey(thread, JSTaggedValue::Undefined()); + double k = 1; + while (k < len) { + bool exists = JSTaggedValue::HasProperty(thread, thisObjVal, k); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + if (exists) { + JSHandle fromValue = JSArray::FastGetPropertyByValue(thread, thisObjVal, k); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + JSArray::FastSetPropertyByValue(thread, thisObjVal, k - 1, fromValue); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + } else { + toKey.Update(JSTaggedValue(k - 1)); + JSTaggedValue::DeletePropertyOrThrow(thread, thisObjVal, toKey); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + } + k++; + } + // 10. Let deleteStatus be DeletePropertyOrThrow(O, ToString(len–1)). + JSHandle deleteKey(thread, JSTaggedValue(len - 1)); + JSTaggedValue::DeletePropertyOrThrow(thread, thisObjVal, deleteKey); + // 11. ReturnIfAbrupt(deleteStatus). + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + + // 12. Let setStatus be Set(O, "length", len–1, true). + JSHandle newLenHandle(thread, JSTaggedValue(len - 1)); + JSTaggedValue::SetProperty(thread, thisObjVal, lengthKey, newLenHandle, true); + // 13. ReturnIfAbrupt(setStatus). + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + + // 14. Return first. + return firstValue.GetTaggedValue(); +} + +// 22.1.3.22 Array.prototype.slice (start, end) +JSTaggedValue BuiltinsArray::Slice(EcmaRuntimeCallInfo *argv) +{ + BUILTINS_API_TRACE(argv->GetThread(), Array, Slice); + ASSERT(argv); + JSThread *thread = argv->GetThread(); + [[maybe_unused]] EcmaHandleScope handleScope(thread); + + // 1. Let O be ToObject(this value). + JSHandle thisHandle = GetThis(argv); + JSHandle thisObjHandle = JSTaggedValue::ToObject(thread, thisHandle); + // 2. ReturnIfAbrupt(O). + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + JSHandle thisObjVal(thisObjHandle); + + // 3. Let len be ToLength(Get(O, "length")). + double len = ArrayHelper::GetArrayLength(thread, thisObjVal); + // 4. ReturnIfAbrupt(len). + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + + // 5. Let relativeStart be ToInteger(start). + JSHandle msg0 = GetCallArg(argv, 0); + JSTaggedNumber argStartTemp = JSTaggedValue::ToInteger(thread, msg0); + // 6. ReturnIfAbrupt(relativeStart). + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + double argStart = argStartTemp.GetNumber(); + + double k; + // 7. If relativeStart < 0, let k be max((len + relativeStart),0); else let k be min(relativeStart, len). + if (argStart < 0) { + k = argStart + len > 0 ? argStart + len : 0; + } else { + k = argStart < len ? argStart : len; + } + // 8. If end is undefined, let relativeEnd be len; else let relativeEnd be ToInteger(end). + // 9. ReturnIfAbrupt(relativeEnd). + // 10. If relativeEnd < 0, let final be max((len + relativeEnd),0); else let final be min(relativeEnd, len). + JSHandle msg1 = GetCallArg(argv, 1); + double argEnd = len; + if (!msg1->IsUndefined()) { + JSTaggedNumber argEndTemp = JSTaggedValue::ToInteger(thread, msg1); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + argEnd = argEndTemp.GetNumber(); + } + double final; + if (argEnd < 0) { + final = argEnd + len > 0 ? argEnd + len : 0; + } else { + final = argEnd < len ? argEnd : len; + } + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + + // 11. Let count be max(final – k, 0). + double count = (final - k) > 0 ? (final - k) : 0; + + // 12. Let A be ArraySpeciesCreate(O, count). + JSTaggedValue newArray = JSArray::ArraySpeciesCreate(thread, thisObjHandle, JSTaggedNumber(count)); + // 13. ReturnIfAbrupt(A). + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + if (count == 0) { + return newArray; + } + JSHandle newArrayHandle(thread, newArray); + + if (thisHandle->IsStableJSArray(thread) && newArray.IsStableJSArray(thread)) { + TaggedArray *destElements = *JSObject::GrowElementsCapacity(thread, newArrayHandle, count); + TaggedArray *srcElements = TaggedArray::Cast(thisObjHandle->GetElements().GetTaggedObject()); + + for (uint32_t idx = 0; idx < count; idx++) { + destElements->Set(thread, idx, srcElements->Get(k + idx)); + } + + JSHandle::Cast(newArrayHandle)->SetArrayLength(thread, count); + return newArrayHandle.GetTaggedValue(); + } + + // 14. Let n be 0. + // 15. Repeat, while k < final + // a. Let Pk be ToString(k). + // b. Let kPresent be HasProperty(O, Pk). + // c. ReturnIfAbrupt(kPresent). + // d. If kPresent is true, then + // i. Let kValue be Get(O, Pk). + // ii. ReturnIfAbrupt(kValue). + // iii. Let status be CreateDataPropertyOrThrow(A, ToString(n), kValue ). + // iv. ReturnIfAbrupt(status). + // e. Increase k by 1. + // f. Increase n by 1. + double n = 0; + JSMutableHandle key(thread, JSTaggedValue::Undefined()); + JSMutableHandle nKey(thread, JSTaggedValue::Undefined()); + while (k < final) { + key.Update(JSTaggedValue(k)); + bool exists = JSTaggedValue::HasProperty(thread, thisObjVal, key); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + if (exists) { + nKey.Update(JSTaggedValue(n)); + JSHandle kValueHandle = JSArray::FastGetPropertyByValue(thread, thisObjVal, key); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + JSObject::CreateDataPropertyOrThrow(thread, newArrayHandle, nKey, kValueHandle); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + } + k++; + n++; + } + + // 16. Let setStatus be Set(A, "length", n, true). + JSHandle lengthKey = thread->GlobalConstants()->GetHandledLengthString(); + JSHandle newLenHandle(thread, JSTaggedValue(n)); + JSTaggedValue::SetProperty(thread, JSHandle::Cast(newArrayHandle), lengthKey, newLenHandle, true); + // 17. ReturnIfAbrupt(setStatus). + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + + // 18. Return A. + return newArrayHandle.GetTaggedValue(); +} + +// 22.1.3.23 Array.prototype.some ( callbackfn [ , thisArg ] ) +JSTaggedValue BuiltinsArray::Some(EcmaRuntimeCallInfo *argv) +{ + ASSERT(argv); + BUILTINS_API_TRACE(argv->GetThread(), Array, Some); + JSThread *thread = argv->GetThread(); + [[maybe_unused]] EcmaHandleScope handleScope(thread); + + // 1. Let O be ToObject(this value). + JSHandle thisHandle = GetThis(argv); + JSHandle thisObjHandle = JSTaggedValue::ToObject(thread, thisHandle); + // 2. ReturnIfAbrupt(O). + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + JSHandle thisObjVal(thisObjHandle); + + // 3. Let len be ToLength(Get(O, "length")). + double len = ArrayHelper::GetLength(thread, thisObjVal); + // 4. ReturnIfAbrupt(len). + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + + // 5. If IsCallable(callbackfn) is false, throw a TypeError exception. + JSHandle callbackFnHandle = GetCallArg(argv, 0); + if (!callbackFnHandle->IsCallable()) { + THROW_TYPE_ERROR_AND_RETURN(thread, "the callbackfun is not callable.", JSTaggedValue::Exception()); + } + + // 6. If thisArg was supplied, let T be thisArg; else let T be undefined. + JSHandle thisArgHandle = GetCallArg(argv, 1); + + // 7. Let k be 0. + // 8. Repeat, while k < len + // a. Let Pk be ToString(k). + // b. Let kPresent be HasProperty(O, Pk). + // c. ReturnIfAbrupt(kPresent). + // d. If kPresent is true, then + // i. Let kValue be Get(O, Pk). + // ii. ReturnIfAbrupt(kValue). + // iii. Let testResult be ToBoolean(Call(callbackfn, T, «kValue, k, and O»)). + // iv. ReturnIfAbrupt(testResult). + // v. If testResult is true, return true. + // e. Increase k by 1. + JSMutableHandle key(thread, JSTaggedValue::Undefined()); + InternalCallParams *arguments = thread->GetInternalCallParams(); + double k = 0; + while (k < len) { + bool exists = (thisHandle->IsTypedArray() || JSTaggedValue::HasProperty(thread, thisObjVal, k)); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + if (exists) { + key.Update(JSTaggedValue(k)); + JSHandle kValue = JSArray::FastGetPropertyByValue(thread, thisObjVal, key); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + arguments->MakeArgv(kValue, key, thisObjVal); + JSTaggedValue callResult = + JSFunction::Call(thread, callbackFnHandle, thisArgHandle, 3, arguments->GetArgv()); // 3: three args + bool boolResult = callResult.ToBoolean(); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + if (boolResult) { + return GetTaggedBoolean(true); + } + } + k++; + } + + // 9. Return false. + return GetTaggedBoolean(false); +} + +// 22.1.3.24 Array.prototype.sort (comparefn) +JSTaggedValue BuiltinsArray::Sort(EcmaRuntimeCallInfo *argv) +{ + ASSERT(argv); + BUILTINS_API_TRACE(argv->GetThread(), Array, Sort); + JSThread *thread = argv->GetThread(); + [[maybe_unused]] EcmaHandleScope handleScope(thread); + + // 1. Let obj be ToObject(this value). + JSHandle thisHandle = GetThis(argv); + JSHandle thisObjHandle = JSTaggedValue::ToObject(thread, thisHandle); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + + JSHandle callbackFnHandle = GetCallArg(argv, 0); + if (!callbackFnHandle->IsUndefined() && !callbackFnHandle->IsCallable()) { + THROW_TYPE_ERROR_AND_RETURN(thread, "Callable is false", JSTaggedValue::Exception()); + } + + // 2. Let len be ToLength(Get(obj, "length")). + double len = ArrayHelper::GetArrayLength(thread, JSHandle(thisObjHandle)); + // 3. ReturnIfAbrupt(len). + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + + JSMutableHandle presentValue(thread, JSTaggedValue::Undefined()); + JSMutableHandle middleValue(thread, JSTaggedValue::Undefined()); + JSMutableHandle previousValue(thread, JSTaggedValue::Undefined()); + for (int i = 1; i < len; i++) { + int beginIndex = 0; + int endIndex = i; + presentValue.Update(FastRuntimeStub::FastGetPropertyByIndex(thread, thisObjHandle.GetTaggedValue(), i)); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + while (beginIndex < endIndex) { + int middleIndex = (beginIndex + endIndex) / 2; // 2 : half + middleValue.Update( + FastRuntimeStub::FastGetPropertyByIndex(thread, thisObjHandle.GetTaggedValue(), middleIndex)); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + int32_t compareResult = ArrayHelper::SortCompare(thread, callbackFnHandle, middleValue, presentValue); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + if (compareResult > 0) { + endIndex = middleIndex; + } else { + beginIndex = middleIndex + 1; + } + } + + if (endIndex >= 0 && endIndex < i) { + for (int j = i; j > endIndex; j--) { + previousValue.Update( + FastRuntimeStub::FastGetPropertyByIndex(thread, thisObjHandle.GetTaggedValue(), j - 1)); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + FastRuntimeStub::FastSetPropertyByIndex(thread, thisObjHandle.GetTaggedValue(), j, + previousValue.GetTaggedValue()); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + } + FastRuntimeStub::FastSetPropertyByIndex(thread, thisObjHandle.GetTaggedValue(), endIndex, + presentValue.GetTaggedValue()); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + } + } + + return thisObjHandle.GetTaggedValue(); +} + +// 22.1.3.25 Array.prototype.splice (start, deleteCount , ...items ) +// NOLINTNEXTLINE(readability-function-size) +JSTaggedValue BuiltinsArray::Splice(EcmaRuntimeCallInfo *argv) +{ + ASSERT(argv); + BUILTINS_API_TRACE(argv->GetThread(), Array, Splice); + JSThread *thread = argv->GetThread(); + [[maybe_unused]] EcmaHandleScope handleScope(thread); + uint32_t argc = argv->GetArgsNumber(); + // 1. Let O be ToObject(this value). + JSHandle thisHandle = GetThis(argv); + JSHandle thisObjHandle = JSTaggedValue::ToObject(thread, thisHandle); + // 2. ReturnIfAbrupt(O). + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + JSHandle thisObjVal(thisObjHandle); + // 3. Let len be ToLength(Get(O, "length")). + double len = ArrayHelper::GetArrayLength(thread, thisObjVal); + // 4. ReturnIfAbrupt(len). + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + // 5. Let relativeStart be ToInteger(start). + double start = 0; + double insertCount = 0; + double actualDeleteCount = 0; + double end = len; + double argStart = 0; + if (argc > 0) { + JSHandle msg0 = GetCallArg(argv, 0); + JSTaggedNumber argStartTemp = JSTaggedValue::ToInteger(thread, msg0); + // 6. ReturnIfAbrupt(relativeStart). + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + argStart = argStartTemp.GetNumber(); + // 7. If relativeStart < 0, let actualStart be max((len + relativeStart),0); else let actualStart be + // min(relativeStart, len). + if (argStart < 0) { + start = argStart + len > 0 ? argStart + len : 0; + } else { + start = argStart < end ? argStart : end; + } + actualDeleteCount = len - start; + } + // 8. If the number of actual arguments is 0, then + // a. Let insertCount be 0. + // b. Let actualDeleteCount be 0. + // 9. Else if the number of actual arguments is 1, then + // a. Let insertCount be 0. + // b. Let actualDeleteCount be len – actualStart. + // 10. Else, + // a. Let insertCount be the number of actual arguments minus 2. + // b. Let dc be ToInteger(deleteCount). + // c. ReturnIfAbrupt(dc). + // d. Let actualDeleteCount be min(max(dc,0), len – actualStart). + if (argc > 1) { + insertCount = argc - 2; // 2:2 means there are two arguments before the insert items. + JSHandle msg1 = GetCallArg(argv, 1); + JSTaggedNumber argDeleteCount = JSTaggedValue::ToInteger(thread, msg1); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + double deleteCount = argDeleteCount.GetNumber(); + deleteCount = deleteCount > 0 ? deleteCount : 0; + actualDeleteCount = deleteCount < (len - start) ? deleteCount : len - start; + } + // 11. If len+insertCount−actualDeleteCount > 253-1, throw a TypeError exception. + if (len + insertCount - actualDeleteCount > ecmascript::base::MAX_SAFE_INTEGER) { + THROW_TYPE_ERROR_AND_RETURN(thread, "out of range.", JSTaggedValue::Exception()); + } + + if (thisHandle->IsStableJSArray(thread)) { + return JSStableArray::Splice(JSHandle::Cast(thisHandle), argv, start, insertCount, actualDeleteCount); + } + // 12. Let A be ArraySpeciesCreate(O, actualDeleteCount). + JSTaggedValue newArray = JSArray::ArraySpeciesCreate(thread, thisObjHandle, JSTaggedNumber(actualDeleteCount)); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + JSHandle newArrayHandle(thread, newArray); + // 14. Let k be 0. + // 15. Repeat, while k < actualDeleteCount + // a. Let from be ToString(actualStart+k). + // b. Let fromPresent be HasProperty(O, from). + // d. If fromPresent is true, then + // i. Let fromValue be Get(O, from). + // iii. Let status be CreateDataPropertyOrThrow(A, ToString(k), fromValue). + // e. Increment k by 1. + JSMutableHandle fromKey(thread, JSTaggedValue::Undefined()); + JSMutableHandle toKey(thread, JSTaggedValue::Undefined()); + double k = 0; + while (k < actualDeleteCount) { + double from = start + k; + fromKey.Update(JSTaggedValue(from)); + bool exists = JSTaggedValue::HasProperty(thread, thisObjVal, fromKey); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + if (exists) { + JSHandle fromValue = JSArray::FastGetPropertyByValue(thread, thisObjVal, fromKey); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + toKey.Update(JSTaggedValue(k)); + if (newArrayHandle->IsJSProxy()) { + toKey.Update(JSTaggedValue::ToString(thread, toKey).GetTaggedValue()); + } + JSObject::CreateDataPropertyOrThrow(thread, newArrayHandle, toKey, fromValue); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + } + k++; + } + // 16. Let setStatus be Set(A, "length", actualDeleteCount, true). + JSHandle lengthKey = thread->GlobalConstants()->GetHandledLengthString(); + JSHandle deleteCountHandle(thread, JSTaggedValue(actualDeleteCount)); + JSTaggedValue::SetProperty(thread, JSHandle::Cast(newArrayHandle), lengthKey, deleteCountHandle, + true); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + // 19. Let itemCount be the number of elements in items. + // 20. If itemCount < actualDeleteCount, then + // a. Let k be actualStart. + // b. Repeat, while k < (len – actualDeleteCount) + // i. Let from be ToString(k+actualDeleteCount). + // ii. Let to be ToString(k+itemCount). + // iii. Let fromPresent be HasProperty(O, from). + // v. If fromPresent is true, then + // 1. Let fromValue be Get(O, from). + // 3. Let setStatus be Set(O, to, fromValue, true). + // vi. Else fromPresent is false, + // 1. Let deleteStatus be DeletePropertyOrThrow(O, to). + // vii. Increase k by 1. + // c. Let k be len. + // d. Repeat, while k > (len – actualDeleteCount + itemCount) + // i. Let deleteStatus be DeletePropertyOrThrow(O, ToString(k–1)). + // iii. Decrease k by 1. + if (insertCount < actualDeleteCount) { + k = start; + while (k < len - actualDeleteCount) { + fromKey.Update(JSTaggedValue(k + actualDeleteCount)); + toKey.Update(JSTaggedValue(k + insertCount)); + bool exists = JSTaggedValue::HasProperty(thread, thisObjVal, fromKey); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + if (exists) { + JSHandle fromValue = JSArray::FastGetPropertyByValue(thread, thisObjVal, fromKey); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + JSArray::FastSetPropertyByValue(thread, thisObjVal, toKey, fromValue); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + } else { + JSTaggedValue::DeletePropertyOrThrow(thread, thisObjVal, toKey); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + } + k++; + } + k = len; + JSMutableHandle deleteKey(thread, JSTaggedValue::Undefined()); + while (k > len - actualDeleteCount + insertCount) { + deleteKey.Update(JSTaggedValue(k - 1)); + JSTaggedValue::DeletePropertyOrThrow(thread, thisObjVal, deleteKey); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + k--; + } + } else if (insertCount > actualDeleteCount) { + // 21. Else if itemCount > actualDeleteCount, then + // a. Let k be (len – actualDeleteCount). + // b. Repeat, while k > actualStart + // i. Let from be ToString(k + actualDeleteCount – 1). + // ii. Let to be ToString(k + itemCount – 1) + // iii. Let fromPresent be HasProperty(O, from). + // iv. ReturnIfAbrupt(fromPresent). + // v. If fromPresent is true, then + // 1. Let fromValue be Get(O, from). + // 2. ReturnIfAbrupt(fromValue). + // 3. Let setStatus be Set(O, to, fromValue, true). + // 4. ReturnIfAbrupt(setStatus). + // vi. Else fromPresent is false, + // 1. Let deleteStatus be DeletePropertyOrThrow(O, to). + // 2. ReturnIfAbrupt(deleteStatus). + // vii. Decrease k by 1. + k = len - actualDeleteCount; + while (k > start) { + fromKey.Update(JSTaggedValue(k + actualDeleteCount - 1)); + toKey.Update(JSTaggedValue(k + insertCount - 1)); + bool exists = JSTaggedValue::HasProperty(thread, thisObjVal, fromKey); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + if (exists) { + JSHandle fromValue = JSArray::FastGetPropertyByValue(thread, thisObjVal, fromKey); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + JSArray::FastSetPropertyByValue(thread, thisObjVal, toKey, fromValue); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + } else { + JSTaggedValue::DeletePropertyOrThrow(thread, thisObjVal, toKey); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + } + k--; + } + } + // 22. Let k be actualStart. + k = start; + // 23. Repeat, while items is not empty + JSMutableHandle key(thread, JSTaggedValue::Undefined()); + for (uint32_t i = 2; i < argc; i++) { + JSHandle itemValue = GetCallArg(argv, i); + key.Update(JSTaggedValue(k)); + JSArray::FastSetPropertyByValue(thread, thisObjVal, key, itemValue); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + k++; + } + // 24. Let setStatus be Set(O, "length", len – actualDeleteCount + itemCount, true). + double newLen = len - actualDeleteCount + insertCount; + JSHandle newLenHandle(thread, JSTaggedValue(newLen)); + JSTaggedValue::SetProperty(thread, thisObjVal, lengthKey, newLenHandle, true); + // 25. ReturnIfAbrupt(setStatus). + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + // 26. Return A. + return newArrayHandle.GetTaggedValue(); +} + +// 22.1.3.26 Array.prototype.toLocaleString ( [ reserved1 [ , reserved2 ] ] ) +JSTaggedValue BuiltinsArray::ToLocaleString(EcmaRuntimeCallInfo *argv) +{ + ASSERT(argv); + BUILTINS_API_TRACE(argv->GetThread(), Array, ToLocaleString); + JSThread *thread = argv->GetThread(); + [[maybe_unused]] EcmaHandleScope handleScope(thread); + auto ecmaVm = thread->GetEcmaVM(); + ObjectFactory *factory = ecmaVm->GetFactory(); + + // 1. Let O be ToObject(this value). + JSHandle thisHandle = GetThis(argv); + JSHandle thisObjHandle = JSTaggedValue::ToObject(thread, thisHandle); + // 2. ReturnIfAbrupt(O). + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + JSHandle thisObjVal(thisObjHandle); + + // 3. Let len be ToLength(Get(O, "length")). + double len = ArrayHelper::GetLength(thread, thisObjVal); + // 4. ReturnIfAbrupt(len). + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + + // 5. Let separator be the String value for the list-separator String appropriate for the host environment’s + // current locale (this is derived in an implementation-defined way). + JSHandle sepHandle; + if ((GetCallArg(argv, 0)->IsUndefined())) { + sepHandle = JSHandle::Cast(ecmaVm->GetFactory()->NewFromCanBeCompressString(",")); + } else { + sepHandle = GetCallArg(argv, 0); + } + JSHandle sepStringHandle = JSTaggedValue::ToString(thread, sepHandle); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + CString sepString = ConvertToString(*sepStringHandle); + // 6. If len is zero, return the empty String. + if (len == 0) { + return GetTaggedString(thread, ""); + } + + // Inject locales and options argument into a taggedArray + JSHandle locales = GetCallArg(argv, 0); + JSHandle options = GetCallArg(argv, 1); + + CString concatStr; + // 7. Let firstElement be Get(array, "0"). + // 8. ReturnIfAbrupt(firstElement). + // 9. If firstElement is undefined or null, then + // a. Let R be the empty String. + // 10. Else + // a. Let R be ToString(Invoke(firstElement, "toLocaleString")). + // b. ReturnIfAbrupt(R). + // 11. Let k be 1. + // 12. Repeat, while k < len + // a. Let S be a String value produced by concatenating R and separator. + // b. Let nextElement be Get(array, ToString(k)). + // c. ReturnIfAbrupt(nextElement). + // d. If nextElement is undefined or null, then + // i. Let R be the empty String. + // e. Else + // i. Let R be ToString(Invoke(nextElement, "toLocaleString")). + // ii. ReturnIfAbrupt(R). + // f. Let R be a String value produced by concatenating S and R. + // g. Increase k by 1. + auto globalConst = thread->GlobalConstants(); + InternalCallParams *arguments = thread->GetInternalCallParams(); + for (int32_t k = 0; k < len; k++) { + JSTaggedValue next = globalConst->GetEmptyString(); + JSHandle nextElement = JSArray::FastGetPropertyByValue(thread, thisObjVal, k); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + if (!nextElement->IsUndefined() && !nextElement->IsNull()) { + JSHandle nextValueHandle = nextElement; + arguments->MakeArgv(locales, options); + JSTaggedValue callResult = + JSFunction::Invoke(thread, nextValueHandle, globalConst->GetHandledToLocaleStringString(), 2, + arguments->GetArgv()); // 2: two args + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + next = callResult; + } + JSHandle nextHandle(thread, next); + JSHandle nextStringHandle = JSTaggedValue::ToString(thread, nextHandle); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + CString nextString = ConvertToString(*nextStringHandle); + if (k > 0) { + concatStr += sepString; + concatStr += nextString; + continue; + } + concatStr += nextString; + } + + // 13. Return R. + return factory->NewFromString(concatStr).GetTaggedValue(); +} + +// 22.1.3.27 Array.prototype.toString ( ) +JSTaggedValue BuiltinsArray::ToString(EcmaRuntimeCallInfo *argv) +{ + ASSERT(argv); + BUILTINS_API_TRACE(argv->GetThread(), Array, ToString); + JSThread *thread = argv->GetThread(); + [[maybe_unused]] EcmaHandleScope handleScope(thread); + auto ecmaVm = thread->GetEcmaVM(); + ObjectFactory *factory = ecmaVm->GetFactory(); + + // 1. Let array be ToObject(this value). + JSHandle thisHandle = GetThis(argv); + JSHandle thisObjHandle = JSTaggedValue::ToObject(thread, thisHandle); + // 2. ReturnIfAbrupt(array). + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + JSHandle thisObjVal(thisObjHandle); + + // 3. Let func be Get(array, "join"). + JSHandle joinKey(factory->NewFromCanBeCompressString("join")); + JSHandle callbackFnHandle = JSTaggedValue::GetProperty(thread, thisObjVal, joinKey).GetValue(); + + // 4. ReturnIfAbrupt(func). + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + + // 5. If IsCallable(func) is false, let func be the intrinsic function %ObjProto_toString% (19.1.3.6). + if (!callbackFnHandle->IsCallable()) { + JSHandle env = ecmaVm->GetGlobalEnv(); + JSHandle objectPrototype = env->GetObjectFunctionPrototype(); + JSHandle toStringKey = thread->GlobalConstants()->GetHandledToStringString(); + callbackFnHandle = JSTaggedValue::GetProperty(thread, objectPrototype, toStringKey).GetValue(); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + } + JSHandle argsList = GetArgsArray(argv); + InternalCallParams *arguments = thread->GetInternalCallParams(); + arguments->MakeArgList(*argsList); + return JSFunction::Call(thread, callbackFnHandle, thisObjVal, argsList->GetLength(), arguments->GetArgv()); +} + +// 22.1.3.28 Array.prototype.unshift ( ...items ) +JSTaggedValue BuiltinsArray::Unshift(EcmaRuntimeCallInfo *argv) +{ + ASSERT(argv); + BUILTINS_API_TRACE(argv->GetThread(), Array, Unshift); + JSThread *thread = argv->GetThread(); + [[maybe_unused]] EcmaHandleScope handleScope(thread); + + // 5. Let argCount be the number of actual arguments. + uint32_t argc = argv->GetArgsNumber(); + + // 1. Let O be ToObject(this value). + JSHandle thisHandle = GetThis(argv); + JSHandle thisObjHandle = JSTaggedValue::ToObject(thread, thisHandle); + // 2. ReturnIfAbrupt(O). + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + JSHandle thisObjVal(thisObjHandle); + + // 3. Let len be ToLength(Get(O, "length")). + double len = ArrayHelper::GetArrayLength(thread, thisObjVal); + // 4. ReturnIfAbrupt(len). + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + + // 6. If argCount > 0, then + // a. If len+ argCount > 253-1, throw a TypeError exception. + // b. Let k be len. + // c. Repeat, while k > 0, + // i. Let from be ToString(k–1). + // ii. Let to be ToString(k+argCount –1). + // iii. Let fromPresent be HasProperty(O, from). + // iv. ReturnIfAbrupt(fromPresent). + // v. If fromPresent is true, then + // 1. Let fromValue be Get(O, from). + // 2. ReturnIfAbrupt(fromValue). + // 3. Let setStatus be Set(O, to, fromValue, true). + // 4. ReturnIfAbrupt(setStatus). + // vi. Else fromPresent is false, + // 1. Let deleteStatus be DeletePropertyOrThrow(O, to). + // 2. ReturnIfAbrupt(deleteStatus). + // vii. Decrease k by 1. + if (argc > 0) { + if (len + argc > ecmascript::base::MAX_SAFE_INTEGER) { + THROW_TYPE_ERROR_AND_RETURN(thread, "out of range.", JSTaggedValue::Exception()); + } + JSMutableHandle fromKey(thread, JSTaggedValue::Undefined()); + JSMutableHandle toKey(thread, JSTaggedValue::Undefined()); + double k = len; + while (k > 0) { + fromKey.Update(JSTaggedValue(k - 1)); + toKey.Update(JSTaggedValue(k + argc - 1)); + bool exists = JSTaggedValue::HasProperty(thread, thisObjVal, fromKey); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + if (exists) { + JSHandle fromValue = JSArray::FastGetPropertyByValue(thread, thisObjVal, fromKey); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + JSArray::FastSetPropertyByValue(thread, thisObjVal, toKey, fromValue); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + } else { + JSTaggedValue::DeletePropertyOrThrow(thread, thisObjVal, toKey); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + } + k--; + } + // d. Let j be 0. + // e. Let items be a List whose elements are, in left to right order, the arguments that were passed to this + // function invocation. + // f. Repeat, while items is not empty + // i. Remove the first element from items and let E be the value of that element. + // ii. Let setStatus be Set(O, ToString(j), E, true). + // iii. ReturnIfAbrupt(setStatus). + // iv. Increase j by 1. + double j = 0; + while (j < argc) { + toKey.Update(JSTaggedValue(j)); + JSHandle toValue = GetCallArg(argv, j); + JSArray::FastSetPropertyByValue(thread, thisObjVal, toKey, toValue); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + j++; + } + } + + // 7. Let setStatus be Set(O, "length", len+argCount, true). + JSHandle lengthKey = thread->GlobalConstants()->GetHandledLengthString(); + double newLen = len + argc; + JSHandle newLenHandle(thread, JSTaggedValue(newLen)); + JSTaggedValue::SetProperty(thread, thisObjVal, lengthKey, newLenHandle, true); + // 8. ReturnIfAbrupt(setStatus). + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + + // 9. Return len+argCount. + return GetTaggedDouble(newLen); +} + +// 22.1.3.29 Array.prototype.values ( ) +JSTaggedValue BuiltinsArray::Values(EcmaRuntimeCallInfo *argv) +{ + ASSERT(argv); + BUILTINS_API_TRACE(argv->GetThread(), Array, Values); + JSThread *thread = argv->GetThread(); + [[maybe_unused]] EcmaHandleScope handleScope(thread); + ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); + // 1. Let O be ToObject(this value). + // 2. ReturnIfAbrupt(O). + JSHandle self = JSTaggedValue::ToObject(thread, GetThis(argv)); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + // 3. Return CreateArrayIterator(O, "value"). + JSHandle iter(factory->NewJSArrayIterator(self, IterationKind::VALUE)); + return iter.GetTaggedValue(); +} +// 22.1.3.31 Array.prototype [ @@unscopables ] +JSTaggedValue BuiltinsArray::Unscopables(EcmaRuntimeCallInfo *argv) +{ + JSThread *thread = argv->GetThread(); + [[maybe_unused]] EcmaHandleScope handleScope(thread); + ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); + + JSHandle nullHandle(thread, JSTaggedValue::Null()); + JSHandle unscopableList = factory->OrdinaryNewJSObjectCreate(nullHandle); + + JSHandle trueVal(thread, JSTaggedValue::True()); + JSHandle copyWithKey(factory->NewFromCanBeCompressString("copyWithin")); + JSObject::CreateDataProperty(thread, unscopableList, copyWithKey, trueVal); + + JSHandle entriesKey(factory->NewFromCanBeCompressString("entries")); + JSObject::CreateDataProperty(thread, unscopableList, entriesKey, trueVal); + + JSHandle fillKey(factory->NewFromCanBeCompressString("fill")); + JSObject::CreateDataProperty(thread, unscopableList, fillKey, trueVal); + + JSHandle findKey(factory->NewFromCanBeCompressString("find")); + JSObject::CreateDataProperty(thread, unscopableList, findKey, trueVal); + + JSHandle findIndexKey(factory->NewFromCanBeCompressString("findIndex")); + JSObject::CreateDataProperty(thread, unscopableList, findIndexKey, trueVal); + + JSHandle flatKey(factory->NewFromCanBeCompressString("flat")); + JSObject::CreateDataProperty(thread, unscopableList, flatKey, trueVal); + + JSHandle flatMapKey(factory->NewFromCanBeCompressString("flatMap")); + JSObject::CreateDataProperty(thread, unscopableList, flatMapKey, trueVal); + + JSHandle includesKey(factory->NewFromCanBeCompressString("includes")); + JSObject::CreateDataProperty(thread, unscopableList, includesKey, trueVal); + + JSHandle keysKey(factory->NewFromCanBeCompressString("keys")); + JSObject::CreateDataProperty(thread, unscopableList, keysKey, trueVal); + + JSHandle valuesKey(factory->NewFromCanBeCompressString("values")); + JSObject::CreateDataProperty(thread, unscopableList, valuesKey, trueVal); + + return unscopableList.GetTaggedValue(); +} +} // namespace panda::ecmascript::builtins diff --git a/runtime/builtins/builtins_array.h b/runtime/builtins/builtins_array.h new file mode 100644 index 000000000..51d9e9964 --- /dev/null +++ b/runtime/builtins/builtins_array.h @@ -0,0 +1,106 @@ +/* + * Copyright (c) 2021-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_BUILTINS_BUILTINS_ARRAY_H +#define ECMASCRIPT_BUILTINS_BUILTINS_ARRAY_H + +#include "plugins/ecmascript/runtime/base/builtins_base.h" + +namespace panda::ecmascript::builtins { +static constexpr uint8_t INDEX_TWO = 2; +static constexpr uint8_t INDEX_THREE = 3; +class BuiltinsArray : public ecmascript::base::BuiltinsBase { +public: + // 22.1.1 + static JSTaggedValue ArrayConstructor(EcmaRuntimeCallInfo *argv); + + // 22.1.2.1 + static JSTaggedValue From(EcmaRuntimeCallInfo *argv); + // 22.1.2.2 + static JSTaggedValue IsArray(EcmaRuntimeCallInfo *argv); + // 22.1.2.3 + static JSTaggedValue Of(EcmaRuntimeCallInfo *argv); + // 22.1.2.5 + static JSTaggedValue Species(EcmaRuntimeCallInfo *argv); + + // prototype + // 22.1.3.1 + static JSTaggedValue Concat(EcmaRuntimeCallInfo *argv); + // 22.1.3.3 + static JSTaggedValue CopyWithin(EcmaRuntimeCallInfo *argv); + // 22.1.3.4 + static JSTaggedValue Entries(EcmaRuntimeCallInfo *argv); + // 22.1.3.5 + static JSTaggedValue Every(EcmaRuntimeCallInfo *argv); + // ES2019 22.1.3.10 + static JSTaggedValue Flat(EcmaRuntimeCallInfo *argv); + // ES2019 22.1.3.11 + static JSTaggedValue FlatMap(EcmaRuntimeCallInfo *argv); + // 22.1.3.6 + static JSTaggedValue Fill(EcmaRuntimeCallInfo *argv); + // 22.1.3.7 + static JSTaggedValue Filter(EcmaRuntimeCallInfo *argv); + // 22.1.3.8 + static JSTaggedValue Find(EcmaRuntimeCallInfo *argv); + // 22.1.3.9 + static JSTaggedValue FindIndex(EcmaRuntimeCallInfo *argv); + // 22.1.3.10 + static JSTaggedValue ForEach(EcmaRuntimeCallInfo *argv); + // ES2021 23.1.3.13 + static JSTaggedValue Includes(EcmaRuntimeCallInfo *argv); + // 22.1.3.11 + static JSTaggedValue IndexOf(EcmaRuntimeCallInfo *argv); + // 22.1.3.12 + static JSTaggedValue Join(EcmaRuntimeCallInfo *argv); + // 22.1.3.13 + static JSTaggedValue Keys(EcmaRuntimeCallInfo *argv); + // 22.1.3.14 + static JSTaggedValue LastIndexOf(EcmaRuntimeCallInfo *argv); + // 22.1.3.15 + static JSTaggedValue Map(EcmaRuntimeCallInfo *argv); + // 22.1.3.16 + static JSTaggedValue Pop(EcmaRuntimeCallInfo *argv); + // 22.1.3.17 + static JSTaggedValue Push(EcmaRuntimeCallInfo *argv); + // 22.1.3.18 + static JSTaggedValue Reduce(EcmaRuntimeCallInfo *argv); + // 22.1.3.19 + static JSTaggedValue ReduceRight(EcmaRuntimeCallInfo *argv); + // 22.1.3.20 + static JSTaggedValue Reverse(EcmaRuntimeCallInfo *argv); + // 22.1.3.21 + static JSTaggedValue Shift(EcmaRuntimeCallInfo *argv); + // 22.1.3.22 + static JSTaggedValue Slice(EcmaRuntimeCallInfo *argv); + // 22.1.3.23 + static JSTaggedValue Some(EcmaRuntimeCallInfo *argv); + // 22.1.3.24 + static JSTaggedValue Sort(EcmaRuntimeCallInfo *argv); + // 22.1.3.25 + static JSTaggedValue Splice(EcmaRuntimeCallInfo *argv); + // 22.1.3.26 + static JSTaggedValue ToLocaleString(EcmaRuntimeCallInfo *argv); + // 22.1.3.27 + static JSTaggedValue ToString(EcmaRuntimeCallInfo *argv); + // 22.1.3.28 + static JSTaggedValue Unshift(EcmaRuntimeCallInfo *argv); + // 22.1.3.29 + static JSTaggedValue Values(EcmaRuntimeCallInfo *argv); + // 22.1.3.31 + static JSTaggedValue Unscopables(EcmaRuntimeCallInfo *argv); +}; +} // namespace panda::ecmascript::builtins + +#endif // ECMASCRIPT_BUILTINS_BUILTINS_ARRAY_H diff --git a/runtime/builtins/builtins_arraybuffer.cpp b/runtime/builtins/builtins_arraybuffer.cpp new file mode 100644 index 000000000..3450c3f8c --- /dev/null +++ b/runtime/builtins/builtins_arraybuffer.cpp @@ -0,0 +1,590 @@ +/* + * Copyright (c) 2021-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 "plugins/ecmascript/runtime/builtins/builtins_arraybuffer.h" + +#include + +#include "plugins/ecmascript/runtime/base/builtins_base.h" +#include "plugins/ecmascript/runtime/base/number_helper.h" +#include "plugins/ecmascript/runtime/ecma_macros.h" +#include "plugins/ecmascript/runtime/ecma_vm.h" +#include "plugins/ecmascript/runtime/global_env.h" +#include "plugins/ecmascript/runtime/internal_call_params.h" +#include "plugins/ecmascript/runtime/js_arraybuffer.h" +#include "plugins/ecmascript/runtime/js_object-inl.h" +#include "plugins/ecmascript/runtime/js_tagged_number.h" +#include "plugins/ecmascript/runtime/js_tagged_value-inl.h" +#include "plugins/ecmascript/runtime/js_tagged_value.h" +#include "plugins/ecmascript/runtime/object_factory.h" +#include "securec.h" + +namespace panda::ecmascript::builtins { +// 24.1.2.1 ArrayBuffer(length) +JSTaggedValue BuiltinsArrayBuffer::ArrayBufferConstructor(EcmaRuntimeCallInfo *argv) +{ + ASSERT(argv); + BUILTINS_API_TRACE(argv->GetThread(), ArrayBuffer, Constructor); + JSThread *thread = argv->GetThread(); + [[maybe_unused]] EcmaHandleScope handleScope(thread); + JSHandle newTarget = GetNewTarget(argv); + // 1. If NewTarget is undefined, throw a TypeError exception. + if (newTarget->IsUndefined()) { + THROW_TYPE_ERROR_AND_RETURN(thread, "newtarget is undefined", JSTaggedValue::Exception()); + } + JSHandle lengthHandle = GetCallArg(argv, 0); + JSTaggedNumber lenNum = JSTaggedValue::ToIndex(thread, lengthHandle); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + double length = lenNum.GetNumber(); + return AllocateArrayBuffer(thread, newTarget, length); +} + +// 24.1.3.1 ArrayBuffer.isView(arg) +JSTaggedValue BuiltinsArrayBuffer::IsView(EcmaRuntimeCallInfo *argv) +{ + ASSERT(argv); + [[maybe_unused]] EcmaHandleScope handleScope(argv->GetThread()); + JSHandle arg = GetCallArg(argv, 0); + // 1. If Type(arg) is not Object, return false. + if (!arg->IsECMAObject()) { + return BuiltinsArrayBuffer::GetTaggedBoolean(false); + } + // 2. If arg has a [[ViewedArrayBuffer]] internal slot, return true. + if (arg->IsDataView() || arg->IsTypedArray()) { + return BuiltinsArrayBuffer::GetTaggedBoolean(true); + } + // 3. Return false. + return BuiltinsArrayBuffer::GetTaggedBoolean(false); +} + +// 24.1.3.3 get ArrayBuffer [ @@species ] +JSTaggedValue BuiltinsArrayBuffer::Species(EcmaRuntimeCallInfo *argv) +{ + ASSERT(argv); + return GetThis(argv).GetTaggedValue(); +} + +// 24.1.4.1 get ArrayBuffer.prototype.byteLength +JSTaggedValue BuiltinsArrayBuffer::GetByteLength(EcmaRuntimeCallInfo *argv) +{ + ASSERT(argv); + [[maybe_unused]] EcmaHandleScope handleScope(argv->GetThread()); + JSThread *thread = argv->GetThread(); + // 1. Let O be the this value. + JSHandle thisHandle = GetThis(argv); + // 2. If Type(O) is not Object, throw a TypeError exception. + if (!thisHandle->IsECMAObject()) { + THROW_TYPE_ERROR_AND_RETURN(thread, "this value is not an object", JSTaggedValue::Exception()); + } + // 3. If O does not have an [[ArrayBufferData]] internal slot, throw a TypeError exception. + if (!thisHandle->IsArrayBuffer()) { + THROW_TYPE_ERROR_AND_RETURN(thread, "don't have internal slot", JSTaggedValue::Exception()); + } + // 4. If IsDetachedBuffer(O) is true, throw a TypeError exception. + if (IsDetachedBuffer(thisHandle.GetTaggedValue())) { + THROW_TYPE_ERROR_AND_RETURN(thread, "IsDetachedBuffer", JSTaggedValue::Exception()); + } + JSHandle arrBuf(thisHandle); + // 5. Let length be the value of O’s [[ArrayBufferByteLength]] internal slot. + JSTaggedValue length = arrBuf->GetArrayBufferByteLength(); + // 6. Return length. + return JSTaggedValue(length); +} + +// 24.1.4.3 ArrayBuffer.prototype.slice(start, end) +JSTaggedValue BuiltinsArrayBuffer::Slice(EcmaRuntimeCallInfo *argv) +{ + ASSERT(argv); + BUILTINS_API_TRACE(argv->GetThread(), ArrayBuffer, Slice); + JSThread *thread = argv->GetThread(); + [[maybe_unused]] EcmaHandleScope handleScope(thread); + JSHandle env = thread->GetEcmaVM()->GetGlobalEnv(); + const GlobalEnvConstants *globalConst = thread->GlobalConstants(); + // 1. Let O be the this value. + JSHandle thisHandle = GetThis(argv); + // 2. If Type(O) is not Object, throw a TypeError exception. + if (!thisHandle->IsObject()) { + THROW_TYPE_ERROR_AND_RETURN(thread, "this value is not an object", JSTaggedValue::Exception()); + } + JSHandle arrBuf(thisHandle); + // 3. If O does not have an [[ArrayBufferData]] internal slot, throw a TypeError exception. + if (!thisHandle->IsArrayBuffer()) { + THROW_TYPE_ERROR_AND_RETURN(thread, "don't have internal slot", JSTaggedValue::Exception()); + } + // 4. If IsDetachedBuffer(O) is true, throw a TypeError exception. + if (IsDetachedBuffer(thisHandle.GetTaggedValue())) { + THROW_TYPE_ERROR_AND_RETURN(thread, "this value IsDetachedBuffer", JSTaggedValue::Exception()); + } + // 5. Let len be the value of O’s [[ArrayBufferByteLength]] internal slot. + JSTaggedNumber lengthNum = JSTaggedNumber::FromIntOrDouble(thread, arrBuf->GetArrayBufferByteLength()); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + JSHandle startHandle = GetCallArg(argv, 0); + // 6. Let relativeStart be ToInteger(start). + JSTaggedNumber relativeStart = JSTaggedValue::ToInteger(thread, startHandle); + // 7. ReturnIfAbrupt(relativeStart). + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + int32_t len = lengthNum.ToInt32(); + int32_t start = ecmascript::base::NumberHelper::DoubleInRangeInt32(relativeStart.GetNumber()); + int32_t end; + int32_t first; + int32_t last; + // 8. If relativeStart < 0, let first be max((len + relativeStart),0); else let first be min(relativeStart, len). + if (start < 0) { + first = std::max((len + start), 0); + } else { + first = std::min(start, len); + } + // 9. If end is undefined, let relativeEnd be len; else let relativeEnd be ToInteger(end). + JSHandle endHandle = GetCallArg(argv, 1); + if (endHandle->IsUndefined()) { + end = len; + } else { + JSTaggedNumber relativeEnd = JSTaggedValue::ToInteger(thread, endHandle); + // 10. ReturnIfAbrupt(relativeEnd). + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + end = ecmascript::base::NumberHelper::DoubleInRangeInt32(relativeEnd.GetNumber()); + } + // 11. If relativeEnd < 0, let final be max((len + relativeEnd),0); else let final be min(relativeEnd, len). + if (end < 0) { + last = std::max((len + end), 0); + } else { + last = std::min(end, len); + } + // 12. Let newLen be max(final-first,0). + int32_t newLen = std::max((last - first), 0); + // 13. Let ctor be SpeciesConstructor(O, %ArrayBuffer%). + JSHandle defaultConstructor = env->GetArrayBufferFunction(); + JSHandle objHandle(thisHandle); + JSHandle constructor = JSObject::SpeciesConstructor(thread, objHandle, defaultConstructor); + // 14. ReturnIfAbrupt(ctor). + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + // 15. Let new be Construct(ctor, «newLen»). + JSHandle undefined = globalConst->GetHandledUndefined(); + InternalCallParams *arguments = thread->GetInternalCallParams(); + arguments->MakeArgv(JSTaggedValue(newLen)); + JSTaggedValue taggedNewArrBuf = JSFunction::Construct(thread, constructor, 1, arguments->GetArgv(), undefined); + JSHandle newArrBuf(thread, taggedNewArrBuf); + // 16. ReturnIfAbrupt(new). + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + // 17. If new does not have an [[ArrayBufferData]] internal slot, throw a TypeError exception. + if (!newArrBuf->IsArrayBuffer()) { + THROW_TYPE_ERROR_AND_RETURN(thread, "don't have bufferdata internal slot", JSTaggedValue::Exception()); + } + // 18. If IsDetachedBuffer(new) is true, throw a TypeError exception. + if (IsDetachedBuffer(newArrBuf.GetTaggedValue())) { + THROW_TYPE_ERROR_AND_RETURN(thread, "new arrayBuffer IsDetachedBuffer", JSTaggedValue::Exception()); + } + // 19. If SameValue(new, O) is true, throw a TypeError exception. + if (JSTaggedValue::SameValue(newArrBuf.GetTaggedValue(), thisHandle.GetTaggedValue())) { + THROW_TYPE_ERROR_AND_RETURN(thread, "value of new arraybuffer and this is same", JSTaggedValue::Exception()); + } + JSHandle newJsArrBuf(newArrBuf); + // 20. If the value of new’s [[ArrayBufferByteLength]] internal slot < newLen, throw a TypeError exception. + JSTaggedNumber newLengthNum = JSTaggedNumber::FromIntOrDouble(thread, newJsArrBuf->GetArrayBufferByteLength()); + int32_t newArrBufLen = newLengthNum.ToInt32(); + if (newArrBufLen < newLen) { + THROW_TYPE_ERROR_AND_RETURN(thread, "new array buffer length smaller than newlen", JSTaggedValue::Exception()); + } + // 21. NOTE: Side-effects of the above steps may have detached O. + // 22. If IsDetachedBuffer(O) is true, throw a TypeError exception. + if (IsDetachedBuffer(thisHandle.GetTaggedValue())) { + THROW_TYPE_ERROR_AND_RETURN(thread, "this value IsDetachedBuffer", JSTaggedValue::Exception()); + } + if (newLen > 0) { + // 23. Let fromBuf be the value of O’s [[ArrayBufferData]] internal slot. + JSTaggedValue from = arrBuf->GetArrayBufferData(); + // 24. Let toBuf be the value of new’s [[ArrayBufferData]] internal slot. + JSTaggedValue to = newJsArrBuf->GetArrayBufferData(); + // 25. Perform CopyDataBlockBytes(toBuf, fromBuf, first, newLen). + JSArrayBuffer::CopyDataBlockBytes(to, from, first, newLen); + } + // Return new. + return newArrBuf.GetTaggedValue(); +} + +// 24.1.1.1 AllocateArrayBuffer(constructor, byteLength) +JSTaggedValue BuiltinsArrayBuffer::AllocateArrayBuffer(JSThread *thread, const JSHandle &newTarget, + double byteLength) +{ + BUILTINS_API_TRACE(thread, ArrayBuffer, AllocateArrayBuffer); + /** + * 1. Let obj be OrdinaryCreateFromConstructor(constructor, "%ArrayBufferPrototype%", + * «[[ArrayBufferData]], [[ArrayBufferByteLength]]» ). + * */ + ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); + JSHandle env = thread->GetEcmaVM()->GetGlobalEnv(); + JSHandle arrBufFunc = env->GetArrayBufferFunction(); + JSHandle obj; + if (!newTarget->IsBoundFunction()) { + obj = factory->NewJSObjectByConstructor(JSHandle(arrBufFunc), newTarget); + // 2. ReturnIfAbrupt + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + } else { + JSHandle prototypeKey = thread->GlobalConstants()->GetHandledPrototypeString(); + JSHandle constructTag(newTarget); + JSHandle constructProto = + JSTaggedValue::GetProperty(thread, constructTag, prototypeKey).GetValue(); + obj = JSObject::ObjectCreate(thread, JSHandle(constructProto)); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + } + // 3. Assert: byteLength is a positive integer. + ASSERT(JSTaggedValue(byteLength).IsInteger()); + ASSERT(byteLength >= 0); + // 4. Let block be CreateByteDataBlock(byteLength). + if (byteLength > INT_MAX) { + THROW_RANGE_ERROR_AND_RETURN(thread, "Out of range", JSTaggedValue::Exception()); + } + JSHandle arrayBuffer(obj); + // 6. Set obj’s [[ArrayBufferData]] internal slot to block. + factory->NewJSArrayBufferData(arrayBuffer, byteLength); + // 7. Set obj’s [[ArrayBufferByteLength]] internal slot to byteLength. + arrayBuffer->SetArrayBufferByteLength(thread, JSTaggedValue(static_cast(byteLength))); + // 8. Return obj. + return arrayBuffer.GetTaggedValue(); +} + +// 24.1.1.2 IsDetachedBuffer() +bool BuiltinsArrayBuffer::IsDetachedBuffer(JSTaggedValue arrayBuffer) +{ + // 1. Assert: Type(arrayBuffer) is Object and it has an [[ArrayBufferData]] internal slot. + ASSERT(arrayBuffer.IsArrayBuffer()); + JSArrayBuffer *buffer = JSArrayBuffer::Cast(arrayBuffer.GetTaggedObject()); + JSTaggedValue dataSlot = buffer->GetArrayBufferData(); + // 2. If arrayBuffer’s [[ArrayBufferData]] internal slot is null, return true. + // 3. Return false. + return dataSlot == JSTaggedValue::Null(); +} + +// 24.1.1.4 +JSTaggedValue BuiltinsArrayBuffer::CloneArrayBuffer(JSThread *thread, const JSHandle &srcBuffer, + int32_t srcByteOffset, JSHandle constructor) +{ + BUILTINS_API_TRACE(thread, ArrayBuffer, CloneArrayBuffer); + // 1. Assert: Type(srcBuffer) is Object and it has an [[ArrayBufferData]] internal slot. + ASSERT(srcBuffer->IsArrayBuffer()); + JSHandle env = thread->GetEcmaVM()->GetGlobalEnv(); + // 2. If cloneConstructor is not present + if (constructor->IsUndefined()) { + // a. Let cloneConstructor be SpeciesConstructor(srcBuffer, %ArrayBuffer%). + JSHandle defaultConstructor = env->GetArrayBufferFunction(); + JSHandle objHandle(srcBuffer); + constructor = JSObject::SpeciesConstructor(thread, objHandle, defaultConstructor); + // b. ReturnIfAbrupt(cloneConstructor). + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + // c. If IsDetachedBuffer(srcBuffer) is true, throw a TypeError exception. + if (IsDetachedBuffer(srcBuffer.GetTaggedValue())) { + THROW_TYPE_ERROR_AND_RETURN(thread, "Is Detached Buffer", JSTaggedValue::Exception()); + } else { + ASSERT(constructor->IsConstructor()); + } + } + // 4. Let srcLength be the value of srcBuffer’s [[ArrayBufferByteLength]] internal slot. + JSHandle arrBuf(srcBuffer); + JSTaggedNumber lengthNumber = JSTaggedNumber::FromIntOrDouble(thread, arrBuf->GetArrayBufferByteLength()); + int32_t srcLen = lengthNumber.ToInt32(); + // 5. Assert: srcByteOffset ≤ srcLength. + ASSERT(srcByteOffset <= srcLen); + // 6. Let cloneLength be srcLength – srcByteOffset. + int32_t cloneLen = srcLen - srcByteOffset; + // 8. Let targetBuffer be AllocateArrayBuffer(cloneConstructor, cloneLength). + JSTaggedValue taggedBuf = AllocateArrayBuffer(thread, constructor, cloneLen); + // 9. ReturnIfAbrupt(targetBuffer). + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + // 10. If IsDetachedBuffer(srcBuffer) is true, throw a TypeError exception. + if (IsDetachedBuffer(srcBuffer.GetTaggedValue())) { + THROW_TYPE_ERROR_AND_RETURN(thread, "Is Detached Buffer", JSTaggedValue::Exception()); + } + // 11. Let targetBlock be the value of targetBuffer’s [[ArrayBufferData]] internal slot. + JSHandle newArrBuf(thread, taggedBuf); + // Perform CopyDataBlockBytes(targetBlock, 0, srcBlock, srcByteOffset, cloneLength). + // 7. Let srcBlock be the value of srcBuffer’s [[ArrayBufferData]] internal slot. + JSTaggedValue srcBlock = arrBuf->GetArrayBufferData(); + JSTaggedValue targetBlock = newArrBuf->GetArrayBufferData(); + if (cloneLen > 0) { + JSArrayBuffer::CopyDataBlockBytes(targetBlock, srcBlock, srcByteOffset, cloneLen); + } + return taggedBuf; +} + +// 24.1.1.5 +// NOLINTNEXTLINE(readability-function-size) +JSTaggedValue BuiltinsArrayBuffer::GetValueFromBuffer(JSTaggedValue arrBuf, int32_t byteIndex, DataViewType type, + bool littleEndian) +{ + JSArrayBuffer *jsArrayBuffer = JSArrayBuffer::Cast(arrBuf.GetTaggedObject()); + JSTaggedValue data = jsArrayBuffer->GetArrayBufferData(); + void *pointer = JSNativePointer::Cast(data.GetTaggedObject())->GetExternalPointer(); + auto *block = reinterpret_cast(pointer); + switch (type) { + case DataViewType::UINT8: + case DataViewType::UINT8_CLAMPED: { + uint8_t res = block[byteIndex]; // NOLINT + return GetTaggedInt(res); + } + case DataViewType::INT8: { + uint8_t res = block[byteIndex]; // NOLINT + auto int8Res = static_cast(res); + return GetTaggedInt(int8Res); + } + case DataViewType::UINT16: + return GetValueFromBufferForInteger(block, byteIndex, littleEndian); + case DataViewType::INT16: + return GetValueFromBufferForInteger(block, byteIndex, littleEndian); + case DataViewType::UINT32: + return GetValueFromBufferForInteger(block, byteIndex, littleEndian); + case DataViewType::INT32: + return GetValueFromBufferForInteger(block, byteIndex, littleEndian); + case DataViewType::FLOAT32: + return GetValueFromBufferForFloat(block, byteIndex, littleEndian); + case DataViewType::FLOAT64: + return GetValueFromBufferForFloat(block, byteIndex, littleEndian); + default: + break; + } + + UNREACHABLE(); +} + +// 24.1.1.6 +JSTaggedValue BuiltinsArrayBuffer::SetValueInBuffer(JSTaggedValue arrBuf, int32_t byteIndex, DataViewType type, + JSTaggedNumber value, bool littleEndian) +{ + JSArrayBuffer *jsArrayBuffer = JSArrayBuffer::Cast(arrBuf.GetTaggedObject()); + JSTaggedValue data = jsArrayBuffer->GetArrayBufferData(); + void *pointer = JSNativePointer::Cast(data.GetTaggedObject())->GetExternalPointer(); + auto *block = reinterpret_cast(pointer); + double val = value.GetNumber(); + switch (type) { + case DataViewType::UINT8: + SetValueInBufferForByte(val, block, byteIndex); + break; + case DataViewType::UINT8_CLAMPED: + SetValueInBufferForUint8Clamped(val, block, byteIndex); + break; + case DataViewType::INT8: + SetValueInBufferForByte(val, block, byteIndex); + break; + case DataViewType::UINT16: + SetValueInBufferForInteger(val, block, byteIndex, littleEndian); + break; + case DataViewType::INT16: + SetValueInBufferForInteger(val, block, byteIndex, littleEndian); + break; + case DataViewType::UINT32: + SetValueInBufferForInteger(val, block, byteIndex, littleEndian); + break; + case DataViewType::INT32: + SetValueInBufferForInteger(val, block, byteIndex, littleEndian); + break; + case DataViewType::FLOAT32: + SetValueInBufferForFloat(val, block, byteIndex, littleEndian); + break; + case DataViewType::FLOAT64: + SetValueInBufferForFloat(val, block, byteIndex, littleEndian); + break; + default: + UNREACHABLE(); + } + return JSTaggedValue::Undefined(); +} + +template +void BuiltinsArrayBuffer::SetTypeData(uint8_t *block, T value, int32_t index) +{ + int32_t sizeCount = sizeof(T); + auto *res = reinterpret_cast(&value); + for (int i = 0; i < sizeCount; i++) { + *(block + index + i) = *(res + i); // NOLINT + } +} + +template +T BuiltinsArrayBuffer::LittleEndianToBigEndian(T liValue) +{ + uint8_t sizeCount = sizeof(T); + T biValue; + switch (sizeCount) { + case NumberSize::UINT16: + biValue = ((liValue & 0x00FF) << BITS_EIGHT) // NOLINT + | ((liValue & 0xFF00) >> BITS_EIGHT); // NOLINT + break; + case NumberSize::UINT32: + biValue = ((liValue & 0x000000FF) << BITS_TWENTY_FOUR) // NOLINT + | ((liValue & 0x0000FF00) << BITS_EIGHT) // NOLINT + | ((liValue & 0x00FF0000) >> BITS_EIGHT) // NOLINT + | ((liValue & 0xFF000000) >> BITS_TWENTY_FOUR); // NOLINT + break; + default: + UNREACHABLE(); + break; + } + return biValue; +} + +uint64_t BuiltinsArrayBuffer::LittleEndianToBigEndianUint64(uint64_t liValue) +{ + return ((liValue & 0x00000000000000FF) << BITS_FIFTY_SIX) // NOLINT + | ((liValue & 0x000000000000FF00) << BITS_FORTY) // NOLINT + | ((liValue & 0x0000000000FF0000) << BITS_TWENTY_FOUR) // NOLINT + | ((liValue & 0x00000000FF000000) << BITS_EIGHT) // NOLINT + | ((liValue & 0x000000FF00000000) >> BITS_EIGHT) // NOLINT + | ((liValue & 0x0000FF0000000000) >> BITS_TWENTY_FOUR) // NOLINT + | ((liValue & 0x00FF000000000000) >> BITS_FORTY) // NOLINT + | ((liValue & 0xFF00000000000000) >> BITS_FIFTY_SIX); // NOLINT +} + +template +// NOLINTNEXTLINE(readability-non-const-parameter) +JSTaggedValue BuiltinsArrayBuffer::GetValueFromBufferForInteger(uint8_t *block, int32_t byteIndex, bool littleEndian) +{ + static_assert(std::is_integral_v, "T must be integral"); + static_assert(sizeof(T) == size, "Invalid number size"); + static_assert(sizeof(T) >= sizeof(uint16_t), "T must have a size more than uint8"); + + // NOLINTNEXTLINE(misc-redundant-expression) + static_assert(size >= NumberSize::UINT16 || size <= NumberSize::FLOAT64); + + // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-pointer-arithmetic) + T res = UnalignedLoad(reinterpret_cast(block + byteIndex)); + if (!littleEndian) { + res = LittleEndianToBigEndian(res); + } + + // uint32_t maybe overflow with TaggedInt + // NOLINTNEXTLINE(readability-braces-around-statements,bugprone-suspicious-semicolon) + if constexpr (std::is_same_v) { + // NOLINTNEXTLINE(clang-diagnostic-sign-compare) + if (res > static_cast(std::numeric_limits::max())) { + return GetTaggedDouble(static_cast(res)); + } + } + return GetTaggedInt(res); +} + +template +// NOLINTNEXTLINE(readability-non-const-parameter) +JSTaggedValue BuiltinsArrayBuffer::GetValueFromBufferForFloat(uint8_t *block, int32_t byteIndex, bool littleEndian) +{ + static_assert(std::is_same_v || std::is_same_v, "T must be float type"); + static_assert(sizeof(T) == size, "Invalid number size"); + // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-pointer-arithmetic) + T tmp = UnalignedLoad(reinterpret_cast(block + byteIndex)); + + // NOLINTNEXTLINE(readability-braces-around-statements) + if constexpr (std::is_same_v) { + if (std::isnan(tmp)) { + return GetTaggedDouble(tmp); + } + if (!littleEndian) { + auto res = bit_cast(tmp); + res = LittleEndianToBigEndian(res); + return GetTaggedDouble(bit_cast(res)); + } + } else if constexpr (std::is_same_v) { // NOLINTNEXTLINE(readability-braces-around-statements) + if (std::isnan(tmp) && !JSTaggedValue::IsImpureNaN(tmp)) { + return GetTaggedDouble(tmp); + } + if (!littleEndian) { + auto res = bit_cast(tmp); + res = LittleEndianToBigEndianUint64(res); + return GetTaggedDouble(bit_cast(res)); + } + } + return GetTaggedDouble(tmp); +} + +template +void BuiltinsArrayBuffer::SetValueInBufferForByte(double val, uint8_t *block, int32_t byteIndex) +{ + static_assert(std::is_same_v || std::is_same_v, "T must be int8/uint8"); + T res; + if (std::isnan(val) || std::isinf(val)) { + res = 0; + SetTypeData(block, res, byteIndex); + return; + } + auto int64Val = static_cast(val); + auto *resArr = reinterpret_cast(&int64Val); + res = *resArr; + SetTypeData(block, res, byteIndex); +} + +void BuiltinsArrayBuffer::SetValueInBufferForUint8Clamped(double val, uint8_t *block, int32_t byteIndex) +{ + uint8_t res; + if (std::isnan(val) || val <= 0) { + res = 0; + SetTypeData(block, res, byteIndex); + return; + } + val = val >= UINT8_MAX ? UINT8_MAX : val; + constexpr double HALF = 0.5; + val = val == HALF ? 0 : std::round(val); + res = static_cast(val); + SetTypeData(block, res, byteIndex); +} + +template +void BuiltinsArrayBuffer::SetValueInBufferForInteger(double val, uint8_t *block, int32_t byteIndex, bool littleEndian) +{ + static_assert(std::is_integral_v, "T must be integral"); + static_assert(sizeof(T) >= sizeof(uint16_t), "T must have a size more than uint8"); + T res; + if (std::isnan(val) || std::isinf(val)) { + res = 0; + SetTypeData(block, res, byteIndex); + return; + } + auto int64Val = static_cast(val); + // NOLINTNEXTLINE(readability-braces-around-statements) + if constexpr (std::is_same_v) { + auto *pTmp = reinterpret_cast(&int64Val); + int16_t tmp = *pTmp; + res = static_cast(tmp); + } else { // NOLINTNEXTLINE(readability-braces-around-statements) + auto *pTmp = reinterpret_cast(&int64Val); + res = *pTmp; + } + + if (!littleEndian) { + res = LittleEndianToBigEndian(res); + } + SetTypeData(block, res, byteIndex); +} + +template +void BuiltinsArrayBuffer::SetValueInBufferForFloat(double val, uint8_t *block, int32_t byteIndex, bool littleEndian) +{ + static_assert(std::is_same_v || std::is_same_v, "T must be float type"); + auto data = static_cast(val); + if (std::isnan(val)) { + SetTypeData(block, data, byteIndex); + return; + } + if (!littleEndian) { + // NOLINTNEXTLINE(readability-braces-around-statements, bugprone-suspicious-semicolon) + if constexpr (std::is_same_v) { + auto res = bit_cast(data); + data = bit_cast(LittleEndianToBigEndian(res)); + } + // NOLINTNEXTLINE(readability-braces-around-statements, bugprone-suspicious-semicolon) + if constexpr (std::is_same_v) { + auto res = bit_cast(data); + data = bit_cast(LittleEndianToBigEndianUint64(res)); + } + } + SetTypeData(block, data, byteIndex); +} +} // namespace panda::ecmascript::builtins diff --git a/runtime/builtins/builtins_arraybuffer.h b/runtime/builtins/builtins_arraybuffer.h new file mode 100644 index 000000000..a9f29b440 --- /dev/null +++ b/runtime/builtins/builtins_arraybuffer.h @@ -0,0 +1,87 @@ +/* + * Copyright (c) 2021-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_BUILTINS_BUILTINS_ARRAYBUFFER_H +#define ECMASCRIPT_BUILTINS_BUILTINS_ARRAYBUFFER_H + +#include "plugins/ecmascript/runtime/base/builtins_base.h" +#include "plugins/ecmascript/runtime/base/number_helper.h" +#include "plugins/ecmascript/runtime/js_dataview.h" + +namespace panda::ecmascript::builtins { +static constexpr double NUMBER_HALF = 0.5; +static constexpr uint32_t BITS_EIGHT = 8; +static constexpr uint32_t BITS_TWENTY_FOUR = 24; +static constexpr uint32_t BITS_FORTY = 40; +static constexpr uint32_t BITS_FIFTY_SIX = 56; +using DataViewType = ecmascript::DataViewType; +class BuiltinsArrayBuffer : public ecmascript::base::BuiltinsBase { +public: + enum NumberSize : uint8_t { UINT16 = 2, INT16 = 2, UINT32 = 4, INT32 = 4, FLOAT32 = 4, FLOAT64 = 8 }; + + // 24.1.2.1 ArrayBuffer(length) + static JSTaggedValue ArrayBufferConstructor(EcmaRuntimeCallInfo *argv); + // 24.1.3.1 ArrayBuffer.isView(arg) + static JSTaggedValue IsView(EcmaRuntimeCallInfo *argv); + // 24.1.3.3 get ArrayBuffer[@@species] + static JSTaggedValue Species(EcmaRuntimeCallInfo *argv); + // 24.1.4.1 get ArrayBuffer.prototype.byteLength + static JSTaggedValue GetByteLength(EcmaRuntimeCallInfo *argv); + // 24.1.4.3 ArrayBuffer.prototype.slice() + static JSTaggedValue Slice(EcmaRuntimeCallInfo *argv); + // 24.1.1.2 IsDetachedBuffer(arrayBuffer) + static bool IsDetachedBuffer(JSTaggedValue arrayBuffer); + // 24.1.1.5 GetValueFromBuffer ( arrayBuffer, byteIndex, type, isLittleEndian ) + static JSTaggedValue GetValueFromBuffer(JSTaggedValue arrBuf, int32_t byteIndex, DataViewType type, + bool littleEndian); + // 24.1.1.6 SetValueInBuffer ( arrayBuffer, byteIndex, type, value, isLittleEndian ) + static JSTaggedValue SetValueInBuffer(JSTaggedValue arrBuf, int32_t byteIndex, DataViewType type, + JSTaggedNumber value, bool littleEndian); + // 24.1.1.4 CloneArrayBuffer( srcBuffer, srcByteOffset [, cloneConstructor] ) + static JSTaggedValue CloneArrayBuffer(JSThread *thread, const JSHandle &srcBuffer, + int32_t srcByteOffset, JSHandle constructor); + // 24.1.1.1 AllocateArrayBuffer(constructor, byteLength) + static JSTaggedValue AllocateArrayBuffer(JSThread *thread, const JSHandle &newTarget, + double byteLength); + +private: + template + static T LittleEndianToBigEndian(T liValue); + + static uint64_t LittleEndianToBigEndianUint64(uint64_t liValue); + + template + static void SetTypeData(uint8_t *block, T value, int32_t index); + + template + static JSTaggedValue GetValueFromBufferForInteger(uint8_t *block, int32_t byteIndex, bool littleEndian); + + template + static JSTaggedValue GetValueFromBufferForFloat(uint8_t *block, int32_t byteIndex, bool littleEndian); + + template + static void SetValueInBufferForByte(double val, uint8_t *block, int32_t byteIndex); + + static void SetValueInBufferForUint8Clamped(double val, uint8_t *block, int32_t byteIndex); + + template + static void SetValueInBufferForInteger(double val, uint8_t *block, int32_t byteIndex, bool littleEndian); + + template + static void SetValueInBufferForFloat(double val, uint8_t *block, int32_t byteIndex, bool littleEndian); +}; +} // namespace panda::ecmascript::builtins + +#endif // ECMASCRIPT_BUILTINS_BUILTINS_ARRAYBUFFER_H diff --git a/runtime/builtins/builtins_async_from_sync_iterator.cpp b/runtime/builtins/builtins_async_from_sync_iterator.cpp new file mode 100644 index 000000000..cc481f40c --- /dev/null +++ b/runtime/builtins/builtins_async_from_sync_iterator.cpp @@ -0,0 +1,235 @@ +/** + * Copyright (c) Huawei Technologies Co., Ltd. 2019-2021. All rights reserved. + * Description: + */ + +#include "plugins/ecmascript/runtime/builtins/builtins_async_from_sync_iterator.h" +#include "plugins/ecmascript/runtime/global_env.h" +#include "plugins/ecmascript/runtime/internal_call_params.h" +#include "plugins/ecmascript/runtime/js_promise.h" +#include "plugins/ecmascript/runtime/js_iterator.h" +#include "plugins/ecmascript/runtime/js_async_from_sync_iterator_object.h" + +namespace panda::ecmascript::builtins { +// 27.1.4.2.2 %AsyncFromSyncIteratorPrototype%.next(value) +JSTaggedValue BuiltinsAsyncFromSyncIterator::AsyncFromSyncIteratorPrototypeNext(EcmaRuntimeCallInfo *argv) +{ + BUILTINS_API_TRACE(argv->GetThread(), AsyncFromSyncIteratorPrototype, Next); + + JSThread *thread = argv->GetThread(); + JSHandle env = thread->GetEcmaVM()->GetGlobalEnv(); + + [[maybe_unused]] EcmaHandleScope handleScope(thread); + + // 1. Let O be the this value. + JSHandle thisObject = GetThis(argv); + // 2. Assert: Type(O) is Object and O has a [[SyncIteratorRecord]] internal slot. + JSHandle asyncFromSyncIteratorObject( + thread, JSAsyncFromSyncIteratorObject::Cast(thisObject->GetHeapObject())); + + // 4. Let syncIteratorRecord be O.[[SyncIteratorRecord]]. + JSHandle iterator(thread, asyncFromSyncIteratorObject->GetIterator()); + JSHandle nextMethod(thread, asyncFromSyncIteratorObject->GetNextMethod()); + + // 3. Let promiseCapability be ! NewPromiseCapability(%Promise%). + JSHandle promiseCapability = + JSPromise::NewPromiseCapability(thread, JSHandle::Cast(env->GetPromiseFunction())); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + + JSMutableHandle result(thread, thread->GlobalConstants()->GetHandledUndefined()); + + // 5. If value is present, then + if (argv->GetArgsNumber() > 0) { + // a. Let result be IteratorNext(syncIteratorRecord, value). + result.Update(JSIterator::IteratorNext(thread, iterator, nextMethod, GetCallArg(argv, 0))); + } else { + // 6. Else, + // a. Let result be IteratorNext(syncIteratorRecord). + result.Update(JSIterator::IteratorNext(thread, iterator, nextMethod)); + } + + // 7. IfAbruptRejectPromise(result, promiseCapability). + if (thread->HasPendingException()) { + result.Update(JSPromise::IfThrowGetThrowValue(thread)); + } + RETURN_REJECT_PROMISE_IF_ABRUPT(thread, result, promiseCapability); + + // 8. Return ! AsyncFromSyncIteratorContinuation(result, promiseCapability). + return JSAsyncFromSyncIteratorObject::AsyncFromSyncIteratorContinuation(thread, result, promiseCapability); +} + +// 27.1.4.2.3 %AsyncFromSyncIteratorPrototype%.return(value) +JSTaggedValue BuiltinsAsyncFromSyncIterator::AsyncFromSyncIteratorPrototypeReturn(EcmaRuntimeCallInfo *argv) +{ + BUILTINS_API_TRACE(argv->GetThread(), AsyncFromSyncIteratorPrototype, Return); + + JSThread *thread = argv->GetThread(); + JSHandle env = thread->GetEcmaVM()->GetGlobalEnv(); + const GlobalEnvConstants *globalConstants = thread->GlobalConstants(); + InternalCallParams *arguments = thread->GetInternalCallParams(); + + [[maybe_unused]] EcmaHandleScope handleScope(thread); + + // 1. Let O be the this value. + JSHandle thisObject = GetThis(argv); + // 2. Assert: Type(O) is Object and O has a [[SyncIteratorRecord]] internal slot. + JSHandle asyncFromSyncIteratorObject( + thread, JSAsyncFromSyncIteratorObject::Cast(thisObject->GetHeapObject())); + + // 3. Let promiseCapability be ! NewPromiseCapability(%Promise%). + JSHandle promiseCapability = + JSPromise::NewPromiseCapability(thread, JSHandle::Cast(env->GetPromiseFunction())); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + + // 4. Let syncIteratorRecord be O.[[SyncIteratorRecord]]. + JSHandle iterator(thread, asyncFromSyncIteratorObject->GetIterator()); + + // 5. Let return be GetMethod(syncIterator, "return"). + JSHandle returnKey = globalConstants->GetHandledReturnString(); + JSHandle returnMethod = JSObject::GetMethod(thread, iterator, returnKey); + + // 6. IfAbruptRejectPromise(return, promiseCapability). + RETURN_REJECT_PROMISE_IF_ABRUPT_THROWN_VALUE(thread, returnMethod, promiseCapability); + + // 7. If return is undefined, then + if (returnMethod->IsUndefined()) { + // a. Let iterResult be ! CreateIterResultObject(value, true). + JSHandle iterResult = JSIterator::CreateIterResultObject(thread, GetCallArg(argv, 0), true); + + // b. Perform ! Call(promiseCapability.[[Resolve]], undefined, « iterResult »). + arguments->MakeArgv(iterResult); + JSHandle resolve(thread, promiseCapability->GetResolve()); + [[maybe_unused]] JSTaggedValue res = + JSFunction::Call(thread, resolve, globalConstants->GetHandledUndefined(), 1, arguments->GetArgv()); + + // c. Return promiseCapability.[[Promise]]. + return promiseCapability->GetPromise(); + } + + JSMutableHandle result(thread, globalConstants->GetHandledUndefined()); + + // 8. If value is present, then + if (argv->GetArgsNumber() > 0) { + // a. Let result be Call(return, syncIterator, « value »). + arguments->MakeArgv(GetCallArg(argv, 0)); + result.Update(JSFunction::Call(thread, returnMethod, iterator, 1, arguments->GetArgv())); + } else { + // 9. Else, + // a. Let result be Call(return, syncIterator). + arguments->MakeEmptyArgv(); + result.Update(JSFunction::Call(thread, returnMethod, iterator, 0, arguments->GetArgv())); + } + + // 10. IfAbruptRejectPromise(result, promiseCapability). + if (thread->HasPendingException()) { + result.Update(JSPromise::IfThrowGetThrowValue(thread)); + } + RETURN_REJECT_PROMISE_IF_ABRUPT(thread, result, promiseCapability); + + // 11. If Type(result) is not Object, then + if (!result->IsECMAObject()) { + // a. Perform ! Call(promiseCapability.[[Reject]], undefined, « a newly created TypeError object »).. + ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); + + JSHandle typeError = factory->GetJSError(ErrorType::TYPE_ERROR, "Return is not an object"); + arguments->MakeArgv(typeError); + JSHandle reject(thread, promiseCapability->GetReject()); + [[maybe_unused]] JSTaggedValue res = + JSFunction::Call(thread, reject, globalConstants->GetHandledUndefined(), 1, arguments->GetArgv()); + + // b. Return promiseCapability.[[Promise]]. + return promiseCapability->GetPromise(); + } + + // 12. Return ! AsyncFromSyncIteratorContinuation(result, promiseCapability). + return JSAsyncFromSyncIteratorObject::AsyncFromSyncIteratorContinuation(thread, result, promiseCapability); +} + +// 27.1.4.2.4 %AsyncFromSyncIteratorPrototype%.throw(exception) +JSTaggedValue BuiltinsAsyncFromSyncIterator::AsyncFromSyncIteratorPrototypeThrow(EcmaRuntimeCallInfo *argv) +{ + BUILTINS_API_TRACE(argv->GetThread(), AsyncFromSyncIteratorPrototype, Throw); + + JSThread *thread = argv->GetThread(); + ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); + JSHandle env = thread->GetEcmaVM()->GetGlobalEnv(); + const GlobalEnvConstants *globalConstants = thread->GlobalConstants(); + InternalCallParams *arguments = thread->GetInternalCallParams(); + + [[maybe_unused]] EcmaHandleScope handleScope(thread); + + // 1. Let O be the this value. + JSHandle thisObject = GetThis(argv); + // 2. Assert: Type(O) is Object and O has a [[SyncIteratorRecord]] internal slot. + JSHandle asyncFromSyncIteratorObject( + thread, JSAsyncFromSyncIteratorObject::Cast(thisObject->GetHeapObject())); + + // 3. Let promiseCapability be ! NewPromiseCapability(%Promise%). + JSHandle promiseCapability = + JSPromise::NewPromiseCapability(thread, JSHandle::Cast(env->GetPromiseFunction())); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + + // 4. Let syncIteratorRecord be O.[[SyncIteratorRecord]]. + JSHandle iterator(thread, asyncFromSyncIteratorObject->GetIterator()); + + // 5. Let return be GetMethod(syncIterator, "throw"). + JSHandle throwKey = globalConstants->GetHandledThrowString(); + JSHandle throwMethod = JSObject::GetMethod(thread, iterator, throwKey); + + // 6. IfAbruptRejectPromise(return, promiseCapability). + RETURN_REJECT_PROMISE_IF_ABRUPT_THROWN_VALUE(thread, throwMethod, promiseCapability); + + // 7. If throw is undefined, then + if (throwMethod->IsUndefined()) { + // a. Perform ! Call(promiseCapability.[[Reject]], undefined, « value »). + if (argv->GetArgsNumber() > 0) { + arguments->MakeArgv(GetCallArg(argv, 0)); + } else { + arguments->MakeEmptyArgv(); + } + + JSHandle reject(thread, promiseCapability->GetReject()); + [[maybe_unused]] JSTaggedValue callResult = JSFunction::Call( + thread, reject, globalConstants->GetHandledUndefined(), arguments->GetLength(), arguments->GetArgv()); + + // b. Return promiseCapability.[[Promise]]. + return promiseCapability->GetPromise(); + } + + JSMutableHandle result(thread, globalConstants->GetHandledUndefined()); + + // 8. If value is present, then + if (argv->GetArgsNumber() > 0) { + // a. Let result be Call(throw, syncIterator, « value »). + arguments->MakeArgv(GetCallArg(argv, 0)); + result.Update(JSFunction::Call(thread, throwMethod, iterator, 1, arguments->GetArgv())); + } else { + // 9. Else, + // a. Let result be Call(throw, syncIterator). + arguments->MakeEmptyArgv(); + result.Update(JSFunction::Call(thread, throwMethod, iterator, 0, arguments->GetArgv())); + } + + // 10. IfAbruptRejectPromise(result, promiseCapability). + if (thread->HasPendingException()) { + result.Update(JSPromise::IfThrowGetThrowValue(thread)); + } + RETURN_REJECT_PROMISE_IF_ABRUPT(thread, result, promiseCapability); + + // 11. If Type(result) is not Object, then + if (!result->IsECMAObject()) { + // a. Perform ! Call(promiseCapability.[[Reject]], undefined, « a newly created TypeError object »).. + JSHandle typeError = factory->GetJSError(ErrorType::TYPE_ERROR, "Throw is not an object"); + arguments->MakeArgv(typeError); + JSHandle reject(thread, promiseCapability->GetReject()); + [[maybe_unused]] JSTaggedValue callResult = + JSFunction::Call(thread, reject, globalConstants->GetHandledUndefined(), 1, arguments->GetArgv()); + + // b. Return promiseCapability.[[Promise]]. + return promiseCapability->GetPromise(); + } + + // 12. Return ! AsyncFromSyncIteratorContinuation(result, promiseCapability). + return JSAsyncFromSyncIteratorObject::AsyncFromSyncIteratorContinuation(thread, result, promiseCapability); +} +} // namespace panda::ecmascript::builtins diff --git a/runtime/builtins/builtins_async_from_sync_iterator.h b/runtime/builtins/builtins_async_from_sync_iterator.h new file mode 100644 index 000000000..dd3bfe5f6 --- /dev/null +++ b/runtime/builtins/builtins_async_from_sync_iterator.h @@ -0,0 +1,25 @@ +/** + * Copyright (c) Huawei Technologies Co., Ltd. 2019-2021. All rights reserved. + * Description: + */ + +#ifndef ECMASCRIPT_BUILTINS_ASYNC_FROM_SYNC_ITERATOR_H +#define ECMASCRIPT_BUILTINS_ASYNC_FROM_SYNC_ITERATOR_H + +#include "plugins/ecmascript/runtime/base/builtins_base.h" + +namespace panda::ecmascript::builtins { +class BuiltinsAsyncFromSyncIterator : public ecmascript::base::BuiltinsBase { +public: + // 27.1.4.2.1 %AsyncFromSyncIteratorPrototype%.next + static JSTaggedValue AsyncFromSyncIteratorPrototypeNext(EcmaRuntimeCallInfo *argv); + + // 27.1.4.2.2 %AsyncFromSyncIteratorPrototype%.return + static JSTaggedValue AsyncFromSyncIteratorPrototypeReturn(EcmaRuntimeCallInfo *argv); + + // 27.1.4.2.3 %AsyncFromSyncIteratorPrototype%.throw + static JSTaggedValue AsyncFromSyncIteratorPrototypeThrow(EcmaRuntimeCallInfo *argv); +}; +} // namespace panda::ecmascript::builtins + +#endif // ECMASCRIPT_BUILTINS_ASYNC_FROM_SYNC_ITERATOR_H diff --git a/runtime/builtins/builtins_async_function.cpp b/runtime/builtins/builtins_async_function.cpp new file mode 100644 index 000000000..a43508996 --- /dev/null +++ b/runtime/builtins/builtins_async_function.cpp @@ -0,0 +1,28 @@ +/* + * Copyright (c) 2021-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 "plugins/ecmascript/runtime/builtins/builtins_async_function.h" +#include "plugins/ecmascript/runtime/ecma_macros.h" + +namespace panda::ecmascript::builtins { +// ecma2017 25.5.1.1 AsyncFunction (p1, p2, ... , pn, body) +JSTaggedValue BuiltinsAsyncFunction::AsyncFunctionConstructor(EcmaRuntimeCallInfo *argv) +{ + ASSERT(argv); + // not support + THROW_TYPE_ERROR_AND_RETURN(argv->GetThread(), "Not support eval. Forbidden using new AsyncFunction().", + JSTaggedValue::Exception()); +} +} // namespace panda::ecmascript::builtins diff --git a/runtime/builtins/builtins_async_function.h b/runtime/builtins/builtins_async_function.h new file mode 100644 index 000000000..26ee04e0c --- /dev/null +++ b/runtime/builtins/builtins_async_function.h @@ -0,0 +1,29 @@ +/* + * Copyright (c) 2021-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_BUILTINS_BUILTINS_ASYNC_FUNCTION_H +#define ECMASCRIPT_BUILTINS_BUILTINS_ASYNC_FUNCTION_H + +#include "plugins/ecmascript/runtime/js_tagged_value-inl.h" +#include "plugins/ecmascript/runtime/base/builtins_base.h" + +namespace panda::ecmascript::builtins { +class BuiltinsAsyncFunction : public ecmascript::base::BuiltinsBase { +public: + // ecma2020 25.5.1.1 AsyncFunction (p1, p2, ... , pn, body) + static JSTaggedValue AsyncFunctionConstructor(EcmaRuntimeCallInfo *argv); +}; +} // namespace panda::ecmascript::builtins +#endif // ECMASCRIPT_BUILTINS_BUILTINS_ASYNC_FUNCTION_H diff --git a/runtime/builtins/builtins_async_generator.cpp b/runtime/builtins/builtins_async_generator.cpp new file mode 100644 index 000000000..1703e85cd --- /dev/null +++ b/runtime/builtins/builtins_async_generator.cpp @@ -0,0 +1,83 @@ +/** + * Copyright (c) Huawei Technologies Co., Ltd. 2019-2021. All rights reserved. + * Description: + */ + +#include "plugins/ecmascript/runtime/builtins/builtins_async_generator.h" +#include "plugins/ecmascript/runtime/js_function.h" +#include "plugins/ecmascript/runtime/js_async_generator_object.h" + +namespace panda::ecmascript::builtins { +// 27.4.1.1 AsyncGeneratorFunction (p1, p2, ... , pn, body) +JSTaggedValue BuiltinsAsyncGenerator::AsyncGeneratorFunctionConstructor(EcmaRuntimeCallInfo *argv) +{ + ASSERT(argv); + // not support + THROW_TYPE_ERROR_AND_RETURN(argv->GetThread(), "Not support eval. Forbidden using new AsyncGeneratorFunction().", + JSTaggedValue::Exception()); +} + +// 27.6.1.2 AsyncGenerator.prototype.next(value) +JSTaggedValue BuiltinsAsyncGenerator::AsyncGeneratorPrototypeNext(EcmaRuntimeCallInfo *argv) +{ + BUILTINS_API_TRACE(argv->GetThread(), AsyncGeneratorPrototype, Next); + // 1.Let generator be the this value. + JSThread *thread = argv->GetThread(); + [[maybe_unused]] EcmaHandleScope handleScope(thread); + JSHandle generator = GetThis(argv); + + // 2. Let completion be NormalCompletion(value). + JSHandle value = GetCallArg(argv, 0); + ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); + JSHandle completionRecord = + factory->NewCompletionRecord(CompletionRecord::NORMAL, value); + + // 3. Return ! AsyncGeneratorEnqueue(generator, completion, empty). + JSHandle result = JSAsyncGeneratorObject::AsyncGeneratorEnqueue(thread, generator, completionRecord); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + return result.GetTaggedValue(); +} + +// 27.6.1.3 AsyncGenerator.prototype.return(value) +JSTaggedValue BuiltinsAsyncGenerator::AsyncGeneratorPrototypeReturn(EcmaRuntimeCallInfo *argv) +{ + BUILTINS_API_TRACE(argv->GetThread(), AsyncGeneratorPrototype, Return); + // 1.Let generator be the this value. + JSThread *thread = argv->GetThread(); + [[maybe_unused]] EcmaHandleScope handleScope(thread); + JSHandle generator = GetThis(argv); + + // 2. Let completion be Completion { [[Type]]: return, [[Value]]: value, [[Target]]: empty }. + JSHandle value = GetCallArg(argv, 0); + ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); + JSHandle completionRecord = + factory->NewCompletionRecord(CompletionRecord::RETURN, value); + + // 3. Return ! AsyncGeneratorEnqueue(generator, completion, empty). + JSHandle result = JSAsyncGeneratorObject::AsyncGeneratorEnqueue(thread, generator, completionRecord); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + return result.GetTaggedValue(); +} + +// 27.6.1.4 AsyncGenerator.prototype.throw(exception) +JSTaggedValue BuiltinsAsyncGenerator::AsyncGeneratorPrototypeThrow(EcmaRuntimeCallInfo *argv) +{ + BUILTINS_API_TRACE(argv->GetThread(), AsyncGeneratorPrototype, Throw); + + // 1.Let generator be the this value. + JSThread *thread = argv->GetThread(); + [[maybe_unused]] EcmaHandleScope handleScope(thread); + JSHandle generator = GetThis(argv); + + // 2. Let completion be ThrowCompletion(exception). + JSHandle value = GetCallArg(argv, 0); + ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); + JSHandle completionRecord = + factory->NewCompletionRecord(CompletionRecord::THROW, value); + + // 3. Return ! AsyncGeneratorEnqueue(generator, completion, empty). + JSHandle result = JSAsyncGeneratorObject::AsyncGeneratorEnqueue(thread, generator, completionRecord); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + return result.GetTaggedValue(); +} +} // namespace panda::ecmascript::builtins diff --git a/runtime/builtins/builtins_async_generator.h b/runtime/builtins/builtins_async_generator.h new file mode 100644 index 000000000..fbb91c85a --- /dev/null +++ b/runtime/builtins/builtins_async_generator.h @@ -0,0 +1,30 @@ +/** + * Copyright (c) Huawei Technologies Co., Ltd. 2019-2021. All rights reserved. + * Description: + */ + +#ifndef ECMASCRIPT_BUILTINS_ASYNC_GENERATOR_H +#define ECMASCRIPT_BUILTINS_ASYNC_GENERATOR_H + +#include "plugins/ecmascript/runtime/base/builtins_base.h" + +namespace panda::ecmascript::builtins { +class BuiltinsAsyncGenerator : public ecmascript::base::BuiltinsBase { +public: + // 27.4.1.1 AsyncGeneratorFunction(p1, p2, … , pn, body) + static JSTaggedValue AsyncGeneratorFunctionConstructor(EcmaRuntimeCallInfo *argv); + + // 27.6.1.2 AsyncGenerator.prototype.next(value) + static JSTaggedValue AsyncGeneratorPrototypeNext(EcmaRuntimeCallInfo *argv); + + // 27.6.1.3 AsyncGenerator.prototype.return(value) + static JSTaggedValue AsyncGeneratorPrototypeReturn(EcmaRuntimeCallInfo *argv); + + // 27.6.1.4 AsyncGenerator.prototype.throw(exception) + static JSTaggedValue AsyncGeneratorPrototypeThrow(EcmaRuntimeCallInfo *argv); + + // 26.4.1.5 AsyncGenerator.prototype[@@toStringTag] +}; +} // namespace panda::ecmascript::builtins + +#endif // ECMASCRIPT_BUILTINS_ASYNC_GENERATOR_H diff --git a/runtime/builtins/builtins_async_iterator.cpp b/runtime/builtins/builtins_async_iterator.cpp new file mode 100644 index 000000000..8aa1a8de0 --- /dev/null +++ b/runtime/builtins/builtins_async_iterator.cpp @@ -0,0 +1,18 @@ +/* + * Copyright (c) Huawei Technologies Co., Ltd. 2019-2021. All rights reserved. + * Description: + */ + +#include "plugins/ecmascript/runtime/builtins/builtins_async_iterator.h" +#include "plugins/ecmascript/runtime/ecma_vm.h" +#include "plugins/ecmascript/runtime/global_env.h" +#include "plugins/ecmascript/runtime/base/builtins_base.h" +#include "plugins/ecmascript/runtime/js_iterator.h" + +namespace panda::ecmascript::builtins { +// 27.1.3.1 %AsyncIteratorPrototype% [ @@asyncIterator ] ( ) +JSTaggedValue BuiltinsAsyncIterator::GetAsyncIteratorObj(EcmaRuntimeCallInfo *argv) +{ + return ecmascript::base::BuiltinsBase::GetThis(argv).GetTaggedValue(); +} +} // namespace panda::ecmascript::builtins diff --git a/runtime/builtins/builtins_async_iterator.h b/runtime/builtins/builtins_async_iterator.h new file mode 100644 index 000000000..a726b080f --- /dev/null +++ b/runtime/builtins/builtins_async_iterator.h @@ -0,0 +1,19 @@ +/** + * Copyright (c) Huawei Technologies Co., Ltd. 2019-2021. All rights reserved. + * Description: + */ + +#ifndef ECMASCRIPT_BUILTINS_ASYNC_ITERATOR_H +#define ECMASCRIPT_BUILTINS_ASYNC_ITERATOR_H + +#include "plugins/ecmascript/runtime/ecma_runtime_call_info.h" +#include "plugins/ecmascript/runtime/base/builtins_base.h" + +namespace panda::ecmascript::builtins { +class BuiltinsAsyncIterator : public ecmascript::base::BuiltinsBase { +public: + // 27.1.3.1 %AsyncIteratorPrototype% [ @@asyncIterator ] ( ) + static JSTaggedValue GetAsyncIteratorObj(EcmaRuntimeCallInfo *argv); +}; +} // namespace panda::ecmascript::builtins +#endif // ECMASCRIPT_BUILTINS_ASYNC_ITERATOR_H diff --git a/runtime/builtins/builtins_boolean.cpp b/runtime/builtins/builtins_boolean.cpp new file mode 100644 index 000000000..6b82b1fd8 --- /dev/null +++ b/runtime/builtins/builtins_boolean.cpp @@ -0,0 +1,95 @@ +/* + * Copyright (c) 2021-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 "plugins/ecmascript/runtime/builtins/builtins_boolean.h" + +#include "plugins/ecmascript/runtime/builtins/builtins_errors.h" +#include "plugins/ecmascript/runtime/ecma_macros.h" +#include "plugins/ecmascript/runtime/global_env.h" +#include "plugins/ecmascript/runtime/js_primitive_ref.h" + +namespace panda::ecmascript::builtins { +// ecma 19.3.1.1 Boolean(value) +JSTaggedValue BuiltinsBoolean::BooleanConstructor(EcmaRuntimeCallInfo *argv) +{ + ASSERT(argv); + BUILTINS_API_TRACE(argv->GetThread(), Boolean, Constructor); + JSThread *thread = argv->GetThread(); + [[maybe_unused]] EcmaHandleScope handleScope(thread); + // 1. Let b be ToBoolean(value). + bool boolValue = GetCallArg(argv, 0)->ToBoolean(); + // 2. If NewTarget is undefined, return b. + JSHandle newTarget = GetNewTarget(argv); + if (newTarget->IsUndefined()) { + return GetTaggedBoolean(boolValue); + } + // 3. Let O be OrdinaryCreateFromConstructor(NewTarget, "%BooleanPrototype%", [[BooleanData]] ). + // 5. Set the value of O's [[BooleanData]] internal slot to b. + ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); + JSHandle ctor = JSHandle(GetConstructor(argv)); + JSHandle result = factory->NewJSObjectByConstructor(ctor, newTarget); + JSTaggedValue objValue = boolValue ? JSTaggedValue::True() : JSTaggedValue::False(); + JSPrimitiveRef::Cast(*result)->SetValue(thread, objValue); + // 4. ReturnIfAbrupt(O). + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + // 6. Return O. + return result.GetTaggedValue(); +} + +// ecma 19.3.3 abstract operation thisBooleanValue(value) +JSTaggedValue BuiltinsBoolean::ThisBooleanValue(JSThread *thread, JSTaggedValue value) +{ + BUILTINS_API_TRACE(thread, Boolean, ThisBooleanValue); + // 1. If Type(value) is Boolean, return value + if (value.IsBoolean()) { + return value == JSTaggedValue::True() ? GetTaggedBoolean(true) : GetTaggedBoolean(false); + } + // 2. If Type(value) is Object and value has a [[BooleanData]] internal slot, then + if (value.IsJSPrimitiveRef()) { + JSTaggedValue primitive = JSPrimitiveRef::Cast(value.GetTaggedObject())->GetValue(); + // a. Assert: value's [[BooleanData]] internal slot is a Boolean value. + if (primitive.IsBoolean()) { + // b. Return the value of value's [[BooleanData]] internal slot. + return primitive == JSTaggedValue::True() ? GetTaggedBoolean(true) : GetTaggedBoolean(false); + } + } + [[maybe_unused]] EcmaHandleScope handleScope(thread); + // 3. Throw a TypeError exception. + THROW_TYPE_ERROR_AND_RETURN(thread, "the type can not convert to BooleanValue", JSTaggedValue::Exception()); +} + +// ecma 19.3.3.2 Boolean.prototype.toString () +JSTaggedValue BuiltinsBoolean::BooleanPrototypeToString(EcmaRuntimeCallInfo *argv) +{ + ASSERT(argv); + JSThread *thread = argv->GetThread(); + [[maybe_unused]] EcmaHandleScope handleScope(thread); + // 1. Let b be thisBooleanValue(this value). + JSTaggedValue thisValueToBoolean = BuiltinsBoolean::ThisBooleanValue(thread, GetThis(argv).GetTaggedValue()); + // 2. ReturnIfAbrupt(b) + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + + // 3. If b is true, return "true"; else return "false". + return thisValueToBoolean.IsTrue() ? GetTaggedString(thread, "true") : GetTaggedString(thread, "false"); +} + +// ecma 19.3.3.3 Boolean.prototype.valueOf () +JSTaggedValue BuiltinsBoolean::BooleanPrototypeValueOf(EcmaRuntimeCallInfo *argv) +{ + ASSERT(argv); + // 1. Return thisBooleanValue(this value). + return BuiltinsBoolean::ThisBooleanValue(argv->GetThread(), GetThis(argv).GetTaggedValue()); +} +} // namespace panda::ecmascript::builtins diff --git a/runtime/builtins/builtins_boolean.h b/runtime/builtins/builtins_boolean.h new file mode 100644 index 000000000..118afed95 --- /dev/null +++ b/runtime/builtins/builtins_boolean.h @@ -0,0 +1,38 @@ +/* + * Copyright (c) 2021-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_BUILTINS_BUILTINS_BOOLEAN_H +#define ECMASCRIPT_BUILTINS_BUILTINS_BOOLEAN_H + +#include "plugins/ecmascript/runtime/js_tagged_value-inl.h" +#include "plugins/ecmascript/runtime/base/builtins_base.h" + +namespace panda::ecmascript::builtins { +class BuiltinsBoolean : public ecmascript::base::BuiltinsBase { +public: + // ecma 19.3.1 + static JSTaggedValue BooleanConstructor(EcmaRuntimeCallInfo *argv); + + // ecma 19.3.3 abstract operation thisBooleanValue(value) + static JSTaggedValue ThisBooleanValue(JSThread *thread, JSTaggedValue value); + + // ecma 19.3.3.2 + static JSTaggedValue BooleanPrototypeToString(EcmaRuntimeCallInfo *argv); + + // ecma 19.3.3.3 + static JSTaggedValue BooleanPrototypeValueOf(EcmaRuntimeCallInfo *argv); +}; +} // namespace panda::ecmascript::builtins +#endif // ECMASCRIPT_BUILTINS_BUILTINS_BOOLEAN_H diff --git a/runtime/builtins/builtins_collator.cpp b/runtime/builtins/builtins_collator.cpp new file mode 100644 index 000000000..d4650ebf4 --- /dev/null +++ b/runtime/builtins/builtins_collator.cpp @@ -0,0 +1,153 @@ +/* + * Copyright (c) 2021-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 "builtins_collator.h" + +#include "plugins/ecmascript/runtime/ecma_vm.h" +#include "plugins/ecmascript/runtime/global_env.h" +#include "plugins/ecmascript/runtime/js_collator.h" +#include "plugins/ecmascript/runtime/js_intl.h" + +namespace panda::ecmascript::builtins { +constexpr uint32_t FUNCTION_LENGTH_TWO = 2; + +// 11.1.2 Intl.Collator ( [ locales [ , options ] ] ) +JSTaggedValue BuiltinsCollator::CollatorConstructor(EcmaRuntimeCallInfo *argv) +{ + JSThread *thread = argv->GetThread(); + [[maybe_unused]] EcmaHandleScope scope(thread); + EcmaVM *ecmaVm = thread->GetEcmaVM(); + ObjectFactory *factory = ecmaVm->GetFactory(); + + // 1. If NewTarget is undefined, let newTarget be the active function object, else let newTarget be NewTarget. + JSHandle constructor = GetConstructor(argv); + JSHandle newTarget = GetNewTarget(argv); + if (newTarget->IsUndefined()) { + newTarget = constructor; + } + // 2. Let internalSlotsList be « [[InitializedCollator]], [[Locale]], [[Usage]], [[Sensitivity]], + // [[IgnorePunctuation]], [[Collation]], [[BoundCompare]] ». + // 3. If %Collator%.[[RelevantExtensionKeys]] contains "kn", then + // a. Append [[Numeric]] as the last element of internalSlotsList. + // 4. If %Collator%.[[RelevantExtensionKeys]] contains "kf", then + // a. Append [[CaseFirst]] as the last element of internalSlotsList. + + // 5. Let collator be ? OrdinaryCreateFromConstructor(newTarget, "%CollatorPrototype%", internalSlotsList). + JSHandle collator = + JSHandle::Cast(factory->NewJSObjectByConstructor(JSHandle(constructor), newTarget)); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + + // 6. Return ? InitializeCollator(collator, locales, options). + JSHandle locales = GetCallArg(argv, 0); + JSHandle options = GetCallArg(argv, 1); + JSHandle result = JSCollator::InitializeCollator(thread, collator, locales, options); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + return result.GetTaggedValue(); +} + +// 11.2.2 Intl.Collator.supportedLocalesOf ( locales [ , options ] ) +JSTaggedValue BuiltinsCollator::SupportedLocalesOf(EcmaRuntimeCallInfo *argv) +{ + JSThread *thread = argv->GetThread(); + [[maybe_unused]] EcmaHandleScope scope(thread); + // 1. Let availableLocales be %Collator%.[[AvailableLocales]]. + JSHandle availableLocales = JSCollator::GetAvailableLocales(thread); + + // 2. Let requestedLocales be ? CanonicalizeLocaleList(locales). + JSHandle locales = GetCallArg(argv, 0); + JSHandle requestedLocales = JSLocale::CanonicalizeLocaleList(thread, locales); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + + // 3. Return ? SupportedLocales(availableLocales, requestedLocales, options). + JSHandle options = GetCallArg(argv, 1); + JSHandle result = JSLocale::SupportedLocales(thread, availableLocales, requestedLocales, options); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + return result.GetTaggedValue(); +} + +// 11.3.3 get Intl.Collator.prototype.compare +JSTaggedValue BuiltinsCollator::Compare(EcmaRuntimeCallInfo *argv) +{ + JSThread *thread = argv->GetThread(); + [[maybe_unused]] EcmaHandleScope scope(thread); + // 1. Let collator be this value. + JSHandle thisValue = GetThis(argv); + + // 2. Perform ? RequireInternalSlot(collator, [[InitializedCollator]]). + if (!thisValue->IsJSCollator()) { + THROW_TYPE_ERROR_AND_RETURN(thread, "this is not collator", JSTaggedValue::Exception()); + } + // 3. If collator.[[BoundCompare]] is undefined, then + // a. Let F be a new built-in function object as defined in 11.3.3.1. + // b. Set F.[[Collator]] to collator. + // c. Set collator.[[BoundCompare]] to F. + // 4. Return collator.[[BoundCompare]]. + JSHandle collator = JSHandle::Cast(thisValue); + JSHandle boundCompare(thread, collator->GetBoundCompare()); + if (boundCompare->IsUndefined()) { + ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); + JSHandle intlBoundFunc = factory->NewJSIntlBoundFunction( + reinterpret_cast(BuiltinsCollator::AnonymousCollator), FUNCTION_LENGTH_TWO); + intlBoundFunc->SetCollator(thread, collator); + collator->SetBoundCompare(thread, intlBoundFunc); + } + return collator->GetBoundCompare(); +} + +// 11.3.3.1 Collator Compare Functions +JSTaggedValue BuiltinsCollator::AnonymousCollator(EcmaRuntimeCallInfo *argv) +{ + // A Collator compare function is an anonymous built-in function that has a [[Collator]] internal slot. + // When a Collator compare function F is called with arguments x and y, the following steps are taken: + JSThread *thread = argv->GetThread(); + [[maybe_unused]] EcmaHandleScope scope(thread); + JSHandle intlBoundFunc = JSHandle::Cast(GetConstructor(argv)); + + // 1. Let collator be F.[[Collator]]. + JSHandle collator(thread, intlBoundFunc->GetCollator()); + + // 2. Assert: Type(collator) is Object and collator has an [[InitializedCollator]] internal slot. + ASSERT_PRINT(collator->IsJSObject() && collator->IsJSCollator(), "collator is not object or JSCollator"); + + // 3. If x is not provided, let x be undefined. + JSHandle x = GetCallArg(argv, 0); + + // 4. If y is not provided, let y be undefined. + JSHandle y = GetCallArg(argv, 1); + + // 5. Let X be ? ToString(x). + JSHandle xValue = JSTaggedValue::ToString(thread, x); + RETURN_VALUE_IF_ABRUPT_COMPLETION(thread, JSTaggedValue::Undefined()); + // 6. Let Y be ? ToString(y). + JSHandle yValue = JSTaggedValue::ToString(thread, y); + RETURN_VALUE_IF_ABRUPT_COMPLETION(thread, JSTaggedValue::Undefined()); + // 7. Return CompareStrings(collator, X, Y). + icu::Collator *icuCollator = (JSHandle::Cast(collator))->GetIcuCollator(); + return JSCollator::CompareStrings(icuCollator, xValue, yValue); +} + +// 11.3.4 Intl.Collator.prototype.resolvedOptions () +JSTaggedValue BuiltinsCollator::ResolvedOptions(EcmaRuntimeCallInfo *argv) +{ + JSThread *thread = argv->GetThread(); + [[maybe_unused]] EcmaHandleScope scope(thread); + JSHandle thisValue = GetThis(argv); + if (!thisValue->IsJSCollator()) { + THROW_TYPE_ERROR_AND_RETURN(thread, "this is not Collator object", JSTaggedValue::Exception()); + } + JSHandle options = JSCollator::ResolvedOptions(thread, JSHandle::Cast(thisValue)); + return options.GetTaggedValue(); +} +} // namespace panda::ecmascript::builtins diff --git a/runtime/builtins/builtins_collator.h b/runtime/builtins/builtins_collator.h new file mode 100644 index 000000000..562b9e3b2 --- /dev/null +++ b/runtime/builtins/builtins_collator.h @@ -0,0 +1,40 @@ +/* + * Copyright (c) 2021-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_BUILTINS_BUILTINS_COLLATOR_H +#define ECMASCRIPT_BUILTINS_BUILTINS_COLLATOR_H + +#include "plugins/ecmascript/runtime/base/builtins_base.h" + +namespace panda::ecmascript::builtins { +class BuiltinsCollator : public base::BuiltinsBase { +public: + // 11.1.2 Intl.Collator ( [ locales [ , options ] ] ) + static JSTaggedValue CollatorConstructor(EcmaRuntimeCallInfo *argv); + + // 11.2.2 Intl.Collator.supportedLocalesOf ( locales [ , options ] ) + static JSTaggedValue SupportedLocalesOf(EcmaRuntimeCallInfo *argv); + + // 11.3.3 get Intl.Collator.prototype.compare + static JSTaggedValue Compare(EcmaRuntimeCallInfo *argv); + + // 11.3.4 Intl.Collator.prototype.resolvedOptions () + static JSTaggedValue ResolvedOptions(EcmaRuntimeCallInfo *argv); + +private: + static JSTaggedValue AnonymousCollator(EcmaRuntimeCallInfo *argv); +}; +} // namespace panda::ecmascript::builtins +#endif // ECMASCRIPT_BUILTINS_BUILTINS_COLLATOR_H diff --git a/runtime/builtins/builtins_dataview.cpp b/runtime/builtins/builtins_dataview.cpp new file mode 100644 index 000000000..6672a4dfe --- /dev/null +++ b/runtime/builtins/builtins_dataview.cpp @@ -0,0 +1,440 @@ +/* + * Copyright (c) 2021-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 "plugins/ecmascript/runtime/builtins/builtins_dataview.h" +#include "plugins/ecmascript/runtime/base/number_helper.h" +#include "plugins/ecmascript/runtime/builtins/builtins_arraybuffer.h" +#include "plugins/ecmascript/runtime/ecma_macros.h" +#include "plugins/ecmascript/runtime/global_env.h" +#include "plugins/ecmascript/runtime/js_arraybuffer.h" +#include "plugins/ecmascript/runtime/js_tagged_number.h" +#include "plugins/ecmascript/runtime/js_tagged_value-inl.h" +#include "plugins/ecmascript/runtime/js_tagged_value.h" + +namespace panda::ecmascript::builtins { +// 24.2.2.1 +JSTaggedValue BuiltinsDataView::DataViewConstructor(EcmaRuntimeCallInfo *argv) +{ + ASSERT(argv); + BUILTINS_API_TRACE(argv->GetThread(), DataView, Constructor); + JSThread *thread = argv->GetThread(); + [[maybe_unused]] EcmaHandleScope handleScope(thread); + JSHandle ctor = GetConstructor(argv); + JSHandle newTarget = GetNewTarget(argv); + // 1. If NewTarget is undefined, throw a TypeError exception. + if (newTarget->IsUndefined()) { + THROW_TYPE_ERROR_AND_RETURN(thread, "newtarget is undefined", JSTaggedValue::Exception()); + } + JSHandle bufferHandle = GetCallArg(argv, 0); + // 2. If Type(buffer) is not Object, throw a TypeError exception. + if (!bufferHandle->IsECMAObject()) { + THROW_TYPE_ERROR_AND_RETURN(thread, "buffer is not Object", JSTaggedValue::Exception()); + } + // 3. If buffer does not have an [[ArrayBufferData]] internal slot, throw a TypeError exception. + if (!bufferHandle->IsArrayBuffer()) { + THROW_TYPE_ERROR_AND_RETURN(thread, "buffer is not ArrayBuffer", JSTaggedValue::Exception()); + } + JSHandle offsetHandle = GetCallArg(argv, 1); + // 4. Let numberOffset be ToNumber(byteOffset). + JSTaggedNumber offsetNumber = JSTaggedValue::ToNumber(thread, offsetHandle); + // 6. ReturnIfAbrupt(offset). + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + int32_t offset = ecmascript::base::NumberHelper::DoubleInRangeInt32(offsetNumber.GetNumber()); + // 7. If numberOffset ≠ offset or offset < 0, throw a RangeError exception. + if (offset < 0) { + THROW_RANGE_ERROR_AND_RETURN(thread, "Offset out of range", JSTaggedValue::Exception()); + } + // 8. If IsDetachedBuffer(buffer) is true, throw a TypeError exception. + if (BuiltinsArrayBuffer::IsDetachedBuffer(bufferHandle.GetTaggedValue())) { + THROW_TYPE_ERROR_AND_RETURN(thread, "buffer is Detached Buffer", JSTaggedValue::Exception()); + } + // 9. Let bufferByteLength be the value of buffer’s [[ArrayBufferByteLength]] internal slot. + JSHandle arrBufHandle(bufferHandle); + JSTaggedNumber bufLenNum = JSTaggedNumber::FromIntOrDouble(thread, arrBufHandle->GetArrayBufferByteLength()); + int32_t bufByteLen = bufLenNum.ToInt32(); + // 10. If offset > bufferByteLength, throw a RangeError exception. + if (offset > bufByteLen) { + THROW_RANGE_ERROR_AND_RETURN(thread, "offset > bufferByteLength", JSTaggedValue::Exception()); + } + int32_t viewByteLen; + JSHandle byteLenHandle = GetCallArg(argv, BuiltinsBase::ArgsPosition::THIRD); + // 11. If byteLength is undefined, then Let viewByteLength be bufferByteLength – offset. + if (byteLenHandle->IsUndefined()) { + viewByteLen = bufByteLen - offset; + } else { + // Let viewByteLength be ToIndex(byteLength). + JSTaggedNumber byteLen = JSTaggedValue::ToIndex(thread, byteLenHandle); + // ReturnIfAbrupt(viewByteLength). + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + viewByteLen = byteLen.ToInt32(); + // If offset+viewByteLength > bufferByteLength, throw a RangeError exception. + if (offset + viewByteLen > bufByteLen) { + THROW_RANGE_ERROR_AND_RETURN(thread, "offset + viewByteLen > bufByteLen", JSTaggedValue::Exception()); + } + } + // 13. Let O be OrdinaryCreateFromConstructor OrdinaryCreateFromConstructor(NewTarget, "%DataViewPrototype%", + // «[[DataView]],[[ViewedArrayBuffer]], [[ByteLength]], [[ByteOffset]]» ). + ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); + JSHandle obj = factory->NewJSObjectByConstructor(JSHandle(ctor), newTarget); + // 14. ReturnIfAbrupt(O). + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + JSHandle dataView(obj); + // 15. Set O’s [[DataView]] internal slot to true. + dataView->SetDataView(thread, JSTaggedValue::True()); + // 16. Set O’s [[ViewedArrayBuffer]] internal slot to buffer. + dataView->SetViewedArrayBuffer(thread, bufferHandle.GetTaggedValue()); + // 17. Set O’s [[ByteLength]] internal slot to viewByteLength. + dataView->SetByteLength(thread, JSTaggedValue(viewByteLen)); + // 18. Set O’s [[ByteOffset]] internal slot to offset. + dataView->SetByteOffset(thread, JSTaggedValue(offset)); + // 19. Return O. + return JSTaggedValue(dataView.GetTaggedValue()); +} + +// 24.2.4.1 +JSTaggedValue BuiltinsDataView::GetBuffer(EcmaRuntimeCallInfo *argv) +{ + ASSERT(argv); + BUILTINS_API_TRACE(argv->GetThread(), DataView, GetBuffer); + JSThread *thread = argv->GetThread(); + [[maybe_unused]] EcmaHandleScope handleScope(thread); + // 1. Let O be the this value. + JSHandle thisHandle = GetThis(argv); + // 2. f Type(O) is not Object, throw a TypeError exception. + if (!thisHandle->IsECMAObject()) { + THROW_TYPE_ERROR_AND_RETURN(thread, "Type(O) is not Object", JSTaggedValue::Exception()); + } + // 3. If O does not have a [[ViewedArrayBuffer]] internal slot, throw a TypeError exception. + if (!thisHandle->IsDataView()) { + THROW_TYPE_ERROR_AND_RETURN(thread, "O does not have a [[ViewedArrayBuffer]]", JSTaggedValue::Exception()); + } + JSHandle dataView(thisHandle); + // 4. Let buffer be the value of O’s [[ViewedArrayBuffer]] internal slot. + JSTaggedValue buffer = dataView->GetViewedArrayBuffer(); + // 5. Return buffer. + return JSTaggedValue(buffer); +} + +// 24.2.4.2 +JSTaggedValue BuiltinsDataView::GetByteLength(EcmaRuntimeCallInfo *argv) +{ + ASSERT(argv); + BUILTINS_API_TRACE(argv->GetThread(), DataView, GetByteLength); + JSThread *thread = argv->GetThread(); + [[maybe_unused]] EcmaHandleScope handleScope(thread); + // 1. Let O be the this value. + JSHandle thisHandle = GetThis(argv); + // 2. If Type(O) is not Object, throw a TypeError exception. + if (!thisHandle->IsECMAObject()) { + THROW_TYPE_ERROR_AND_RETURN(thread, "Type(O) is not Object", JSTaggedValue::Exception()); + } + // 3. If O does not have a [[ViewedArrayBuffer]] internal slot, throw a TypeError exception. + if (!thisHandle->IsDataView()) { + THROW_TYPE_ERROR_AND_RETURN(thread, "O does not have a [[ViewedArrayBuffer]]", JSTaggedValue::Exception()); + } + JSHandle dataView(thisHandle); + // 4. Let buffer be the value of O’s [[ViewedArrayBuffer]] internal slot. + JSTaggedValue buffer = dataView->GetViewedArrayBuffer(); + // 5. If IsDetachedBuffer(buffer) is true, throw a TypeError exception. + if (BuiltinsArrayBuffer::IsDetachedBuffer(buffer)) { + THROW_TYPE_ERROR_AND_RETURN(thread, "Is Detached Buffer", JSTaggedValue::Exception()); + } + // 6. Let size be the value of O’s [[ByteLength]] internal slot. + JSTaggedValue size = dataView->GetByteLength(); + // 7. Return size. + return JSTaggedValue(size); +} + +// 24.2.4.3 +JSTaggedValue BuiltinsDataView::GetOffset(EcmaRuntimeCallInfo *argv) +{ + ASSERT(argv); + BUILTINS_API_TRACE(argv->GetThread(), DataView, GetOffset); + JSThread *thread = argv->GetThread(); + [[maybe_unused]] EcmaHandleScope handleScope(thread); + // 1. Let O be the this value. + JSHandle thisHandle = GetThis(argv); + // 2. If Type(O) is not Object, throw a TypeError exception. + if (!thisHandle->IsECMAObject()) { + THROW_TYPE_ERROR_AND_RETURN(thread, "Type(O) is not Object", JSTaggedValue::Exception()); + } + // 3. If O does not have a [[ViewedArrayBuffer]] internal slot, throw a TypeError exception. + if (!thisHandle->IsDataView()) { + THROW_TYPE_ERROR_AND_RETURN(thread, "O does not have a [[ViewedArrayBuffer]]", JSTaggedValue::Exception()); + } + JSHandle dataView(thisHandle); + // 4. Let buffer be the value of O’s [[ViewedArrayBuffer]] internal slot. + JSTaggedValue buffer = dataView->GetViewedArrayBuffer(); + // 5. If IsDetachedBuffer(buffer) is true, throw a TypeError exception. + if (BuiltinsArrayBuffer::IsDetachedBuffer(buffer)) { + THROW_TYPE_ERROR_AND_RETURN(thread, "Is Detached Buffer", JSTaggedValue::Exception()); + } + // 6. Let offset be the value of O’s [[ByteOffset]] internal slot. + JSTaggedValue offset = dataView->GetByteOffset(); + // 7. Return offset. + return JSTaggedValue(offset); +} + +// 24.2.4.5 +JSTaggedValue BuiltinsDataView::GetFloat32(EcmaRuntimeCallInfo *argv) +{ + ASSERT(argv); + return GetTypedValue(argv, DataViewType::FLOAT32); +} + +// 24.2.4.6 +JSTaggedValue BuiltinsDataView::GetFloat64(EcmaRuntimeCallInfo *argv) +{ + ASSERT(argv); + return GetTypedValue(argv, DataViewType::FLOAT64); +} + +// 24.2.4.7 +JSTaggedValue BuiltinsDataView::GetInt8(EcmaRuntimeCallInfo *argv) +{ + ASSERT(argv); + return GetTypedValue(argv, DataViewType::INT8); +} + +// 24.2.4.8 +JSTaggedValue BuiltinsDataView::GetInt16(EcmaRuntimeCallInfo *argv) +{ + ASSERT(argv); + return GetTypedValue(argv, DataViewType::INT16); +} + +// 24.2.4.9 +JSTaggedValue BuiltinsDataView::GetInt32(EcmaRuntimeCallInfo *argv) +{ + ASSERT(argv); + return GetTypedValue(argv, DataViewType::INT32); +} + +// 24.2.4.10 +JSTaggedValue BuiltinsDataView::GetUint8(EcmaRuntimeCallInfo *argv) +{ + ASSERT(argv); + return GetTypedValue(argv, DataViewType::UINT8); +} + +// 24.2.4.11 +JSTaggedValue BuiltinsDataView::GetUint16(EcmaRuntimeCallInfo *argv) +{ + ASSERT(argv); + return GetTypedValue(argv, DataViewType::UINT16); +} + +// 24.2.4.12 +JSTaggedValue BuiltinsDataView::GetUint32(EcmaRuntimeCallInfo *argv) +{ + ASSERT(argv); + return GetTypedValue(argv, DataViewType::UINT32); +} + +// 24.2.4.13 +JSTaggedValue BuiltinsDataView::SetFloat32(EcmaRuntimeCallInfo *argv) +{ + ASSERT(argv); + return SetTypedValue(argv, DataViewType::FLOAT32); +} + +// 24.2.4.14 +JSTaggedValue BuiltinsDataView::SetFloat64(EcmaRuntimeCallInfo *argv) +{ + ASSERT(argv); + return SetTypedValue(argv, DataViewType::FLOAT64); +} + +// 24.2.4.15 +JSTaggedValue BuiltinsDataView::SetInt8(EcmaRuntimeCallInfo *argv) +{ + ASSERT(argv); + return SetTypedValue(argv, DataViewType::INT8); +} + +// 24.2.4.16 +JSTaggedValue BuiltinsDataView::SetInt16(EcmaRuntimeCallInfo *argv) +{ + ASSERT(argv); + return SetTypedValue(argv, DataViewType::INT16); +} + +// 24.2.4.17 +JSTaggedValue BuiltinsDataView::SetInt32(EcmaRuntimeCallInfo *argv) +{ + ASSERT(argv); + return SetTypedValue(argv, DataViewType::INT32); +} + +// 24.2.4.18 +JSTaggedValue BuiltinsDataView::SetUint8(EcmaRuntimeCallInfo *argv) +{ + ASSERT(argv); + return SetTypedValue(argv, DataViewType::UINT8); +} + +// 24.2.4.19 +JSTaggedValue BuiltinsDataView::SetUint16(EcmaRuntimeCallInfo *argv) +{ + ASSERT(argv); + return SetTypedValue(argv, DataViewType::UINT16); +} + +// 24.2.4.20 +JSTaggedValue BuiltinsDataView::SetUint32(EcmaRuntimeCallInfo *argv) +{ + ASSERT(argv); + return SetTypedValue(argv, DataViewType::UINT32); +} + +// 24.2.1.1 +JSTaggedValue BuiltinsDataView::GetViewValue(JSThread *thread, const JSHandle &view, + const JSHandle &requestIndex, JSTaggedValue littleEndian, + DataViewType type) +{ + BUILTINS_API_TRACE(thread, DataView, GetViewValue); + // 1. If Type(view) is not Object, throw a TypeError exception. + if (!view->IsECMAObject()) { + THROW_TYPE_ERROR_AND_RETURN(thread, "Type(O) is not Object", JSTaggedValue::Exception()); + } + // 2. If view does not have a [[DataView]] internal slot, throw a TypeError exception. + if (!view->IsDataView()) { + THROW_TYPE_ERROR_AND_RETURN(thread, "view is not dataview", JSTaggedValue::Exception()); + } + // 3. Let numberIndex be ToNumber(requestIndex). + JSTaggedNumber numberIndex = JSTaggedValue::ToNumber(thread, requestIndex); + // 5. ReturnIfAbrupt(getIndex). + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + int64_t index = ecmascript::base::NumberHelper::DoubleInRangeInt32(numberIndex.GetNumber()); + // 6. If numberIndex ≠ getIndex or getIndex < 0, throw a RangeError exception. + if (index < 0) { + THROW_RANGE_ERROR_AND_RETURN(thread, "getIndex < 0", JSTaggedValue::Exception()); + } + // 7. Let isLittleEndian be ToBoolean(isLittleEndian). + bool isLittleEndian; + if (littleEndian.IsUndefined()) { + isLittleEndian = false; + } else { + isLittleEndian = littleEndian.ToBoolean(); + } + // 8. Let buffer be the value of view’s [[ViewedArrayBuffer]] internal slot. + JSHandle dataView(view); + JSTaggedValue buffer = dataView->GetViewedArrayBuffer(); + // 9. If IsDetachedBuffer(buffer) is true, throw a TypeError exception. + if (BuiltinsArrayBuffer::IsDetachedBuffer(buffer)) { + THROW_TYPE_ERROR_AND_RETURN(thread, "Is Detached Buffer", JSTaggedValue::Exception()); + } + // 10. Let viewOffset be the value of view’s [[ByteOffset]] internal slot. + JSTaggedNumber offsetNum = JSTaggedNumber::FromIntOrDouble(thread, dataView->GetByteOffset()); + int32_t offset = offsetNum.ToInt32(); + // 11. Let viewSize be the value of view’s [[ByteLength]] internal slot. + JSTaggedNumber viewNum = JSTaggedNumber::FromIntOrDouble(thread, dataView->GetByteLength()); + int32_t size = viewNum.ToInt32(); + // 12. Let elementSize be the Number value of the Element Size value specified in Table 49 for Element Type type. + int32_t elementSize = JSDataView::GetElementSize(type); + // 13. If getIndex +elementSize > viewSize, throw a RangeError exception. + if (index + elementSize > size) { + THROW_RANGE_ERROR_AND_RETURN(thread, "getIndex +elementSize > viewSize", JSTaggedValue::Exception()); + } + // 14. Let bufferIndex be getIndex + viewOffset. + int32_t bufferIndex = index + offset; + // 15. Return GetValueFromBuffer(buffer, bufferIndex, type, isLittleEndian). + return BuiltinsArrayBuffer::GetValueFromBuffer(buffer, bufferIndex, type, isLittleEndian); +} + +// 24.2.1.2 +JSTaggedValue BuiltinsDataView::SetViewValue(JSThread *thread, const JSHandle &view, + const JSHandle &requestIndex, JSTaggedValue littleEndian, + DataViewType type, const JSHandle &value) +{ + // 1. If Type(view) is not Object, throw a TypeError exception. + BUILTINS_API_TRACE(thread, DataView, SetViewValue); + if (!view->IsECMAObject()) { + THROW_TYPE_ERROR_AND_RETURN(thread, "Type(O) is not Object", JSTaggedValue::Exception()); + } + // 2. If view does not have a [[DataView]] internal slot, throw a TypeError exception. + if (!view->IsDataView()) { + THROW_TYPE_ERROR_AND_RETURN(thread, "view is not dataview", JSTaggedValue::Exception()); + } + // 3. Let numberIndex be ToNumber(requestIndex). + JSTaggedNumber numberIndex = JSTaggedValue::ToIndex(thread, requestIndex); + // 5. ReturnIfAbrupt(getIndex). + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + int64_t index = ecmascript::base::NumberHelper::DoubleInRangeInt32(numberIndex.GetNumber()); + // 6. If numberIndex ≠ getIndex or getIndex < 0, throw a RangeError exception. + if (index < 0) { + THROW_RANGE_ERROR_AND_RETURN(thread, "getIndex < 0", JSTaggedValue::Exception()); + } + JSTaggedNumber numVal = JSTaggedValue::ToNumber(thread, value); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + // 7. Let isLittleEndian be ToBoolean(isLittleEndian). + bool isLittleEndian; + if (littleEndian.IsUndefined()) { + isLittleEndian = false; + } else { + isLittleEndian = littleEndian.ToBoolean(); + } + // 8. Let buffer be the value of view’s [[ViewedArrayBuffer]] internal slot. + JSHandle dataView(view); + JSTaggedValue buffer = dataView->GetViewedArrayBuffer(); + // 9. If IsDetachedBuffer(buffer) is true, throw a TypeError exception. + if (BuiltinsArrayBuffer::IsDetachedBuffer(buffer)) { + THROW_TYPE_ERROR_AND_RETURN(thread, "Is Detached Buffer", JSTaggedValue::Exception()); + } + // 10. Let viewOffset be the value of view’s [[ByteOffset]] internal slot. + JSTaggedNumber offsetNum = JSTaggedNumber::FromIntOrDouble(thread, dataView->GetByteOffset()); + int32_t offset = offsetNum.ToInt32(); + // 11. Let viewSize be the value of view’s [[ByteLength]] internal slot. + JSTaggedNumber viewNum = JSTaggedNumber::FromIntOrDouble(thread, dataView->GetByteLength()); + int32_t size = viewNum.ToInt32(); + // 12. Let elementSize be the Number value of the Element Size value specified in Table 49 for Element Type type. + int32_t elementSize = JSDataView::GetElementSize(type); + // 13. If getIndex +elementSize > viewSize, throw a RangeError exception. + if (index + elementSize > size) { + THROW_RANGE_ERROR_AND_RETURN(thread, "getIndex +elementSize > viewSize", JSTaggedValue::Exception()); + } + // 14. Let bufferIndex be getIndex + viewOffset. + int32_t bufferIndex = index + offset; + // 15. Return SetValueFromBuffer(buffer, bufferIndex, type, value, isLittleEndian). + return BuiltinsArrayBuffer::SetValueInBuffer(buffer, bufferIndex, type, numVal, isLittleEndian); +} + +JSTaggedValue BuiltinsDataView::GetTypedValue(EcmaRuntimeCallInfo *argv, DataViewType type) +{ + JSThread *thread = argv->GetThread(); + [[maybe_unused]] EcmaHandleScope handleScope(thread); + JSHandle thisHandle = GetThis(argv); + JSHandle offsetHandle = GetCallArg(argv, 0); + if (type == DataViewType::UINT8 || type == DataViewType::INT8) { + return GetViewValue(thread, thisHandle, offsetHandle, JSTaggedValue::True(), type); + } + JSHandle littleEndianHandle = GetCallArg(argv, 1); + return GetViewValue(thread, thisHandle, offsetHandle, littleEndianHandle.GetTaggedValue(), type); +} + +JSTaggedValue BuiltinsDataView::SetTypedValue(EcmaRuntimeCallInfo *argv, DataViewType type) +{ + JSThread *thread = argv->GetThread(); + [[maybe_unused]] EcmaHandleScope handleScope(thread); + JSHandle thisHandle = GetThis(argv); + JSHandle offsetHandle = GetCallArg(argv, 0); + JSHandle value = GetCallArg(argv, 1); + if (type == DataViewType::UINT8 || type == DataViewType::INT8) { + return SetViewValue(thread, thisHandle, offsetHandle, JSTaggedValue::True(), type, value); + } + JSHandle littleEndianHandle = GetCallArg(argv, BuiltinsBase::ArgsPosition::THIRD); + return SetViewValue(thread, thisHandle, offsetHandle, littleEndianHandle.GetTaggedValue(), type, value); +} +} // namespace panda::ecmascript::builtins diff --git a/runtime/builtins/builtins_dataview.h b/runtime/builtins/builtins_dataview.h new file mode 100644 index 000000000..24170642b --- /dev/null +++ b/runtime/builtins/builtins_dataview.h @@ -0,0 +1,81 @@ +/* + * Copyright (c) 2021-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_BUILTINS_BUILTINS_DATAVIEW_H +#define ECMASCRIPT_BUILTINS_BUILTINS_DATAVIEW_H + +#include "plugins/ecmascript/runtime/base/builtins_base.h" +#include "plugins/ecmascript/runtime/js_dataview.h" + +namespace panda::ecmascript::builtins { +using DataViewType = ecmascript::DataViewType; +class BuiltinsDataView : public ecmascript::base::BuiltinsBase { +public: + // 24.2.2.1 DataView (buffer [ , byteOffset [ , byteLength ] ] ) + static JSTaggedValue DataViewConstructor(EcmaRuntimeCallInfo *argv); + // 24.2.4.1 get DataView.prototype.buffer + static JSTaggedValue GetBuffer(EcmaRuntimeCallInfo *argv); + // 24.2.4.2 get DataView.prototype.byteLength + static JSTaggedValue GetByteLength(EcmaRuntimeCallInfo *argv); + // 24.2.4.3 get DataView.prototype.byteOffset + static JSTaggedValue GetOffset(EcmaRuntimeCallInfo *argv); + // 24.2.4.5 DataView.prototype.getFloat32 ( byteOffset [ , littleEndian ] ) + static JSTaggedValue GetFloat32(EcmaRuntimeCallInfo *argv); + // 24.2.4.6 DataView.prototype.getFloat64 ( byteOffset [ , littleEndian ] ) + static JSTaggedValue GetFloat64(EcmaRuntimeCallInfo *argv); + // 24.2.4.7 DataView.prototype.getInt8 ( byteOffset ) + static JSTaggedValue GetInt8(EcmaRuntimeCallInfo *argv); + // 24.2.4.8 DataView.prototype.getInt16 ( byteOffset [ , littleEndian ] ) + static JSTaggedValue GetInt16(EcmaRuntimeCallInfo *argv); + // 24.2.4.9 DataView.prototype.getInt32 ( byteOffset [ , littleEndian ] ) + static JSTaggedValue GetInt32(EcmaRuntimeCallInfo *argv); + // 24.2.4.10 DataView.prototype.getUint8 ( byteOffset ) + static JSTaggedValue GetUint8(EcmaRuntimeCallInfo *argv); + // 24.2.4.11 DataView.prototype.getUint16 ( byteOffset [ , littleEndian ] ) + static JSTaggedValue GetUint16(EcmaRuntimeCallInfo *argv); + // 24.2.4.12 DataView.prototype.getUint32 ( byteOffset [ , littleEndian ] ) + static JSTaggedValue GetUint32(EcmaRuntimeCallInfo *argv); + // 24.2.4.13 DataView.prototype.setFloat32 ( byteOffset, value [ , littleEndian ] ) + static JSTaggedValue SetFloat32(EcmaRuntimeCallInfo *argv); + // 24.2.4.14 DataView.prototype.setFloat64 ( byteOffset, value [ , littleEndian ] ) + static JSTaggedValue SetFloat64(EcmaRuntimeCallInfo *argv); + // 24.2.4.15 DataView.prototype.setInt8 ( byteOffset, value ) + static JSTaggedValue SetInt8(EcmaRuntimeCallInfo *argv); + // 24.2.4.16 DataView.prototype.setInt16 ( byteOffset, value [ , littleEndian ] ) + static JSTaggedValue SetInt16(EcmaRuntimeCallInfo *argv); + // 24.2.4.17 DataView.prototype.setInt32 ( byteOffset, value [ , littleEndian ] ) + static JSTaggedValue SetInt32(EcmaRuntimeCallInfo *argv); + // 24.2.4.18 DataView.prototype.setUint8 ( byteOffset, value ) + static JSTaggedValue SetUint8(EcmaRuntimeCallInfo *argv); + // 24.2.4.19 DataView.prototype.setUint16( byteOffset, value [ , littleEndian ] ) + static JSTaggedValue SetUint16(EcmaRuntimeCallInfo *argv); + // 24.2.4.20 DataView.prototype.setUint32 ( byteOffset, value [ , littleEndian ] ) + static JSTaggedValue SetUint32(EcmaRuntimeCallInfo *argv); + +private: + // 24.2.1.1 GetViewValue ( view, requestIndex, isLittleEndian, type ) + static JSTaggedValue GetViewValue(JSThread *thread, const JSHandle &view, + const JSHandle &requestIndex, JSTaggedValue littleEndian, + DataViewType type); + static JSTaggedValue SetViewValue(JSThread *thread, const JSHandle &view, + const JSHandle &requestIndex, JSTaggedValue littleEndian, + DataViewType type, const JSHandle &value); + + static JSTaggedValue GetTypedValue(EcmaRuntimeCallInfo *argv, DataViewType type); + static JSTaggedValue SetTypedValue(EcmaRuntimeCallInfo *argv, DataViewType type); +}; +} // namespace panda::ecmascript::builtins + +#endif // ECMASCRIPT_BUILTINS_BUILTINS_DATAVIEW_H diff --git a/runtime/builtins/builtins_date.cpp b/runtime/builtins/builtins_date.cpp new file mode 100644 index 000000000..59c17f712 --- /dev/null +++ b/runtime/builtins/builtins_date.cpp @@ -0,0 +1,358 @@ +/* + * Copyright (c) 2021-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 "plugins/ecmascript/runtime/builtins/builtins_date.h" +#include "plugins/ecmascript/runtime/ecma_macros.h" +#include "plugins/ecmascript/runtime/ecma_vm.h" +#include "plugins/ecmascript/runtime/global_env.h" +#include "plugins/ecmascript/runtime/js_date.h" +#include "plugins/ecmascript/runtime/js_date_time_format.h" +#include "plugins/ecmascript/runtime/js_function.h" +#include "plugins/ecmascript/runtime/js_object-inl.h" +#include "plugins/ecmascript/runtime/js_tagged_value-inl.h" +#include "plugins/ecmascript/runtime/js_thread.h" +#include "plugins/ecmascript/runtime/tagged_array.h" + +namespace panda::ecmascript::builtins { +// constructor +JSTaggedValue BuiltinsDate::DateConstructor(EcmaRuntimeCallInfo *argv) +{ + BUILTINS_API_TRACE(argv->GetThread(), Date, Constructor); + JSThread *thread = argv->GetThread(); + [[maybe_unused]] EcmaHandleScope handleScope(thread); + JSHandle newTarget = GetNewTarget(argv); + if (newTarget->IsUndefined()) { + double now = JSDate::Now().GetDouble(); + CString str = JSDate::ToDateString(now); + return GetTaggedString(thread, str.c_str()); + } + + JSTaggedValue timeValue(0.0); + uint32_t length = argv->GetArgsNumber(); + if (length == 0) { // no value + timeValue = JSDate::Now(); + } else if (length == 1) { // one value + JSHandle value = GetCallArg(argv, 0); + if (value->IsDate()) { // The value is a date object. + JSHandle jsDate(thread, JSDate::Cast(value->GetTaggedObject())); + timeValue = jsDate->GetTimeValue(); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + } else { + JSHandle objValue(thread, JSTaggedValue::ToPrimitive(thread, value)); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + if (objValue->IsString()) { // The value is a string object. + timeValue = JSDate::Parse(argv); + } else { // The value is a number. + JSTaggedNumber val = JSTaggedValue::ToNumber(thread, objValue); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + timeValue = JSTaggedValue(val.GetNumber()); + } + timeValue = JSTaggedValue(JSDate::TimeClip(timeValue.GetDouble())); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + } + } else { // two or more values + std::array fields = {0, 0, 1, 0, 0, 0, 0, 0, 0}; + if (length > CONSTRUCTOR_MAX_LENGTH) { // The max length is 7. + length = CONSTRUCTOR_MAX_LENGTH; + } + uint32_t i = 0; + for (; i < length; ++i) { + JSHandle value = GetCallArg(argv, i); + JSTaggedNumber res = JSTaggedValue::ToNumber(thread, value); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + double temp = res.GetNumber(); + if (std::isnan(temp) || !std::isfinite(temp)) { // Check the double value is finite. + break; + } + fields[i] = static_cast(temp); + if (i == 0 && fields[0] >= 0 && fields[0] < JSDate::HUNDRED) { + fields[0] += JSDate::NINETEEN_HUNDRED_YEAR; + } + } + timeValue = JSTaggedValue((i == length) ? JSDate::SetDateValues(&fields, true) : ecmascript::base::NAN_VALUE); + } + + ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); + JSHandle constructor = GetConstructor(argv); + JSHandle dateObject = + JSHandle::Cast(factory->NewJSObjectByConstructor(JSHandle(constructor), newTarget)); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + dateObject->SetTimeValue(thread, timeValue); + return JSTaggedValue(JSObject::Cast(static_cast(*dateObject))); +} + +// 20.4.3.1 +JSTaggedValue BuiltinsDate::Now([[maybe_unused]] EcmaRuntimeCallInfo *argv) +{ + BUILTINS_API_TRACE(argv->GetThread(), Date, Now); + return JSDate::Now(); +} + +// 20.4.3.2 +JSTaggedValue BuiltinsDate::Parse(EcmaRuntimeCallInfo *argv) +{ + BUILTINS_API_TRACE(argv->GetThread(), Date, Parse); + [[maybe_unused]] EcmaHandleScope handleScope(argv->GetThread()); + return JSDate::Parse(argv); +} + +// 20.4.3.4 +JSTaggedValue BuiltinsDate::UTC(EcmaRuntimeCallInfo *argv) +{ + BUILTINS_API_TRACE(argv->GetThread(), Date, UTC); + [[maybe_unused]] EcmaHandleScope handleScope(argv->GetThread()); + return JSDate::UTC(argv); +} + +// 20.4.4.10 +JSTaggedValue BuiltinsDate::GetTime(EcmaRuntimeCallInfo *argv) +{ + ASSERT(argv); + BUILTINS_API_TRACE(argv->GetThread(), Date, GetTime); + JSThread *thread = argv->GetThread(); + JSHandle msg = GetThis(argv); + if (!msg->IsDate()) { + [[maybe_unused]] EcmaHandleScope handleScope(argv->GetThread()); + THROW_TYPE_ERROR_AND_RETURN(thread, "Not a Date Object", JSTaggedValue::Exception()); + } + return JSDate::Cast(msg->GetTaggedObject())->GetTime(); +} + +JSTaggedValue BuiltinsDate::SetTime(EcmaRuntimeCallInfo *argv) +{ + ASSERT(argv); + BUILTINS_API_TRACE(argv->GetThread(), Date, SetTime); + JSThread *thread = argv->GetThread(); + [[maybe_unused]] EcmaHandleScope handleScope(thread); + + JSHandle msg = GetThis(argv); + if (!msg->IsDate()) { + THROW_TYPE_ERROR_AND_RETURN(thread, "Not a Date Object", JSTaggedValue::Exception()); + } + JSHandle js_data(thread, JSDate::Cast(msg->GetTaggedObject())); + JSTaggedNumber res = JSTaggedValue::ToNumber(thread, GetCallArg(argv, 0)); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(argv->GetThread()); + double number = res.GetNumber(); + double value = JSDate::TimeClip(number); + js_data->SetTimeValue(thread, JSTaggedValue(value)); + return GetTaggedDouble(value); +} + +// 20.4.4.37 +JSTaggedValue BuiltinsDate::ToJSON(EcmaRuntimeCallInfo *argv) +{ + ASSERT(argv); + BUILTINS_API_TRACE(argv->GetThread(), Date, ToJSON); + JSThread *thread = argv->GetThread(); + [[maybe_unused]] EcmaHandleScope handleScope(thread); + + // 1. Let O be ToObject(this value). + JSHandle msg = GetThis(argv); + JSHandle object = JSTaggedValue::ToObject(thread, msg); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + + // 2. Let tv be ToPrimitive(hint Number) + JSHandle objectHandle = JSHandle::Cast(object); + JSHandle tv(thread, + JSTaggedValue::ToPrimitive(thread, objectHandle, PreferredPrimitiveType::PREFER_NUMBER)); + + // 3. If Type(tv) is Number and tv is not finite, return null + if (tv->IsNumber()) { + if (tv->IsDouble() && !std::isfinite(tv->GetDouble())) { + return JSTaggedValue::Null(); + } + } + JSHandle calleeKey(thread->GetEcmaVM()->GetFactory()->NewFromCanBeCompressString("toISOString")); + return JSFunction::Invoke(thread, objectHandle, calleeKey, 0, nullptr); +} + +// 20.4.4.44 +JSTaggedValue BuiltinsDate::ValueOf(EcmaRuntimeCallInfo *argv) +{ + ASSERT(argv); + BUILTINS_API_TRACE(argv->GetThread(), Date, ValueOf); + JSThread *thread = argv->GetThread(); + JSHandle msg = GetThis(argv); + if (!msg->IsDate()) { + [[maybe_unused]] EcmaHandleScope handleScope(thread); + THROW_TYPE_ERROR_AND_RETURN(thread, "Not a Date Object", JSTaggedValue::Exception()); + } + return JSDate::Cast(msg->GetTaggedObject())->ValueOf(); +} + +// 20.4.4.45 +JSTaggedValue BuiltinsDate::ToPrimitive(EcmaRuntimeCallInfo *argv) +{ + ASSERT(argv); + BUILTINS_API_TRACE(argv->GetThread(), Date, ToPrimitive); + JSThread *thread = argv->GetThread(); + [[maybe_unused]] EcmaHandleScope handleScope(thread); + + ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); + JSHandle object = GetThis(argv); + if (!object->IsECMAObject()) { + THROW_TYPE_ERROR_AND_RETURN(thread, "Not a JSObject", JSTaggedValue::Exception()); + } + JSHandle hint = GetCallArg(argv, 0); + PreferredPrimitiveType tryFirst = PREFER_STRING; + if (hint->IsString()) { + JSHandle numberStrHandle = factory->NewFromCanBeCompressString("number"); + if (EcmaString::StringsAreEqual(hint.GetObject(), *numberStrHandle)) { + tryFirst = PREFER_NUMBER; + } else { + JSHandle stringStrHandle = factory->NewFromCanBeCompressString("string"); + JSHandle defaultStrHandle = factory->NewFromCanBeCompressString("default"); + if (EcmaString::StringsAreEqual(hint.GetObject(), *stringStrHandle) || + EcmaString::StringsAreEqual(hint.GetObject(), *defaultStrHandle)) { + tryFirst = PREFER_STRING; + } else { + THROW_TYPE_ERROR_AND_RETURN(thread, "This is not a primitiveType.", JSTaggedValue::Exception()); + } + } + } else { + THROW_TYPE_ERROR_AND_RETURN(thread, "This is not an primitiveType.", JSTaggedValue::Exception()); + } + return JSTaggedValue::OrdinaryToPrimitive(thread, object, tryFirst); +} + +// ecma 402 16.4.1 Date.prototype.toLocaleString ( [ locales [ , options ] ] ) +JSTaggedValue BuiltinsDate::ToLocaleString(EcmaRuntimeCallInfo *argv) +{ + ASSERT(argv); + JSThread *thread = argv->GetThread(); + EcmaVM *ecmaVm = thread->GetEcmaVM(); + ObjectFactory *factory = ecmaVm->GetFactory(); + JSHandle env = ecmaVm->GetGlobalEnv(); + [[maybe_unused]] EcmaHandleScope handleScope(thread); + + // Let x be ? thisTimeValue(this value). + JSHandle msg = GetThis(argv); + if (!msg->IsDate()) { + THROW_TYPE_ERROR_AND_RETURN(thread, "Not a Date Object", JSTaggedValue::Exception()); + } + JSTaggedValue value = JSDate::Cast(msg->GetTaggedObject())->GetTime(); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + + // If x is NaN, return "Invalid Date". + double x = value.GetNumber(); + if (std::isnan(x)) { + return thread->GlobalConstants()->GetInvalidDateString(); + } + + // Let options be ? ToDateTimeOptions(options, "any", "all"). + JSHandle locales = GetCallArg(argv, 0); + JSHandle options = GetCallArg(argv, 1); + JSHandle dateTimeOptions = + JSDateTimeFormat::ToDateTimeOptions(thread, options, RequiredOption::ANY, DefaultsOption::ALL); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + + // Let dateFormat be ? Construct(%DateTimeFormat%, « locales, options »). + JSHandle ctor = env->GetDateTimeFormatFunction(); + JSHandle obj = factory->NewJSObjectByConstructor(JSHandle(ctor), ctor); + JSHandle dtf = JSDateTimeFormat::InitializeDateTimeFormat( + thread, JSHandle::Cast(obj), locales, JSHandle::Cast(dateTimeOptions)); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + + // Return ? FormatDateTime(dateFormat, x). + JSHandle result = JSDateTimeFormat::FormatDateTime(thread, dtf, x); + return result.GetTaggedValue(); +} + +// ecma 402 16.4.1 Date.prototype.toLocaleString ( [ locales [ , options ] ] ) +JSTaggedValue BuiltinsDate::ToLocaleDateString(EcmaRuntimeCallInfo *argv) +{ + ASSERT(argv); + JSThread *thread = argv->GetThread(); + EcmaVM *ecmaVm = thread->GetEcmaVM(); + ObjectFactory *factory = ecmaVm->GetFactory(); + JSHandle env = ecmaVm->GetGlobalEnv(); + [[maybe_unused]] EcmaHandleScope handleScope(thread); + + // Let x be ? thisTimeValue(this value). + JSHandle msg = GetThis(argv); + if (!msg->IsDate()) { + THROW_TYPE_ERROR_AND_RETURN(thread, "Not a Date Object", JSTaggedValue::Exception()); + } + JSTaggedValue value = JSDate::Cast(msg->GetTaggedObject())->GetTime(); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + + // If x is NaN, return "Invalid Date". + double x = value.GetNumber(); + if (std::isnan(x)) { + return thread->GlobalConstants()->GetInvalidDateString(); + } + + // Let options be ? ToDateTimeOptions(options, "any", "all"). + JSHandle locales = GetCallArg(argv, 0); + JSHandle options = GetCallArg(argv, 1); + JSHandle dateTimeOptions = + JSDateTimeFormat::ToDateTimeOptions(thread, options, RequiredOption::DATE, DefaultsOption::DATE); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + + // Let dateFormat be ? Construct(%DateTimeFormat%, « locales, options »). + JSHandle ctor = env->GetDateTimeFormatFunction(); + JSHandle obj = factory->NewJSObjectByConstructor(JSHandle(ctor), ctor); + JSHandle dtf = JSDateTimeFormat::InitializeDateTimeFormat( + thread, JSHandle::Cast(obj), locales, JSHandle::Cast(dateTimeOptions)); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + + // Return ? FormatDateTime(dateFormat, x). + JSHandle result = JSDateTimeFormat::FormatDateTime(thread, dtf, x); + return result.GetTaggedValue(); +} + +// ecma 402 16.4.1 Date.prototype.toLocaleString ( [ locales [ , options ] ] ) +JSTaggedValue BuiltinsDate::ToLocaleTimeString(EcmaRuntimeCallInfo *argv) +{ + ASSERT(argv); + JSThread *thread = argv->GetThread(); + EcmaVM *ecmaVm = thread->GetEcmaVM(); + ObjectFactory *factory = ecmaVm->GetFactory(); + JSHandle env = ecmaVm->GetGlobalEnv(); + [[maybe_unused]] EcmaHandleScope handleScope(thread); + + // Let x be ? thisTimeValue(this value). + JSHandle msg = GetThis(argv); + if (!msg->IsDate()) { + THROW_TYPE_ERROR_AND_RETURN(thread, "Not a Date Object", JSTaggedValue::Exception()); + } + JSTaggedValue value = JSDate::Cast(msg->GetTaggedObject())->GetTime(); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + + // If x is NaN, return "Invalid Date". + double x = value.GetNumber(); + if (std::isnan(x)) { + return thread->GlobalConstants()->GetInvalidDateString(); + } + + // Let options be ? ToDateTimeOptions(options, "any", "all"). + JSHandle locales = GetCallArg(argv, 0); + JSHandle options = GetCallArg(argv, 1); + JSHandle dateTimeOptions = + JSDateTimeFormat::ToDateTimeOptions(thread, options, RequiredOption::TIME, DefaultsOption::TIME); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + + // Let dateFormat be ? Construct(%DateTimeFormat%, « locales, options »). + JSHandle ctor = env->GetDateTimeFormatFunction(); + JSHandle obj = factory->NewJSObjectByConstructor(JSHandle(ctor), ctor); + JSHandle dtf = JSDateTimeFormat::InitializeDateTimeFormat( + thread, JSHandle::Cast(obj), locales, JSHandle::Cast(dateTimeOptions)); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + + // Return ? FormatDateTime(dateFormat, x). + JSHandle result = JSDateTimeFormat::FormatDateTime(thread, dtf, x); + return result.GetTaggedValue(); +} +} // namespace panda::ecmascript::builtins diff --git a/runtime/builtins/builtins_date.h b/runtime/builtins/builtins_date.h new file mode 100644 index 000000000..d96014ff0 --- /dev/null +++ b/runtime/builtins/builtins_date.h @@ -0,0 +1,180 @@ +/* + * Copyright (c) 2021-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_BUILTINS_BUILTINS_DATE_H +#define ECMASCRIPT_BUILTINS_BUILTINS_DATE_H + +#include "plugins/ecmascript/runtime/base/builtins_base.h" +#include "plugins/ecmascript/runtime/js_date.h" + +namespace panda::ecmascript::builtins { +class BuiltinsDate : public ecmascript::base::BuiltinsBase { +public: + // 20.4.2 The Date Constructor + static JSTaggedValue DateConstructor(EcmaRuntimeCallInfo *argv); + + // 20.4.3.1 Date.now ( ) + static JSTaggedValue Now(EcmaRuntimeCallInfo *argv); + + // 20.4.3.4 Date.UTC ( year [ , month [ , date [ , hours [ , minutes [ , seconds [ , ms ] ] ] ] ] ] ) + static JSTaggedValue UTC(EcmaRuntimeCallInfo *argv); + + static JSTaggedValue Parse(EcmaRuntimeCallInfo *argv); + + // 20.4.4.2 Date.prototype.getDate ( ) + GET_DATE_VALUE(GetDate, DAYS, true); + + // 20.4.4.3 Date.prototype.getDay ( ) + GET_DATE_VALUE(GetDay, WEEKDAY, true); + + // 20.4.4.4 Date.prototype.getFullYear ( ) + GET_DATE_VALUE(GetFullYear, YEAR, true); + + // 20.4.4.5 Date.prototype.getHours ( ) + GET_DATE_VALUE(GetHours, HOUR, true); + + // 20.4.4.6 Date.prototype.getMilliseconds ( ) + GET_DATE_VALUE(GetMilliseconds, MS, true); + + // 20.4.4.7 Date.prototype.getMinutes ( ) + GET_DATE_VALUE(GetMinutes, MIN, true); + + // 20.4.4.8 Date.prototype.getMonth ( ) + GET_DATE_VALUE(GetMonth, MONTH, true); + + // 20.4.4.9 Date.prototype.getSeconds ( ) + GET_DATE_VALUE(GetSeconds, SEC, true); + + // 20.4.4.10 Date.prototype.getTime ( ) + static JSTaggedValue GetTime(EcmaRuntimeCallInfo *argv); + + // 20.4.4.11 Date.prototype.getTimezoneOffset ( ) + GET_DATE_VALUE(GetTimezoneOffset, TIMEZONE, true); + + // 20.4.4.12 Date.prototype.getUTCDate ( ) + GET_DATE_VALUE(GetUTCDate, DAYS, false); + + // 20.4.4.13 Date.prototype.getUTCDay ( ) + GET_DATE_VALUE(GetUTCDay, WEEKDAY, false); + + // 20.4.4.14 Date.prototype.getUTCFullYear ( ) + GET_DATE_VALUE(GetUTCFullYear, YEAR, false); + + // 20.4.4.15 Date.prototype.getUTCHours ( ) + GET_DATE_VALUE(GetUTCHours, HOUR, false); + + // 20.4.4.16 Date.prototype.getUTCMilliseconds ( ) + GET_DATE_VALUE(GetUTCMilliseconds, MS, false); + + // 20.4.4.17 Date.prototype.getUTCMinutes ( ) + GET_DATE_VALUE(GetUTCMinutes, MIN, false); + + // 20.4.4.18 Date.prototype.getUTCMonth ( ) + GET_DATE_VALUE(GetUTCMonth, MONTH, false); + + // 20.4.4.19 Date.prototype.getUTCSeconds ( ) + GET_DATE_VALUE(GetUTCSeconds, SEC, false); + + // 20.3.4.20 Date.prototype.setDate ( date ) + SET_DATE_VALUE(SetDate, CODE_SET_DATE, true); + + // 20.3.4.21 Date.prototype.setFullYear ( year [ , month [ , date ] ] ) + SET_DATE_VALUE(SetFullYear, CODE_SET_FULL_YEAR, true); + + // 20.3.4.22 Date.prototype.setHours ( hour [ , min [ , sec [ , ms ] ] ] ) + SET_DATE_VALUE(SetHours, CODE_SET_HOURS, true); + + // 20.3.4.23 Date.prototype.setMilliseconds ( ms ) + SET_DATE_VALUE(SetMilliseconds, CODE_SET_MILLISECONDS, true); + + // 20.3.4.24 Date.prototype.setMinutes ( min [ , sec [ , ms ] ] ) + SET_DATE_VALUE(SetMinutes, CODE_SET_MINUTES, true); + + // 20.3.4.25 Date.prototype.setMonth ( month [ , date ] ) + SET_DATE_VALUE(SetMonth, CODE_SET_MONTH, true); + + // 20.3.4.26 Date.prototype.setSeconds ( sec [ , ms ] ) + SET_DATE_VALUE(SetSeconds, CODE_SET_SECONDS, true); + + // 20.3.4.27 Date.prototype.setTime ( time ) + static JSTaggedValue SetTime(EcmaRuntimeCallInfo *argv); + + // 20.3.4.28 Date.prototype.setUTCDate ( date ) + SET_DATE_VALUE(SetUTCDate, CODE_SET_DATE, false); + + // 20.3.4.29 Date.prototype.setUTCFullYear ( year [ , month [ , date ] ] ) + SET_DATE_VALUE(SetUTCFullYear, CODE_SET_FULL_YEAR, false); + + // 20.3.4.30 Date.prototype.setUTCHours ( hour [ , min [ , sec [ , ms ] ] ] ) + SET_DATE_VALUE(SetUTCHours, CODE_SET_HOURS, false); + + // 20.3.4.31 Date.prototype.setUTCMilliseconds ( ms ) + SET_DATE_VALUE(SetUTCMilliseconds, CODE_SET_MILLISECONDS, false); + + // 20.3.4.32 Date.prototype.setUTCMinutes ( min [ , sec [, ms ] ] ) + SET_DATE_VALUE(SetUTCMinutes, CODE_SET_MINUTES, false); + + // 20.3.4.33 Date.prototype.setUTCMonth ( month [ , date ] ) + SET_DATE_VALUE(SetUTCMonth, CODE_SET_MONTH, false); + + // 20.3.4.34 Date.prototype.setUTCSeconds ( sec [ , ms ] ) + SET_DATE_VALUE(SetUTCSeconds, CODE_SET_SECONDS, false); + + // 20.4.4.35 Date.prototype.toDateString ( ) + DATE_STRING(ToDateString); + + // 20.4.4.36 Date.prototype.toISOString ( ) + DATE_TO_STRING(ToISOString); + + // 20.4.4.37 Date.prototype.toJSON ( key ) + static JSTaggedValue ToJSON(EcmaRuntimeCallInfo *argv); + + // 20.4.4.38 Date.prototype.toLocaleDateString ( [ reserved1 [ , reserved2 ] ] ) + static JSTaggedValue ToLocaleDateString(EcmaRuntimeCallInfo *argv); + + // 20.4.4.39 Date.prototype.toLocaleString ( [ reserved1 [ , reserved2 ] ] ) + static JSTaggedValue ToLocaleString(EcmaRuntimeCallInfo *argv); + + // 20.4.4.40 Date.prototype.toLocaleTimeString ( [ reserved1 [ , reserved2 ] ] ) + static JSTaggedValue ToLocaleTimeString(EcmaRuntimeCallInfo *argv); + + // 20.4.4.41 Date.prototype.toString ( ) + DATE_STRING(ToString); + + // 20.4.4.42 Date.prototype.toTimeString ( ) + DATE_STRING(ToTimeString); + + // 20.4.4.43 Date.prototype.toUTCString ( ) + DATE_STRING(ToUTCString); + + // 20.4.4.44 Date.prototype.valueOf ( ) + static JSTaggedValue ValueOf(EcmaRuntimeCallInfo *argv); + + // 20.4.4.45 Date.prototype [ @@toPrimitive ] + static JSTaggedValue ToPrimitive(EcmaRuntimeCallInfo *argv); + +private: + // definition for set data code. + static constexpr uint32_t CODE_SET_DATE = 0x32; + static constexpr uint32_t CODE_SET_MILLISECONDS = 0x76; + static constexpr uint32_t CODE_SET_SECONDS = 0x75; + static constexpr uint32_t CODE_SET_MINUTES = 0x74; + static constexpr uint32_t CODE_SET_HOURS = 0x73; + static constexpr uint32_t CODE_SET_MONTH = 0x31; + static constexpr uint32_t CODE_SET_FULL_YEAR = 0x30; + static constexpr uint8_t CONSTRUCTOR_MAX_LENGTH = 7; +}; +} // namespace panda::ecmascript::builtins +#endif // ECMASCRIPT_BUILTINS_BUILTINS_DATE_H diff --git a/runtime/builtins/builtins_date_time_format.cpp b/runtime/builtins/builtins_date_time_format.cpp new file mode 100644 index 000000000..fcd77baaf --- /dev/null +++ b/runtime/builtins/builtins_date_time_format.cpp @@ -0,0 +1,324 @@ +/* + * Copyright (c) 2021-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 "builtins_date_time_format.h" +#include "plugins/ecmascript/runtime/ecma_vm.h" +#include "plugins/ecmascript/runtime/global_env.h" +#include "plugins/ecmascript/runtime/js_date.h" +#include "plugins/ecmascript/runtime/js_date_time_format.h" +#include "plugins/ecmascript/runtime/js_intl.h" + +namespace panda::ecmascript::builtins { +// 13.2.1 Intl.DateTimeFormat ( [ locales [ , options ] ] ) +JSTaggedValue BuiltinsDateTimeFormat::DateTimeFormatConstructor(EcmaRuntimeCallInfo *argv) +{ + JSThread *thread = argv->GetThread(); + [[maybe_unused]] EcmaHandleScope scope(thread); + EcmaVM *ecmaVm = thread->GetEcmaVM(); + JSHandle env = ecmaVm->GetGlobalEnv(); + ObjectFactory *factory = ecmaVm->GetFactory(); + + // 1. If NewTarget is undefined, let newTarget be the active function object, else let newTarget be NewTarget. + JSHandle constructor = GetConstructor(argv); + JSHandle newTarget = GetNewTarget(argv); + if (newTarget->IsUndefined()) { + newTarget = constructor; + } + + // 2. Let dateTimeFormat be ? OrdinaryCreateFromConstructor(newTarget, "%DateTimeFormatPrototype%", « + // [[InitializedDateTimeFormat]], [[Locale]], [[Calendar]], [[NumberingSystem]], [[TimeZone]], [[Weekday]], + // [[Era]], [[Year]], [[Month]], [[Day]], [[Hour]], [[Minute]], [[Second]], [[TimeZoneName]], [[HourCycle]], + // [[Pattern]], [[BoundFormat]] »). + JSHandle dateTimeFormat = JSHandle::Cast( + factory->NewJSObjectByConstructor(JSHandle(constructor), newTarget)); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + + // 3. Perform ? InitializeDateTimeFormat(dateTimeFormat, locales, options). + JSHandle locales = GetCallArg(argv, 0); + JSHandle options = GetCallArg(argv, 1); + JSHandle dtf = + JSDateTimeFormat::InitializeDateTimeFormat(thread, dateTimeFormat, locales, options); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + + // 4. Let this be the this value. + JSHandle thisValue = GetThis(argv); + + // 5. If NewTarget is undefined and Type(this) is Object and ? InstanceofOperator(this, %DateTimeFormat%) is true, + // then + // a. Perform ? DefinePropertyOrThrow(this, %Intl%.[[FallbackSymbol]], PropertyDescriptor{ [[Value]]: + // dateTimeFormat, [[Writable]]: false, [[Enumerable]]: false, [[Configurable]]: false }). + // b. Return this. + bool isInstanceOf = JSObject::InstanceOf(thread, thisValue, env->GetDateTimeFormatFunction()); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + if (newTarget->IsUndefined() && thisValue->IsJSObject() && isInstanceOf) { + PropertyDescriptor descriptor(thread, JSHandle::Cast(dtf), false, false, false); + JSHandle key(thread, JSHandle::Cast(env->GetIntlFunction())->GetFallbackSymbol()); + JSTaggedValue::DefinePropertyOrThrow(thread, thisValue, key, descriptor); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + return thisValue.GetTaggedValue(); + } + + // 6. Return dateTimeFormat. + return dtf.GetTaggedValue(); +} + +// 13.3.2 Intl.DateTimeFormat.supportedLocalesOf ( locales [ , options ] ) +JSTaggedValue BuiltinsDateTimeFormat::SupportedLocalesOf(EcmaRuntimeCallInfo *argv) +{ + JSThread *thread = argv->GetThread(); + [[maybe_unused]] EcmaHandleScope scope(thread); + // 1. Let availableLocales be %DateTimeFormat%.[[AvailableLocales]]. + JSHandle availableLocales = JSDateTimeFormat::GainAvailableLocales(thread); + + // 2. Let requestedLocales be ? CanonicalizeLocaleList(locales). + JSHandle locales = GetCallArg(argv, 0); + JSHandle requestedLocales = JSLocale::CanonicalizeLocaleList(thread, locales); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + + // 3. Return ? SupportedLocales(availableLocales, requestedLocales, options). + JSHandle options = GetCallArg(argv, 1); + JSHandle result = JSLocale::SupportedLocales(thread, availableLocales, requestedLocales, options); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + return result.GetTaggedValue(); +} + +// 13.4.3 get Intl.DateTimeFormat.prototype.format +JSTaggedValue BuiltinsDateTimeFormat::Format(EcmaRuntimeCallInfo *argv) +{ + JSThread *thread = argv->GetThread(); + [[maybe_unused]] EcmaHandleScope scope(thread); + + // 1. Let dtf be this value. + JSHandle thisValue = GetThis(argv); + + // 2. If Type(dtf) is not Object, throw a TypeError exception. + if (!thisValue->IsJSObject()) { + THROW_TYPE_ERROR_AND_RETURN(thread, "dtf is not object", JSTaggedValue::Exception()); + } + + // 3. Let dtf be ? UnwrapDateTimeFormat(dtf). + JSHandle dtfValue = JSDateTimeFormat::UnwrapDateTimeFormat(thread, thisValue); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + if (dtfValue->IsUndefined()) { + THROW_TYPE_ERROR_AND_RETURN(thread, "dtfValue is not object", JSTaggedValue::Exception()); + } + + // 4. If dtf.[[BoundFormat]] is undefined, then + // a. Let F be a new built-in function object as defined in DateTime Format Functions (13.1.5). + // b. Set F.[[DateTimeFormat]] to dtf. + // c. Set dtf.[[BoundFormat]] to F. + // 5. Return dtf.[[BoundFormat]]. + JSHandle dtf = JSHandle::Cast(dtfValue); + JSHandle boundFormat(thread, dtf->GetBoundFormat()); + if (boundFormat->IsUndefined()) { + ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); + JSHandle intlBoundFunc = + factory->NewJSIntlBoundFunction(reinterpret_cast(BuiltinsDateTimeFormat::AnonymousDateTimeFormat)); + intlBoundFunc->SetDateTimeFormat(thread, dtf); + dtf->SetBoundFormat(thread, intlBoundFunc); + } + return dtf->GetBoundFormat(); +} + +// 13.1.5 DateTime Format Functions +JSTaggedValue BuiltinsDateTimeFormat::AnonymousDateTimeFormat(EcmaRuntimeCallInfo *argv) +{ + // A DateTime format function is an anonymous built-in function that has a [[DateTimeFormat]] internal slot. + // When a DateTime format function F is called with optional argument date, the following steps are taken: + JSThread *thread = argv->GetThread(); + [[maybe_unused]] EcmaHandleScope scope(thread); + JSHandle intlBoundFunc = JSHandle::Cast(GetConstructor(argv)); + + // 1. Let dtf be F.[[DateTimeFormat]]. + JSHandle dtf(thread, intlBoundFunc->GetDateTimeFormat()); + + // 2. Assert: Type(dtf) is Object and dtf has an [[InitializedDateTimeFormat]] internal slot. + ASSERT_PRINT(dtf->IsJSObject() && dtf->IsJSDateTimeFormat(), "dtf is not object or JSDateTimeFormat"); + + // 3. If date is not provided or is undefined, then + // a. Let x be Call(%Date_now%, undefined). + // 4. Else, + // a. Let x be ? ToNumber(date). + JSHandle date = GetCallArg(argv, 0); + double x = 0.0; + if (date->IsUndefined()) { + x = JSDate::Now().GetNumber(); + } else { + JSTaggedNumber xNumber = JSTaggedValue::ToNumber(thread, date); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + x = xNumber.GetNumber(); + } + + // 5. Return ? FormatDateTime(dtf, x). + JSHandle result = JSDateTimeFormat::FormatDateTime(thread, JSHandle::Cast(dtf), x); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + return result.GetTaggedValue(); +} + +// 13.4.4 Intl.DateTimeFormat.prototype.formatToParts ( date ) +JSTaggedValue BuiltinsDateTimeFormat::FormatToParts(EcmaRuntimeCallInfo *argv) +{ + JSThread *thread = argv->GetThread(); + [[maybe_unused]] EcmaHandleScope scope(thread); + // 1. Let dtf be this value. + JSHandle dtf = GetThis(argv); + // 2. Perform ? RequireInternalSlot(dtf, [[InitializedDateTimeFormat]]). + if (!dtf->IsJSDateTimeFormat()) { + THROW_TYPE_ERROR_AND_RETURN(thread, "is not JSDateTimeFormat", JSTaggedValue::Exception()); + } + + // 3. If date is undefined, then + // a. Let x be Call(%Date_now%, undefined). + // 4. Else, + // a. Let x be ? ToNumber(date). + JSHandle date = GetCallArg(argv, 0); + double x = 0.0; + if (date->IsUndefined()) { + x = JSDate::Now().GetNumber(); + } else { + JSTaggedNumber xNumber = JSTaggedValue::ToNumber(thread, date); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + x = xNumber.GetNumber(); + } + + double xValue = JSDate::TimeClip(x); + if (std::isnan(xValue)) { + THROW_RANGE_ERROR_AND_RETURN(thread, "Invalid time value", JSTaggedValue::Exception()); + } + + // 5. Return ? FormatDateTimeToParts(dtf, x). + JSHandle result = + JSDateTimeFormat::FormatDateTimeToParts(thread, JSHandle::Cast(dtf), xValue); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + return result.GetTaggedValue(); +} + +// 13.4.5 Intl.DateTimeFormat.prototype.resolvedOptions () +JSTaggedValue BuiltinsDateTimeFormat::ResolvedOptions(EcmaRuntimeCallInfo *argv) +{ + JSThread *thread = argv->GetThread(); + [[maybe_unused]] EcmaHandleScope scope(thread); + // 1. Let dtf be this value. + JSHandle thisValue = GetThis(argv); + // 2. If Type(dtf) is not Object, throw a TypeError exception. + if (!thisValue->IsJSObject()) { + THROW_TYPE_ERROR_AND_RETURN(thread, "this is not object", JSTaggedValue::Exception()); + } + // 3. Let dtf be ? UnwrapDateTimeFormat(dtf). + thisValue = JSDateTimeFormat::UnwrapDateTimeFormat(thread, thisValue); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + + auto ecmaVm = thread->GetEcmaVM(); + JSHandle env = ecmaVm->GetGlobalEnv(); + ObjectFactory *factory = ecmaVm->GetFactory(); + JSHandle ctor = env->GetObjectFunction(); + JSHandle options(factory->NewJSObjectByConstructor(JSHandle(ctor), ctor)); + JSDateTimeFormat::ResolvedOptions(thread, JSHandle::Cast(thisValue), options); + // 6. Return options. + return options.GetTaggedValue(); +} + +// Intl.DateTimeFormat.prototype.formatRange +JSTaggedValue BuiltinsDateTimeFormat::FormatRange(EcmaRuntimeCallInfo *argv) +{ + JSThread *thread = argv->GetThread(); + [[maybe_unused]] EcmaHandleScope scope(thread); + + // 1. Let dtf be this value. + JSHandle thisValue = GetThis(argv); + // 2. If Type(dtf) is not Object, throw a TypeError exception. + if (!thisValue->IsJSObject()) { + THROW_TYPE_ERROR_AND_RETURN(thread, "this is not object", JSTaggedValue::Exception()); + } + + // 3. If dtf does not have an [[InitializedDateTimeFormat]] internal slot, throw a TypeError exception. + if (!thisValue->IsJSDateTimeFormat()) { + THROW_TYPE_ERROR_AND_RETURN(thread, "this is not JSDateTimeFormat", JSTaggedValue::Exception()); + } + + // 4. If startDate is undefined or endDate is undefined, throw a TypeError exception. + JSHandle startDate = GetCallArg(argv, 0); + JSHandle endDate = GetCallArg(argv, 1); + if (startDate->IsUndefined() || endDate->IsUndefined()) { + THROW_TYPE_ERROR_AND_RETURN(thread, "startDate or endDate is undefined", JSTaggedValue::Exception()); + } + + // 5. Let x be ? ToNumber(startDate). + JSTaggedNumber valueX = JSTaggedValue::ToNumber(thread, startDate); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + double x = valueX.GetNumber(); + + // 6. Let y be ? ToNumber(endDate). + JSTaggedNumber valueY = JSTaggedValue::ToNumber(thread, endDate); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + double y = valueY.GetNumber(); + // 7. If x is greater than y, throw a RangeError exception. + if (x > y) { + THROW_RANGE_ERROR_AND_RETURN(thread, "x is greater than y", JSTaggedValue::Exception()); + } + + // 8. Return ? FormatDateTimeRange(dtf, x, y) + JSHandle dtf = JSHandle::Cast(thisValue); + JSHandle result = JSDateTimeFormat::NormDateTimeRange(thread, dtf, x, y); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + return result.GetTaggedValue(); +} + +// Intl.DateTimeFormat.prototype.formatRangeToParts +JSTaggedValue BuiltinsDateTimeFormat::FormatRangeToParts(EcmaRuntimeCallInfo *argv) +{ + JSThread *thread = argv->GetThread(); + [[maybe_unused]] EcmaHandleScope scope(thread); + // 1. Let dtf be this value. + JSHandle thisValue = GetThis(argv); + // 2. If Type(dtf) is not Object, throw a TypeError exception. + if (!thisValue->IsJSObject()) { + THROW_TYPE_ERROR_AND_RETURN(thread, "this is not object", JSTaggedValue::Exception()); + } + + // 3. If dtf does not have an [[InitializedDateTimeFormat]] internal slot, + // throw a TypeError exception. + if (!thisValue->IsJSDateTimeFormat()) { + THROW_TYPE_ERROR_AND_RETURN(thread, "this is not JSDateTimeFormat", JSTaggedValue::Exception()); + } + + // 4. If startDate is undefined or endDate is undefined, throw a TypeError exception. + JSHandle startDate = GetCallArg(argv, 0); + JSHandle endDate = GetCallArg(argv, 1); + if (startDate->IsUndefined() || endDate->IsUndefined()) { + THROW_TYPE_ERROR_AND_RETURN(thread, "startDate or endDate is undefined", JSTaggedValue::Exception()); + } + + // 5. Let x be ? ToNumber(startDate). + JSTaggedNumber valueX = JSTaggedValue::ToNumber(thread, startDate); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + double x = valueX.GetNumber(); + + // 6. Let y be ? ToNumber(endDate). + JSTaggedNumber valueY = JSTaggedValue::ToNumber(thread, endDate); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + double y = valueY.GetNumber(); + // 7. If x is greater than y, throw a RangeError exception. + if (x > y) { + THROW_RANGE_ERROR_AND_RETURN(thread, "x is greater than y", JSTaggedValue::Exception()); + } + + // 8. Return ? FormatDateTimeRangeToParts(dtf, x, y) + JSHandle dtf = JSHandle::Cast(thisValue); + JSHandle result = JSDateTimeFormat::NormDateTimeRangeToParts(thread, dtf, x, y); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + return result.GetTaggedValue(); +} +} // namespace panda::ecmascript::builtins diff --git a/runtime/builtins/builtins_date_time_format.h b/runtime/builtins/builtins_date_time_format.h new file mode 100644 index 000000000..e539f6ef1 --- /dev/null +++ b/runtime/builtins/builtins_date_time_format.h @@ -0,0 +1,51 @@ +/* + * Copyright (c) 2021-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_BUILTINS_BUILTINS_DATE_TIME_FORMAT_H +#define ECMASCRIPT_BUILTINS_BUILTINS_DATE_TIME_FORMAT_H + +#include "plugins/ecmascript/runtime/base/builtins_base.h" +#include "plugins/ecmascript/runtime/ecma_runtime_call_info.h" + +namespace panda::ecmascript::builtins { +class BuiltinsDateTimeFormat : public ecmascript::base::BuiltinsBase { +public: + // 13.2.1 Intl.DateTimeFormat ( [ locales [ , options ] ] ) + static JSTaggedValue DateTimeFormatConstructor(EcmaRuntimeCallInfo *argv); + + // 13.3.2 Intl.DateTimeFormat.supportedLocalesOf ( locales [ , options ] ) + static JSTaggedValue SupportedLocalesOf(EcmaRuntimeCallInfo *argv); + + // 13.4.3 get Intl.DateTimeFormat.prototype.format + static JSTaggedValue Format(EcmaRuntimeCallInfo *argv); + + // 13.4.4 Intl.DateTimeFormat.prototype.formatToParts ( date ) + static JSTaggedValue FormatToParts(EcmaRuntimeCallInfo *argv); + + // 13.4.5 Intl.DateTimeFormat.prototype.resolvedOptions () + static JSTaggedValue ResolvedOptions(EcmaRuntimeCallInfo *argv); + + // Intl.DateTimeFormat.prototype.formatRange + static JSTaggedValue FormatRange(EcmaRuntimeCallInfo *argv); + + // Intl.DateTimeFormat.prototype.formatRangeToParts + static JSTaggedValue FormatRangeToParts(EcmaRuntimeCallInfo *argv); + +private: + // 13.1.5 DateTime Format Functions + static JSTaggedValue AnonymousDateTimeFormat(EcmaRuntimeCallInfo *argv); +}; +} // namespace panda::ecmascript::builtins +#endif // ECMASCRIPT_BUILTINS_BUILTINS_DATE_TIME_FORMAT_H_ diff --git a/runtime/builtins/builtins_errors.cpp b/runtime/builtins/builtins_errors.cpp new file mode 100644 index 000000000..4293aa339 --- /dev/null +++ b/runtime/builtins/builtins_errors.cpp @@ -0,0 +1,106 @@ +/* + * Copyright (c) 2021-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 "plugins/ecmascript/runtime/builtins/builtins_errors.h" +#include "plugins/ecmascript/runtime/base/error_helper.h" +#include "plugins/ecmascript/runtime/js_tagged_value-inl.h" + +namespace panda::ecmascript::builtins { +using ErrorHelper = ecmascript::base::ErrorHelper; +using ErrorType = ecmascript::base::ErrorType; +// Error +JSTaggedValue BuiltinsError::ErrorConstructor(EcmaRuntimeCallInfo *argv) +{ + return ErrorHelper::ErrorCommonConstructor(argv, ErrorType::ERROR); +} + +JSTaggedValue BuiltinsError::ToString(EcmaRuntimeCallInfo *argv) +{ + return ErrorHelper::ErrorCommonToString(argv, ErrorType::ERROR); +} + +// RangeError +JSTaggedValue BuiltinsRangeError::RangeErrorConstructor(EcmaRuntimeCallInfo *argv) +{ + return ErrorHelper::ErrorCommonConstructor(argv, ErrorType::RANGE_ERROR); +} + +JSTaggedValue BuiltinsRangeError::ToString(EcmaRuntimeCallInfo *argv) +{ + return ErrorHelper::ErrorCommonToString(argv, ErrorType::RANGE_ERROR); +} + +// ReferenceError +JSTaggedValue BuiltinsReferenceError::ReferenceErrorConstructor(EcmaRuntimeCallInfo *argv) +{ + return ErrorHelper::ErrorCommonConstructor(argv, ErrorType::REFERENCE_ERROR); +} + +JSTaggedValue BuiltinsReferenceError::ToString(EcmaRuntimeCallInfo *argv) +{ + return ErrorHelper::ErrorCommonToString(argv, ErrorType::REFERENCE_ERROR); +} + +// TypeError +JSTaggedValue BuiltinsTypeError::TypeErrorConstructor(EcmaRuntimeCallInfo *argv) +{ + return ErrorHelper::ErrorCommonConstructor(argv, ErrorType::TYPE_ERROR); +} + +JSTaggedValue BuiltinsTypeError::ToString(EcmaRuntimeCallInfo *argv) +{ + return ErrorHelper::ErrorCommonToString(argv, ErrorType::TYPE_ERROR); +} + +JSTaggedValue BuiltinsTypeError::ThrowTypeError(EcmaRuntimeCallInfo *argv) +{ + JSThread *thread = argv->GetThread(); + [[maybe_unused]] EcmaHandleScope handle_scope(thread); + THROW_TYPE_ERROR_AND_RETURN(thread, "type error", JSTaggedValue::Exception()); +} + +// URIError +JSTaggedValue BuiltinsURIError::URIErrorConstructor(EcmaRuntimeCallInfo *argv) +{ + return ErrorHelper::ErrorCommonConstructor(argv, ErrorType::URI_ERROR); +} + +JSTaggedValue BuiltinsURIError::ToString(EcmaRuntimeCallInfo *argv) +{ + return ErrorHelper::ErrorCommonToString(argv, ErrorType::URI_ERROR); +} + +// SyntaxError +JSTaggedValue BuiltinsSyntaxError::SyntaxErrorConstructor(EcmaRuntimeCallInfo *argv) +{ + return ErrorHelper::ErrorCommonConstructor(argv, ErrorType::SYNTAX_ERROR); +} + +JSTaggedValue BuiltinsSyntaxError::ToString(EcmaRuntimeCallInfo *argv) +{ + return ErrorHelper::ErrorCommonToString(argv, ErrorType::SYNTAX_ERROR); +} + +// EvalError +JSTaggedValue BuiltinsEvalError::EvalErrorConstructor(EcmaRuntimeCallInfo *argv) +{ + return ErrorHelper::ErrorCommonConstructor(argv, ErrorType::EVAL_ERROR); +} + +JSTaggedValue BuiltinsEvalError::ToString(EcmaRuntimeCallInfo *argv) +{ + return ErrorHelper::ErrorCommonToString(argv, ErrorType::EVAL_ERROR); +} +} // namespace panda::ecmascript::builtins diff --git a/runtime/builtins/builtins_errors.h b/runtime/builtins/builtins_errors.h new file mode 100644 index 000000000..ded192b7b --- /dev/null +++ b/runtime/builtins/builtins_errors.h @@ -0,0 +1,82 @@ +/* + * Copyright (c) 2021-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_BUILTINS_BUILTINS_ERRORS_H +#define ECMASCRIPT_BUILTINS_BUILTINS_ERRORS_H + +#include "plugins/ecmascript/runtime/base/builtins_base.h" +#include "plugins/ecmascript/runtime/ecma_runtime_call_info.h" + +namespace panda::ecmascript::builtins { +class BuiltinsError : public ecmascript::base::BuiltinsBase { +public: + // 19.5.1.1 + static JSTaggedValue ErrorConstructor(EcmaRuntimeCallInfo *argv); + // 19.5.2.4 + static JSTaggedValue ToString(EcmaRuntimeCallInfo *argv); +}; + +// 19.5.5.2 +class BuiltinsRangeError : public ecmascript::base::BuiltinsBase { +public: + static JSTaggedValue RangeErrorConstructor(EcmaRuntimeCallInfo *argv); + + static JSTaggedValue ToString(EcmaRuntimeCallInfo *argv); +}; + +// 19.5.5.3 +class BuiltinsReferenceError : public ecmascript::base::BuiltinsBase { +public: + static JSTaggedValue ReferenceErrorConstructor(EcmaRuntimeCallInfo *argv); + + static JSTaggedValue ToString(EcmaRuntimeCallInfo *argv); +}; + +// 19.5.5.5 +class BuiltinsTypeError : public ecmascript::base::BuiltinsBase { +public: + static JSTaggedValue TypeErrorConstructor(EcmaRuntimeCallInfo *argv); + + static JSTaggedValue ToString(EcmaRuntimeCallInfo *argv); + + static JSTaggedValue ThrowTypeError(EcmaRuntimeCallInfo *argv); +}; + +// 19.5.5.6 +class BuiltinsURIError : public ecmascript::base::BuiltinsBase { +public: + static JSTaggedValue URIErrorConstructor(EcmaRuntimeCallInfo *argv); + + static JSTaggedValue ToString(EcmaRuntimeCallInfo *argv); +}; + +// 19.5.5.4 +class BuiltinsSyntaxError : public ecmascript::base::BuiltinsBase { +public: + static JSTaggedValue SyntaxErrorConstructor(EcmaRuntimeCallInfo *argv); + + static JSTaggedValue ToString(EcmaRuntimeCallInfo *argv); +}; + +// 19.5.5.1 +class BuiltinsEvalError : public ecmascript::base::BuiltinsBase { +public: + static JSTaggedValue EvalErrorConstructor(EcmaRuntimeCallInfo *argv); + + static JSTaggedValue ToString(EcmaRuntimeCallInfo *argv); +}; +} // namespace panda::ecmascript::builtins + +#endif // ECMASCRIPT_BUILTINS_BUILTINS_ERRORS_H diff --git a/runtime/builtins/builtins_function.cpp b/runtime/builtins/builtins_function.cpp new file mode 100644 index 000000000..0b243d4b8 --- /dev/null +++ b/runtime/builtins/builtins_function.cpp @@ -0,0 +1,351 @@ +/* + * Copyright (c) 2021-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 "plugins/ecmascript/runtime/builtins/builtins_function.h" +#include "plugins/ecmascript/runtime/ecma_vm.h" +#include "plugins/ecmascript/runtime/global_env.h" +#include "plugins/ecmascript/runtime/internal_call_params.h" +#include "plugins/ecmascript/runtime/js_arguments.h" +#include "plugins/ecmascript/runtime/js_stable_array.h" +#include "plugins/ecmascript/runtime/tagged_array-inl.h" + +namespace panda::ecmascript::builtins { + +static JSTaggedValue EvalHackReturnThis([[maybe_unused]] EcmaRuntimeCallInfo *argv) +{ + return argv->GetThis().GetTaggedValue(); +} + +static JSTaggedValue EvalHackNop([[maybe_unused]] EcmaRuntimeCallInfo *argv) +{ + return JSTaggedValue(JSTaggedValue::VALUE_UNDEFINED); +} + +static JSMethod *LookupEvalHack(JSThread *thread, uint8_t const *str, uint32_t str_len) +{ + using key_type = Span; + auto constexpr makeKey = [](char const *s) { return key_type(reinterpret_cast(s), strlen(s)); }; + auto constexpr hash = [](key_type k) { return ComputeHashForData(k.data(), k.size()); }; + auto constexpr keyEq = [](key_type k1, key_type k2) { + if (k1.size() != k2.size()) { + return false; + } + return std::memcmp(k1.data(), k2.data(), k1.size()) == 0; + }; + + EcmaVM *vm = thread->GetEcmaVM(); + static const std::unordered_map evalMap( + {{makeKey("return this"), vm->GetMethodForNativeFunction(reinterpret_cast(EvalHackReturnThis))}, + {makeKey("return this;"), vm->GetMethodForNativeFunction(reinterpret_cast(EvalHackReturnThis))}, + {makeKey(""), vm->GetMethodForNativeFunction(reinterpret_cast(EvalHackNop))}}, + 0, hash, keyEq); + + auto e = evalMap.find(key_type(str, str_len)); + if (e == evalMap.end()) { + return nullptr; + } + return e->second; +} + +static JSHandle WrapEvalHack(JSThread *thread, JSMethod *target, JSTaggedValue funcLen) +{ + EcmaVM *vm = thread->GetEcmaVM(); + + ObjectFactory *factory = vm->GetFactory(); + JSHandle env = vm->GetGlobalEnv(); + auto globalConst = thread->GlobalConstants(); + JSHandle dynclass = JSHandle::Cast(env->GetNormalFunctionClass()); + JSHandle func = factory->NewJSFunctionByDynClass(target, dynclass, FunctionKind::NORMAL_FUNCTION); + func->SetStrict(thread, false); + func->SetLexicalEnv(thread, vm->GetGlobalEnv()); + JSFunction::SetFunctionLength(thread, func, funcLen); + JSHandle emptyString = globalConst->GetHandledEmptyString(); + JSHandle nameKey = globalConst->GetHandledNameString(); + PropertyDescriptor nameDesc(thread, emptyString, false, false, true); + JSTaggedValue::DefinePropertyOrThrow(thread, JSHandle::Cast(func), nameKey, nameDesc); + return func; +} + +static inline JSTaggedValue TryEvalHack(EcmaRuntimeCallInfo *argv) +{ + uint32_t nargs = argv->GetArgsNumber(); + if (nargs > 1) { + return JSTaggedValue::Hole(); + } + + uint8_t const *data; + uint32_t dataLen; + if (nargs == 0) { + data = reinterpret_cast(""); + dataLen = 0; + } else { + JSHandle functionBody = BuiltinsFunction::GetCallArg(argv, nargs - 1); + if (!functionBody->IsString()) { + return JSTaggedValue::Hole(); + } + auto str = EcmaString::Cast(functionBody->GetHeapObject()); + if (!str->IsUtf8()) { + return JSTaggedValue::Hole(); + } + data = str->GetDataUtf8(); + dataLen = str->GetUtf8Length() - 1; + } + + auto method = LookupEvalHack(argv->GetThread(), data, dataLen); + if (method == nullptr) { + return JSTaggedValue::Hole(); + } + + return WrapEvalHack(argv->GetThread(), method, JSTaggedValue(0)).GetTaggedValue(); +} + +// ecma 19.2.1 Function (p1, p2, ... , pn, body) +JSTaggedValue BuiltinsFunction::FunctionConstructor(EcmaRuntimeCallInfo *argv) +{ + // not support + JSThread *thread = argv->GetThread(); + [[maybe_unused]] EcmaHandleScope handleScope(thread); + + auto func = TryEvalHack(argv); + if (func != JSTaggedValue::Hole()) { + return func; + } + + THROW_TYPE_ERROR_AND_RETURN(thread, "Not support eval. Forbidden using new Function()/Function().", + JSTaggedValue::Exception()); +} + +// ecma 19.2.3 The Function prototype object is itself a built-in function object. +// When invoked, it accepts any arguments and returns undefined. +JSTaggedValue BuiltinsFunction::FunctionPrototypeInvokeSelf([[maybe_unused]] EcmaRuntimeCallInfo *argv) +{ + return JSTaggedValue::Undefined(); +} + +namespace { +bool BuildArgumentsListFast(JSThread *thread, const JSHandle &arrayObj) +{ + if (!arrayObj->HasStableElements(thread)) { + return false; + } + InternalCallParams *arguments = thread->GetInternalCallParams(); + if (arrayObj->IsStableJSArguments(thread)) { + JSHandle argList = JSHandle::Cast(arrayObj); + TaggedArray *elements = TaggedArray::Cast(argList->GetElements().GetTaggedObject()); + auto env = thread->GetEcmaVM()->GetGlobalEnv(); + if (argList->GetClass() != env->GetArgumentsClass().GetObject()) { + return false; + } + auto result = argList->GetPropertyInlinedProps(JSArguments::LENGTH_INLINE_PROPERTY_INDEX); + if (!result.IsInt()) { + return false; + } + auto length = static_cast(result.GetInt()); + arguments->MakeArgListWithHole(elements, length); + } else if (arrayObj->IsStableJSArray(thread)) { + JSHandle argList = JSHandle::Cast(arrayObj); + TaggedArray *elements = TaggedArray::Cast(argList->GetElements().GetTaggedObject()); + uint32_t length = argList->GetArrayLength(); + arguments->MakeArgListWithHole(elements, length); + } else { + UNREACHABLE(); + } + return true; +} +} // anonymous namespace + +// ecma 19.2.3.1 Function.prototype.apply (thisArg, argArray) +JSTaggedValue BuiltinsFunction::FunctionPrototypeApply(EcmaRuntimeCallInfo *argv) +{ + ASSERT(argv); + BUILTINS_API_TRACE(argv->GetThread(), Function, PrototypeApply); + JSThread *thread = argv->GetThread(); + [[maybe_unused]] EcmaHandleScope handleScope(thread); + + // 1. If IsCallable(func) is false, throw a TypeError exception. + if (!GetThis(argv)->IsCallable()) { + THROW_TYPE_ERROR_AND_RETURN(thread, "apply target is not callable", JSTaggedValue::Exception()); + } + + JSHandle func = GetThis(argv); + JSHandle thisArg = GetCallArg(argv, 0); + // 2. If argArray is null or undefined, then + if (GetCallArg(argv, 1)->IsUndefined()) { // null will also get undefined + // a. Return Call(func, thisArg). + return JSFunction::Call(thread, func, thisArg, 0, nullptr); + } + // 3. Let argList be CreateListFromArrayLike(argArray). + JSHandle arrayObj = GetCallArg(argv, 1); + InternalCallParams *arguments = thread->GetInternalCallParams(); + if (!BuildArgumentsListFast(thread, arrayObj)) { + auto listRes = JSObject::CreateListFromArrayLike(thread, arrayObj); + // 4. ReturnIfAbrupt(argList). + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + JSHandle argList = JSHandle::Cast(listRes); + arguments->MakeArgList(*argList); + } + + // 6. Return Call(func, thisArg, argList). + return JSFunction::Call(thread, func, thisArg, arguments->GetLength(), arguments->GetArgv()); +} + +// ecma 19.2.3.2 Function.prototype.bind (thisArg , ...args) +JSTaggedValue BuiltinsFunction::FunctionPrototypeBind(EcmaRuntimeCallInfo *argv) +{ + ASSERT(argv); + BUILTINS_API_TRACE(argv->GetThread(), Function, PrototypeBind); + JSThread *thread = argv->GetThread(); + [[maybe_unused]] EcmaHandleScope handleScope(thread); + + ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); + // 1. Let Target be the this value. + JSHandle target = GetThis(argv); + // 2. If IsCallable(Target) is false, throw a TypeError exception. + if (!target->IsCallable()) { + THROW_TYPE_ERROR_AND_RETURN(thread, "bind target is not callable", JSTaggedValue::Exception()); + } + + JSHandle thisArg = GetCallArg(argv, 0); + uint32_t argsLength = 0; + if (argv->GetArgsNumber() > 1) { + argsLength = argv->GetArgsNumber() - 1; + } + + // 3. Let args be a new (possibly empty) List consisting of all of the argument + // values provided after thisArg in order. + JSHandle argsArray = factory->NewTaggedArray(argsLength); + for (uint32_t index = 0; index < argsLength; ++index) { + argsArray->Set(thread, index, GetCallArg(argv, index + 1)); + } + // 4. Let F be BoundFunctionCreate(Target, thisArg, args). + JSHandle targetFunction = JSHandle::Cast(target); + JSHandle boundFunction = factory->NewJSBoundFunction(targetFunction, thisArg, argsArray); + // 5. ReturnIfAbrupt(F) + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + + // 6. Let targetHasLength be HasOwnProperty(Target, "length"). + auto globalConst = thread->GlobalConstants(); + JSHandle lengthKey = globalConst->GetHandledLengthString(); + bool targetHasLength = + JSTaggedValue::HasOwnProperty(thread, JSHandle::Cast(targetFunction), lengthKey); + // 7. ReturnIfAbrupt(targetHasLength). + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + + double lengthValue = 0.0; + // 8. If targetHasLength is true, then + if (targetHasLength) { + // a. Let targetLen be Get(Target, "length"). + JSHandle targetLen = JSObject::GetProperty(thread, target, lengthKey).GetValue(); + // b. ReturnIfAbrupt(targetLen). + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + + // c. If Type(targetLen) is not Number, let L be 0. + // d. Else, + // i. Let targetLen be ToInteger(targetLen). + // ii. Let L be the larger of 0 and the result of targetLen minus the number of elements of args. + if (targetLen->IsNumber()) { + // argv include thisArg + lengthValue = + std::max(0.0, JSTaggedValue::ToNumber(thread, targetLen).GetNumber() - static_cast(argsLength)); + } + } + // 9. Else let L be 0. + + // 10. Let status be DefinePropertyOrThrow(F, "length", PropertyDescriptor {[[Value]]: L, + // [[Writable]]: false, [[Enumerable]]: false, [[Configurable]]: true}). + PropertyDescriptor desc(thread, JSHandle(thread, JSTaggedValue(lengthValue)), false, false, true); + [[maybe_unused]] bool status = + JSTaggedValue::DefinePropertyOrThrow(thread, JSHandle(boundFunction), lengthKey, desc); + // 11. Assert: status is not an abrupt completion. + ASSERT_PRINT(status, "DefinePropertyOrThrow failed"); + + // 12. Let targetName be Get(Target, "name"). + JSHandle nameKey = globalConst->GetHandledNameString(); + JSHandle targetName = JSObject::GetProperty(thread, target, nameKey).GetValue(); + // 13. ReturnIfAbrupt(targetName). + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + + JSHandle boundName(factory->NewFromCanBeCompressString("bound")); + // 14. If Type(targetName) is not String, let targetName be the empty string. + // 15. Perform SetFunctionName(F, targetName, "bound"). + if (!targetName->IsString()) { + JSHandle emptyString(factory->GetEmptyString()); + status = JSFunction::SetFunctionName(thread, JSHandle(boundFunction), emptyString, boundName); + } else { + status = JSFunction::SetFunctionName(thread, JSHandle(boundFunction), targetName, boundName); + } + // Assert: status is not an abrupt completion. + ASSERT_PRINT(status, "SetFunctionName failed"); + + // 16. Return F. + return boundFunction.GetTaggedValue(); +} + +// ecma 19.2.3.3 Function.prototype.call (thisArg , ...args) +JSTaggedValue BuiltinsFunction::FunctionPrototypeCall(EcmaRuntimeCallInfo *argv) +{ + ASSERT(argv); + BUILTINS_API_TRACE(argv->GetThread(), Function, PrototypeCall); + JSThread *thread = argv->GetThread(); + [[maybe_unused]] EcmaHandleScope handleScope(thread); + + // 1. If IsCallable(func) is false, throw a TypeError exception. + if (!GetThis(argv)->IsCallable()) { + THROW_TYPE_ERROR_AND_RETURN(thread, "call target is not callable", JSTaggedValue::Exception()); + } + + JSHandle func = GetThis(argv); + JSHandle thisArg = GetCallArg(argv, 0); + uint32_t argsLength = 0; + if (argv->GetArgsNumber() > 1) { + argsLength = argv->GetArgsNumber() - 1; + } + // 2. Let argList be an empty List. + // 3. If this method was called with more than one argument then in left to right order, + // starting with the second argument, append each argument as the last element of argList. + InternalCallParams *argsList = thread->GetInternalCallParams(); + argsList->MakeArgv(argv, 1); + + // 5. Return Call(func, thisArg, argList). + return JSFunction::Call(thread, func, thisArg, argsLength, argsList->GetArgv()); +} + +// ecma 19.2.3.5 Function.prototype.toString () +JSTaggedValue BuiltinsFunction::FunctionPrototypeToString(EcmaRuntimeCallInfo *argv) +{ + BUILTINS_API_TRACE(argv->GetThread(), Function, PrototypeToString); + // not implement due to that runtime can not get JS Source Code now. + JSThread *thread = argv->GetThread(); + [[maybe_unused]] EcmaHandleScope handleScope(thread); + JSHandle thisValue = GetThis(argv); + if (!thisValue->IsCallable()) { + THROW_TYPE_ERROR_AND_RETURN(thread, "function.toString() target is not callable", JSTaggedValue::Exception()); + } + return GetTaggedString(thread, "Not support function.toString() due to Runtime can not obtain Source Code yet."); +} + +// ecma 19.2.3.6 Function.prototype[@@hasInstance] (V) +JSTaggedValue BuiltinsFunction::FunctionPrototypeHasInstance(EcmaRuntimeCallInfo *argv) +{ + BUILTINS_API_TRACE(argv->GetThread(), Function, PrototypeHasInstance); + [[maybe_unused]] EcmaHandleScope handleScope(argv->GetThread()); + // 1. Let F be the this value. + JSHandle thisValue = GetThis(argv); + // 2. Return OrdinaryHasInstance(F, V). + JSHandle arg = GetCallArg(argv, 0); + return JSFunction::OrdinaryHasInstance(argv->GetThread(), thisValue, arg) ? GetTaggedBoolean(true) + : GetTaggedBoolean(false); +} +} // namespace panda::ecmascript::builtins diff --git a/runtime/builtins/builtins_function.h b/runtime/builtins/builtins_function.h new file mode 100644 index 000000000..a4e8486e3 --- /dev/null +++ b/runtime/builtins/builtins_function.h @@ -0,0 +1,47 @@ +/* + * Copyright (c) 2021-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_BUILTINS_BUILTINS_FUNCTION_H +#define ECMASCRIPT_BUILTINS_BUILTINS_FUNCTION_H + +#include "plugins/ecmascript/runtime/js_tagged_value-inl.h" +#include "plugins/ecmascript/runtime/base/builtins_base.h" + +namespace panda::ecmascript::builtins { +class BuiltinsFunction : public ecmascript::base::BuiltinsBase { +public: + // ecma 19.2.1 Function (p1, p2, ... , pn, body) + static JSTaggedValue FunctionConstructor(EcmaRuntimeCallInfo *argv); + + // ecma 19.2.3 The Function prototype object is itself a built-in function object. + static JSTaggedValue FunctionPrototypeInvokeSelf(EcmaRuntimeCallInfo *argv); + + // ecma 19.2.3.1 Function.prototype.apply (thisArg, argArray) + static JSTaggedValue FunctionPrototypeApply(EcmaRuntimeCallInfo *argv); + + // ecma 19.2.3.2 Function.prototype.bind (thisArg , ...args) + static JSTaggedValue FunctionPrototypeBind(EcmaRuntimeCallInfo *argv); + + // ecma 19.2.3.3 Function.prototype.call (thisArg , ...args) + static JSTaggedValue FunctionPrototypeCall(EcmaRuntimeCallInfo *argv); + + // ecma 19.2.3.5 Function.prototype.toString () + static JSTaggedValue FunctionPrototypeToString(EcmaRuntimeCallInfo *argv); + + // ecma 19.2.3.6 Function.prototype[@@hasInstance] (V) + static JSTaggedValue FunctionPrototypeHasInstance(EcmaRuntimeCallInfo *argv); +}; +} // namespace panda::ecmascript::builtins +#endif // ECMASCRIPT_BUILTINS_BUILTINS_FUNCTION_H diff --git a/runtime/builtins/builtins_generator.cpp b/runtime/builtins/builtins_generator.cpp new file mode 100644 index 000000000..f20a2c3e1 --- /dev/null +++ b/runtime/builtins/builtins_generator.cpp @@ -0,0 +1,99 @@ +/* + * Copyright (c) 2021-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 "builtins_generator.h" +#include "plugins/ecmascript/runtime/js_function.h" +#include "plugins/ecmascript/runtime/js_generator_object.h" + +namespace panda::ecmascript::builtins { +// 26.2.1.1 GeneratorFunction(p1, p2, … , pn, body) +JSTaggedValue BuiltinsGenerator::GeneratorFunctionConstructor(EcmaRuntimeCallInfo *argv) +{ + BUILTINS_API_TRACE(argv->GetThread(), Generator, Constructor); + // not support + THROW_TYPE_ERROR_AND_RETURN(argv->GetThread(), "Not support eval. Forbidden using new GeneratorFunction().", + JSTaggedValue::Exception()); +} + +// 26.4.1.2 Generator.prototype.next(value) +JSTaggedValue BuiltinsGenerator::GeneratorPrototypeNext(EcmaRuntimeCallInfo *argv) +{ + BUILTINS_API_TRACE(argv->GetThread(), GeneratorPrototype, Next); + // 1.Let g be the this value. + JSThread *thread = argv->GetThread(); + [[maybe_unused]] EcmaHandleScope handleScope(thread); + JSHandle msg = GetThis(argv); + if (!msg->IsGeneratorObject()) { + THROW_TYPE_ERROR_AND_RETURN(thread, "Not a generator object.", JSTaggedValue::Exception()); + } + JSHandle generator(thread, JSGeneratorObject::Cast(*JSTaggedValue::ToObject(thread, msg))); + JSHandle value = GetCallArg(argv, 0); + + // 2.Return ? GeneratorResume(g, value). + JSHandle result = JSGeneratorObject::GeneratorResume(thread, generator, value.GetTaggedValue()); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + return result.GetTaggedValue(); +} + +// 26.4.1.3 Generator.prototype.return(value) +JSTaggedValue BuiltinsGenerator::GeneratorPrototypeReturn(EcmaRuntimeCallInfo *argv) +{ + BUILTINS_API_TRACE(argv->GetThread(), GeneratorPrototype, Return); + // 1.Let g be the this value. + JSThread *thread = argv->GetThread(); + [[maybe_unused]] EcmaHandleScope handleScope(thread); + JSHandle msg = GetThis(argv); + if (!msg->IsGeneratorObject()) { + THROW_TYPE_ERROR_AND_RETURN(thread, "Not a generator object.", JSTaggedValue::Exception()); + } + JSHandle generator(thread, JSGeneratorObject::Cast(*JSTaggedValue::ToObject(thread, msg))); + + // 2.Let C be Completion { [[Type]]: return, [[Value]]: value, [[Target]]: empty }. + JSHandle value = GetCallArg(argv, 0); + ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); + JSHandle completionRecord = + factory->NewCompletionRecord(CompletionRecord::RETURN, value); + + // 3.Return ? GeneratorResumeAbrupt(g, C). + JSHandle result = JSGeneratorObject::GeneratorResumeAbrupt(thread, generator, completionRecord); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + return result.GetTaggedValue(); +} + +// 26.4.1.4 Generator.prototype.throw(exception) +JSTaggedValue BuiltinsGenerator::GeneratorPrototypeThrow(EcmaRuntimeCallInfo *argv) +{ + BUILTINS_API_TRACE(argv->GetThread(), GeneratorPrototype, Throw); + // 1.Let g be the this value. + JSThread *thread = argv->GetThread(); + [[maybe_unused]] EcmaHandleScope handleScope(thread); + JSHandle msg = GetThis(argv); + if (!msg->IsGeneratorObject()) { + THROW_TYPE_ERROR_AND_RETURN(thread, "Not a generator object.", JSTaggedValue::Exception()); + } + JSHandle generator(thread, JSGeneratorObject::Cast(*JSTaggedValue::ToObject(thread, msg))); + + // 2.Let C be ThrowCompletion(exception). + JSHandle exception = GetCallArg(argv, 0); + ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); + JSHandle completionRecord = + factory->NewCompletionRecord(CompletionRecord::THROW, exception); + + // 3.Return ? GeneratorResumeAbrupt(g, C). + JSHandle result = JSGeneratorObject::GeneratorResumeAbrupt(thread, generator, completionRecord); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + return result.GetTaggedValue(); +} +} // namespace panda::ecmascript::builtins diff --git a/runtime/builtins/builtins_generator.h b/runtime/builtins/builtins_generator.h new file mode 100644 index 000000000..ddb9ad44a --- /dev/null +++ b/runtime/builtins/builtins_generator.h @@ -0,0 +1,41 @@ +/* + * Copyright (c) 2021-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_BUILTINS_BUILTINS_GENERATOR_H +#define ECMASCRIPT_BUILTINS_BUILTINS_GENERATOR_H + +#include "plugins/ecmascript/runtime/base/builtins_base.h" +#include "plugins/ecmascript/runtime/js_tagged_value-inl.h" + +namespace panda::ecmascript::builtins { +class BuiltinsGenerator : public ecmascript::base::BuiltinsBase { +public: + // 26.2.1.1 GeneratorFunction(p1, p2, … , pn, body) + static JSTaggedValue GeneratorFunctionConstructor(EcmaRuntimeCallInfo *argv); + + // 26.4.1.2 Generator.prototype.next(value) + static JSTaggedValue GeneratorPrototypeNext(EcmaRuntimeCallInfo *argv); + + // 26.4.1.3 Generator.prototype.return(value) + static JSTaggedValue GeneratorPrototypeReturn(EcmaRuntimeCallInfo *argv); + + // 26.4.1.4 Generator.prototype.throw(exception) + static JSTaggedValue GeneratorPrototypeThrow(EcmaRuntimeCallInfo *argv); + + // 26.4.1.5 Generator.prototype[@@toStringTag] +}; +} // namespace panda::ecmascript::builtins + +#endif // ECMASCRIPT_BUILTINS_BUILTINS_GENERATOR_H diff --git a/runtime/builtins/builtins_global.cpp b/runtime/builtins/builtins_global.cpp new file mode 100644 index 000000000..7fec05da9 --- /dev/null +++ b/runtime/builtins/builtins_global.cpp @@ -0,0 +1,559 @@ +/* + * Copyright (c) 2021-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 "plugins/ecmascript/runtime/builtins/builtins_global.h" +#include +#include +#include +#include +#include "plugins/ecmascript/runtime/base/number_helper.h" +#include "plugins/ecmascript/runtime/base/string_helper.h" +#include "plugins/ecmascript/runtime/ecma_macros.h" +#include "plugins/ecmascript/runtime/internal_call_params.h" +#include "plugins/ecmascript/runtime/interpreter/slow_runtime_helper.h" +#include "plugins/ecmascript/runtime/js_invoker.h" +#include "plugins/ecmascript/runtime/mem/c_containers.h" +#include "plugins/ecmascript/runtime/tagged_array-inl.h" + +namespace panda::ecmascript::builtins { +using NumberHelper = ecmascript::base::NumberHelper; +using StringHelper = ecmascript::base::StringHelper; + +// 18.2.1 +JSTaggedValue BuiltinsGlobal::NotSupportEval(EcmaRuntimeCallInfo *msg) +{ + JSThread *thread = msg->GetThread(); + [[maybe_unused]] EcmaHandleScope handleScope(thread); + if (msg->GetArgsNumber() == 0) { + return JSTaggedValue(JSTaggedValue::VALUE_UNDEFINED); + } + THROW_TYPE_ERROR_AND_RETURN(thread, "not support eval()", JSTaggedValue::Exception()); +} + +// 18.2.2 +JSTaggedValue BuiltinsGlobal::IsFinite(EcmaRuntimeCallInfo *msg) +{ + ASSERT(msg); + JSThread *thread = msg->GetThread(); + BUILTINS_API_TRACE(thread, Global, IsFinite); + [[maybe_unused]] EcmaHandleScope handleScope(thread); + JSHandle numberInput = GetCallArg(msg, 0); + // 1. Let num be ToNumber(number). + JSTaggedNumber number = JSTaggedValue::ToNumber(thread, numberInput); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + // 3. If num is NaN, +Infinite, or -Infinite, return false. + // 4. Otherwise, return true. + if (std::isfinite(number.GetNumber())) { + return GetTaggedBoolean(true); + } + return GetTaggedBoolean(false); +} + +// 18.2.3 +JSTaggedValue BuiltinsGlobal::IsNaN(EcmaRuntimeCallInfo *msg) +{ + ASSERT(msg); + JSThread *thread = msg->GetThread(); + BUILTINS_API_TRACE(thread, Global, IsNaN); + [[maybe_unused]] EcmaHandleScope handleScope(thread); + JSHandle numberInput = GetCallArg(msg, 0); + // 1. Let num be ToNumber(number). + JSTaggedNumber number = JSTaggedValue::ToNumber(thread, numberInput); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + + // 3. If num is NaN, return true. + if (std::isnan(number.GetNumber())) { + return GetTaggedBoolean(true); + } + // 4. Otherwise, return false. + return GetTaggedBoolean(false); +} + +bool BuiltinsGlobal::IsUnescapedURI(uint16_t ch) +{ + if ((ch >= 'a' && ch <= 'z') || (ch >= 'A' && ch <= 'Z') || (ch >= '0' && ch <= '9')) { + return true; + } + return IsInMarkURISet(ch); +} + +bool BuiltinsGlobal::IsInUnescapedURISet(uint16_t ch) +{ + if (ch == '#') { + return true; + } + return IsUnescapedURI(ch) || IsReservedURI(ch); +} + +bool BuiltinsGlobal::IsInReservedURISet(uint16_t ch) +{ + if (ch == '#') { + return true; + } + return IsReservedURI(ch); +} + +bool BuiltinsGlobal::IsReservedURI(uint16_t ch) +{ + std::u16string str(u";/?:@&=+$,"); + std::u16string::size_type index = str.find(ch); + return (index != std::u16string::npos); +} + +bool BuiltinsGlobal::IsInMarkURISet(uint16_t ch) +{ + std::u16string str(u"-_.!~*'()"); + std::u16string::size_type index = str.find(ch); + return (index != std::u16string::npos); +} + +bool BuiltinsGlobal::IsHexDigits(uint16_t ch) +{ + return ('0' <= ch && ch <= '9') || ('A' <= ch && ch <= 'F') || ('a' <= ch && ch <= 'f'); +} + +// 18.2.6 +JSTaggedValue BuiltinsGlobal::DecodeURI(EcmaRuntimeCallInfo *msg) +{ + ASSERT(msg); + JSThread *thread = msg->GetThread(); + BUILTINS_API_TRACE(thread, Global, DecodeURI); + [[maybe_unused]] EcmaHandleScope handleScope(thread); + // 1. Let uriString be ToString(encodedURI). + // 2. ReturnIfAbrupt(uriString). + [[maybe_unused]] JSHandle uriString = JSTaggedValue::ToString(thread, GetCallArg(msg, 0)); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + + // 3. Let reservedURISet be a String containing one instance of each code unit valid in uriReserved plus "#". + // 4. Return Decode(uriString, reservedURISet). + return Decode(thread, uriString, IsInReservedURISet); +} + +JSTaggedValue BuiltinsGlobal::EncodeURI(EcmaRuntimeCallInfo *msg) +{ + ASSERT(msg); + JSThread *thread = msg->GetThread(); + BUILTINS_API_TRACE(thread, Global, EncodeURI); + [[maybe_unused]] EcmaHandleScope handleScope(thread); + // 1. Let uriString be ToString(uri). + // 2. ReturnIfAbrupt(uriString). + [[maybe_unused]] JSHandle uriString = JSTaggedValue::ToString(thread, GetCallArg(msg, 0)); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + + // 3. Let unescapedURISet be a String containing one instance of + // each code unit valid in uriReserved and uriUnescaped plus "#". + // 4. Return Encode(uriString, unescapedURISet). + return Encode(thread, uriString, IsInUnescapedURISet); +} + +JSTaggedValue BuiltinsGlobal::DecodeURIComponent(EcmaRuntimeCallInfo *msg) +{ + ASSERT(msg); + JSThread *thread = msg->GetThread(); + BUILTINS_API_TRACE(thread, Global, DecodeURIComponent); + [[maybe_unused]] EcmaHandleScope handleScope(thread); + // 1. Let componentString be ToString(encodedURIComponent). + // 2. ReturnIfAbrupt(componentString). + [[maybe_unused]] JSHandle componentString = JSTaggedValue::ToString(thread, GetCallArg(msg, 0)); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + + // 3. Let reservedURIComponentSet be the empty String. + // 4. Return Decode(componentString, reservedURIComponentSet). + return Decode(thread, componentString, []([[maybe_unused]] uint16_t unused) { return false; }); +} + +JSTaggedValue BuiltinsGlobal::EncodeURIComponent(EcmaRuntimeCallInfo *msg) +{ + ASSERT(msg); + JSThread *thread = msg->GetThread(); + BUILTINS_API_TRACE(thread, Global, EncodeURIComponent); + [[maybe_unused]] EcmaHandleScope handleScope(thread); + // 1. Let componentString be ToString(uriComponent). + // 2. ReturnIfAbrupt(componentString). + [[maybe_unused]] JSHandle componentString = JSTaggedValue::ToString(thread, GetCallArg(msg, 0)); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + + // 3. Let unescapedURIComponentSet be a String containing one instance of each code unit valid in uriUnescaped. + // 4. Return Encode(componentString, unescapedURIComponentSet). + return Encode(thread, componentString, IsUnescapedURI); +} + +// Runtime Semantics +JSTaggedValue BuiltinsGlobal::Encode(JSThread *thread, const JSHandle &str, judgURIFunc IsInURISet) +{ + // 1. Let strLen be the number of code units in string. + uint32_t strLen = str->GetLength(); + // 2. Let R be the empty String. + ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); + std::u16string resStr; + + // 3. Let k be 0. + // 4. Repeat + uint32_t k = 0; + while (true) { + // a. If k equals strLen, return R. + if (k == strLen) { + auto *uint16tData = reinterpret_cast(resStr.data()); + int32_t resSize = resStr.size(); + return factory->NewFromUtf16Literal(uint16tData, resSize).GetTaggedValue(); + } + + // b. Let C be the code unit at index k within string. + // c. If C is in unescapedSet, then + // i. Let S be a String containing only the code unit C. + // ii. Let R be a new String value computed by concatenating the previous value of R and S. + // d. Else C is not in unescapedSet, + uint16_t cc = str->At(k); + if (IsInURISet(cc)) { + std::u16string sStr = StringHelper::Utf16ToU16String(&cc, 1); + resStr.append(sStr); + } else { + // i. If the code unit value of C is not less than 0xDC00 and not greater than 0xDFFF, + // throw a URIError exception. + if (cc >= ecmascript::base::utf_helper::DECODE_TRAIL_LOW && + cc <= ecmascript::base::utf_helper::DECODE_TRAIL_HIGH) { + THROW_URI_ERROR_AND_RETURN(thread, "EncodeURI: The format of the URI to be parsed is incorrect", + JSTaggedValue::Exception()); + } + + // ii. If the code unit value of C is less than 0xD800 or greater than 0xDBFF, then + // 1. Let V be the code unit value of C. + // iii. Else, + // 1. Increase k by 1. + // 2. If k equals strLen, throw a URIError exception. + // 3. Let kChar be the code unit value of the code unit at index k within string. + // 4. If kChar is less than 0xDC00 or greater than 0xDFFF, throw a URIError exception. + // 5. Let V be UTF16Decode(C, kChar). + uint32_t vv; + if (cc < ecmascript::base::utf_helper::DECODE_LEAD_LOW || + cc > ecmascript::base::utf_helper::DECODE_LEAD_HIGH) { + vv = cc; + } else { + k++; + if (k == strLen) { + THROW_URI_ERROR_AND_RETURN(thread, "k is invalid", JSTaggedValue::Exception()); + } + uint16_t kc = str->At(k); + if (kc < ecmascript::base::utf_helper::DECODE_TRAIL_LOW || + kc > ecmascript::base::utf_helper::DECODE_TRAIL_HIGH) { + THROW_URI_ERROR_AND_RETURN(thread, "EncodeURI: The format of the URI to be parsed is incorrect", + JSTaggedValue::Exception()); + } + vv = ecmascript::base::utf_helper::UTF16Decode(cc, kc); + } + + // iv. Let Octets be the array of octets resulting by applying the UTF-8 transformation to V, + // and let L be the array size. + // v. Let j be 0. + // vi. Repeat, while j < L + // 1. Let jOctet be the value at index j within Octets. + // 2. Let S be a String containing three code units "%XY" where XY are two uppercase hexadecimal + // digits encoding the value of jOctet. + // 3. Let R be a new String value computed by concatenating the previous value of R and S. + // 4. Increase j by 1. + std::string oct = StringHelper::Utf32ToString(vv); + std::string hexStr("0123456789ABCDEF"); + + uint32_t length = oct.length(); + std::stringstream tmpStr; + for (uint32_t j = 0; j < length; j++) { + uint8_t joct = oct.at(j); + tmpStr << '%' << hexStr.at((joct >> 4U) & BIT_MASK) // NOLINT + << hexStr.at(joct & BIT_MASK); // 4: means shift right by 4 digits + } + resStr.append(StringHelper::StringToU16string(tmpStr.str())); + } + + // e. Increase k by 1. + k++; + } +} + +uint8_t BuiltinsGlobal::GetValueFromTwoHex(uint16_t front, uint16_t behind) +{ + ASSERT(IsHexDigits(front) && IsHexDigits(behind)); + std::u16string hexString(u"0123456789ABCDEF"); + + size_t idxf = StringHelper::FindFromU16ToUpper(hexString, &front); + size_t idxb = StringHelper::FindFromU16ToUpper(hexString, &behind); + uint8_t res = ((idxf << 4U) | idxb) & BIT_MASK_FF; // NOLINT 4: means shift left by 4 digits + return res; +} + +// Runtime Semantics +JSTaggedValue BuiltinsGlobal::Decode(JSThread *thread, const JSHandle &str, judgURIFunc IsInURISet) +{ + // 1. Let strLen be the number of code units in string. + [[maybe_unused]] uint32_t strLen = str->GetLength(); + // 2. Let R be the empty String. + ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); + std::u16string resStr; + + // 3. Let k be 0. + // 4. Repeat + uint32_t k = 0; + while (true) { + // a. If k equals strLen, return R. + if (k == strLen) { + auto *uint16tData = reinterpret_cast(resStr.data()); + int32_t resSize = resStr.size(); + return factory->NewFromUtf16Literal(uint16tData, resSize).GetTaggedValue(); + } + + // b. Let C be the code unit at index k within string. + // c. If C is not "%", then + // i. Let S be the String containing only the code unit C. + // d. Else C is "%", + // i. Let start be k. + // iv. Let B be the 8-bit value represented by the two hexadecimal digits at index (k + 1) and (k + 2). + // v. Increment k by 2. + // vi. If the most significant bit in B is 0, then + // 1. Let C be the code unit with code unit value B. + // 2. If C is not in reservedSet, then + // a. Let S be the String containing only the code unit C. + // 3. Else C is in reservedSet, + // a. Let S be the substring of string from index start to index k inclusive. + uint16_t cc = str->At(k); + std::u16string sStr; + if (cc != '%') { + if (cc == 0 && strLen == 1) { + JSHandle tmpEcmaString = factory->NewFromUtf16Literal(&cc, 1); + return tmpEcmaString.GetTaggedValue(); + } + sStr = StringHelper::Utf16ToU16String(&cc, 1); + } else { + [[maybe_unused]] uint32_t start = k; + + // ii. If k + 2 is greater than or equal to strLen, throw a URIError exception. + // iii. If the code units at index (k+1) and (k + 2) within string do not represent hexadecimal digits, + // throw a URIError exception. + if ((k + 2) >= strLen) { // 2: means plus 2 + THROW_URI_ERROR_AND_RETURN(thread, "DecodeURI: The format of the URI to be parsed is incorrect", + JSTaggedValue::Exception()); + } + if (!(IsHexDigits(str->At(k + 1)) && IsHexDigits(str->At(k + 2)))) { // 2: means plus 2 + THROW_URI_ERROR_AND_RETURN(thread, "DecodeURI: The format of the URI to be parsed is incorrect", + JSTaggedValue::Exception()); + } + + uint16_t frontChar = str->At(k + 1); + uint16_t behindChar = str->At(k + 2); // 2: means plus 2 + uint8_t bb = GetValueFromTwoHex(frontChar, behindChar); + k += 2; // 2: means plus 2 + if ((bb & BIT_MASK_ONE) == 0) { + if (!IsInURISet(bb)) { + sStr = StringHelper::Utf8ToU16String(&bb, 1); + if (bb == 0) { + return factory->NewFromUtf16Literal(reinterpret_cast(sStr.data()), 1) + .GetTaggedValue(); + } + } else { + sStr = StringHelper::StringToU16string(StringHelper::SubString(str, start, k - start + 1)); + } + } else { + // vii. Else the most significant bit in B is 1, + // 1. Let n be the smallest nonnegative integer such that (B << n) & 0x80 is equal to 0. + // 3. Let Octets be an array of 8-bit integers of size n. + // 4. Put B into Octets at index 0. + // 6. Let j be 1. + // 7. Repeat, while j < n + // a. Increment k by 1. + // d. Let B be the 8-bit value represented by the two hexadecimal digits at + // index (k + 1) and (k + 2). + // f. Increment k by 2. + // g. Put B into Octets at index j. + // h. Increment j by 1. + // 9. If V < 0x10000, then + // a. Let C be the code unit V. + // b. If C is not in reservedSet, then + // i. Let S be the String containing only the code unit C. + // c. Else C is in reservedSet, + // i. Let S be the substring of string from index start to index k inclusive. + // 10. Else V ≥ 0x10000, + // a. Let L be (((V – 0x10000) & 0x3FF) + 0xDC00). + // b. Let H be ((((V – 0x10000) >> 10) & 0x3FF) + 0xD800). + // c. Let S be the String containing the two code units H and L. + uint32_t n = 0; + // NOLINTNEXTLINE(hicpp-signed-bitwise) + while ((((bb << n) & BIT_MASK_ONE) != 0)) { + n++; + if (n > 4) { // 4 : 4 means less than 4 + break; + } + } + // 2. If n equals 1 or n is greater than 4, throw a URIError exception. + if ((n == 1) || (n > 4)) { // 4: means greater than 4 + THROW_URI_ERROR_AND_RETURN(thread, "DecodeURI: The format of the URI to be parsed is incorrect", + JSTaggedValue::Exception()); + } + + std::vector oct = {bb}; + + // 5. If k + (3 × (n – 1)) is greater than or equal to strLen, throw a URIError exception. + if (k + (3 * (n - 1)) >= strLen) { // 3: means multiply by 3 + THROW_URI_ERROR_AND_RETURN(thread, "DecodeURI: The format of the URI to be parsed is incorrect", + JSTaggedValue::Exception()); + } + uint32_t j = 1; + while (j < n) { + k++; + uint16_t codeUnit = str->At(k); + // b. If the code unit at index k within string is not "%", throw a URIError exception. + // c. If the code units at index (k +1) and (k + 2) within string do not represent hexadecimal + // digits, throw a URIError exception. + if (!(codeUnit == '%')) { + THROW_URI_ERROR_AND_RETURN(thread, "DecodeURI: The format of the URI to be parsed is incorrect", + JSTaggedValue::Exception()); + } + if (!(IsHexDigits(str->At(k + 1)) && IsHexDigits(str->At(k + 2)))) { // 2: means plus 2 + THROW_URI_ERROR_AND_RETURN(thread, "DecodeURI: The format of the URI to be parsed is incorrect", + JSTaggedValue::Exception()); + } + + uint16_t frontChart = str->At(k + 1); + uint16_t behindChart = str->At(k + 2); // 2: means plus 2 + bb = GetValueFromTwoHex(frontChart, behindChart); + // e. If the two most significant bits in B are not 10, throw a URIError exception. + if (!((bb & BIT_MASK_TWO) == BIT_MASK_ONE)) { + THROW_URI_ERROR_AND_RETURN(thread, "DecodeURI: The format of the URI to be parsed is incorrect", + JSTaggedValue::Exception()); + } + + k += 2; // 2: means plus 2 + oct.push_back(bb); + j++; + } + + // 8. Let V be the value obtained by applying the UTF-8 transformation to Octets, that is, + // from an array of octets into a 21-bit value. If Octets does not contain a valid UTF-8 encoding of + // a Unicode code point throw a URIError exception. + if (!ecmascript::base::utf_helper::IsValidUTF8(oct)) { + THROW_URI_ERROR_AND_RETURN(thread, "DecodeURI: The format of the URI to be parsed is incorrect", + JSTaggedValue::Exception()); + } + uint32_t vv = StringHelper::Utf8ToU32String(oct); + if (vv < ecmascript::base::utf_helper::DECODE_SECOND_FACTOR) { + if (!IsInURISet(vv)) { + sStr = StringHelper::Utf16ToU16String(reinterpret_cast(&vv), 1); + } else { + sStr = + StringHelper::StringToU16string(StringHelper::SubString(str, start, k - start + 1)); + } + } else { + uint16_t lv = (((vv - ecmascript::base::utf_helper::DECODE_SECOND_FACTOR) & BIT16_MASK) + + ecmascript::base::utf_helper::DECODE_TRAIL_LOW); + uint16_t hv = + ((((vv - ecmascript::base::utf_helper::DECODE_SECOND_FACTOR) >> 10U) & BIT16_MASK) + // NOLINT + ecmascript::base::utf_helper::DECODE_LEAD_LOW); // 10: means shift left by 10 digits + sStr = StringHelper::Append(StringHelper::Utf16ToU16String(&hv, 1), + StringHelper::Utf16ToU16String(&lv, 1)); + } + } + } + // e. Let R be a new String value computed by concatenating the previous value of R and S. + // f. Increase k by 1. + resStr.append(sStr); + k++; + } +} + +void BuiltinsGlobal::PrintString([[maybe_unused]] JSThread *thread, EcmaString *string) +{ + if (string == nullptr) { + return; + } + + CString buffer = ConvertToString(string); + std::cout << buffer; +} + +JSTaggedValue BuiltinsGlobal::PrintEntrypoint(EcmaRuntimeCallInfo *msg) +{ + if (msg == nullptr) { + return JSTaggedValue::Undefined(); + } + JSThread *thread = msg->GetThread(); + [[maybe_unused]] EcmaHandleScope handleScope(thread); + BUILTINS_API_TRACE(thread, Global, PrintEntryPoint); + + uint32_t numArgs = msg->GetArgsNumber(); + for (uint32_t i = 0; i < numArgs; i++) { + JSHandle stringContent = JSTaggedValue::ToString(thread, GetCallArg(msg, i)); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + PrintString(thread, *stringContent); + + if (i != numArgs - 1) { + std::cout << " "; + } + } + std::cout << std::endl; + return JSTaggedValue::Undefined(); +} + +JSTaggedValue BuiltinsGlobal::CallJsBoundFunction(EcmaRuntimeCallInfo *msg) +{ + JSThread *thread = msg->GetThread(); + BUILTINS_API_TRACE(thread, Global, CallJsBoundFunction); + [[maybe_unused]] EcmaHandleScope handleScope(thread); + // msg contains jsfunc, this, arg1,... + + JSHandle boundFunc(GetConstructor(msg)); + JSHandle thisObj(thread, boundFunc->GetBoundThis()); + + InternalCallParams *arguments = thread->GetInternalCallParams(); + arguments->MakeArgv(msg, 0); + return SlowRuntimeHelper::CallBoundFunction(thread, boundFunc, thisObj); +} + +JSTaggedValue BuiltinsGlobal::CallJsProxy(EcmaRuntimeCallInfo *msg) +{ + JSThread *thread = msg->GetThread(); + BUILTINS_API_TRACE(thread, Global, CallJsProxy); + [[maybe_unused]] EcmaHandleScope handleScope(thread); + // msg contains js_proxy, this, arg1,... + JSHandle proxy(GetConstructor(msg)); + if (!proxy->IsCallable()) { + THROW_TYPE_ERROR_AND_RETURN(thread, "Proxy target is not callable", JSTaggedValue::Undefined()); + } + + // Calling proxy directly should transfer 'undefined' as this + JSHandle thisObj(GetThis(msg)); + + JSHandle argsList = GetArgsArray(msg); + InternalCallParams *arguments = thread->GetInternalCallParams(); + arguments->MakeArgList(*argsList); + return JSProxy::CallInternal(thread, proxy, thisObj, argsList->GetLength(), arguments->GetArgv()); +} + +#if ECMASCRIPT_ENABLE_RUNTIME_STAT +JSTaggedValue BuiltinsGlobal::StartRuntimeStat(EcmaRuntimeCallInfo *msg) +{ + JSThread *thread = msg->GetThread(); + [[maybe_unused]] EcmaHandleScope handleScope(thread); + // start vm runtime stat statistic + thread->GetEcmaVM()->SetRuntimeStatEnable(true); + return JSTaggedValue::Undefined(); +} + +JSTaggedValue BuiltinsGlobal::StopRuntimeStat(EcmaRuntimeCallInfo *msg) +{ + JSThread *thread = msg->GetThread(); + [[maybe_unused]] EcmaHandleScope handleScope(thread); + // start vm runtime stat statistic + thread->GetEcmaVM()->SetRuntimeStatEnable(false); + return JSTaggedValue::Undefined(); +} +#endif +} // namespace panda::ecmascript::builtins diff --git a/runtime/builtins/builtins_global.h b/runtime/builtins/builtins_global.h new file mode 100644 index 000000000..b0f713ca0 --- /dev/null +++ b/runtime/builtins/builtins_global.h @@ -0,0 +1,67 @@ +/* + * Copyright (c) 2021-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_BUILTINS_BUILTINS_GLOBAL_H +#define ECMASCRIPT_BUILTINS_BUILTINS_GLOBAL_H + +#include "plugins/ecmascript/runtime/base/builtins_base.h" +#include "plugins/ecmascript/runtime/js_thread.h" + +namespace panda::ecmascript::builtins { +static constexpr uint8_t BIT_MASK = 0x0F; +static constexpr uint8_t BIT_MASK_FF = 0xFF; +static constexpr uint16_t BIT16_MASK = 0x3FF; +static constexpr uint8_t BIT_MASK_ONE = 0x80; +static constexpr uint8_t BIT_MASK_TWO = 0xC0; +using judgURIFunc = bool (*)(uint16_t); + +class BuiltinsGlobal : public ecmascript::base::BuiltinsBase { +public: + // 18.2.1 + static JSTaggedValue NotSupportEval(EcmaRuntimeCallInfo *msg); + // 18.2.2 + static JSTaggedValue IsFinite(EcmaRuntimeCallInfo *msg); + // 18.2.3 + static JSTaggedValue IsNaN(EcmaRuntimeCallInfo *msg); + // 18.2.6 + static JSTaggedValue DecodeURI(EcmaRuntimeCallInfo *msg); + static JSTaggedValue EncodeURI(EcmaRuntimeCallInfo *msg); + static JSTaggedValue DecodeURIComponent(EcmaRuntimeCallInfo *msg); + static JSTaggedValue EncodeURIComponent(EcmaRuntimeCallInfo *msg); + + static JSTaggedValue PrintEntrypoint(EcmaRuntimeCallInfo *msg); + static JSTaggedValue CallJsBoundFunction(EcmaRuntimeCallInfo *msg); + static JSTaggedValue CallJsProxy(EcmaRuntimeCallInfo *msg); +#if ECMASCRIPT_ENABLE_RUNTIME_STAT + static JSTaggedValue StartRuntimeStat(EcmaRuntimeCallInfo *msg); + static JSTaggedValue StopRuntimeStat(EcmaRuntimeCallInfo *msg); +#endif + +private: + static void PrintString(JSThread *thread, EcmaString *string); + static void PrintValue(int64_t value, int64_t tag); + static JSTaggedValue Encode(JSThread *thread, const JSHandle &str, judgURIFunc IsInURISet); + static JSTaggedValue Decode(JSThread *thread, const JSHandle &str, judgURIFunc IsInURISet); + static bool IsUnescapedURI(uint16_t ch); + static bool IsInUnescapedURISet(uint16_t ch); + static bool IsInReservedURISet(uint16_t ch); + static bool IsReservedURI(uint16_t ch); + static bool IsInMarkURISet(uint16_t ch); + static bool IsHexDigits(uint16_t ch); + static uint8_t GetValueFromTwoHex(uint16_t front, uint16_t behind); +}; +} // namespace panda::ecmascript::builtins + +#endif // ECMASCRIPT_BUILTINS_BUILTINS_ERROR_H diff --git a/runtime/builtins/builtins_intl.cpp b/runtime/builtins/builtins_intl.cpp new file mode 100644 index 000000000..b9295f43e --- /dev/null +++ b/runtime/builtins/builtins_intl.cpp @@ -0,0 +1,38 @@ +/* + * Copyright (c) 2021-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 "plugins/ecmascript/runtime/builtins/builtins_intl.h" + +#include "plugins/ecmascript/runtime/js_array.h" +#include "plugins/ecmascript/runtime/js_locale.h" +#include "plugins/ecmascript/runtime/js_tagged_value.h" + +namespace panda::ecmascript::builtins { +// 8.2.1 Intl.getCanonicalLocales ( locales ) +JSTaggedValue BuiltinsIntl::GetCanonicalLocales(EcmaRuntimeCallInfo *argv) +{ + JSThread *thread = argv->GetThread(); + [[maybe_unused]] EcmaHandleScope scope(thread); + + // 1.Let ll be ? CanonicalizeLocaleList(locales). + JSHandle locales = GetCallArg(argv, 0); + JSHandle elements = JSLocale::CanonicalizeLocaleList(thread, locales); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + + // 2.Return CreateArrayFromList(ll). + JSHandle result = JSArray::CreateArrayFromList(thread, elements); + return result.GetTaggedValue(); +} +} // namespace panda::ecmascript::builtins diff --git a/runtime/builtins/builtins_intl.h b/runtime/builtins/builtins_intl.h new file mode 100644 index 000000000..12ce000f5 --- /dev/null +++ b/runtime/builtins/builtins_intl.h @@ -0,0 +1,28 @@ +/* + * Copyright (c) 2021-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_BUILTINS_BUILTINS_INTL_H +#define ECMASCRIPT_BUILTINS_BUILTINS_INTL_H + +#include "plugins/ecmascript/runtime/base/builtins_base.h" + +namespace panda::ecmascript::builtins { +class BuiltinsIntl : public ecmascript::base::BuiltinsBase { +public: + // 8.2.1 Intl.getCanonicalLocales ( locales ) + static JSTaggedValue GetCanonicalLocales(EcmaRuntimeCallInfo *argv); +}; +} // namespace panda::ecmascript::builtins +#endif // ECMASCRIPT_BUILTINS_BUILTINS_INTL_H diff --git a/runtime/builtins/builtins_iterator.cpp b/runtime/builtins/builtins_iterator.cpp new file mode 100644 index 000000000..64ca13a84 --- /dev/null +++ b/runtime/builtins/builtins_iterator.cpp @@ -0,0 +1,52 @@ +/* + * Copyright (c) 2021-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 "builtins_iterator.h" +#include "plugins/ecmascript/runtime/base/builtins_base.h" +#include "plugins/ecmascript/runtime/ecma_vm.h" +#include "plugins/ecmascript/runtime/global_env.h" +#include "plugins/ecmascript/runtime/js_iterator.h" + +namespace panda::ecmascript::builtins { +JSTaggedValue BuiltinsIterator::IteratorConstructor([[maybe_unused]] EcmaRuntimeCallInfo *argv) +{ + return JSTaggedValue::Undefined(); +} + +JSTaggedValue BuiltinsIterator::Next([[maybe_unused]] EcmaRuntimeCallInfo *argv) +{ + return JSTaggedValue::Undefined(); +} + +JSTaggedValue BuiltinsIterator::Throw([[maybe_unused]] EcmaRuntimeCallInfo *argv) +{ + return JSTaggedValue::Undefined(); +} + +JSTaggedValue BuiltinsIterator::Return(EcmaRuntimeCallInfo *argv) +{ + BUILTINS_API_TRACE(argv->GetThread(), Iterator, Return); + JSThread *thread = argv->GetThread(); + [[maybe_unused]] EcmaHandleScope handleScope(thread); + JSHandle value = GetCallArg(argv, 0); + JSHandle iterResult = JSIterator::CreateIterResultObject(thread, value, true); + return iterResult.GetTaggedValue(); +} + +JSTaggedValue BuiltinsIterator::GetIteratorObj(EcmaRuntimeCallInfo *argv) +{ + return ecmascript::base::BuiltinsBase::GetThis(argv).GetTaggedValue(); +} +} // namespace panda::ecmascript::builtins diff --git a/runtime/builtins/builtins_iterator.h b/runtime/builtins/builtins_iterator.h new file mode 100644 index 000000000..1d705b375 --- /dev/null +++ b/runtime/builtins/builtins_iterator.h @@ -0,0 +1,36 @@ +/* + * Copyright (c) 2021-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_BUILTINS_BUILTINS_ITERATOR_H +#define ECMASCRIPT_BUILTINS_BUILTINS_ITERATOR_H + +#include "plugins/ecmascript/runtime/base/builtins_base.h" +#include "plugins/ecmascript/runtime/ecma_runtime_call_info.h" + +namespace panda::ecmascript::builtins { +class BuiltinsIterator : public ecmascript::base::BuiltinsBase { +public: + static JSTaggedValue IteratorConstructor(EcmaRuntimeCallInfo *argv); + + static JSTaggedValue Next(EcmaRuntimeCallInfo *argv); + + static JSTaggedValue Throw(EcmaRuntimeCallInfo *argv); + + static JSTaggedValue Return(EcmaRuntimeCallInfo *argv); + + static JSTaggedValue GetIteratorObj(EcmaRuntimeCallInfo *argv); +}; +} // namespace panda::ecmascript::builtins +#endif // ECMASCRIPT_BUILTINS_BUILTINS_ITERATOR_H diff --git a/runtime/builtins/builtins_json.cpp b/runtime/builtins/builtins_json.cpp new file mode 100644 index 000000000..25f8be899 --- /dev/null +++ b/runtime/builtins/builtins_json.cpp @@ -0,0 +1,106 @@ +/* + * Copyright (c) 2021-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 "plugins/ecmascript/runtime/builtins/builtins_json.h" +#include "plugins/ecmascript/runtime/base/json_parser.h" +#include "plugins/ecmascript/runtime/base/json_stringifier.h" +#include "plugins/ecmascript/runtime/base/number_helper.h" +#include "plugins/ecmascript/runtime/ecma_vm.h" +#include "plugins/ecmascript/runtime/global_env.h" +#include "plugins/ecmascript/runtime/js_handle.h" +#include "plugins/ecmascript/runtime/js_primitive_ref.h" +#include "plugins/ecmascript/runtime/js_tagged_value-inl.h" +#include "plugins/ecmascript/runtime/object_factory.h" + +namespace panda::ecmascript::builtins { +// 24.5.1 +JSTaggedValue BuiltinsJson::Parse(EcmaRuntimeCallInfo *argv) +{ + BUILTINS_API_TRACE(argv->GetThread(), Json, Parse); + ASSERT(argv); + JSThread *thread = argv->GetThread(); + [[maybe_unused]] EcmaHandleScope handleScope(thread); + + ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); + + uint32_t argc = argv->GetArgsNumber(); + if (argc == 0) { + JSHandle syntaxError = factory->GetJSError(ecmascript::base::ErrorType::SYNTAX_ERROR, "arg is empty"); + THROW_NEW_ERROR_AND_RETURN_VALUE(thread, syntaxError.GetTaggedValue(), JSTaggedValue::Exception()); + } + + JSHandle msg = GetCallArg(argv, 0); + JSHandle parseString = JSTaggedValue::ToString(thread, msg); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + JSHandle result; + if (parseString->IsUtf8()) { + panda::ecmascript::base::JsonParser parser(thread); + result = parser.ParseUtf8(*parseString); + } else { + panda::ecmascript::base::JsonParser parser(thread); + result = parser.ParseUtf16(*parseString); + } + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + + JSTaggedValue reviver = JSTaggedValue::Undefined(); + if (argc == 2) { // 2: 2 args + reviver = GetCallArg(argv, 1).GetTaggedValue(); + if (reviver.IsCallable()) { + JSHandle callbackfnHandle(thread, reviver); + // Let root be ! OrdinaryObjectCreate(%Object.prototype%). + JSHandle env = thread->GetEcmaVM()->GetGlobalEnv(); + JSHandle constructor = env->GetObjectFunction(); + JSHandle root = factory->NewJSObjectByConstructor(JSHandle(constructor), constructor); + // Let rootName be the empty String. + JSHandle rootName(factory->GetEmptyString()); + // Perform ! CreateDataPropertyOrThrow(root, rootName, unfiltered). + bool success = JSObject::CreateDataProperty(thread, root, rootName, result); + if (success) { + result = base::Internalize::InternalizeJsonProperty(thread, root, rootName, callbackfnHandle); + } + } + } + return result.GetTaggedValue(); +} + +// 24.5.2 +JSTaggedValue BuiltinsJson::Stringify(EcmaRuntimeCallInfo *argv) +{ + BUILTINS_API_TRACE(argv->GetThread(), Json, Parse); + ASSERT(argv); + JSThread *thread = argv->GetThread(); + [[maybe_unused]] EcmaHandleScope handleScope(thread); + + uint32_t argc = argv->GetArgsNumber(); + JSTaggedValue value = GetCallArg(argv, 0).GetTaggedValue(); + JSTaggedValue replacer = JSTaggedValue::Undefined(); + JSTaggedValue gap = JSTaggedValue::Undefined(); + + if (argc == 2) { // 2: 2 args + replacer = GetCallArg(argv, 1).GetTaggedValue(); + } else if (argc == 3) { // 3: 3 args + replacer = GetCallArg(argv, 1).GetTaggedValue(); + gap = GetCallArg(argv, BuiltinsBase::ArgsPosition::THIRD).GetTaggedValue(); + } + + JSHandle handleValue(thread, value); + JSHandle handleReplacer(thread, replacer); + JSHandle handleGap(thread, gap); + panda::ecmascript::base::JsonStringifier stringifier(thread); + JSHandle result = stringifier.Stringify(handleValue, handleReplacer, handleGap); + + return result.GetTaggedValue(); +} +} // namespace panda::ecmascript::builtins diff --git a/runtime/builtins/builtins_json.h b/runtime/builtins/builtins_json.h new file mode 100644 index 000000000..6f83003b7 --- /dev/null +++ b/runtime/builtins/builtins_json.h @@ -0,0 +1,31 @@ +/* + * Copyright (c) 2021-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_BUILTINS_BUILTINS_JSON_H +#define ECMASCRIPT_BUILTINS_BUILTINS_JSON_H + +#include "plugins/ecmascript/runtime/base/builtins_base.h" +#include "plugins/ecmascript/runtime/ecma_runtime_call_info.h" + +namespace panda::ecmascript::builtins { +using JSTaggedValue = JSTaggedValue; + +class BuiltinsJson : public ecmascript::base::BuiltinsBase { +public: + static JSTaggedValue Parse(EcmaRuntimeCallInfo *argv); + static JSTaggedValue Stringify(EcmaRuntimeCallInfo *argv); +}; +} // namespace panda::ecmascript::builtins +#endif // ECMASCRIPT_BUILTINS_BUILTINS_JSON_H diff --git a/runtime/builtins/builtins_locale.cpp b/runtime/builtins/builtins_locale.cpp new file mode 100644 index 000000000..e0f2c5099 --- /dev/null +++ b/runtime/builtins/builtins_locale.cpp @@ -0,0 +1,357 @@ +/* + * Copyright (c) 2021-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 "plugins/ecmascript/runtime/builtins/builtins_locale.h" + +#include "plugins/ecmascript/runtime/ecma_vm.h" +#include "plugins/ecmascript/runtime/global_env.h" +#include "plugins/ecmascript/runtime/js_locale.h" +#include "plugins/ecmascript/runtime/object_factory.h" + +namespace panda::ecmascript::builtins { +// 10.1.3 Intl.Locale( tag [, options] ) +JSTaggedValue BuiltinsLocale::LocaleConstructor(EcmaRuntimeCallInfo *argv) +{ + JSThread *thread = argv->GetThread(); + [[maybe_unused]] EcmaHandleScope scope(thread); + EcmaVM *ecmaVm = thread->GetEcmaVM(); + ObjectFactory *factory = ecmaVm->GetFactory(); + + // 1. If NewTarget is undefined, throw a TypeError exception. + JSHandle newTarget = GetNewTarget(argv); + if (newTarget->IsUndefined()) { + THROW_TYPE_ERROR_AND_RETURN(thread, "newTarget is undefined", JSTaggedValue::Exception()); + } + + // 6. Let locale be ? OrdinaryCreateFromConstructor(NewTarget, %LocalePrototype%, internalSlotsList). + JSHandle constructor = GetConstructor(argv); + JSHandle locale = + JSHandle::Cast(factory->NewJSObjectByConstructor(JSHandle(constructor), newTarget)); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + + // 7. If Type(tag) is not String or Object, throw a TypeError exception. + JSHandle tag = GetCallArg(argv, 0); + if (!tag->IsString() && !tag->IsJSObject()) { + THROW_TYPE_ERROR_AND_RETURN(thread, "tag is not String or Object", JSTaggedValue::Exception()); + } + + // 8. If Type(tag) is Object and tag has an [[InitializedLocale]] internal slot, then + // a.Let tag be tag.[[Locale]]. + // 9. Else, + // a.Let tag be ? ToString(tag). + JSHandle localeString = factory->GetEmptyString(); + if (!tag->IsJSLocale()) { + localeString = JSTaggedValue::ToString(thread, tag); + } else { + icu::Locale *icuLocale = (JSHandle::Cast(tag))->GetIcuLocale(); + localeString = JSLocale::ToLanguageTag(thread, *icuLocale); + } + // 10. If options is undefined, then + // a.Let options be ! ObjectCreate(null). + // 11. Else + // a.Let options be ? ToObject(options). + JSHandle options = GetCallArg(argv, 1); + JSHandle optionsObj; + if (options->IsUndefined()) { + optionsObj = factory->OrdinaryNewJSObjectCreate(JSHandle(thread, JSTaggedValue::Null())); + } else { + optionsObj = JSTaggedValue::ToObject(thread, options); + } + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + + JSHandle result = JSLocale::InitializeLocale(thread, locale, localeString, optionsObj); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + return result.GetTaggedValue(); +} + +JSTaggedValue BuiltinsLocale::Maximize(EcmaRuntimeCallInfo *argv) +{ + JSThread *thread = argv->GetThread(); + [[maybe_unused]] EcmaHandleScope scope(thread); + // 1. Let loc be the this value. + JSHandle loc = GetThis(argv); + + // 2. Perform ? RequireInternalSlot(loc, [[InitializedLocale]]). + if (!loc->IsJSLocale()) { + THROW_TYPE_ERROR_AND_RETURN(thread, "not locale", JSTaggedValue::Exception()); + } + // 3. Let maximal be the result of the Add Likely Subtags algorithm applied to loc.[[Locale]]. If an error is + // signaled, set maximal to loc.[[Locale]]. + JSHandle locale = JSHandle::Cast(loc); + icu::Locale source(*(locale->GetIcuLocale())); + UErrorCode status = U_ZERO_ERROR; + source.addLikelySubtags(status); + ASSERT(U_SUCCESS(status)); + ASSERT(!source.isBogus()); + + // 4. Return ! Construct(%Locale%, maximal). + EcmaVM *ecmaVm = thread->GetEcmaVM(); + JSHandle env = ecmaVm->GetGlobalEnv(); + ObjectFactory *factory = ecmaVm->GetFactory(); + + JSHandle ctor = env->GetLocaleFunction(); + JSHandle obj = factory->NewJSObjectByConstructor(JSHandle(ctor), ctor); + factory->NewJSIntlIcuData(JSHandle::Cast(obj), source, JSLocale::FreeIcuLocale); + return obj.GetTaggedValue(); +} + +JSTaggedValue BuiltinsLocale::Minimize(EcmaRuntimeCallInfo *argv) +{ + JSThread *thread = argv->GetThread(); + [[maybe_unused]] EcmaHandleScope scope(thread); + // 1. Let loc be the this value. + JSHandle loc = GetThis(argv); + + // 2. Perform ? RequireInternalSlot(loc, [[InitializedLocale]]). + if (!loc->IsJSLocale()) { + THROW_TYPE_ERROR_AND_RETURN(thread, "not locale", JSTaggedValue::Exception()); + } + + // 3. Let minimal be the result of the Remove Likely Subtags algorithm applied to loc.[[Locale]]. + // If an error is signaled, set minimal to loc.[[Locale]]. + JSHandle locale = JSHandle::Cast(loc); + icu::Locale source(*(locale->GetIcuLocale())); + UErrorCode status = U_ZERO_ERROR; + source.minimizeSubtags(status); + ASSERT(U_SUCCESS(status)); + ASSERT(!source.isBogus()); + + [[maybe_unused]] auto res = source.toLanguageTag(status); + + // 4. Return ! Construct(%Locale%, minimal). + EcmaVM *ecmaVm = thread->GetEcmaVM(); + JSHandle env = ecmaVm->GetGlobalEnv(); + ObjectFactory *factory = ecmaVm->GetFactory(); + + JSHandle ctor = env->GetLocaleFunction(); + JSHandle obj = factory->NewJSObjectByConstructor(JSHandle(ctor), ctor); + factory->NewJSIntlIcuData(JSHandle::Cast(obj), source, JSLocale::FreeIcuLocale); + return obj.GetTaggedValue(); +} + +JSTaggedValue BuiltinsLocale::ToString(EcmaRuntimeCallInfo *argv) +{ + JSThread *thread = argv->GetThread(); + [[maybe_unused]] EcmaHandleScope scope(thread); + // 1. Let loc be the this value. + JSHandle loc = GetThis(argv); + // 2. Perform ? RequireInternalSlot(loc, [[InitializedLocale]]). + if (!loc->IsJSLocale()) { + THROW_TYPE_ERROR_AND_RETURN(thread, "not locale", JSTaggedValue::Exception()); + } + // 3. Return loc.[[Locale]]. + JSHandle result = JSLocale::ToString(thread, JSHandle::Cast(loc)); + return result.GetTaggedValue(); +} + +JSTaggedValue BuiltinsLocale::GetBaseName(EcmaRuntimeCallInfo *argv) +{ + JSThread *thread = argv->GetThread(); + [[maybe_unused]] EcmaHandleScope scope(thread); + // 1. Let loc be the this value. + JSHandle loc = GetThis(argv); + // 2. Perform ? RequireInternalSlot(loc, [[InitializedLocale]]). + if (!loc->IsJSLocale()) { + THROW_TYPE_ERROR_AND_RETURN(thread, "not locale", JSTaggedValue::Exception()); + } + // 3. Let locale be loc.[[Locale]]. + // 4. Return the substring of locale corresponding to the unicode_language_id production. + JSHandle locale = JSHandle::Cast(loc); + icu::Locale icuLocale = icu::Locale::createFromName(locale->GetIcuLocale()->getBaseName()); + JSHandle baseName = JSLocale::ToLanguageTag(thread, icuLocale); + return baseName.GetTaggedValue(); +} + +JSTaggedValue BuiltinsLocale::GetCalendar(EcmaRuntimeCallInfo *argv) +{ + JSThread *thread = argv->GetThread(); + [[maybe_unused]] EcmaHandleScope scope(thread); + // 1. Let loc be the this value. + JSHandle loc = GetThis(argv); + // 2. Perform ? RequireInternalSlot(loc, [[InitializedLocale]]). + if (!loc->IsJSLocale()) { + THROW_TYPE_ERROR_AND_RETURN(thread, "not locale", JSTaggedValue::Exception()); + } + // 3. Return loc.[[Calendar]]. + JSHandle locale = JSHandle::Cast(loc); + JSHandle calendar = JSLocale::NormalizeKeywordValue(thread, locale, "ca"); + return calendar.GetTaggedValue(); +} + +JSTaggedValue BuiltinsLocale::GetCaseFirst(EcmaRuntimeCallInfo *argv) +{ + // This property only exists if %Locale%.[[RelevantExtensionKeys]] contains "kf". + JSThread *thread = argv->GetThread(); + [[maybe_unused]] EcmaHandleScope scope(thread); + // 1. Let loc be the this value. + JSHandle loc = GetThis(argv); + // 2. Perform ? RequireInternalSlot(loc, [[InitializedLocale]]). + if (!loc->IsJSLocale()) { + THROW_TYPE_ERROR_AND_RETURN(thread, "not locale", JSTaggedValue::Exception()); + } + // 3. Return loc.[[CaseFirst]]. + JSHandle locale = JSHandle::Cast(loc); + JSHandle caseFirst = JSLocale::NormalizeKeywordValue(thread, locale, "kf"); + return caseFirst.GetTaggedValue(); +} + +JSTaggedValue BuiltinsLocale::GetCollation(EcmaRuntimeCallInfo *argv) +{ + JSThread *thread = argv->GetThread(); + [[maybe_unused]] EcmaHandleScope scope(thread); + // 1. Let loc be the this value. + JSHandle loc = GetThis(argv); + // 2. Perform ? RequireInternalSlot(loc, [[InitializedLocale]]). + if (!loc->IsJSLocale()) { + THROW_TYPE_ERROR_AND_RETURN(thread, "not locale", JSTaggedValue::Exception()); + } + // 3. Return loc.[[Collation]]. + JSHandle locale = JSHandle::Cast(loc); + JSHandle collation = JSLocale::NormalizeKeywordValue(thread, locale, "co"); + return collation.GetTaggedValue(); +} + +JSTaggedValue BuiltinsLocale::GetHourCycle(EcmaRuntimeCallInfo *argv) +{ + JSThread *thread = argv->GetThread(); + [[maybe_unused]] EcmaHandleScope scope(thread); + // 1. Let loc be the this value. + JSHandle loc = GetThis(argv); + // 2. Perform ? RequireInternalSlot(loc, [[InitializedLocale]]). + if (!loc->IsJSLocale()) { + THROW_TYPE_ERROR_AND_RETURN(thread, "not locale", JSTaggedValue::Exception()); + } + // 3. Return loc.[[HourCycle]]. + JSHandle locale = JSHandle::Cast(loc); + JSHandle hourCycle = JSLocale::NormalizeKeywordValue(thread, locale, "hc"); + return hourCycle.GetTaggedValue(); +} + +JSTaggedValue BuiltinsLocale::GetNumeric(EcmaRuntimeCallInfo *argv) +{ + // This property only exists if %Locale%.[[RelevantExtensionKeys]] contains "kn". + JSThread *thread = argv->GetThread(); + [[maybe_unused]] EcmaHandleScope scope(thread); + // 1. Let loc be the this value. + JSHandle loc = GetThis(argv); + // 2. Perform ? RequireInternalSlot(loc, [[InitializedLocale]]). + if (!loc->IsJSLocale()) { + THROW_TYPE_ERROR_AND_RETURN(thread, "not locale", JSTaggedValue::Exception()); + } + // 3. Return loc.[[Numeric]]. + JSHandle locale = JSHandle::Cast(loc); + icu::Locale *icuLocale = locale->GetIcuLocale(); + UErrorCode status = U_ZERO_ERROR; + auto numeric = icuLocale->getUnicodeKeywordValue("kn", status); + JSTaggedValue result = (numeric == "true") ? JSTaggedValue::True() : JSTaggedValue::False(); + return result; +} + +JSTaggedValue BuiltinsLocale::GetNumberingSystem(EcmaRuntimeCallInfo *argv) +{ + JSThread *thread = argv->GetThread(); + [[maybe_unused]] EcmaHandleScope scope(thread); + // 1. Let loc be the this value. + JSHandle loc = GetThis(argv); + // 2. Perform ? RequireInternalSlot(loc, [[InitializedLocale]]). + if (!loc->IsJSLocale()) { + THROW_TYPE_ERROR_AND_RETURN(thread, "not locale", JSTaggedValue::Exception()); + } + // 3. Return loc.[[NumberingSystem]]. + JSHandle locale = JSHandle::Cast(loc); + JSHandle numberingSystem = JSLocale::NormalizeKeywordValue(thread, locale, "nu"); + return numberingSystem.GetTaggedValue(); +} + +JSTaggedValue BuiltinsLocale::GetLanguage(EcmaRuntimeCallInfo *argv) +{ + JSThread *thread = argv->GetThread(); + [[maybe_unused]] EcmaHandleScope scope(thread); + ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); + // 1. Let loc be the this value. + JSHandle loc = GetThis(argv); + // 2. Perform ? RequireInternalSlot(loc, [[InitializedLocale]]). + if (!loc->IsJSLocale()) { + THROW_TYPE_ERROR_AND_RETURN(thread, "not locale", JSTaggedValue::Exception()); + } + // 3. Let locale be loc.[[Locale]]. + JSHandle locale = JSHandle::Cast(loc); + // 4. Assert: locale matches the unicode_locale_id production. + // 5. Return the substring of locale corresponding to the unicode_language_subtag production of the + // unicode_language_id. + JSHandle result = factory->NewFromString("undefined"); + CString language = locale->GetIcuLocale()->getLanguage(); + if (language.empty()) { + return result.GetTaggedValue(); + } + result = factory->NewFromString(language); + return result.GetTaggedValue(); +} + +JSTaggedValue BuiltinsLocale::GetScript(EcmaRuntimeCallInfo *argv) +{ + JSThread *thread = argv->GetThread(); + [[maybe_unused]] EcmaHandleScope scope(thread); + ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); + // 1. Let loc be the this value. + JSHandle loc = GetThis(argv); + // 2. Perform ? RequireInternalSlot(loc, [[InitializedLocale]]). + if (!loc->IsJSLocale()) { + THROW_TYPE_ERROR_AND_RETURN(thread, "not locale", JSTaggedValue::Exception()); + } + // 3. Let locale be loc.[[Locale]]. + JSHandle locale = JSHandle::Cast(loc); + + // 4. Assert: locale matches the unicode_locale_id production. + // 5. If the unicode_language_id production of locale does not contain the ["-" unicode_script_subtag] sequence, + // return undefined. + // 6. Return the substring of locale corresponding to the unicode_script_subtag production of the + // unicode_language_id. + JSHandle result(thread, JSTaggedValue::Undefined()); + CString script = locale->GetIcuLocale()->getScript(); + if (script.empty()) { + return result.GetTaggedValue(); + } + result = factory->NewFromString(script); + return result.GetTaggedValue(); +} + +JSTaggedValue BuiltinsLocale::GetRegion(EcmaRuntimeCallInfo *argv) +{ + JSThread *thread = argv->GetThread(); + [[maybe_unused]] EcmaHandleScope scope(thread); + EcmaVM *ecmaVm = thread->GetEcmaVM(); + const GlobalEnvConstants *globalConst = thread->GlobalConstants(); + ObjectFactory *factory = ecmaVm->GetFactory(); + // 1. Let loc be the this value. + JSHandle loc = GetThis(argv); + // 2. Perform ? RequireInternalSlot(loc, [[InitializedLocale]]). + if (!loc->IsJSLocale()) { + THROW_TYPE_ERROR_AND_RETURN(thread, "not locale", JSTaggedValue::Exception()); + } + // 3. Let locale be loc.[[Locale]]. + JSHandle locale = JSHandle::Cast(loc); + // 4. Assert: locale matches the unicode_locale_id production. + // 5. If the unicode_language_id production of locale does not contain the ["-" unicode_region_subtag] sequence, + // return undefined. + // 6. Return the substring of locale corresponding to the unicode_region_subtag production of the + // unicode_language_id. + CString region = locale->GetIcuLocale()->getCountry(); + if (region.empty()) { + return globalConst->GetUndefined(); + } + return factory->NewFromString(region).GetTaggedValue(); +} +} // namespace panda::ecmascript::builtins diff --git a/runtime/builtins/builtins_locale.h b/runtime/builtins/builtins_locale.h new file mode 100644 index 000000000..c7b4fd37b --- /dev/null +++ b/runtime/builtins/builtins_locale.h @@ -0,0 +1,56 @@ +/* + * Copyright (c) 2021-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_BUILTINS_BUILTINS_LOCALE_H +#define ECMASCRIPT_BUILTINS_BUILTINS_LOCALE_H + +#include "plugins/ecmascript/runtime/base/builtins_base.h" + +namespace panda::ecmascript::builtins { +class BuiltinsLocale : public ecmascript::base::BuiltinsBase { +public: + // 10.1.3 Intl.Locale( tag [, options] ) + static JSTaggedValue LocaleConstructor(EcmaRuntimeCallInfo *argv); + + // 10.3.3 Intl.Locale.prototype.maximize () + static JSTaggedValue Maximize(EcmaRuntimeCallInfo *argv); + // 10.3.4 Intl.Locale.prototype.minimize () + static JSTaggedValue Minimize(EcmaRuntimeCallInfo *argv); + // 10.3.5 Intl.Locale.prototype.toString () + static JSTaggedValue ToString(EcmaRuntimeCallInfo *argv); + + // 10.3.6 get Intl.Locale.prototype.baseName + static JSTaggedValue GetBaseName(EcmaRuntimeCallInfo *argv); + // 10.3.7 get Intl.Locale.prototype.calendar + static JSTaggedValue GetCalendar(EcmaRuntimeCallInfo *argv); + // 10.3.8 get Intl.Locale.prototype.caseFirst + static JSTaggedValue GetCaseFirst(EcmaRuntimeCallInfo *argv); + // 10.3.9 get Intl.Locale.prototype.collation + static JSTaggedValue GetCollation(EcmaRuntimeCallInfo *argv); + // 10.3.10 get Intl.Locale.prototype.hourCycle + static JSTaggedValue GetHourCycle(EcmaRuntimeCallInfo *argv); + // 10.3.11 get Intl.Locale.prototype.numeric + static JSTaggedValue GetNumeric(EcmaRuntimeCallInfo *argv); + // 10.3.12 get Intl.Locale.prototype.numberingSystem + static JSTaggedValue GetNumberingSystem(EcmaRuntimeCallInfo *argv); + // 10.3.13 get Intl.Locale.prototype.language + static JSTaggedValue GetLanguage(EcmaRuntimeCallInfo *argv); + // 10.3.14 get Intl.Locale.prototype.script + static JSTaggedValue GetScript(EcmaRuntimeCallInfo *argv); + // 10.3.15 get Intl.Locale.prototype.region + static JSTaggedValue GetRegion(EcmaRuntimeCallInfo *argv); +}; +} // namespace panda::ecmascript::builtins +#endif // ECMASCRIPT_BUILTINS_BUILTINS_LOCALE_H diff --git a/runtime/builtins/builtins_map.cpp b/runtime/builtins/builtins_map.cpp new file mode 100644 index 000000000..9d6edc0c2 --- /dev/null +++ b/runtime/builtins/builtins_map.cpp @@ -0,0 +1,256 @@ +/* + * Copyright (c) 2021-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 "builtins_map.h" +#include "plugins/ecmascript/runtime/ecma_vm.h" +#include "plugins/ecmascript/runtime/global_env.h" +#include "plugins/ecmascript/runtime/internal_call_params.h" +#include "plugins/ecmascript/runtime/js_invoker.h" +#include "plugins/ecmascript/runtime/js_map.h" +#include "plugins/ecmascript/runtime/js_map_iterator.h" +#include "plugins/ecmascript/runtime/base/object_helper.h" +#include "plugins/ecmascript/runtime/linked_hash_table.h" +#include "plugins/ecmascript/runtime/object_factory.h" + +namespace panda::ecmascript::builtins { +JSTaggedValue BuiltinsMap::MapConstructor(EcmaRuntimeCallInfo *argv) +{ + BUILTINS_API_TRACE(argv->GetThread(), Map, Constructor); + JSThread *thread = argv->GetThread(); + [[maybe_unused]] EcmaHandleScope handleScope(thread); + ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); + // 1.If NewTarget is undefined, throw a TypeError exception + JSHandle newTarget = GetNewTarget(argv); + if (newTarget->IsUndefined()) { + // throw type error + THROW_TYPE_ERROR_AND_RETURN(thread, "new target can't be undefined", JSTaggedValue::Exception()); + } + // 2.Let Map be OrdinaryCreateFromConstructor(NewTarget, "%MapPrototype%", «‍[[MapData]]» ). + JSHandle constructor = GetConstructor(argv); + JSHandle obj = factory->NewJSObjectByConstructor(JSHandle(constructor), newTarget); + // 3.returnIfAbrupt() + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + JSHandle map = JSHandle::Cast(obj); + + // 4.Set map’s [[MapData]] internal slot to a new empty List. + JSHandle linkedMap = LinkedHashMap::Create(thread); + map->SetLinkedMap(thread, linkedMap); + // add data into set from iterable + // 5.If iterable is not present, let iterable be undefined. + // 6.If iterable is either undefined or null, let iter be undefined. + JSHandle iterable = GetCallArg(argv, 0); + // 8.If iter is undefined, return set + if (iterable->IsUndefined() || iterable->IsNull()) { + return map.GetTaggedValue(); + } + if (!iterable->IsECMAObject()) { + THROW_TYPE_ERROR_AND_RETURN(thread, "iterable is not object", JSTaggedValue::Exception()); + } + // Let adder be Get(map, "set"). + JSHandle adderKey(factory->NewFromCanBeCompressString("set")); + JSHandle adder = JSObject::GetProperty(thread, JSHandle(map), adderKey).GetValue(); + // ReturnIfAbrupt(adder). + RETURN_VALUE_IF_ABRUPT_COMPLETION(thread, adder.GetTaggedValue()); + JSTaggedValue result = + base::ObjectHelper::AddEntriesFromIterable(thread, JSHandle(map), iterable, adder); + RETURN_VALUE_IF_ABRUPT_COMPLETION(thread, result); + return result; +} + +JSTaggedValue BuiltinsMap::Set(EcmaRuntimeCallInfo *argv) +{ + BUILTINS_API_TRACE(argv->GetThread(), Map, Set); + JSThread *thread = argv->GetThread(); + [[maybe_unused]] EcmaHandleScope handleScope(thread); + JSHandle self = GetThis(argv); + + // 2.If Type(S) is not Object, throw a TypeError exception. + // 3.If S does not have a [[MapData]] internal slot, throw a TypeError exception. + if (!self->IsJSMap()) { + THROW_TYPE_ERROR_AND_RETURN(thread, "obj is not JSMap", JSTaggedValue::Exception()); + } + + JSHandle key = GetCallArg(argv, 0); + JSHandle value = GetCallArg(argv, 1); + + JSHandle map(self); + JSMap::Set(thread, map, key, value); + return map.GetTaggedValue(); +} + +JSTaggedValue BuiltinsMap::Clear(EcmaRuntimeCallInfo *argv) +{ + BUILTINS_API_TRACE(argv->GetThread(), Map, Clear); + JSThread *thread = argv->GetThread(); + [[maybe_unused]] EcmaHandleScope handleScope(thread); + + JSHandle self = GetThis(argv); + + // 2.If Type(S) is not Object, throw a TypeError exception. + // 3.If S does not have a [[MapData]] internal slot, throw a TypeError exception. + if (!self->IsJSMap()) { + THROW_TYPE_ERROR_AND_RETURN(thread, "obj is not JSMap", JSTaggedValue::Exception()); + } + JSHandle map(self); + JSMap::Clear(thread, map); + return JSTaggedValue::Undefined(); +} + +JSTaggedValue BuiltinsMap::Delete(EcmaRuntimeCallInfo *argv) +{ + BUILTINS_API_TRACE(argv->GetThread(), Map, Delete); + JSThread *thread = argv->GetThread(); + [[maybe_unused]] EcmaHandleScope handleScope(thread); + JSHandle self = GetThis(argv); + // 2.If Type(S) is not Object, throw a TypeError exception. + // 3.If S does not have a [[MapData]] internal slot, throw a TypeError exception. + if (!self->IsJSMap()) { + THROW_TYPE_ERROR_AND_RETURN(thread, "obj is not JSMap", JSTaggedValue::Exception()); + } + + JSHandle map(self); + JSHandle key = GetCallArg(argv, 0); + bool flag = JSMap::Delete(thread, map, key); + return GetTaggedBoolean(flag); +} + +JSTaggedValue BuiltinsMap::Has(EcmaRuntimeCallInfo *argv) +{ + BUILTINS_API_TRACE(argv->GetThread(), Map, Has); + JSThread *thread = argv->GetThread(); + [[maybe_unused]] EcmaHandleScope handleScope(thread); + JSHandle self(GetThis(argv)); + // 2.If Type(S) is not Object, throw a TypeError exception. + // 3.If S does not have a [[MapData]] internal slot, throw a TypeError exception. + if (!self->IsJSMap()) { + THROW_TYPE_ERROR_AND_RETURN(thread, "obj is not JSMap", JSTaggedValue::Exception()); + } + JSMap *jsMap = JSMap::Cast(*JSTaggedValue::ToObject(thread, self)); + JSHandle key = GetCallArg(argv, 0); + bool flag = jsMap->Has(key.GetTaggedValue()); + return GetTaggedBoolean(flag); +} + +JSTaggedValue BuiltinsMap::Get(EcmaRuntimeCallInfo *argv) +{ + BUILTINS_API_TRACE(argv->GetThread(), Map, Get); + JSThread *thread = argv->GetThread(); + [[maybe_unused]] EcmaHandleScope handleScope(thread); + JSHandle self(GetThis(argv)); + // 2.If Type(S) is not Object, throw a TypeError exception. + // 3.If S does not have a [[MapData]] internal slot, throw a TypeError exception. + if (!self->IsJSMap()) { + THROW_TYPE_ERROR_AND_RETURN(thread, "obj is not JSMap", JSTaggedValue::Exception()); + } + JSMap *jsMap = JSMap::Cast(*JSTaggedValue::ToObject(thread, self)); + JSHandle key = GetCallArg(argv, 0); + JSTaggedValue value = jsMap->Get(key.GetTaggedValue()); + return value; +} + +JSTaggedValue BuiltinsMap::ForEach([[maybe_unused]] EcmaRuntimeCallInfo *argv) +{ + JSThread *thread = argv->GetThread(); + [[maybe_unused]] EcmaHandleScope handleScope(thread); + ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); + JSHandle self = GetThis(argv); + // 2.If Type(S) is not Object, throw a TypeError exception. + // 3.If S does not have a [[MapData]] internal slot, throw a TypeError exception. + if (!self->IsJSMap()) { + THROW_TYPE_ERROR_AND_RETURN(thread, "obj is not JSMap", JSTaggedValue::Exception()); + } + JSHandle map(thread, JSMap::Cast(*JSTaggedValue::ToObject(thread, self))); + + // 4.If IsCallable(callbackfn) is false, throw a TypeError exception. + JSHandle func(GetCallArg(argv, 0)); + if (!func->IsCallable()) { + THROW_TYPE_ERROR_AND_RETURN(thread, "obj is not Callable", JSTaggedValue::Exception()); + } + // 5.If thisArg was supplied, let T be thisArg; else let T be undefined. + JSHandle thisArg = GetCallArg(argv, 1); + + // composed arguments + JSHandle iter(factory->NewJSMapIterator(map, IterationKind::KEY_AND_VALUE)); + JSHandle keyIndex(thread, JSTaggedValue(0)); + JSHandle valueIndex(thread, JSTaggedValue(1)); + JSHandle result = JSIterator::IteratorStep(thread, iter); + InternalCallParams *arguments = thread->GetInternalCallParams(); + while (!result->IsFalse()) { + RETURN_VALUE_IF_ABRUPT_COMPLETION(thread, result.GetTaggedValue()); + JSHandle iterValue(JSIterator::IteratorValue(thread, result)); + JSHandle key = JSObject::GetProperty(thread, iterValue, keyIndex).GetValue(); + JSHandle value = JSObject::GetProperty(thread, iterValue, valueIndex).GetValue(); + // Let funcResult be Call(callbackfn, T, «e, e, S»). + arguments->MakeArgv(value, key, JSHandle(map)); + JSTaggedValue ret = JSFunction::Call(thread, func, thisArg, 3, arguments->GetArgv()); // 3: three args + // returnIfAbrupt + RETURN_VALUE_IF_ABRUPT_COMPLETION(thread, ret); + result = JSIterator::IteratorStep(thread, iter); + } + + return JSTaggedValue::Undefined(); +} + +JSTaggedValue BuiltinsMap::Species([[maybe_unused]] EcmaRuntimeCallInfo *argv) +{ + return GetThis(argv).GetTaggedValue(); +} + +JSTaggedValue BuiltinsMap::GetSize(EcmaRuntimeCallInfo *argv) +{ + BUILTINS_API_TRACE(argv->GetThread(), Map, GetSize); + JSThread *thread = argv->GetThread(); + [[maybe_unused]] EcmaHandleScope handleScope(thread); + JSHandle self(GetThis(argv)); + // 2.If Type(S) is not Object, throw a TypeError exception. + // 3.If S does not have a [[MapData]] internal slot, throw a TypeError exception. + if (!self->IsJSMap()) { + THROW_TYPE_ERROR_AND_RETURN(thread, "obj is not JSMap", JSTaggedValue::Exception()); + } + JSMap *jsMap = JSMap::Cast(*JSTaggedValue::ToObject(thread, self)); + int count = jsMap->GetSize(); + return JSTaggedValue(count); +} + +JSTaggedValue BuiltinsMap::Entries(EcmaRuntimeCallInfo *argv) +{ + BUILTINS_API_TRACE(argv->GetThread(), Map, Entries); + JSThread *thread = argv->GetThread(); + [[maybe_unused]] EcmaHandleScope handleScope(thread); + JSHandle self = GetThis(argv); + JSHandle iter = JSMapIterator::CreateMapIterator(thread, self, IterationKind::KEY_AND_VALUE); + return iter.GetTaggedValue(); +} + +JSTaggedValue BuiltinsMap::Keys(EcmaRuntimeCallInfo *argv) +{ + BUILTINS_API_TRACE(argv->GetThread(), Map, Keys); + JSThread *thread = argv->GetThread(); + [[maybe_unused]] EcmaHandleScope handleScope(thread); + JSHandle self = GetThis(argv); + JSHandle iter = JSMapIterator::CreateMapIterator(thread, self, IterationKind::KEY); + return iter.GetTaggedValue(); +} + +JSTaggedValue BuiltinsMap::Values(EcmaRuntimeCallInfo *argv) +{ + BUILTINS_API_TRACE(argv->GetThread(), Map, Values); + JSThread *thread = argv->GetThread(); + [[maybe_unused]] EcmaHandleScope handleScope(thread); + JSHandle self = GetThis(argv); + JSHandle iter = JSMapIterator::CreateMapIterator(thread, self, IterationKind::VALUE); + return iter.GetTaggedValue(); +} +} // namespace panda::ecmascript::builtins diff --git a/runtime/builtins/builtins_map.h b/runtime/builtins/builtins_map.h new file mode 100644 index 000000000..6c47044e7 --- /dev/null +++ b/runtime/builtins/builtins_map.h @@ -0,0 +1,51 @@ +/* + * Copyright (c) 2021-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_BUILTINS_BUILTINS_MAP_H +#define ECMASCRIPT_BUILTINS_BUILTINS_MAP_H + +#include "plugins/ecmascript/runtime/base/builtins_base.h" +#include "plugins/ecmascript/runtime/ecma_runtime_call_info.h" + +namespace panda::ecmascript::builtins { +class BuiltinsMap : public ecmascript::base::BuiltinsBase { +public: + // 23.1.1.1 + static JSTaggedValue MapConstructor(EcmaRuntimeCallInfo *argv); + // 23.1.2.2 + static JSTaggedValue Species(EcmaRuntimeCallInfo *argv); + // 23.1.3.1 + static JSTaggedValue Clear(EcmaRuntimeCallInfo *argv); + // 23.1.3.3 + static JSTaggedValue Delete(EcmaRuntimeCallInfo *argv); + // 23.1.3.4 + static JSTaggedValue Entries(EcmaRuntimeCallInfo *argv); + // 23.1.3.5 + static JSTaggedValue ForEach(EcmaRuntimeCallInfo *argv); + // 23.1.3.6 + static JSTaggedValue Get(EcmaRuntimeCallInfo *argv); + // 23.1.3.7 + static JSTaggedValue Has(EcmaRuntimeCallInfo *argv); + // 23.1.3.8 + static JSTaggedValue Keys(EcmaRuntimeCallInfo *argv); + // 23.1.3.9 + static JSTaggedValue Set(EcmaRuntimeCallInfo *argv); + // 23.1.3.10 + static JSTaggedValue GetSize(EcmaRuntimeCallInfo *argv); + // 23.1.3.11 + static JSTaggedValue Values(EcmaRuntimeCallInfo *argv); +}; +} // namespace panda::ecmascript::builtins +#endif // ECMASCRIPT_BUILTINS_BUILTINS_MAP_H diff --git a/runtime/builtins/builtins_math.cpp b/runtime/builtins/builtins_math.cpp new file mode 100644 index 000000000..b6a5b175b --- /dev/null +++ b/runtime/builtins/builtins_math.cpp @@ -0,0 +1,745 @@ +/* + * Copyright (c) 2021-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 "builtins_math.h" +#include +#include +#include "plugins/ecmascript/runtime/ecma_runtime_call_info.h" +#include "plugins/ecmascript/runtime/js_tagged_number.h" +#include "utils/bit_utils.h" + +namespace panda::ecmascript::builtins { +using NumberHelper = ecmascript::base::NumberHelper; + +// 20.2.2.1 +JSTaggedValue BuiltinsMath::Abs(EcmaRuntimeCallInfo *argv) +{ + ASSERT(argv); + BUILTINS_API_TRACE(argv->GetThread(), Math, Abs); + JSThread *thread = argv->GetThread(); + [[maybe_unused]] EcmaHandleScope handleScope(thread); + JSHandle msg = GetCallArg(argv, 0); + JSTaggedNumber numberValue = JSTaggedValue::ToNumber(thread, msg); + if (numberValue.IsDouble()) { + // if number_value is double,NaN,Undefine, deal in this case + // if number_value is a String ,which can change to double. e.g."100",deal in this case + return GetTaggedDouble(std::fabs(numberValue.GetDouble())); + } + // if number_value is int,boolean,null, deal in this case + return GetTaggedInt(std::abs(numberValue.GetInt())); +} + +// 20.2.2.2 +JSTaggedValue BuiltinsMath::Acos(EcmaRuntimeCallInfo *argv) +{ + ASSERT(argv); + BUILTINS_API_TRACE(argv->GetThread(), Math, Acos); + JSThread *thread = argv->GetThread(); + [[maybe_unused]] EcmaHandleScope handleScope(thread); + JSHandle msg = GetCallArg(argv, 0); + JSTaggedNumber numberValue = JSTaggedValue::ToNumber(thread, msg); + double value = numberValue.GetNumber(); + double result = ecmascript::base::NAN_VALUE; + // value == -NaN , <-1 or > 1,result is NaN + if (!std::isnan(std::abs(value)) && value <= 1 && value >= -1) { + result = std::acos(value); + } + return GetTaggedDouble(result); +} + +// 20.2.2.3 +JSTaggedValue BuiltinsMath::Acosh(EcmaRuntimeCallInfo *argv) +{ + ASSERT(argv); + BUILTINS_API_TRACE(argv->GetThread(), Math, Acosh); + JSThread *thread = argv->GetThread(); + [[maybe_unused]] EcmaHandleScope handleScope(thread); + JSHandle msg = GetCallArg(argv, 0); + JSTaggedNumber numberValue = JSTaggedValue::ToNumber(thread, msg); + double value = numberValue.GetNumber(); + double result = ecmascript::base::NAN_VALUE; + if (value >= 1) { + result = std::acosh(value); + } + return GetTaggedDouble(result); +} + +// 20.2.2.4 +JSTaggedValue BuiltinsMath::Asin(EcmaRuntimeCallInfo *argv) +{ + ASSERT(argv); + BUILTINS_API_TRACE(argv->GetThread(), Math, Asin); + JSThread *thread = argv->GetThread(); + [[maybe_unused]] EcmaHandleScope handleScope(thread); + JSHandle msg = GetCallArg(argv, 0); + JSTaggedNumber numberValue = JSTaggedValue::ToNumber(thread, msg); + double value = numberValue.GetNumber(); + double result = ecmascript::base::NAN_VALUE; + if (value >= -1 && value <= 1) { + result = std::asin(value); + } + return GetTaggedDouble(result); +} + +// 20.2.2.5 +JSTaggedValue BuiltinsMath::Asinh(EcmaRuntimeCallInfo *argv) +{ + ASSERT(argv); + BUILTINS_API_TRACE(argv->GetThread(), Math, Asinh); + JSThread *thread = argv->GetThread(); + [[maybe_unused]] EcmaHandleScope handleScope(thread); + JSHandle msg = GetCallArg(argv, 0); + JSTaggedNumber numberValue = JSTaggedValue::ToNumber(thread, msg); + double value = numberValue.GetNumber(); + double result = ecmascript::base::NAN_VALUE; + // value == -NaN, NaN, result is NaN + if (!std::isnan(std::abs(value))) { + result = std::asinh(value); + } + return GetTaggedDouble(result); +} + +// 20.2.2.6 +JSTaggedValue BuiltinsMath::Atan(EcmaRuntimeCallInfo *argv) +{ + ASSERT(argv); + BUILTINS_API_TRACE(argv->GetThread(), Math, Atan); + JSThread *thread = argv->GetThread(); + [[maybe_unused]] EcmaHandleScope handleScope(thread); + JSHandle msg = GetCallArg(argv, 0); + JSTaggedNumber numberValue = JSTaggedValue::ToNumber(thread, msg); + double value = numberValue.GetNumber(); + double result = ecmascript::base::NAN_VALUE; + // value == -NaN, NaN, result is NaN + if (!std::isnan(std::abs(value))) { + result = std::atan(value); + } + return GetTaggedDouble(result); +} + +// 20.2.2.7 +JSTaggedValue BuiltinsMath::Atanh(EcmaRuntimeCallInfo *argv) +{ + ASSERT(argv); + BUILTINS_API_TRACE(argv->GetThread(), Math, Atanh); + JSThread *thread = argv->GetThread(); + [[maybe_unused]] EcmaHandleScope handleScope(thread); + JSHandle msg = GetCallArg(argv, 0); + JSTaggedNumber numberValue = JSTaggedValue::ToNumber(thread, msg); + double value = numberValue.GetNumber(); + double result = ecmascript::base::NAN_VALUE; + if (value >= -1 && value <= 1) { + result = std::atanh(value); + } + return GetTaggedDouble(result); +} + +// 20.2.2.8 +JSTaggedValue BuiltinsMath::Atan2(EcmaRuntimeCallInfo *argv) +{ + ASSERT(argv); + BUILTINS_API_TRACE(argv->GetThread(), Math, Atan2); + JSThread *thread = argv->GetThread(); + [[maybe_unused]] EcmaHandleScope handleScope(thread); + JSHandle msgY = GetCallArg(argv, 0); + JSHandle msgX = GetCallArg(argv, 1); + double result = ecmascript::base::NAN_VALUE; + JSTaggedNumber numberValueY = JSTaggedValue::ToNumber(thread, msgY); + JSTaggedNumber numberValueX = JSTaggedValue::ToNumber(thread, msgX); + double valueY = numberValueY.GetNumber(); + double valueX = numberValueX.GetNumber(); + // y = +0 and x > +0, return +0 + // y = -0 and x > +0, return -0 + if (valueY == 0 && valueX > 0) { + result = valueY; + } else if (std::isfinite(valueY) && valueX == std::numeric_limits::infinity()) { + // y < 0 and y is finite and x is POSITIVE_INFINITY,return -0 + // y >= 0 and y is finite and x is POSITIVE_INFINITY,return +0 + result = valueY >= 0 ? 0 : -0.0; + } else if (!std::isnan(std::abs(valueY)) && !std::isnan(std::abs(valueX))) { + // If either x or y is NaN, the result is NaN + result = std::atan2(valueY, valueX); + } + return GetTaggedDouble(result); +} + +// 20.2.2.9 +JSTaggedValue BuiltinsMath::Cbrt(EcmaRuntimeCallInfo *argv) +{ + ASSERT(argv); + BUILTINS_API_TRACE(argv->GetThread(), Math, Cbrt); + JSThread *thread = argv->GetThread(); + [[maybe_unused]] EcmaHandleScope handleScope(thread); + JSHandle msg = GetCallArg(argv, 0); + JSTaggedNumber numberValue = JSTaggedValue::ToNumber(thread, msg); + double value = numberValue.GetNumber(); + double result = ecmascript::base::NAN_VALUE; + // if value == -NaN, NaN, result is NaN + if (!std::isnan(std::abs(value))) { + result = std::cbrt(value); + } + return GetTaggedDouble(result); +} + +// 20.2.2.10 +JSTaggedValue BuiltinsMath::Ceil(EcmaRuntimeCallInfo *argv) +{ + ASSERT(argv); + BUILTINS_API_TRACE(argv->GetThread(), Math, Ceil); + JSThread *thread = argv->GetThread(); + [[maybe_unused]] EcmaHandleScope handleScope(thread); + JSHandle msg = GetCallArg(argv, 0); + JSTaggedNumber numberValue = JSTaggedValue::ToNumber(thread, msg); + double value = numberValue.GetNumber(); + double result = ecmascript::base::NAN_VALUE; + // If value is NaN or -NaN, +infinite, -infinite,return value + if (!std::isfinite(value)) { + // if value is -NaN , return NaN, else return value + if (!std::isnan(std::abs(value))) { + result = value; + } + } else { + result = std::ceil(value); + } + return GetTaggedDouble(result); +} + +// 20.2.2.11 +JSTaggedValue BuiltinsMath::Clz32(EcmaRuntimeCallInfo *argv) +{ + ASSERT(argv); + BUILTINS_API_TRACE(argv->GetThread(), Math, Clz32); + JSThread *thread = argv->GetThread(); + [[maybe_unused]] EcmaHandleScope handleScope(thread); + constexpr int defaultValue = 32; + JSHandle msg = GetCallArg(argv, 0); + JSTaggedNumber numberValue = JSTaggedValue::ToNumber(thread, msg); + double value = numberValue.GetNumber(); + auto tmpValue = std::abs(value); + auto result = numberValue.ToUint32(); + if (!std::isfinite(tmpValue) || tmpValue == 0 || result == 0) { + // If value is NaN or -NaN, +infinite, -infinite, 0,return 32 + return GetTaggedInt(defaultValue); + } + return GetTaggedInt(__builtin_clz(result)); +} + +// 20.2.2.12 +JSTaggedValue BuiltinsMath::Cos(EcmaRuntimeCallInfo *argv) +{ + ASSERT(argv); + BUILTINS_API_TRACE(argv->GetThread(), Math, Cos); + JSThread *thread = argv->GetThread(); + [[maybe_unused]] EcmaHandleScope handleScope(thread); + JSHandle msg = GetCallArg(argv, 0); + JSTaggedNumber numberValue = JSTaggedValue::ToNumber(thread, msg); + double value = numberValue.GetNumber(); + double result = ecmascript::base::NAN_VALUE; + // If value is NaN or -NaN, +infinite, -infinite, result is NaN + if (std::isfinite(std::abs(value))) { + result = std::cos(value); + } + return GetTaggedDouble(result); +} + +// 20.2.2.13 +JSTaggedValue BuiltinsMath::Cosh(EcmaRuntimeCallInfo *argv) +{ + ASSERT(argv); + BUILTINS_API_TRACE(argv->GetThread(), Math, Cosh); + JSThread *thread = argv->GetThread(); + [[maybe_unused]] EcmaHandleScope handleScope(thread); + JSHandle msg = GetCallArg(argv, 0); + JSTaggedNumber numberValue = JSTaggedValue::ToNumber(thread, msg); + double value = numberValue.GetNumber(); + double result = ecmascript::base::NAN_VALUE; + // if value is NaN or -NaN, result is NaN + if (!std::isnan(std::abs(value))) { + result = std::cosh(value); + } + return GetTaggedDouble(result); +} + +// 20.2.2.14 +JSTaggedValue BuiltinsMath::Exp(EcmaRuntimeCallInfo *argv) +{ + ASSERT(argv); + BUILTINS_API_TRACE(argv->GetThread(), Math, Exp); + JSThread *thread = argv->GetThread(); + [[maybe_unused]] EcmaHandleScope handleScope(thread); + JSHandle msg = GetCallArg(argv, 0); + JSTaggedNumber numberValue = JSTaggedValue::ToNumber(thread, msg); + double value = numberValue.GetNumber(); + double result = ecmascript::base::NAN_VALUE; + // if value is NaN or -NaN, result is NaN + if (!std::isnan(std::abs(value))) { + result = std::exp(value); + } + return GetTaggedDouble(result); +} + +// 20.2.2.15 +JSTaggedValue BuiltinsMath::Expm1(EcmaRuntimeCallInfo *argv) +{ + ASSERT(argv); + BUILTINS_API_TRACE(argv->GetThread(), Math, Expm1); + JSThread *thread = argv->GetThread(); + [[maybe_unused]] EcmaHandleScope handleScope(thread); + JSHandle msg = GetCallArg(argv, 0); + JSTaggedNumber numberValue = JSTaggedValue::ToNumber(thread, msg); + double value = numberValue.GetNumber(); + double result = ecmascript::base::NAN_VALUE; + // if value is NaN or -NaN, result is NaN + if (!std::isnan(std::abs(value))) { + result = std::expm1(value); + } + return GetTaggedDouble(result); +} + +// 20.2.2.16 +JSTaggedValue BuiltinsMath::Floor(EcmaRuntimeCallInfo *argv) +{ + ASSERT(argv); + BUILTINS_API_TRACE(argv->GetThread(), Math, Floor); + JSThread *thread = argv->GetThread(); + [[maybe_unused]] EcmaHandleScope handleScope(thread); + JSHandle msg = GetCallArg(argv, 0); + JSTaggedNumber numberValue = JSTaggedValue::ToNumber(thread, msg); + double value = numberValue.GetNumber(); + double result = ecmascript::base::NAN_VALUE; + // If value is NaN or -NaN, +infinite, -infinite, +0, -0, return value + if (!std::isfinite(value) || value == 0) { + // If value is -NaN, return NaN, else return value + if (!std::isnan(std::abs(value))) { + result = value; + } + } else if (value > 0 && value < 1) { + // If x is greater than 0 but less than 1, the result is +0 + result = 0; + } else { + result = std::floor(value); + } + return GetTaggedDouble(result); +} + +// 20.2.2.17 +JSTaggedValue BuiltinsMath::Fround(EcmaRuntimeCallInfo *argv) +{ + ASSERT(argv); + BUILTINS_API_TRACE(argv->GetThread(), Math, Fround); + JSThread *thread = argv->GetThread(); + [[maybe_unused]] EcmaHandleScope handleScope(thread); + JSHandle msg = GetCallArg(argv, 0); + JSTaggedNumber numberValue = JSTaggedValue::ToNumber(thread, msg); + double value = numberValue.GetNumber(); + double result; + if (std::isnan(std::abs(value))) { + // If result is NaN or -NaN, the result is NaN + result = ecmascript::base::NAN_VALUE; + } else { + result = static_cast(value); + } + return GetTaggedDouble(result); +} + +// 20.2.2.18 +JSTaggedValue BuiltinsMath::Hypot(EcmaRuntimeCallInfo *argv) +{ + ASSERT(argv); + BUILTINS_API_TRACE(argv->GetThread(), Math, Hypot); + JSThread *thread = argv->GetThread(); + [[maybe_unused]] EcmaHandleScope handleScope(thread); + double result = 0; + double value = 0; + int argLen = argv->GetArgsNumber(); + auto numberValue = JSTaggedNumber(0); + for (int i = 0; i < argLen; i++) { + JSHandle msg = GetCallArg(argv, i); + numberValue = JSTaggedValue::ToNumber(thread, msg); + value = numberValue.GetNumber(); + result = std::hypot(result, value); + } + return GetTaggedDouble(result); +} + +// 20.2.2.19 +JSTaggedValue BuiltinsMath::Imul(EcmaRuntimeCallInfo *argv) +{ + ASSERT(argv); + BUILTINS_API_TRACE(argv->GetThread(), Math, Imul); + JSThread *thread = argv->GetThread(); + [[maybe_unused]] EcmaHandleScope handleScope(thread); + JSHandle msg1 = GetCallArg(argv, 0); + JSHandle msg2 = GetCallArg(argv, 1); + JSTaggedNumber numberValue1 = JSTaggedValue::ToNumber(thread, msg1); + JSTaggedNumber numberValue2 = JSTaggedValue::ToNumber(thread, msg2); + auto value1 = numberValue1.GetNumber(); + auto value2 = numberValue2.GetNumber(); + if (!std::isfinite(value1) || !std::isfinite(value2)) { + // If value is NaN or -NaN, +infinite, -infinite + return GetTaggedInt(0); + } + value1 = numberValue1.ToInt32(); + value2 = numberValue2.ToInt32(); + // purposely ignoring overflow + auto result = static_cast(static_cast(value1) * static_cast(value2)); + return GetTaggedInt(result); +} + +// 20.2.2.20 +JSTaggedValue BuiltinsMath::Log(EcmaRuntimeCallInfo *argv) +{ + ASSERT(argv); + BUILTINS_API_TRACE(argv->GetThread(), Math, Log); + JSThread *thread = argv->GetThread(); + [[maybe_unused]] EcmaHandleScope handleScope(thread); + JSHandle msg = GetCallArg(argv, 0); + JSTaggedNumber numberValue = JSTaggedValue::ToNumber(thread, msg); + double value = numberValue.GetNumber(); + double result = ecmascript::base::NAN_VALUE; + // If value is NaN , -NaN , or < 0,result is NaN + if (!std::isnan(std::abs(value)) && value >= 0) { + result = std::log(value); + } + return GetTaggedDouble(result); +} + +// 20.2.2.21 +JSTaggedValue BuiltinsMath::Log1p(EcmaRuntimeCallInfo *argv) +{ + ASSERT(argv); + BUILTINS_API_TRACE(argv->GetThread(), Math, Log1p); + JSThread *thread = argv->GetThread(); + [[maybe_unused]] EcmaHandleScope handleScope(thread); + JSHandle msg = GetCallArg(argv, 0); + JSTaggedNumber numberValue = JSTaggedValue::ToNumber(thread, msg); + double value = numberValue.GetNumber(); + double result = ecmascript::base::NAN_VALUE; + // If value is NaN , -NaN , or < -1,result is NaN + if (!std::isnan(std::abs(value)) && value >= -1) { + result = std::log1p(value); + } + return GetTaggedDouble(result); +} + +// 20.2.2.22 +JSTaggedValue BuiltinsMath::Log10(EcmaRuntimeCallInfo *argv) +{ + ASSERT(argv); + BUILTINS_API_TRACE(argv->GetThread(), Math, Log10); + JSThread *thread = argv->GetThread(); + [[maybe_unused]] EcmaHandleScope handleScope(thread); + JSHandle msg = GetCallArg(argv, 0); + JSTaggedNumber numberValue = JSTaggedValue::ToNumber(thread, msg); + double value = numberValue.GetNumber(); + double result = ecmascript::base::NAN_VALUE; + // If value is NaN , -NaN , or < 0,result is NaN + if (!std::isnan(std::abs(value)) && value >= 0) { + result = std::log10(value); + } + return GetTaggedDouble(result); +} + +// 20.2.2.23 +JSTaggedValue BuiltinsMath::Log2(EcmaRuntimeCallInfo *argv) +{ + ASSERT(argv); + BUILTINS_API_TRACE(argv->GetThread(), Math, Log2); + JSThread *thread = argv->GetThread(); + [[maybe_unused]] EcmaHandleScope handleScope(thread); + JSHandle msg = GetCallArg(argv, 0); + JSTaggedNumber numberValue = JSTaggedValue::ToNumber(thread, msg); + double value = numberValue.GetNumber(); + double result = ecmascript::base::NAN_VALUE; + // If value is NaN , -NaN , or < 0,result is NaN + if (!std::isnan(std::abs(value)) && value >= 0) { + result = std::log2(value); + } + return GetTaggedDouble(result); +} + +inline bool IsNegZero(double value) +{ + return (value == 0.0 && (bit_cast(value) & ecmascript::base::DOUBLE_SIGN_MASK) == ecmascript::base::DOUBLE_SIGN_MASK); +} + +// 20.2.2.24 +JSTaggedValue BuiltinsMath::Max(EcmaRuntimeCallInfo *argv) +{ + ASSERT(argv); + BUILTINS_API_TRACE(argv->GetThread(), Math, Max); + JSThread *thread = argv->GetThread(); + [[maybe_unused]] EcmaHandleScope handleScope(thread); + int argLen = argv->GetArgsNumber(); + auto numberValue = JSTaggedNumber(-ecmascript::base::POSITIVE_INFINITY); + // If no arguments are given, the result is -inf + auto result = JSTaggedNumber(-ecmascript::base::POSITIVE_INFINITY); + auto tmpMax = -ecmascript::base::POSITIVE_INFINITY; + auto value = -ecmascript::base::POSITIVE_INFINITY; + for (int i = 0; i < argLen; i++) { + JSHandle msg = GetCallArg(argv, i); + numberValue = JSTaggedValue::ToNumber(thread, msg); + value = numberValue.GetNumber(); + if (std::isnan(std::abs(value))) { + // If any value is NaN, or -NaN, the max result is NaN + result = numberValue; + break; + } + if (value > tmpMax) { + result = numberValue; + tmpMax = value; + } else if (value == 0 && tmpMax == 0 && IsNegZero(tmpMax) && !IsNegZero(value)) { + // if tmp_max is -0, value is 0, max is 0 + result = numberValue; + tmpMax = value; + } + } + return result; +} + +// 20.2.2.25 +JSTaggedValue BuiltinsMath::Min(EcmaRuntimeCallInfo *argv) +{ + ASSERT(argv); + BUILTINS_API_TRACE(argv->GetThread(), Math, Min); + JSThread *thread = argv->GetThread(); + [[maybe_unused]] EcmaHandleScope handleScope(thread); + int argLen = argv->GetArgsNumber(); + auto numberValue = JSTaggedNumber(ecmascript::base::POSITIVE_INFINITY); + // If no arguments are given, the result is inf + auto result = JSTaggedNumber(ecmascript::base::POSITIVE_INFINITY); + auto tmpMin = ecmascript::base::POSITIVE_INFINITY; + auto value = ecmascript::base::POSITIVE_INFINITY; + for (int i = 0; i < argLen; i++) { + JSHandle msg = GetCallArg(argv, i); + numberValue = JSTaggedValue::ToNumber(thread, msg); + value = numberValue.GetNumber(); + if (std::isnan(std::abs(value))) { + // If any value is NaN or -NaN, the min result is NaN + result = numberValue; + break; + } + if (value < tmpMin) { + result = numberValue; + tmpMin = value; + } else if (value == 0 && tmpMin == 0 && !IsNegZero(tmpMin) && IsNegZero(value)) { + // if tmp_min is 0, value is -0, min is -0 + result = numberValue; + tmpMin = value; + } + } + return result; +} + +// 20.2.2.26 +JSTaggedValue BuiltinsMath::Pow(EcmaRuntimeCallInfo *argv) +{ + ASSERT(argv); + BUILTINS_API_TRACE(argv->GetThread(), Math, Pow); + JSThread *thread = argv->GetThread(); + [[maybe_unused]] EcmaHandleScope handleScope(thread); + JSHandle msgX = GetCallArg(argv, 0); + JSHandle msgY = GetCallArg(argv, 1); + JSTaggedNumber numberValueX = JSTaggedValue::ToNumber(thread, msgX); + JSTaggedNumber numberValueY = JSTaggedValue::ToNumber(thread, msgY); + double valueX = numberValueX.GetNumber(); + double valueY = numberValueY.GetNumber(); + return base::NumberHelper::Pow(valueX, valueY); +} + +// 20.2.2.27 +JSTaggedValue BuiltinsMath::Random([[maybe_unused]] EcmaRuntimeCallInfo *argv) +{ + ASSERT(argv); + BUILTINS_API_TRACE(argv->GetThread(), Math, Random); + std::random_device rd; + std::default_random_engine engine(rd()); + std::uniform_real_distribution dis(0, std::random_device::max() - 1); + // result range [0,1) + double result = dis(engine) / std::random_device::max(); + return GetTaggedDouble(result); +} + +// 20.2.2.28 +JSTaggedValue BuiltinsMath::Round(EcmaRuntimeCallInfo *argv) +{ + ASSERT(argv); + BUILTINS_API_TRACE(argv->GetThread(), Math, Round); + JSThread *thread = argv->GetThread(); + [[maybe_unused]] EcmaHandleScope handleScope(thread); + JSHandle msg = GetCallArg(argv, 0); + JSTaggedNumber numberValue = JSTaggedValue::ToNumber(thread, msg); + double value = numberValue.GetNumber(); + auto result = ecmascript::base::NAN_VALUE; + const double diff = 0.5; + double absValue = std::abs(value); + if (!std::isfinite(absValue) || absValue == 0) { + // If value is NaN, +infinite, or -infinite, VRegisterTag is DOUBLE + if (!std::isnan(absValue)) { + // If value is NaN or -NaN, the result is default NaN, else is value + result = value; + } + return GetTaggedDouble(result); + } + // If x is less than 0 but greater than or equal to -0.5, the result is -0 + if (value < 0 && value >= -diff) { + return GetTaggedDouble(-0.0); + } + // If x is greater than 0 but less than 0.5, the result is +0 + if (value > 0 && value < diff) { + return GetTaggedInt(0); + } + // For huge integers + result = std::ceil(value); + if (result - value > diff) { + result -= 1; + } + return GetTaggedDouble(result); +} + +// 20.2.2.29 +JSTaggedValue BuiltinsMath::Sign(EcmaRuntimeCallInfo *argv) +{ + ASSERT(argv); + BUILTINS_API_TRACE(argv->GetThread(), Math, Sign); + JSThread *thread = argv->GetThread(); + [[maybe_unused]] EcmaHandleScope handleScope(thread); + JSHandle msg = GetCallArg(argv, 0); + JSTaggedNumber numberValue = JSTaggedValue::ToNumber(thread, msg); + double value = numberValue.GetNumber(); + if (std::isnan(std::abs(value))) { + return GetTaggedDouble(std::abs(value)); + } + if (value == 0.0) { + return GetTaggedDouble(value); + } + if (value < 0) { + return GetTaggedInt(-1); + } + return GetTaggedInt(1); +} + +// 20.2.2.30 +JSTaggedValue BuiltinsMath::Sin(EcmaRuntimeCallInfo *argv) +{ + ASSERT(argv); + BUILTINS_API_TRACE(argv->GetThread(), Math, Sin); + JSThread *thread = argv->GetThread(); + [[maybe_unused]] EcmaHandleScope handleScope(thread); + JSHandle msg = GetCallArg(argv, 0); + JSTaggedNumber numberValue = JSTaggedValue::ToNumber(thread, msg); + double value = numberValue.GetNumber(); + double result = ecmascript::base::NAN_VALUE; + // If value is NaN or -NaN, the result is NaN + if (std::isfinite(std::abs(value))) { + result = std::sin(value); + } + return GetTaggedDouble(result); +} + +// 20.2.2.31 +JSTaggedValue BuiltinsMath::Sinh(EcmaRuntimeCallInfo *argv) +{ + ASSERT(argv); + BUILTINS_API_TRACE(argv->GetThread(), Math, Sinh); + JSThread *thread = argv->GetThread(); + [[maybe_unused]] EcmaHandleScope handleScope(thread); + JSHandle msg = GetCallArg(argv, 0); + JSTaggedNumber numberValue = JSTaggedValue::ToNumber(thread, msg); + double value = numberValue.GetNumber(); + double result = ecmascript::base::NAN_VALUE; + // If value is NaN or -NaN, the result is NaN + if (!std::isnan(std::abs(value))) { + result = std::sinh(value); + } + return GetTaggedDouble(result); +} + +// 20.2.2.32 +JSTaggedValue BuiltinsMath::Sqrt(EcmaRuntimeCallInfo *argv) +{ + ASSERT(argv); + BUILTINS_API_TRACE(argv->GetThread(), Math, Sqrt); + JSThread *thread = argv->GetThread(); + [[maybe_unused]] EcmaHandleScope handleScope(thread); + JSHandle msg = GetCallArg(argv, 0); + JSTaggedNumber numberValue = JSTaggedValue::ToNumber(thread, msg); + double value = numberValue.GetNumber(); + double result = ecmascript::base::NAN_VALUE; + // If value is NaN or -NaN, or value < 0, the result is NaN + if (!std::isnan(std::abs(value)) && value >= 0) { + result = std::sqrt(value); + } + return GetTaggedDouble(result); +} + +// 20.2.2.33 +JSTaggedValue BuiltinsMath::Tan(EcmaRuntimeCallInfo *argv) +{ + ASSERT(argv); + BUILTINS_API_TRACE(argv->GetThread(), Math, Tan); + JSThread *thread = argv->GetThread(); + [[maybe_unused]] EcmaHandleScope handleScope(thread); + JSHandle msg = GetCallArg(argv, 0); + JSTaggedNumber numberValue = JSTaggedValue::ToNumber(thread, msg); + double value = numberValue.GetNumber(); + double result = ecmascript::base::NAN_VALUE; + // If value is NaN or -NaN, +infinite, -infinite, result is NaN + if (std::isfinite(value)) { + result = std::tan(value); + } + return GetTaggedDouble(result); +} + +// 20.2.2.34 +JSTaggedValue BuiltinsMath::Tanh(EcmaRuntimeCallInfo *argv) +{ + ASSERT(argv); + BUILTINS_API_TRACE(argv->GetThread(), Math, Tanh); + JSThread *thread = argv->GetThread(); + [[maybe_unused]] EcmaHandleScope handleScope(thread); + JSHandle msg = GetCallArg(argv, 0); + JSTaggedNumber numberValue = JSTaggedValue::ToNumber(thread, msg); + double value = numberValue.GetNumber(); + double result = ecmascript::base::NAN_VALUE; + if (!std::isnan(std::abs(value))) { + result = std::tanh(value); + } + return GetTaggedDouble(result); +} + +// 20.2.2.35 +JSTaggedValue BuiltinsMath::Trunc(EcmaRuntimeCallInfo *argv) +{ + ASSERT(argv); + BUILTINS_API_TRACE(argv->GetThread(), Math, Trunc); + JSThread *thread = argv->GetThread(); + [[maybe_unused]] EcmaHandleScope handleScope(thread); + JSHandle msg = GetCallArg(argv, 0); + JSTaggedNumber numberValue = JSTaggedValue::ToNumber(thread, msg); + double value = numberValue.GetNumber(); + double result = ecmascript::base::NAN_VALUE; + if (!std::isfinite(value)) { + // if value is +infinite, -infinite, NaN, -NaN, VRegisterTag is double + if (!std::isnan(std::abs(value))) { + // if value is +infinite, -infinite, result is value ; + result = value; + } + } else { + result = std::trunc(value); + } + return GetTaggedDouble(result); +} +} // namespace panda::ecmascript::builtins diff --git a/runtime/builtins/builtins_math.h b/runtime/builtins/builtins_math.h new file mode 100644 index 000000000..ea6a53f12 --- /dev/null +++ b/runtime/builtins/builtins_math.h @@ -0,0 +1,112 @@ +/* + * Copyright (c) 2021-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_BUILTINS_BUILTINS_MATH_H +#define ECMASCRIPT_BUILTINS_BUILTINS_MATH_H + +#include "plugins/ecmascript/runtime/base/builtins_base.h" + +namespace panda::ecmascript::builtins { +class BuiltinsMath : public ecmascript::base::BuiltinsBase { +public: + // 20.2.1.1 + static constexpr double E = 2.718281828459045; + // 20.2.1.2 + static constexpr double LN10 = 2.302585092994046; + // 20.2.1.3 + static constexpr double LN2 = 0.6931471805599453; + // 20.2.1.4 + static constexpr double LOG10E = 0.4342944819032518; + // 20.2.1.5 + static constexpr double LOG2E = 1.4426950408889634; + // 20.2.1.6 + static constexpr double PI = 3.141592653589793; + // 20.2.1.7 + static constexpr double SQRT1_2 = 0.7071067811865476; + // 20.2.1.8 + static constexpr double SQRT2 = 1.4142135623730951; + // 20.2.2.1 + static JSTaggedValue Abs(EcmaRuntimeCallInfo *argv); + // 20.2.2.2 + static JSTaggedValue Acos(EcmaRuntimeCallInfo *argv); + // 20.2.2.3 + static JSTaggedValue Acosh(EcmaRuntimeCallInfo *argv); + // 20.2.2.4 + static JSTaggedValue Asin(EcmaRuntimeCallInfo *argv); + // 20.2.2.5 + static JSTaggedValue Asinh(EcmaRuntimeCallInfo *argv); + // 20.2.2.6 + static JSTaggedValue Atan(EcmaRuntimeCallInfo *argv); + // 20.2.2.7 + static JSTaggedValue Atanh(EcmaRuntimeCallInfo *argv); + // 20.2.2.8 + static JSTaggedValue Atan2(EcmaRuntimeCallInfo *argv); + // 20.2.2.9 + static JSTaggedValue Cbrt(EcmaRuntimeCallInfo *argv); + // 20.2.2.10 + static JSTaggedValue Ceil(EcmaRuntimeCallInfo *argv); + // 20.2.2.11 + static JSTaggedValue Clz32(EcmaRuntimeCallInfo *argv); + // 20.2.2.12 + static JSTaggedValue Cos(EcmaRuntimeCallInfo *argv); + // 20.2.2.13 + static JSTaggedValue Cosh(EcmaRuntimeCallInfo *argv); + // 20.2.2.14 + static JSTaggedValue Exp(EcmaRuntimeCallInfo *argv); + // 20.2.2.15 + static JSTaggedValue Expm1(EcmaRuntimeCallInfo *argv); + // 20.2.2.16 + static JSTaggedValue Floor(EcmaRuntimeCallInfo *argv); + // 20.2.2.17 + static JSTaggedValue Fround(EcmaRuntimeCallInfo *argv); + // 20.2.2.18 + static JSTaggedValue Hypot(EcmaRuntimeCallInfo *argv); + // 20.2.2.19 + static JSTaggedValue Imul(EcmaRuntimeCallInfo *argv); + // 20.2.2.20 + static JSTaggedValue Log(EcmaRuntimeCallInfo *argv); + // 20.2.2.21 + static JSTaggedValue Log1p(EcmaRuntimeCallInfo *argv); + // 20.2.2.22 + static JSTaggedValue Log10(EcmaRuntimeCallInfo *argv); + // 20.2.2.23 + static JSTaggedValue Log2(EcmaRuntimeCallInfo *argv); + // 20.2.2.24 + static JSTaggedValue Max(EcmaRuntimeCallInfo *argv); + // 20.2.2.25 + static JSTaggedValue Min(EcmaRuntimeCallInfo *argv); + // 20.2.2.26 + static JSTaggedValue Pow(EcmaRuntimeCallInfo *argv); + // 20.2.2.27 + static JSTaggedValue Random(EcmaRuntimeCallInfo *argv); + // 20.2.2.28 + static JSTaggedValue Round(EcmaRuntimeCallInfo *argv); + // 20.2.2.29 + static JSTaggedValue Sign(EcmaRuntimeCallInfo *argv); + // 20.2.2.30 + static JSTaggedValue Sin(EcmaRuntimeCallInfo *argv); + // 20.2.2.31 + static JSTaggedValue Sinh(EcmaRuntimeCallInfo *argv); + // 20.2.2.32 + static JSTaggedValue Sqrt(EcmaRuntimeCallInfo *argv); + // 20.2.2.33 + static JSTaggedValue Tan(EcmaRuntimeCallInfo *argv); + // 20.2.2.34 + static JSTaggedValue Tanh(EcmaRuntimeCallInfo *argv); + // 20.2.2.35 + static JSTaggedValue Trunc(EcmaRuntimeCallInfo *argv); +}; +} // namespace panda::ecmascript::builtins +#endif // ECMASCRIPT_BUILTINS_BUILTINS_MATH_H diff --git a/runtime/builtins/builtins_number.cpp b/runtime/builtins/builtins_number.cpp new file mode 100644 index 000000000..0c7657dba --- /dev/null +++ b/runtime/builtins/builtins_number.cpp @@ -0,0 +1,455 @@ +/* + * Copyright (c) 2021-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 "plugins/ecmascript/runtime/builtins/builtins_number.h" + +#include "plugins/ecmascript/runtime/base/number_helper.h" +#include "plugins/ecmascript/runtime/ecma_macros.h" +#include "plugins/ecmascript/runtime/global_env.h" +#include "plugins/ecmascript/runtime/js_handle.h" +#include "plugins/ecmascript/runtime/js_hclass.h" +#include "plugins/ecmascript/runtime/js_number_format.h" +#include "plugins/ecmascript/runtime/js_primitive_ref.h" +#include "plugins/ecmascript/runtime/js_tagged_number.h" +#include "plugins/ecmascript/runtime/js_tagged_value-inl.h" +#include "plugins/ecmascript/runtime/mem/c_containers.h" +#include "plugins/ecmascript/runtime/object_factory.h" +#include "plugins/ecmascript/runtime/tagged_hash_table.h" + +namespace panda::ecmascript::builtins { +using NumberHelper = ecmascript::base::NumberHelper; + +JSTaggedValue BuiltinsNumber::NumberConstructor(EcmaRuntimeCallInfo *argv) +{ + ASSERT(argv); + BUILTINS_API_TRACE(argv->GetThread(), Number, Constructor); + JSThread *thread = argv->GetThread(); + [[maybe_unused]] EcmaHandleScope handleScope(thread); + JSHandle newTarget = GetNewTarget(argv); + // 1. If no arguments were passed to this function invocation, let n be +0. + JSTaggedNumber numberValue(0); + if (argv->GetArgsNumber() > 0) { + // 2. Else, let n be ToNumber(value). + JSHandle numberInput = GetCallArg(argv, 0); + numberValue = JSTaggedValue::ToNumber(thread, numberInput); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + } + // 4. If NewTarget is undefined, return n. + if (newTarget->IsUndefined()) { + return numberValue; + } + // 5. Let O be OrdinaryCreateFromConstructor(NewTarget, "%NumberPrototype%", «[[NumberData]]» ). + JSHandle constructor = GetConstructor(argv); + JSHandle result = + thread->GetEcmaVM()->GetFactory()->NewJSObjectByConstructor(JSHandle::Cast(constructor), newTarget); + // 6. ReturnIfAbrupt(O). + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + // 7. Set the value of O’s [[NumberData]] internal slot to n. + JSPrimitiveRef::Cast(*result)->SetValue(thread, numberValue); + // 8. Return O. + return result.GetTaggedValue(); +} + +// 20.1.2.2 +JSTaggedValue BuiltinsNumber::IsFinite(EcmaRuntimeCallInfo *argv) +{ + ASSERT(argv); + BUILTINS_API_TRACE(argv->GetThread(), Number, IsFinite); + JSTaggedValue msg = GetCallArg(argv, 0).GetTaggedValue(); + // 1. If Type(number) is not Number, return false + // 2. If number is NaN, +infinite, or -infinite, return false + if (NumberHelper::IsFinite(msg)) { + return GetTaggedBoolean(true); + } + return GetTaggedBoolean(false); +} + +// 20.1.2.3 +JSTaggedValue BuiltinsNumber::IsInteger(EcmaRuntimeCallInfo *argv) +{ + ASSERT(argv); + BUILTINS_API_TRACE(argv->GetThread(), Number, IsInteger); + JSThread *thread = argv->GetThread(); + JSHandle msg = GetCallArg(argv, 0); + bool result = false; + // 1. If Type(number) is not Number, return false. + // 2. If number is NaN, +infinite, or -infinite, return false + if (NumberHelper::IsFinite(msg.GetTaggedValue())) { + [[maybe_unused]] EcmaHandleScope handleScope(thread); + double value = JSTaggedNumber(msg.GetTaggedValue()).GetNumber(); + // 3. Let integer be ToInteger(number). + JSTaggedNumber number = JSTaggedValue::ToInteger(thread, msg); + // 4. If integer is not equal to number, return false. + // 5. Otherwise, return true. + result = (value == number.GetNumber()); + } + return GetTaggedBoolean(result); +} + +// 20.1.2.4 +JSTaggedValue BuiltinsNumber::IsNaN(EcmaRuntimeCallInfo *argv) +{ + ASSERT(argv); + BUILTINS_API_TRACE(argv->GetThread(), Number, IsNaN); + JSTaggedValue msg = GetCallArg(argv, 0).GetTaggedValue(); + // 1. If Type(number) is not Number, return false. + // 2. If number is NaN, return true. + if (NumberHelper::IsNaN(msg)) { + return GetTaggedBoolean(true); + } + // 3. Otherwise, return false. + return GetTaggedBoolean(false); +} + +// 20.1.2.5 +JSTaggedValue BuiltinsNumber::IsSafeInteger(EcmaRuntimeCallInfo *argv) +{ + ASSERT(argv); + BUILTINS_API_TRACE(argv->GetThread(), Number, IsSafeInteger); + JSThread *thread = argv->GetThread(); + JSHandle msg = GetCallArg(argv, 0); + bool result = false; + // 1. If Type(number) is not Number, return false. + // 2. If number is NaN, +infinite, or -infinite, return false + if (NumberHelper::IsFinite(msg.GetTaggedValue())) { + [[maybe_unused]] EcmaHandleScope handleScope(thread); + double value = JSTaggedNumber(msg.GetTaggedValue()).GetNumber(); + // 3. Let integer be ToInteger(number). + JSTaggedNumber number = JSTaggedValue::ToInteger(thread, msg); + // 4. If integer is not equal to number, return false. + // 5. If abs(integer) ≤ 253−1, return true. + result = (value == number.GetNumber()) && std::abs(value) <= ecmascript::base::MAX_SAFE_INTEGER; + } + return GetTaggedBoolean(result); +} + +// 18.2.4 +// 20.1.2.12 +JSTaggedValue BuiltinsNumber::ParseFloatStr(const Span &str) +{ + // 4. If neither trimmedString nor any prefix of trimmedString satisfies the syntax of a StrDecimalLiteral + // (see 7.1.3.1), return NaN. + if (NumberHelper::IsEmptyString(str.begin(), str.end())) { + return BuiltinsBase::GetTaggedDouble(base::NAN_VALUE); + } + double result = NumberHelper::StringToDouble(str.begin(), str.end(), 0, base::IGNORE_TRAILING); + return GetTaggedDouble(result); +} + +JSTaggedValue BuiltinsNumber::ParseFloat(EcmaRuntimeCallInfo *argv) +{ + ASSERT(argv); + BUILTINS_API_TRACE(argv->GetThread(), Number, ParseFloat); + JSThread *thread = argv->GetThread(); + JSHandle msg = GetCallArg(argv, 0); + if (msg->IsUndefined()) { + return GetTaggedDouble(ecmascript::base::NAN_VALUE); + } + [[maybe_unused]] EcmaHandleScope handleScope(thread); + // 1. Let inputString be ToString(string). + JSHandle numberString = JSTaggedValue::ToString(thread, msg); + // 2. ReturnIfAbrupt(inputString). + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + + if (UNLIKELY(numberString->IsUtf16())) { + size_t len = ecmascript::base::utf_helper::Utf16ToUtf8Size(numberString->GetDataUtf16(), numberString->GetLength()) - 1; + CVector buf(len); + len = ecmascript::base::utf_helper::ConvertRegionUtf16ToUtf8(numberString->GetDataUtf16(), buf.data(), + numberString->GetLength(), len, 0); + auto str = Span(buf.data(), len); + return ParseFloatStr(str); + } + + auto str = Span(numberString->GetDataUtf8(), numberString->GetUtf8Length() - 1); + return ParseFloatStr(str); +} + +// 18.2.5 +// 20.1.2.13 +JSTaggedValue BuiltinsNumber::ParseIntStr(const Span &str, int32_t radix) +{ + return NumberHelper::StringToDoubleWithRadix(str.begin(), str.end(), radix); +} + +JSTaggedValue BuiltinsNumber::ParseInt(EcmaRuntimeCallInfo *argv) +{ + ASSERT(argv); + BUILTINS_API_TRACE(argv->GetThread(), Number, ParseInt); + JSThread *thread = argv->GetThread(); + [[maybe_unused]] EcmaHandleScope handleScope(thread); + JSHandle msg = GetCallArg(argv, 0); + JSHandle arg2 = GetCallArg(argv, 1); + int32_t radix = 0; + + if (!arg2->IsUndefined()) { + // 7. Let R = ToInt32(radix). + radix = JSTaggedValue::ToInt32(thread, arg2); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + } + // 1. Let inputString be ToString(string). + JSHandle numberString = JSTaggedValue::ToString(thread, msg); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + + if (UNLIKELY(numberString->IsUtf16())) { + size_t len = ecmascript::base::utf_helper::Utf16ToUtf8Size(numberString->GetDataUtf16(), numberString->GetLength()) - 1; + CVector buf(len); + len = ecmascript::base::utf_helper::ConvertRegionUtf16ToUtf8(numberString->GetDataUtf16(), buf.data(), + numberString->GetLength(), len, 0); + auto str = Span(buf.data(), len); + return ParseIntStr(str, radix); + } + + auto str = Span(numberString->GetDataUtf8(), numberString->GetUtf8Length() - 1); + return ParseIntStr(str, radix); +} + +// prototype +// 20.1.3.2 +JSTaggedValue BuiltinsNumber::ToExponential(EcmaRuntimeCallInfo *argv) +{ + ASSERT(argv); + BUILTINS_API_TRACE(argv->GetThread(), Number, ToExponential); + JSThread *thread = argv->GetThread(); + [[maybe_unused]] EcmaHandleScope handleScope(thread); + // 1. Let x be ? thisNumberValue(this value). + JSTaggedNumber value = ThisNumberValue(argv); + // 2. ReturnIfAbrupt(x). + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + + // 3. Let f be ToInteger(fractionDigits). + JSHandle digits = GetCallArg(argv, 0); + JSTaggedNumber digitInt = JSTaggedValue::ToInteger(thread, digits); + // 5. ReturnIfAbrupt(f). + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + + double values = value.GetNumber(); + // 6. If x is NaN, return the String "NaN". + if (std::isnan(values)) { + return GetTaggedString(thread, "NaN"); + } + // 8. If x < 0, then + // a. Let s be "-". + // b. Let x = –x. + // 9. If x = +infinity, then + // a. Return the concatenation of the Strings s and "Infinity". + if (!std::isfinite(values)) { + if (values < 0) { + return GetTaggedString(thread, "-Infinity"); + } + return GetTaggedString(thread, "Infinity"); + } + + // 4. Assert: f is 0, when fractionDigits is undefined. + // 10. If f < 0 or f > 20, throw a RangeError exception + double fraction = digitInt.GetNumber(); + if (digits->IsUndefined()) { + fraction = -1; + } else { + if (fraction < ecmascript::base::MIN_FRACTION || fraction > ecmascript::base::MAX_FRACTION) { + THROW_RANGE_ERROR_AND_RETURN(thread, "fraction must be 0 to 100", JSTaggedValue::Exception()); + } + } + return NumberHelper::DoubleToExponential(thread, values, static_cast(fraction)); +} + +// 20.1.3.3 +JSTaggedValue BuiltinsNumber::ToFixed(EcmaRuntimeCallInfo *argv) +{ + ASSERT(argv); + BUILTINS_API_TRACE(argv->GetThread(), Number, ToFixed); + JSThread *thread = argv->GetThread(); + [[maybe_unused]] EcmaHandleScope handleScope(thread); + // 1. Let x be ? thisNumberValue(this value). + JSTaggedNumber value = ThisNumberValue(argv); + // 2. ReturnIfAbrupt(x). + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + // 3. Let f be ToInteger(fractionDigits). (If fractionDigits is undefined, this step produces the value 0). + JSHandle digitArgv = GetCallArg(argv, 0); + JSTaggedNumber digitInt = JSTaggedValue::ToInteger(thread, digitArgv); + if (digitArgv->IsUndefined()) { + digitInt = JSTaggedNumber(0); + } + // 4. ReturnIfAbrupt(f). + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + + double digit = digitInt.GetNumber(); + if (digit < ecmascript::base::MIN_FRACTION || digit > ecmascript::base::MAX_FRACTION) { + THROW_RANGE_ERROR_AND_RETURN(thread, "fraction must be 0 to 100", JSTaggedValue::Exception()); + } + + // 6. If x is NaN, return the String "NaN". + double valueNumber = value.GetNumber(); + if (std::isnan(valueNumber)) { + return GetTaggedString(thread, "NaN"); + } + // 9. If x  1021, then + // a. Let m = ToString(x). + const double FIRST_NO_FIXED = 1e21; + if (valueNumber >= FIRST_NO_FIXED) { + return value.ToString(thread).GetTaggedValue(); + } + + return NumberHelper::DoubleToFixed(thread, valueNumber, static_cast(digit)); +} + +// 20.1.3.4 +JSTaggedValue BuiltinsNumber::ToLocaleString(EcmaRuntimeCallInfo *argv) +{ + ASSERT(argv); + JSThread *thread = argv->GetThread(); + BUILTINS_API_TRACE(thread, Number, ToLocaleString); + [[maybe_unused]] EcmaHandleScope handleScope(thread); + // 1. Let x be ? thisNumberValue(this value). + JSTaggedNumber x = ThisNumberValue(argv); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + // 2. Let numberFormat be ? Construct(%NumberFormat%, « locales, options »). + JSHandle ctor = thread->GetEcmaVM()->GetGlobalEnv()->GetNumberFormatFunction(); + ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); + JSHandle obj = factory->NewJSObjectByConstructor(JSHandle(ctor), ctor); + JSHandle numberFormat = JSHandle::Cast(obj); + JSHandle locales = GetCallArg(argv, 0); + JSHandle options = GetCallArg(argv, 1); + JSNumberFormat::InitializeNumberFormat(thread, numberFormat, locales, options); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + + // Return ? FormatNumeric(numberFormat, x). + JSHandle result = JSNumberFormat::FormatNumeric(thread, numberFormat, x); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + return result.GetTaggedValue(); +} + +// 20.1.3.5 +JSTaggedValue BuiltinsNumber::ToPrecision(EcmaRuntimeCallInfo *argv) +{ + ASSERT(argv); + BUILTINS_API_TRACE(argv->GetThread(), Number, ToPrecision); + JSThread *thread = argv->GetThread(); + [[maybe_unused]] EcmaHandleScope handleScope(thread); + // 1. Let x be ? thisNumberValue(this value). + JSTaggedNumber value = ThisNumberValue(argv); + // 2. ReturnIfAbrupt(x). + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + + // 3. If precision is undefined, return ToString(x). + JSHandle digitArgv = GetCallArg(argv, 0); + if (digitArgv->IsUndefined()) { + return value.ToString(thread).GetTaggedValue(); + } + // 4. Let p be ToInteger(precision). + JSTaggedNumber digitInt = JSTaggedValue::ToInteger(thread, digitArgv); + // 5. ReturnIfAbrupt(p). + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + + // 6. If x is NaN, return the String "NaN". + double valueNumber = value.GetNumber(); + if (std::isnan(valueNumber)) { + return GetTaggedString(thread, "NaN"); + } + // 9. If x = +infinity, then + // a. Return the String that is the concatenation of s and "Infinity". + if (!std::isfinite(valueNumber)) { + if (valueNumber < 0) { + return GetTaggedString(thread, "-Infinity"); + } + return GetTaggedString(thread, "Infinity"); + } + + // If p < 1 or p > 21, throw a RangeError exception + double digit = digitInt.GetNumber(); + if (digit < ecmascript::base::MIN_FRACTION + 1 || digit > ecmascript::base::MAX_FRACTION) { + THROW_RANGE_ERROR_AND_RETURN(thread, "fraction must be 1 to 100", JSTaggedValue::Exception()); + } + return NumberHelper::DoubleToPrecision(thread, valueNumber, static_cast(digit)); +} + +// 20.1.3.6 +JSTaggedValue BuiltinsNumber::ToString(EcmaRuntimeCallInfo *argv) +{ + ASSERT(argv); + BUILTINS_API_TRACE(argv->GetThread(), Number, ToString); + JSThread *thread = argv->GetThread(); + [[maybe_unused]] EcmaHandleScope handleScope(thread); + // 1. Let x be ? thisNumberValue(this value). + JSTaggedNumber value = ThisNumberValue(argv); + // 2. ReturnIfAbrupt(x). + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + + // 3. If radix is not present, let radixNumber be 10. + // 4. Else if radix is undefined, let radixNumber be 10. + double radix = ecmascript::base::DECIMAL; + JSHandle radixValue = GetCallArg(argv, 0); + // 5. Else let radixNumber be ToInteger(radix). + if (!radixValue->IsUndefined()) { + JSTaggedNumber radixNumber = JSTaggedValue::ToInteger(thread, radixValue); + // 6. ReturnIfAbrupt(x). + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + radix = radixNumber.GetNumber(); + } + + // 7. If radixNumber < 2 or radixNumber > 36, throw a RangeError exception. + if (radix < ecmascript::base::MIN_RADIX || radix > ecmascript::base::MAX_RADIX) { + THROW_RANGE_ERROR_AND_RETURN(thread, "radix must be 2 to 36", JSTaggedValue::Exception()); + } + // 8. If radixNumber = 10, return ToString(x). + if (radix == ecmascript::base::DECIMAL) { + return value.ToString(thread).GetTaggedValue(); + } + + double valueNumber = value.GetNumber(); + // If x is NaN, return the String "NaN". + if (std::isnan(valueNumber)) { + return GetTaggedString(thread, "NaN"); + } + // If x = +infinity, then + // Return the String that is the concatenation of s and "Infinity". + if (!std::isfinite(valueNumber)) { + if (valueNumber < 0) { + return GetTaggedString(thread, "-Infinity"); + } + return GetTaggedString(thread, "Infinity"); + } + return NumberHelper::DoubleToString(thread, valueNumber, static_cast(radix)); +} + +// 20.1.3.7 +JSTaggedValue BuiltinsNumber::ValueOf(EcmaRuntimeCallInfo *argv) +{ + ASSERT(argv); + BUILTINS_API_TRACE(argv->GetThread(), Number, ValueOf); + // 1. Let x be ? thisNumberValue(this value). + JSTaggedValue x = ThisNumberValue(argv); + JSThread *thread = argv->GetThread(); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + return x; +} + +JSTaggedNumber BuiltinsNumber::ThisNumberValue(EcmaRuntimeCallInfo *argv) +{ + BUILTINS_API_TRACE(argv->GetThread(), Number, ThisNumberValue); + JSHandle value = GetThis(argv); + if (value->IsNumber()) { + return JSTaggedNumber(value.GetTaggedValue()); + } + if (value->IsJSPrimitiveRef()) { + JSTaggedValue primitive = JSPrimitiveRef::Cast(value->GetTaggedObject())->GetValue(); + if (primitive.IsNumber()) { + return JSTaggedNumber(primitive); + } + } + JSThread *thread = argv->GetThread(); + [[maybe_unused]] EcmaHandleScope handleScope(thread); + THROW_TYPE_ERROR_AND_RETURN(thread, "not number type", JSTaggedNumber::Exception()); +} +} // namespace panda::ecmascript::builtins diff --git a/runtime/builtins/builtins_number.h b/runtime/builtins/builtins_number.h new file mode 100644 index 000000000..40d30e5c1 --- /dev/null +++ b/runtime/builtins/builtins_number.h @@ -0,0 +1,61 @@ +/* + * Copyright (c) 2021-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_BUILTINS_BUILTINS_NUMBER_H +#define ECMASCRIPT_BUILTINS_BUILTINS_NUMBER_H + +#include "plugins/ecmascript/runtime/base/builtins_base.h" +#include "plugins/ecmascript/runtime/js_tagged_value.h" + +namespace panda::ecmascript::builtins { +class BuiltinsNumber : public ecmascript::base::BuiltinsBase { +public: + // 20.1.1.1 + static JSTaggedValue NumberConstructor(EcmaRuntimeCallInfo *argv); + + // 20.1.2.2 + static JSTaggedValue IsFinite(EcmaRuntimeCallInfo *argv); + // 20.1.2.3 + static JSTaggedValue IsInteger(EcmaRuntimeCallInfo *argv); + // 20.1.2.4 + static JSTaggedValue IsNaN(EcmaRuntimeCallInfo *argv); + // 20.1.2.5 + static JSTaggedValue IsSafeInteger(EcmaRuntimeCallInfo *argv); + // 20.1.2.12 + static JSTaggedValue ParseFloat(EcmaRuntimeCallInfo *argv); + // 20.1.2.13 + static JSTaggedValue ParseInt(EcmaRuntimeCallInfo *argv); + + // prototype + // 20.1.3.2 + static JSTaggedValue ToExponential(EcmaRuntimeCallInfo *argv); + // 20.1.3.3 + static JSTaggedValue ToFixed(EcmaRuntimeCallInfo *argv); + // 20.1.3.4 + static JSTaggedValue ToLocaleString(EcmaRuntimeCallInfo *argv); + // 20.1.3.5 + static JSTaggedValue ToPrecision(EcmaRuntimeCallInfo *argv); + // 20.1.3.6 + static JSTaggedValue ToString(EcmaRuntimeCallInfo *argv); + // 20.1.3.7 + static JSTaggedValue ValueOf(EcmaRuntimeCallInfo *argv); + +private: + static JSTaggedValue ParseFloatStr(const Span &str); + static JSTaggedValue ParseIntStr(const Span &str, int32_t radix); + static JSTaggedNumber ThisNumberValue(EcmaRuntimeCallInfo *argv); +}; +} // namespace panda::ecmascript::builtins +#endif // ECMASCRIPT_BUILTINS_BUILTINS_NUBMER_H diff --git a/runtime/builtins/builtins_number_format.cpp b/runtime/builtins/builtins_number_format.cpp new file mode 100644 index 000000000..e5889473c --- /dev/null +++ b/runtime/builtins/builtins_number_format.cpp @@ -0,0 +1,206 @@ +/* + * Copyright (c) 2021-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 "builtins_number_format.h" +#include "plugins/ecmascript/runtime/ecma_vm.h" +#include "plugins/ecmascript/runtime/global_env.h" +#include "plugins/ecmascript/runtime/js_array.h" +#include "plugins/ecmascript/runtime/js_intl.h" +#include "plugins/ecmascript/runtime/js_locale.h" +#include "plugins/ecmascript/runtime/js_number_format.h" +#include "plugins/ecmascript/runtime/js_tagged_value.h" +#include "plugins/ecmascript/runtime/object_factory.h" + +namespace panda::ecmascript::builtins { +// 13.2.1 Intl.NumberFormat ( [ locales [ , options ] ] ) +JSTaggedValue BuiltinsNumberFormat::NumberFormatConstructor(EcmaRuntimeCallInfo *argv) +{ + JSThread *thread = argv->GetThread(); + [[maybe_unused]] EcmaHandleScope scope(thread); + EcmaVM *ecmaVm = thread->GetEcmaVM(); + JSHandle env = ecmaVm->GetGlobalEnv(); + ObjectFactory *factory = ecmaVm->GetFactory(); + + // 1. If NewTarget is undefined, let newTarget be the active function object, else let newTarget be NewTarget. + JSHandle constructor = GetConstructor(argv); + JSHandle newTarget = GetNewTarget(argv); + if (newTarget->IsUndefined()) { + newTarget = constructor; + } + + // Let numberFormat be ? OrdinaryCreateFromConstructor(newTarget, "%NumberFormatPrototype%", + // « [[InitializedNumberFormat]], [[Locale]], [[DataLocale]], [[NumberingSystem]], [[Style]], [[Unit]], + // [[UnitDisplay]], [[Currency]], [[CurrencyDisplay]], [[CurrencySign]], [[MinimumIntegerDigits]], + // [[MinimumFractionDigits]], [[MaximumFractionDigits]], [[MinimumSignificantDigits]], [[MaximumSignificantDigits]], + // [[RoundingType]], [[Notation]], [[CompactDisplay]], [[UseGrouping]], [[SignDisplay]], [[BoundFormat]] »). + JSHandle numberFormat = + JSHandle::Cast(factory->NewJSObjectByConstructor(JSHandle(constructor), newTarget)); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + + // 3. Perform ? InitializeNumberFormat(numberFormat, locales, options). + JSHandle locales = GetCallArg(argv, 0); + JSHandle options = GetCallArg(argv, 1); + JSNumberFormat::InitializeNumberFormat(thread, numberFormat, locales, options); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + + // 4. Let this be the this value. + JSHandle thisValue = GetThis(argv); + + // 5. If NewTarget is undefined and Type(this) is Object and ? InstanceofOperator(this, %NumberFormat%) is true, + // then + // a. Perform ? DefinePropertyOrThrow(this, %Intl%.[[FallbackSymbol]], PropertyDescriptor{ + // [[Value]]: numberFormat, [[Writable]]: false, [[Enumerable]]: false, [[Configurable]]: false }). + // b. Return this. + bool isInstanceOf = JSObject::InstanceOf(thread, thisValue, env->GetNumberFormatFunction()); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + if (newTarget->IsUndefined() && thisValue->IsJSObject() && isInstanceOf) { + PropertyDescriptor descriptor(thread, JSHandle::Cast(numberFormat), false, false, false); + JSHandle key(thread, JSHandle::Cast(env->GetIntlFunction())->GetFallbackSymbol()); + JSTaggedValue::DefinePropertyOrThrow(thread, thisValue, key, descriptor); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + return thisValue.GetTaggedValue(); + } + + // 6. Return numberFormat. + return numberFormat.GetTaggedValue(); +} + +// 13.3.2 Intl.NumberFormat.supportedLocalesOf ( locales [ , options ] ) +JSTaggedValue BuiltinsNumberFormat::SupportedLocalesOf(EcmaRuntimeCallInfo *argv) +{ + JSThread *thread = argv->GetThread(); + [[maybe_unused]] EcmaHandleScope scope(thread); + // 1. Let availableLocales be %NumberFormat%.[[AvailableLocales]]. + JSHandle availableLocales = JSNumberFormat::GetAvailableLocales(thread); + + // 2. Let requestedLocales be ? CanonicalizeLocaleList(locales). + JSHandle locales = GetCallArg(argv, 0); + JSHandle requestedLocales = JSLocale::CanonicalizeLocaleList(thread, locales); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + + // 3. Return ? SupportedLocales(availableLocales, requestedLocales, options). + JSHandle options = GetCallArg(argv, 1); + JSHandle result = JSLocale::SupportedLocales(thread, availableLocales, requestedLocales, options); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + return result.GetTaggedValue(); +} + +// 13.4.3 get Intl.NumberFormat.prototype.format +JSTaggedValue BuiltinsNumberFormat::Format(EcmaRuntimeCallInfo *argv) +{ + JSThread *thread = argv->GetThread(); + [[maybe_unused]] EcmaHandleScope scope(thread); + + // 1. Let nf be this value. + JSHandle thisValue = GetThis(argv); + // 2. If Type(nf) is not Object, throw a TypeError exception. + if (!thisValue->IsJSObject()) { + THROW_TYPE_ERROR_AND_RETURN(thread, "nf is not object", JSTaggedValue::Exception()); + } + // 3. Let nf be ? UnwrapNumberFormat(nf). + JSHandle nf = JSNumberFormat::UnwrapNumberFormat(thread, thisValue); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + if (nf->IsUndefined()) { + THROW_TYPE_ERROR_AND_RETURN(thread, "nf is not object", JSTaggedValue::Exception()); + } + + JSHandle typpedNf = JSHandle::Cast(nf); + JSHandle boundFunc(thread, typpedNf->GetBoundFormat()); + // 4. If nf.[[BoundFormat]] is undefined, then + // a. Let F be a new built-in function object as defined in Number Format Functions (12.1.4). + // b. Set F.[[NumberFormat]] to nf. + // c. Set nf.[[BoundFormat]] to F. + if (boundFunc->IsUndefined()) { + ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); + JSHandle intlBoundFunc = factory->NewJSIntlBoundFunction( + reinterpret_cast(BuiltinsNumberFormat::NumberFormatInternalFormatNumber)); + intlBoundFunc->SetNumberFormat(thread, typpedNf); + typpedNf->SetBoundFormat(thread, intlBoundFunc); + } + return typpedNf->GetBoundFormat(); +} + +// 13.4.4 Intl.NumberFormat.prototype.formatToParts ( date ) +JSTaggedValue BuiltinsNumberFormat::FormatToParts(EcmaRuntimeCallInfo *argv) +{ + JSThread *thread = argv->GetThread(); + [[maybe_unused]] EcmaHandleScope scope(thread); + // 1. Let nf be the this value. + JSHandle nf = GetThis(argv); + // 2. Perform ? RequireInternalSlot(nf, [[InitializedNumberFormat]]). + if (!nf->IsJSNumberFormat()) { + THROW_TYPE_ERROR_AND_RETURN(thread, "", JSTaggedValue::Exception()); + } + // 3. Let x be ? ToNumeric(value). + JSHandle value = GetCallArg(argv, 0); + JSTaggedNumber x = JSTaggedValue::ToNumber(thread, value); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + + JSHandle result = JSNumberFormat::FormatNumericToParts(thread, JSHandle::Cast(nf), x); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + + return result.GetTaggedValue(); +} +// 13.4.5 Intl.NumberFormat.prototype.resolvedOptions () +JSTaggedValue BuiltinsNumberFormat::ResolvedOptions(EcmaRuntimeCallInfo *argv) +{ + JSThread *thread = argv->GetThread(); + [[maybe_unused]] EcmaHandleScope scope(thread); + // 1. Let nf be this value. + JSHandle thisValue = GetThis(argv); + // 2. If Type(nf) is not Object, throw a TypeError exception. + if (!thisValue->IsJSObject()) { + THROW_TYPE_ERROR_AND_RETURN(thread, "this is not object", JSTaggedValue::Exception()); + } + // 3. Let nf be ? UnwrapNumberFormat(nf). + JSHandle nf = JSNumberFormat::UnwrapNumberFormat(thread, thisValue); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + // 4. Let options be ! ObjectCreate(%ObjectPrototype%). + auto ecmaVm = thread->GetEcmaVM(); + JSHandle env = ecmaVm->GetGlobalEnv(); + ObjectFactory *factory = ecmaVm->GetFactory(); + JSHandle ctor = env->GetObjectFunction(); + JSHandle options(factory->NewJSObjectByConstructor(JSHandle(ctor), ctor)); + + // 5. For each row of Table 5, except the header row, in table order, do + // Let p be the Property value of the current row. + // Let v be the value of nf's internal slot whose name is the Internal Slot value of the current row. + // If v is not undefined, then + // Perform ! CreateDataPropertyOrThrow(options, p, v). + JSNumberFormat::ResolvedOptions(thread, JSHandle::Cast(nf), options); + return options.GetTaggedValue(); +} + +JSTaggedValue BuiltinsNumberFormat::NumberFormatInternalFormatNumber(EcmaRuntimeCallInfo *argv) +{ + JSThread *thread = argv->GetThread(); + [[maybe_unused]] EcmaHandleScope scope(thread); + JSHandle intlBoundFunc = JSHandle::Cast(GetConstructor(argv)); + + // 1. Let nf be F.[[NumberFormat]]. + JSHandle nf(thread, intlBoundFunc->GetNumberFormat()); + // 2. Assert: Type(nf) is Object and nf has an [[InitializedNumberFormat]] internal slot. + ASSERT(nf->IsJSObject() && nf->IsJSNumberFormat()); + // 3. If value is not provided, let value be undefined. + JSHandle value = GetCallArg(argv, 0); + // 4 Let x be ? ToNumeric(value). + JSTaggedNumber x = JSTaggedValue::ToNumber(thread, value); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + // 5 Return ? FormatNumeric(nf, x). + JSHandle result = JSNumberFormat::FormatNumeric(thread, JSHandle::Cast(nf), x); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + return result.GetTaggedValue(); +} +} // namespace panda::ecmascript::builtins diff --git a/runtime/builtins/builtins_number_format.h b/runtime/builtins/builtins_number_format.h new file mode 100644 index 000000000..c8ea65754 --- /dev/null +++ b/runtime/builtins/builtins_number_format.h @@ -0,0 +1,42 @@ +/* + * Copyright (c) 2021-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_BUILTINS_BUILTINS_NUMBER_FORMAT_H +#define ECMASCRIPT_BUILTINS_BUILTINS_NUMBER_FORMAT_H + +#include "plugins/ecmascript/runtime/base/builtins_base.h" + +namespace panda::ecmascript::builtins { +class BuiltinsNumberFormat : public ecmascript::base::BuiltinsBase { +public: + // 13.2.1 Intl.DateTimeFormat ( [ locales [ , options ] ] ) + static JSTaggedValue NumberFormatConstructor(EcmaRuntimeCallInfo *argv); + + // 13.3.2 Intl.DateTimeFormat.supportedLocalesOf ( locales [ , options ] ) + static JSTaggedValue SupportedLocalesOf(EcmaRuntimeCallInfo *argv); + + // 13.4.3 get Intl.DateTimeFormat.prototype.format + static JSTaggedValue Format(EcmaRuntimeCallInfo *argv); + + // 13.4.4 Intl.DateTimeFormat.prototype.formatToParts ( date ) + static JSTaggedValue FormatToParts(EcmaRuntimeCallInfo *argv); + + // 13.4.5 Intl.DateTimeFormat.prototype.resolvedOptions () + static JSTaggedValue ResolvedOptions(EcmaRuntimeCallInfo *argv); + + static JSTaggedValue NumberFormatInternalFormatNumber(EcmaRuntimeCallInfo *argv); +}; +} // namespace panda::ecmascript::builtins +#endif // ECMASCRIPT_BUILTINS_BUILTINS_NUMBER_FORMAT_H diff --git a/runtime/builtins/builtins_object.cpp b/runtime/builtins/builtins_object.cpp new file mode 100644 index 000000000..773a39320 --- /dev/null +++ b/runtime/builtins/builtins_object.cpp @@ -0,0 +1,1179 @@ +/* + * Copyright (c) 2021-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 "plugins/ecmascript/runtime/builtins/builtins_object.h" +#include "plugins/ecmascript/runtime/base/object_helper.h" +#include "plugins/ecmascript/runtime/builtins.h" +#include "plugins/ecmascript/runtime/ecma_macros.h" +#include "plugins/ecmascript/runtime/global_env.h" +#include "plugins/ecmascript/runtime/internal_call_params.h" +#include "plugins/ecmascript/runtime/interpreter/fast_runtime_stub-inl.h" +#include "plugins/ecmascript/runtime/js_array.h" +#include "plugins/ecmascript/runtime/js_function.h" +#include "plugins/ecmascript/runtime/js_handle.h" +#include "plugins/ecmascript/runtime/js_primitive_ref.h" +#include "plugins/ecmascript/runtime/js_realm.h" +#include "plugins/ecmascript/runtime/object_factory.h" + +namespace panda::ecmascript::builtins { +// 19.1.1.1 Object ( [ value ] ) +JSTaggedValue BuiltinsObject::ObjectConstructor(EcmaRuntimeCallInfo *argv) +{ + ASSERT(argv); + BUILTINS_API_TRACE(argv->GetThread(), Object, Constructor); + JSThread *thread = argv->GetThread(); + [[maybe_unused]] EcmaHandleScope handleScope(thread); + auto ecmaVm = thread->GetEcmaVM(); + JSHandle env = ecmaVm->GetGlobalEnv(); + + // 1.If NewTarget is neither undefined nor the active function, then + // a.Return OrdinaryCreateFromConstructor(NewTarget, "%ObjectPrototype%"). + JSHandle constructor = GetConstructor(argv); + JSHandle newTarget = GetNewTarget(argv); + if (!newTarget->IsUndefined() && !(newTarget.GetTaggedValue() == constructor.GetTaggedValue())) { + JSHandle obj = + ecmaVm->GetFactory()->NewJSObjectByConstructor(JSHandle(constructor), newTarget); + return obj.GetTaggedValue(); + } + + // 2.If value is null, undefined or not supplied, return ObjectCreate(%ObjectPrototype%). + JSHandle value = GetCallArg(argv, 0); + if (value->IsNull() || value->IsUndefined()) { + JSHandle obj = ecmaVm->GetFactory()->OrdinaryNewJSObjectCreate(env->GetObjectFunctionPrototype()); + return obj.GetTaggedValue(); + } + + // 3.Return ToObject(value). + return JSTaggedValue::ToObject(thread, value).GetTaggedValue(); +} + +// 19.1.2.1 Object.assign ( target, ...sources ) +JSTaggedValue BuiltinsObject::Assign(EcmaRuntimeCallInfo *argv) +{ + ASSERT(argv); + BUILTINS_API_TRACE(argv->GetThread(), Object, Assign); + JSThread *thread = argv->GetThread(); + [[maybe_unused]] EcmaHandleScope handleScope(thread); + + uint32_t numArgs = argv->GetArgsNumber(); + // 1.Let to be ToObject(target). + JSHandle target = GetCallArg(argv, 0); + JSHandle toAssign = JSTaggedValue::ToObject(thread, target); + // 2.ReturnIfAbrupt(to). + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + + // 3.If only one argument was passed, return to. + // 4.Let sources be the List of argument values starting with the second argument. + // 5.For each element nextSource of sources, in ascending index order + // a.If nextSource is undefined or null, let keys be an empty List. + // b.Else, + // i.Let from be ToObject(nextSource). + // ii.Let keys be from.[[OwnPropertyKeys]](). + // iii.ReturnIfAbrupt(keys). + JSMutableHandle key(thread, JSTaggedValue::Undefined()); + for (uint32_t i = 1; i < numArgs; i++) { + JSHandle source = GetCallArg(argv, i); + if (!source->IsNull() && !source->IsUndefined()) { + JSHandle from = JSTaggedValue::ToObject(thread, source); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + + JSHandle keys = JSTaggedValue::GetOwnPropertyKeys(thread, JSHandle::Cast(from)); + // ReturnIfAbrupt(keys) + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + + // c.Repeat for each element nextKey of keys in List order, + // i.Let desc be from.[[GetOwnProperty]](nextKey). + // ii.ReturnIfAbrupt(desc). + // iii.if desc is not undefined and desc.[[Enumerable]] is true, then + // 1.Let propValue be Get(from, nextKey). + // 2.ReturnIfAbrupt(propValue). + // 3.Let status be Set(to, nextKey, propValue, true). + // 4.ReturnIfAbrupt(status). + uint32_t keysLen = keys->GetLength(); + for (uint32_t j = 0; j < keysLen; j++) { + PropertyDescriptor desc(thread); + key.Update(keys->Get(j)); + bool success = JSTaggedValue::GetOwnProperty(thread, JSHandle::Cast(from), key, desc); + // ReturnIfAbrupt(desc) + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + + if (success && desc.IsEnumerable()) { + JSTaggedValue value = + FastRuntimeStub::FastGetPropertyByValue(thread, from.GetTaggedValue(), key.GetTaggedValue()); + // ReturnIfAbrupt(prop_value) + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + + FastRuntimeStub::FastSetPropertyByValue(thread, toAssign.GetTaggedValue(), key.GetTaggedValue(), + value); + // ReturnIfAbrupt(status) + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + } + } + } + } + + // 6.Return to. + return toAssign.GetTaggedValue(); +} + +// Runtime Semantics +JSTaggedValue BuiltinsObject::ObjectDefineProperties(JSThread *thread, const JSHandle &obj, + const JSHandle &prop) +{ + BUILTINS_API_TRACE(thread, Object, DefineProperties); + ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); + // 1.If Type(O) is not Object, throw a TypeError exception. + if (!obj->IsECMAObject()) { + // throw a TypeError exception + THROW_TYPE_ERROR_AND_RETURN(thread, "is not an object", JSTaggedValue::Exception()); + } + + // 2.Let props be ToObject(Properties). + JSHandle props = JSTaggedValue::ToObject(thread, prop); + + // 3.ReturnIfAbrupt(props). + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + + // 4.Let keys be props.[[OwnPropertyKeys]](). + JSHandle handleKeys = JSTaggedValue::GetOwnPropertyKeys(thread, JSHandle::Cast(props)); + + // 5.ReturnIfAbrupt(keys). + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + + // 6.Let descriptors be an empty List. + // new an empty array and append + uint32_t length = handleKeys->GetLength(); + [[maybe_unused]] JSHandle descriptors = + factory->NewTaggedArray(2 * length); // 2: 2 means two element list + + // 7.Repeat for each element nextKey of keys in List order, + // a.Let propDesc be props.[[GetOwnProperty]](nextKey). + // b.ReturnIfAbrupt(propDesc). + // c.If propDesc is not undefined and propDesc.[[Enumerable]] is true, then + // i.Let descObj be Get( props, nextKey). + // ii.ReturnIfAbrupt(descObj). + // iii.Let desc be ToPropertyDescriptor(descObj). + // iv.ReturnIfAbrupt(desc). + // v.Append the pair (a two element List) consisting of nextKey and desc to the end of descriptors. + JSMutableHandle handleKey(thread, JSTaggedValue::Undefined()); + for (uint32_t i = 0; i < length; i++) { + PropertyDescriptor propDesc(thread); + handleKey.Update(handleKeys->Get(i)); + + bool success = JSTaggedValue::GetOwnProperty(thread, JSHandle::Cast(props), handleKey, propDesc); + // ReturnIfAbrupt(propDesc) + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + + if (success && propDesc.IsEnumerable()) { + JSHandle descObj = + JSTaggedValue::GetProperty(thread, JSHandle::Cast(props), handleKey).GetValue(); + // ReturnIfAbrupt(descObj) + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + + PropertyDescriptor desc(thread); + JSObject::ToPropertyDescriptor(thread, descObj, desc); + + // ReturnIfAbrupt(desc) + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + + // 8.For each pair from descriptors in list order, + // a.Let P be the first element of pair. + // b.Let desc be the second element of pair. + // c.Let status be DefinePropertyOrThrow(O,P, desc). + // d.ReturnIfAbrupt(status). + [[maybe_unused]] bool setSuccess = JSTaggedValue::DefinePropertyOrThrow(thread, obj, handleKey, desc); + + // ReturnIfAbrupt(status) + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + } + } + + // 9.Return O. + return obj.GetTaggedValue(); +} + +// 19.1.2.2 Object.create ( O [ , Properties ] ) +JSTaggedValue BuiltinsObject::Create(EcmaRuntimeCallInfo *argv) +{ + ASSERT(argv); + BUILTINS_API_TRACE(argv->GetThread(), Object, Create); + JSThread *thread = argv->GetThread(); + [[maybe_unused]] EcmaHandleScope handleScope(thread); + // 1.If Type(O) is neither Object nor Null, throw a TypeError exception. + JSHandle obj = GetCallArg(argv, 0); + if (!obj->IsECMAObject() && !obj->IsNull()) { + // throw a TypeError exception + THROW_TYPE_ERROR_AND_RETURN(thread, "Create: O is neither Object nor Null", JSTaggedValue::Exception()); + } + + JSHandle properties = GetCallArg(argv, 1); + + // 2.Let obj be ObjectCreate(O). + JSHandle objCreate = thread->GetEcmaVM()->GetFactory()->OrdinaryNewJSObjectCreate(obj); + + // 3.If the argument Properties is present and not undefined, then + // a.Return ObjectDefineProperties(obj, Properties). + if (!properties->IsUndefined()) { + return ObjectDefineProperties(thread, JSHandle::Cast(objCreate), properties); + } + + // 4.Return obj. + return objCreate.GetTaggedValue(); +} + +// 19.1.2.3 Object.defineProperties ( O, Properties ) +JSTaggedValue BuiltinsObject::DefineProperties(EcmaRuntimeCallInfo *argv) +{ + ASSERT(argv); + BUILTINS_API_TRACE(argv->GetThread(), Object, DefineProperties); + JSThread *thread = argv->GetThread(); + [[maybe_unused]] EcmaHandleScope handleScope(thread); + // 1.Return ObjectDefineProperties(O, Properties). + return ObjectDefineProperties(thread, GetCallArg(argv, 0), GetCallArg(argv, 1)); +} + +// 19.1.2.4 Object.defineProperty ( O, P, Attributes ) +JSTaggedValue BuiltinsObject::DefineProperty(EcmaRuntimeCallInfo *argv) +{ + ASSERT(argv); + BUILTINS_API_TRACE(argv->GetThread(), Object, DefineProperty); + JSThread *thread = argv->GetThread(); + [[maybe_unused]] EcmaHandleScope handleScope(thread); + + // 1.If Type(O) is not Object, throw a TypeError exception. + JSHandle obj = GetCallArg(argv, 0); + if (!obj->IsECMAObject()) { + // throw a TypeError + THROW_TYPE_ERROR_AND_RETURN(thread, "DefineProperty: O is not Object", JSTaggedValue::Exception()); + } + + // 2.Let key be ToPropertyKey(P). + JSHandle prop = GetCallArg(argv, 1); + JSHandle key = JSTaggedValue::ToPropertyKey(thread, prop); + + // 3.ReturnIfAbrupt(key). + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + // 4.Let desc be ToPropertyDescriptor(Attributes). + PropertyDescriptor desc(thread); + JSObject::ToPropertyDescriptor(thread, GetCallArg(argv, BuiltinsBase::ArgsPosition::THIRD), desc); + + // 5.ReturnIfAbrupt(desc). + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + + // 6.Let success be DefinePropertyOrThrow(O,key, desc). + [[maybe_unused]] bool success = JSTaggedValue::DefinePropertyOrThrow(thread, obj, key, desc); + + // 7.ReturnIfAbrupt(success). + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + // 8.Return O. + return obj.GetTaggedValue(); +} + +// 19.1.2.5 Object.freeze ( O ) +JSTaggedValue BuiltinsObject::Freeze(EcmaRuntimeCallInfo *argv) +{ + ASSERT(argv); + BUILTINS_API_TRACE(argv->GetThread(), Object, Freeze); + + // 1.If Type(O) is not Object, return O. + JSHandle obj = GetCallArg(argv, 0); + if (!obj->IsECMAObject()) { + return obj.GetTaggedValue(); + } + + JSThread *thread = argv->GetThread(); + [[maybe_unused]] EcmaHandleScope handleScope(thread); + + // 2.Let status be SetIntegrityLevel( O, "frozen"). + bool status = JSObject::SetIntegrityLevel(thread, JSHandle(obj), IntegrityLevel::FROZEN); + + // 3.ReturnIfAbrupt(status). + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + + // 4.If status is false, throw a TypeError exception. + if (!status) { + // throw a TypeError exception + THROW_TYPE_ERROR_AND_RETURN(thread, "Freeze: freeze failed", JSTaggedValue::Exception()); + } + + // 5.Return O. + return obj.GetTaggedValue(); +} + +// ES2021 20.1.2.7 Object.fromEntries ( iterable ) +JSTaggedValue BuiltinsObject::FromEntries(EcmaRuntimeCallInfo *argv) +{ + ASSERT(argv); + BUILTINS_API_TRACE(argv->GetThread(), Object, SetPrototypeOf); + JSThread *thread = argv->GetThread(); + [[maybe_unused]] EcmaHandleScope handleScope(thread); + + // 1. Perform ? RequireObjectCoercible(iterable). + JSHandle iterable = GetCallArg(argv, 0); + JSTaggedValue::RequireObjectCoercible(thread, iterable); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + + // 2. Let obj be ! OrdinaryObjectCreate(%Object.prototype%). + JSHandle constructor(thread->GetEcmaVM()->GetGlobalEnv()->GetObjectFunction()); + JSHandle obj( + thread->GetEcmaVM()->GetFactory()->NewJSObjectByConstructor(JSHandle(constructor), constructor)); + + // 3. Assert: obj is an extensible ordinary object with no own properties. + ASSERT(obj->IsExtensible()); + + // 4. Let stepsDefine be the algorithm steps defined in CreateDataPropertyOnObject Functions. + EcmaEntrypoint stepsDefine = base::ObjectHelper::CreateDataPropertyOnObject; + + // 5. Let lengthDefine be the number of non-optional parameters of the function definition in + size_t lengthDefine = 2; + // CreateDataPropertyOnObject Functions. + // 6. Let adder be ! CreateBuiltinFunction(stepsDefine, lengthDefine, "", « »). + + ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); + JSHandle adder = + factory->CreateBuiltinFunction(stepsDefine, lengthDefine, JSHandle(factory->NewFromString("")), + JSTaggedValue::Undefined(), thread->GlobalConstants()->GetHandledUndefined()); + + // 7. Return ? AddEntriesFromIterable(obj, iterable, adder). + JSHandle adderHandler(thread, adder.GetTaggedValue()); + JSTaggedValue result = + base::ObjectHelper::AddEntriesFromIterable(thread, JSHandle(obj), iterable, adderHandler); + RETURN_VALUE_IF_ABRUPT_COMPLETION(thread, result); + return result; +} + +// 19.1.2.6 Object.getOwnPropertyDescriptor ( O, P ) +JSTaggedValue BuiltinsObject::GetOwnPropertyDesciptor(EcmaRuntimeCallInfo *argv) +{ + ASSERT(argv); + BUILTINS_API_TRACE(argv->GetThread(), Object, GetOwnPropertyDesciptor); + JSThread *thread = argv->GetThread(); + [[maybe_unused]] EcmaHandleScope handleScope(thread); + + // 1.Let obj be ToObject(O). + JSHandle func = GetCallArg(argv, 0); + JSHandle handle = JSTaggedValue::ToObject(thread, func); + + // 2.ReturnIfAbrupt(obj). + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + + // 3.Let key be ToPropertyKey(P). + JSHandle prop = GetCallArg(argv, 1); + JSHandle key = JSTaggedValue::ToPropertyKey(thread, prop); + + // 4.ReturnIfAbrupt(key). + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + + // 5.Let desc be obj.[[GetOwnProperty]](key). + PropertyDescriptor desc(thread); + JSTaggedValue::GetOwnProperty(thread, JSHandle::Cast(handle), key, desc); + + // 6.ReturnIfAbrupt(desc). + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + + // 7.Return FromPropertyDescriptor(desc). + JSHandle res = JSObject::FromPropertyDescriptor(thread, desc); + return res.GetTaggedValue(); +} + +// ES2021 20.1.2.9 Object.getOwnPropertyDescriptors ( O ) +JSTaggedValue BuiltinsObject::GetOwnPropertyDesciptors(EcmaRuntimeCallInfo *argv) +{ + ASSERT(argv); + BUILTINS_API_TRACE(argv->GetThread(), Object, GetOwnPropertyDesciptor); + JSThread *thread = argv->GetThread(); + [[maybe_unused]] EcmaHandleScope handleScope(thread); + + // 1. Let obj be ? ToObject(O). + JSHandle func = GetCallArg(argv, 0); + JSHandle obj = JSTaggedValue::ToObject(thread, func); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + + // 2. Let ownKeys be ? obj.[[OwnPropertyKeys]](). + JSHandle ownKeys = JSTaggedValue::GetOwnPropertyKeys(thread, JSHandle::Cast(obj)); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + + // 3. Let obj be ! OrdinaryObjectCreate(%Object.prototype%). + JSHandle constructor(thread->GetEcmaVM()->GetGlobalEnv()->GetObjectFunction()); + JSHandle descriptors( + thread->GetEcmaVM()->GetFactory()->NewJSObjectByConstructor(JSHandle(constructor), constructor)); + + // 4. For each element key of ownKeys, do + for (array_size_t i = 0; i < ownKeys->GetLength(); i++) { + // a. Let desc be ? obj.[[GetOwnProperty]](key). + JSHandle key(thread, ownKeys->Get(i)); + PropertyDescriptor desc(thread); + JSTaggedValue::GetOwnProperty(thread, JSHandle::Cast(obj), key, desc); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + + // b. Let descriptor be ! FromPropertyDescriptor(desc). + JSHandle descriptor = JSObject::FromPropertyDescriptor(thread, desc); + + // c. If descriptor is not undefined, perform ! CreateDataPropertyOrThrow(descriptors, key, descriptor). + if (!descriptor->IsUndefined()) { + JSObject::CreateDataPropertyOrThrow(thread, descriptors, key, descriptor); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + } + } + + // 5. return descriptors + return descriptors.GetTaggedValue(); +} + +// Runtime Semantics +JSTaggedValue BuiltinsObject::GetOwnPropertyKeys(JSThread *thread, const JSHandle &object, + const KeyType &type) +{ + BUILTINS_API_TRACE(thread, Object, GetOwnPropertyKeys); + // 1.Let obj be ToObject(O). + ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); + JSHandle obj = JSTaggedValue::ToObject(thread, object); + + // 2.ReturnIfAbrupt(obj). + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + + // 3.Let keys be obj.[[OwnPropertyKeys]](). + JSHandle handleKeys = JSTaggedValue::GetOwnPropertyKeys(thread, JSHandle::Cast(obj)); + + // 4.ReturnIfAbrupt(keys). + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + + // 5.Let nameList be a new empty List. + // new an empty array and append + uint32_t length = handleKeys->GetLength(); + JSHandle nameList = factory->NewTaggedArray(length); + + // 6.Repeat for each element nextKey of keys in List order, + uint32_t copyLength = 0; + switch (type) { + case KeyType::STRING_TYPE: { + for (uint32_t i = 0; i < length; i++) { + JSTaggedValue key = handleKeys->Get(i); + if (key.IsString()) { + nameList->Set(thread, copyLength, key); + copyLength++; + } + } + break; + } + case KeyType::SYMBOL_TYPE: { + for (uint32_t i = 0; i < length; i++) { + JSTaggedValue key = handleKeys->Get(i); + if (key.IsSymbol()) { + nameList->Set(thread, copyLength, key); + copyLength++; + } + } + break; + } + default: + break; + } + + // 7.Return CreateArrayFromList(nameList). + JSHandle resultList = factory->CopyArray(nameList, length, copyLength); + JSHandle resultArray = JSArray::CreateArrayFromList(thread, resultList); + return resultArray.GetTaggedValue(); +} + +// 19.1.2.7 Object.getOwnPropertyNames ( O ) +JSTaggedValue BuiltinsObject::GetOwnPropertyNames(EcmaRuntimeCallInfo *argv) +{ + ASSERT(argv); + BUILTINS_API_TRACE(argv->GetThread(), Object, GetOwnPropertyNames); + [[maybe_unused]] EcmaHandleScope handleScope(argv->GetThread()); + JSHandle obj = GetCallArg(argv, 0); + KeyType type = KeyType::STRING_TYPE; + + // 1.Return GetOwnPropertyKeys(O, String). + return GetOwnPropertyKeys(argv->GetThread(), obj, type); +} + +// 19.1.2.8 Object.getOwnPropertySymbols ( O ) +JSTaggedValue BuiltinsObject::GetOwnPropertySymbols(EcmaRuntimeCallInfo *argv) +{ + ASSERT(argv); + BUILTINS_API_TRACE(argv->GetThread(), Object, GetOwnPropertySymbols); + [[maybe_unused]] EcmaHandleScope handleScope(argv->GetThread()); + JSHandle obj = GetCallArg(argv, 0); + KeyType type = KeyType::SYMBOL_TYPE; + + // 1.Return GetOwnPropertyKeys(O, Symbol). + return GetOwnPropertyKeys(argv->GetThread(), obj, type); +} + +// 19.1.2.9 Object.getPrototypeOf ( O ) +JSTaggedValue BuiltinsObject::GetPrototypeOf(EcmaRuntimeCallInfo *argv) +{ + ASSERT(argv); + BUILTINS_API_TRACE(argv->GetThread(), Object, GetPrototypeOf); + JSThread *thread = argv->GetThread(); + [[maybe_unused]] EcmaHandleScope handleScope(thread); + + // 1.Let obj be ToObject(O). + JSHandle func = GetCallArg(argv, 0); + + JSHandle obj = JSTaggedValue::ToObject(thread, func); + + // 2.ReturnIfAbrupt(obj). + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + + // 3.Return obj.[[GetPrototypeOf]](). + return obj->GetPrototype(thread); +} + +// 19.1.2.10 Object.is ( value1, value2 ) +JSTaggedValue BuiltinsObject::Is(EcmaRuntimeCallInfo *argv) +{ + ASSERT(argv); + BUILTINS_API_TRACE(argv->GetThread(), Object, Is); + + // 1.Return SameValue(value1, value2). + bool result = JSTaggedValue::SameValue(GetCallArg(argv, 0), GetCallArg(argv, 1)); + return GetTaggedBoolean(result); +} + +// 19.1.2.11 Object.isExtensible ( O ) +JSTaggedValue BuiltinsObject::IsExtensible(EcmaRuntimeCallInfo *argv) +{ + ASSERT(argv); + JSThread *thread = argv->GetThread(); + // 1.If Type(O) is not Object, return false. + JSTaggedValue obj = GetCallArg(argv, 0).GetTaggedValue(); + if (!obj.IsObject()) { + return GetTaggedBoolean(false); + } + [[maybe_unused]] EcmaHandleScope handleScope(thread); + // 2.Return IsExtensible(O). + return GetTaggedBoolean(obj.IsExtensible(thread)); +} + +// 19.1.2.12 Object.isFrozen ( O ) +JSTaggedValue BuiltinsObject::IsFrozen(EcmaRuntimeCallInfo *argv) +{ + ASSERT(argv); + // 1.If Type(O) is not Object, return true. + JSHandle obj = GetCallArg(argv, 0); + if (!obj->IsECMAObject()) { + return GetTaggedBoolean(true); + } + + JSThread *thread = argv->GetThread(); + [[maybe_unused]] EcmaHandleScope handleScope(thread); + + // 2.Return TestIntegrityLevel(O, "frozen"). + bool status = JSObject::TestIntegrityLevel(thread, JSHandle(obj), IntegrityLevel::FROZEN); + return GetTaggedBoolean(status); +} + +// 19.1.2.13 Object.isSealed ( O ) +JSTaggedValue BuiltinsObject::IsSealed(EcmaRuntimeCallInfo *argv) +{ + ASSERT(argv); + + // 1.If Type(O) is not Object, return true. + JSHandle obj = GetCallArg(argv, 0); + if (!obj->IsECMAObject()) { + return GetTaggedBoolean(true); + } + + JSThread *thread = argv->GetThread(); + [[maybe_unused]] EcmaHandleScope handleScope(thread); + + // 2.Return TestIntegrityLevel(O, "sealed"). + bool status = JSObject::TestIntegrityLevel(thread, JSHandle(obj), IntegrityLevel::SEALED); + return GetTaggedBoolean(status); +} + +// 19.1.2.14 Object.keys(O) +JSTaggedValue BuiltinsObject::Keys(EcmaRuntimeCallInfo *argv) +{ + ASSERT(argv); + BUILTINS_API_TRACE(argv->GetThread(), Object, Keys); + JSThread *thread = argv->GetThread(); + [[maybe_unused]] EcmaHandleScope handleScope(thread); + + // 1. Let obj be ? ToObject(O). + JSHandle msg = GetCallArg(argv, 0); + + JSHandle obj = JSTaggedValue::ToObject(thread, msg); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + + // 2. Let nameList be ? EnumerableOwnPropertyNames(obj, key) + array_size_t count = 0; + JSHandle nameList = JSObject::EnumerableOwnPropertyNames(thread, obj, PropertyKind::KEY, &count); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + + // 3. Return CreateArrayFromList(nameList). + JSHandle result = JSArray::CreateArrayFromList(thread, nameList, count); + return result.GetTaggedValue(); +} + +// 19.1.2.15 Object.preventExtensions(O) +JSTaggedValue BuiltinsObject::PreventExtensions(EcmaRuntimeCallInfo *argv) +{ + ASSERT(argv); + BUILTINS_API_TRACE(argv->GetThread(), Object, PreventExtensions); + // 1. If Type(O) is not Object, return O. + JSHandle obj = GetCallArg(argv, 0); + if (!obj->IsECMAObject()) { + return obj.GetTaggedValue(); + } + [[maybe_unused]] EcmaHandleScope handleScope(argv->GetThread()); + // 2. Let status be O.[[PreventExtensions]](). + bool status = JSTaggedValue::PreventExtensions(argv->GetThread(), obj); + + // 3. ReturnIfAbrupt(status). + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(argv->GetThread()); + + // 4. If status is false, throw a TypeError exception. + if (!status) { + // throw a TypeError exception. + THROW_TYPE_ERROR_AND_RETURN(argv->GetThread(), "PreventExtensions: preventExtensions failed", + JSTaggedValue::Exception()); + } + + // 5. Return O. + return obj.GetTaggedValue(); +} +// 19.1.2.16 Object.prototype + +// 19.1.2.17 Object.seal(O) +JSTaggedValue BuiltinsObject::Seal(EcmaRuntimeCallInfo *argv) +{ + ASSERT(argv); + BUILTINS_API_TRACE(argv->GetThread(), Object, Seal); + + // 1. If Type(O) is not Object, return O. + JSHandle msg = GetCallArg(argv, 0); + if (!msg->IsECMAObject()) { + return msg.GetTaggedValue(); + } + + JSThread *thread = argv->GetThread(); + [[maybe_unused]] EcmaHandleScope handleScope(thread); + + // 2. Let status be SetIntegrityLevel(O, "sealed"). + JSHandle object = JSTaggedValue::ToObject(thread, msg); + bool status = JSObject::SetIntegrityLevel(thread, object, IntegrityLevel::SEALED); + + // 3. ReturnIfAbrupt(status). + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + + // 4. If status is false, throw a TypeError exception. + if (!status) { + // throw a TypeError exception. + THROW_TYPE_ERROR_AND_RETURN(argv->GetThread(), "Seal: seal failed", JSTaggedValue::Exception()); + } + + // 5. Return O. + return object.GetTaggedValue(); +} + +// 19.1.2.18 Object.setPrototypeOf(O, proto) +JSTaggedValue BuiltinsObject::SetPrototypeOf(EcmaRuntimeCallInfo *argv) +{ + ASSERT(argv); + BUILTINS_API_TRACE(argv->GetThread(), Object, SetPrototypeOf); + JSThread *thread = argv->GetThread(); + [[maybe_unused]] EcmaHandleScope handleScope(thread); + // 1. Let O be RequireObjectCoercible(O). + JSHandle object = JSTaggedValue::RequireObjectCoercible(thread, GetCallArg(argv, 0)); + + // 2. ReturnIfAbrupt(O). + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + + // 3. If Type(proto) is neither Object nor Null, throw a TypeError exception. + JSHandle proto = GetCallArg(argv, 1); + if (!proto->IsNull() && !proto->IsECMAObject()) { + // throw a TypeError exception. + THROW_TYPE_ERROR_AND_RETURN(thread, "SetPrototypeOf: proto is neither Object nor Null", + JSTaggedValue::Exception()); + } + + // 4. If Type(O) is not Object, return O. + if (!object->IsECMAObject()) { + return object.GetTaggedValue(); + } + + // 5. Let status be O.[[SetPrototypeOf]](proto). + bool status = JSTaggedValue::SetPrototype(thread, object, proto); + + // 6. ReturnIfAbrupt(status). + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + + // 7. If status is false, throw a TypeError exception. + if (!status) { + // throw a TypeError exception. + THROW_TYPE_ERROR_AND_RETURN(thread, "SetPrototypeOf: prototype set failed", JSTaggedValue::Exception()); + } + + // 8. Return O. + return object.GetTaggedValue(); +} + +// 19.1.3.1 Object.prototype.constructor + +// 19.1.3.2 Object.prototype.hasOwnProperty(V) +JSTaggedValue BuiltinsObject::HasOwnProperty(EcmaRuntimeCallInfo *argv) +{ + ASSERT(argv); + BUILTINS_API_TRACE(argv->GetThread(), Object, HasOwnProperty); + JSThread *thread = argv->GetThread(); + [[maybe_unused]] EcmaHandleScope handleScope(thread); + // 1. Let P be ToPropertyKey(V). + JSHandle prop = GetCallArg(argv, 0); + JSHandle property = JSTaggedValue::ToPropertyKey(thread, prop); + + // 2. ReturnIfAbrupt(P). + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + + // 3. Let O be ToObject(this value). + JSHandle object = JSTaggedValue::ToObject(thread, GetThis(argv)); + + // 4. ReturnIfAbrupt(O). + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + + // 5. Return HasOwnProperty(O, P). + bool res = JSTaggedValue::HasOwnProperty(thread, JSHandle::Cast(object), property); + return GetTaggedBoolean(res); +} + +// 19.1.3.3 Object.prototype.isPrototypeOf(V) +JSTaggedValue BuiltinsObject::IsPrototypeOf(EcmaRuntimeCallInfo *argv) +{ + ASSERT(argv); + BUILTINS_API_TRACE(argv->GetThread(), Object, IsPrototypeOf); + JSThread *thread = argv->GetThread(); + // 1. If Type(V) is not Object, return false. + JSHandle msg = GetCallArg(argv, 0); + if (!msg->IsECMAObject()) { + return GetTaggedBoolean(false); + } + [[maybe_unused]] EcmaHandleScope handleScope(thread); + // 2. Let O be ToObject(this value). + JSHandle object = JSTaggedValue::ToObject(thread, GetThis(argv)); + // 3. ReturnIfAbrupt(O). + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + + // 4. Repeat + // a. Let V be V.[[GetPrototypeOf]](). + // b. If V is null, return false + // c. If SameValue(O, V) is true, return true. + JSTaggedValue msgValue = msg.GetTaggedValue(); + while (!msgValue.IsNull()) { + if (JSTaggedValue::SameValue(object.GetTaggedValue(), msgValue)) { + return GetTaggedBoolean(true); + } + msgValue = JSObject::Cast(msgValue)->GetPrototype(thread); + } + return GetTaggedBoolean(false); +} + +// 19.1.3.4 Object.prototype.propertyIsEnumerable(V) +JSTaggedValue BuiltinsObject::PropertyIsEnumerable(EcmaRuntimeCallInfo *argv) +{ + ASSERT(argv); + // 1. Let P be ToPropertyKey(V). + JSThread *thread = argv->GetThread(); + [[maybe_unused]] EcmaHandleScope handleScope(thread); + JSHandle msg = GetCallArg(argv, 0); + JSHandle property = JSTaggedValue::ToPropertyKey(thread, msg); + + // 2. ReturnIfAbrupt(P). + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + + // 3. Let O be ToObject(this value). + JSHandle object = JSTaggedValue::ToObject(thread, GetThis(argv)); + // 4. ReturnIfAbrupt(O). + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + + // 5. Let desc be O.[[GetOwnProperty]](P). + PropertyDescriptor desc(thread); + JSTaggedValue::GetOwnProperty(thread, JSHandle::Cast(object), property, desc); + + // 6. ReturnIfAbrupt(desc). + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + + // 7. If desc is undefined, return false. + if (desc.IsEmpty()) { + return GetTaggedBoolean(false); + } + + // 8. Return the value of desc.[[Enumerable]]. + return GetTaggedBoolean(desc.IsEnumerable()); +} + +// 19.1.3.5 Object.prototype.toLocaleString([reserved1[, reserved2]]) +JSTaggedValue BuiltinsObject::ToLocaleString(EcmaRuntimeCallInfo *argv) +{ + ASSERT(argv); + BUILTINS_API_TRACE(argv->GetThread(), Object, ToLocaleString); + JSThread *thread = argv->GetThread(); + [[maybe_unused]] EcmaHandleScope handleScope(thread); + // 1. Let O be the this value. + JSHandle object = GetThis(argv); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(argv->GetThread()); + + // 2. Return Invoke(O, "toString"). + JSHandle calleeKey = thread->GlobalConstants()->GetHandledToStringString(); + + JSHandle argsList = GetArgsArray(argv); + ecmascript::InternalCallParams *arguments = thread->GetInternalCallParams(); + arguments->MakeArgList(*argsList); + return JSFunction::Invoke(thread, object, calleeKey, argsList->GetLength(), arguments->GetArgv()); +} + +JSTaggedValue BuiltinsObject::GetBuiltinTag(JSThread *thread, const JSHandle &object) +{ + BUILTINS_API_TRACE(thread, Object, GetBuiltinTag); + // 4. Let isArray be IsArray(O). + bool isArray = object->IsJSArray(); + // 5. ReturnIfAbrupt(isArray). + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + + ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); + JSHandle builtinTag = factory->NewFromCanBeCompressString("Object"); + // 6. If isArray is true, let builtinTag be "Array". + if (isArray) { + builtinTag = factory->NewFromCanBeCompressString("Array"); + } else if (object->IsJSPrimitiveRef()) { + // 7. Else, if O is an exotic String object, let builtinTag be "String". + JSPrimitiveRef *primitiveRef = JSPrimitiveRef::Cast(*object); + if (primitiveRef->IsString()) { + builtinTag = factory->NewFromCanBeCompressString("String"); + } else if (primitiveRef->IsBoolean()) { + // 11. Else, if O has a [[BooleanData]] internal slot, let builtinTag be "Boolean". + builtinTag = factory->NewFromCanBeCompressString("Boolean"); + } else if (primitiveRef->IsNumber()) { + // 12. Else, if O has a [[NumberData]] internal slot, let builtinTag be "Number". + builtinTag = factory->NewFromCanBeCompressString("Number"); + } + } else if (object->IsArguments()) { + builtinTag = factory->NewFromCanBeCompressString("Arguments"); + } else if (object->IsCallable()) { + builtinTag = factory->NewFromCanBeCompressString("Function"); + } else if (object->IsJSError()) { + builtinTag = factory->NewFromCanBeCompressString("Error"); + } else if (object->IsDate()) { + builtinTag = factory->NewFromCanBeCompressString("Date"); + } else if (object->IsJSRegExp()) { + builtinTag = factory->NewFromCanBeCompressString("RegExp"); + } + // 15. Else, let builtinTag be "Object". + return builtinTag.GetTaggedValue(); +} + +// 19.1.3.6 Object.prototype.toString() +JSTaggedValue BuiltinsObject::ToString(EcmaRuntimeCallInfo *argv) +{ + ASSERT(argv); + BUILTINS_API_TRACE(argv->GetThread(), Object, ToString); + JSThread *thread = argv->GetThread(); + [[maybe_unused]] EcmaHandleScope handleScope(thread); + // 1. If the this value is undefined, return "[object Undefined]". + + JSHandle msg = GetThis(argv); + if (msg->IsUndefined()) { + return GetTaggedString(thread, "[object Undefined]"); + } + // 2. If the this value is null, return "[object Null]". + if (msg->IsNull()) { + return GetTaggedString(thread, "[object Null]"); + } + + // 3. Let O be ToObject(this value). + JSHandle object = JSTaggedValue::ToObject(thread, GetThis(argv)); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + JSHandle builtinTag(thread, GetBuiltinTag(thread, object)); + + // 16. Let tag be Get (O, @@toStringTag). + auto ecmaVm = thread->GetEcmaVM(); + JSHandle env = ecmaVm->GetGlobalEnv(); + auto factory = ecmaVm->GetFactory(); + + JSHandle tag = JSTaggedValue::GetProperty(thread, msg, env->GetToStringTagSymbol()).GetValue(); + + // 17. ReturnIfAbrupt(tag). + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + + // 18. If Type(tag) is not String, let tag be builtinTag. + if (!tag->IsString()) { + tag = builtinTag; + } + + // 19. Return the String that is the result of concatenating "[object ", tag, and "]". + JSHandle leftString(factory->NewFromCanBeCompressString("[object ")); + JSHandle rightString(factory->NewFromCanBeCompressString("]")); + + JSHandle newLeftStringHandle = + factory->ConcatFromString(leftString, JSTaggedValue::ToString(thread, tag)); + auto result = factory->ConcatFromString(newLeftStringHandle, rightString); + return result.GetTaggedValue(); +} + +// 19.1.3.7 Object.prototype.valueOf() +JSTaggedValue BuiltinsObject::ValueOf(EcmaRuntimeCallInfo *argv) +{ + ASSERT(argv); + BUILTINS_API_TRACE(argv->GetThread(), Object, ValueOf); + JSThread *thread = argv->GetThread(); + [[maybe_unused]] EcmaHandleScope handleScope(thread); + + // 1. Return ToObject(this value). + JSHandle object = JSTaggedValue::ToObject(thread, GetThis(argv)); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + return object.GetTaggedValue(); +} +// B.2.2.1 Object.prototype.__proto__ +JSTaggedValue BuiltinsObject::ProtoGetter(EcmaRuntimeCallInfo *argv) +{ + ASSERT(argv); + BUILTINS_API_TRACE(argv->GetThread(), Object, ProtoGetter); + JSThread *thread = argv->GetThread(); + [[maybe_unused]] EcmaHandleScope handleScope(thread); + + // 1.Let obj be ToObject(this value). + JSHandle obj = JSTaggedValue::ToObject(thread, GetThis(argv)); + + // 2.ReturnIfAbrupt(obj). + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + + // 3.Return obj.[[GetPrototypeOf]](). + return obj->GetPrototype(thread); +} + +JSTaggedValue BuiltinsObject::ProtoSetter(EcmaRuntimeCallInfo *argv) +{ + ASSERT(argv); + BUILTINS_API_TRACE(argv->GetThread(), Object, ProtoSetter); + JSThread *thread = argv->GetThread(); + [[maybe_unused]] EcmaHandleScope handleScope(thread); + // 1. Let O be RequireObjectCoercible(this value). + JSHandle obj = JSTaggedValue::RequireObjectCoercible(thread, GetThis(argv)); + + // 2. ReturnIfAbrupt(O). + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + + // 3. If Type(proto) is neither Object nor Null, return undefined.. + JSHandle proto = GetCallArg(argv, 0); + if (!proto->IsNull() && !proto->IsECMAObject()) { + return JSTaggedValue::Undefined(); + } + + // 4. If Type(O) is not Object, return undefined. + if (!obj->IsECMAObject()) { + return JSTaggedValue::Undefined(); + } + + // 5. Let status be O.[[SetPrototypeOf]](proto). + bool status = JSTaggedValue::SetPrototype(thread, obj, proto); + + // 6. ReturnIfAbrupt(status). + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + + // 7. If status is false, throw a TypeError exception. + if (!status) { + // throw a TypeError exception. + THROW_TYPE_ERROR_AND_RETURN(thread, "ProtoSetter: proto set failed", JSTaggedValue::Exception()); + } + + // 8. Return O. + return JSTaggedValue::Undefined(); +} + +// B.2.2.2 Object.prototype.__defineGetter__ ( P, getter ) +JSTaggedValue BuiltinsObject::DefineGetter(EcmaRuntimeCallInfo *argv) +{ + ASSERT(argv); + BUILTINS_API_TRACE(argv->GetThread(), Object, DefineGetter); + JSThread *thread = argv->GetThread(); + + // 1. Let O be ? ToObject(this value). + JSHandle obj = JSTaggedValue::ToObject(thread, GetThis(argv)); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + + // 2. If IsCallable(getter) is false, throw a TypeError exception. + JSHandle getter = GetCallArg(argv, 1); + if (!getter->IsCallable()) { + THROW_TYPE_ERROR_AND_RETURN(thread, "invalid getter usage", JSTaggedValue::Exception()); + } + + // 3. Let desc be PropertyDescriptor { [[Get]]: getter, [[Enumerable]]: true, [[Configurable]]: true }. + PropertyDescriptor descriptor(thread); + descriptor.SetGetter(getter); + descriptor.SetEnumerable(true); + descriptor.SetConfigurable(true); + + // 4. Let key be ? ToPropertyKey(P). + JSHandle key = JSTaggedValue::ToPropertyKey(thread, GetCallArg(argv, 0)); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + + // 5. Perform ? DefinePropertyOrThrow(O, key, desc). + JSTaggedValue::DefinePropertyOrThrow(thread, JSHandle::Cast(obj), key, descriptor); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + + // 6. Return undefined. + return JSTaggedValue::Undefined(); +} + +// B.2.2.3 Object.prototype.__defineSetter__ ( P, setter ) +JSTaggedValue BuiltinsObject::DefineSetter(EcmaRuntimeCallInfo *argv) +{ + ASSERT(argv); + BUILTINS_API_TRACE(argv->GetThread(), Object, DefineGetter); + JSThread *thread = argv->GetThread(); + + // 1. Let O be ? ToObject(this value). + JSHandle obj = JSTaggedValue::ToObject(thread, GetThis(argv)); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + + // 2. If IsCallable(setter) is false, throw a TypeError exception. + JSHandle setter = GetCallArg(argv, 1); + if (!setter->IsCallable()) { + THROW_TYPE_ERROR_AND_RETURN(thread, "invalid setter usage", JSTaggedValue::Exception()); + } + + // 3. Let desc be PropertyDescriptor { [[Set]]: setter, [[Enumerable]]: true, [[Configurable]]: true }. + PropertyDescriptor descriptor(thread); + descriptor.SetSetter(setter); + descriptor.SetEnumerable(true); + descriptor.SetConfigurable(true); + + // 4. Let key be ? ToPropertyKey(P). + JSHandle key = JSTaggedValue::ToPropertyKey(thread, GetCallArg(argv, 0)); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + + // Perform ? DefinePropertyOrThrow(O, key, desc). + JSTaggedValue::DefinePropertyOrThrow(thread, JSHandle::Cast(obj), key, descriptor); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + + // 6. Return undefined. + return JSTaggedValue::Undefined(); +} + +static JSTaggedValue LookupDesc(EcmaRuntimeCallInfo *argv, + const std::function &getDesc) +{ + ASSERT(argv); + BUILTINS_API_TRACE(argv->GetThread(), Object, DefineGetter); + JSThread *thread = argv->GetThread(); + + // 1. Let O be ? ToObject(this value). + JSHandle obj = JSTaggedValue::ToObject(thread, ecmascript::base::BuiltinsBase::GetThis(argv)); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + + // 2. Let key be ? ToPropertyKey(P). + JSHandle key = + JSTaggedValue::ToPropertyKey(thread, ecmascript::base::BuiltinsBase::GetCallArg(argv, 0)); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + + // 3. Repeat, + // a. Let desc be ? O.[[GetOwnProperty]](key). + // b. If desc is not undefined, then + // i. If IsAccessorDescriptor(desc) is true, return desc.[[Get]]. + // ii. Return undefined. + // c. Set O to ? O.[[GetPrototypeOf]](). + // d. If O is null, return undefined. + JSTaggedValue objval = obj.GetTaggedValue(); + while (!objval.IsNull()) { + PropertyDescriptor desc(thread); + JSTaggedValue::GetOwnProperty(thread, JSHandle::Cast(obj), key, desc); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + + if (!desc.IsEmpty()) { + return getDesc(desc); + } + + objval = JSObject::Cast(objval)->GetPrototype(thread); + } + + return JSTaggedValue::Undefined(); +} + +// B.2.2.4 Object.prototype.__lookupGetter__ ( P ) +JSTaggedValue BuiltinsObject::LookupGetter(EcmaRuntimeCallInfo *argv) +{ + return LookupDesc(argv, [](PropertyDescriptor desc) { + if (desc.IsAccessorDescriptor()) { + return desc.GetGetter().GetTaggedValue(); + } + + return JSTaggedValue::Undefined(); + }); +} + +// B.2.2.5 Object.prototype.__lookupSetter__ ( P ) +JSTaggedValue BuiltinsObject::LookupSetter(EcmaRuntimeCallInfo *argv) +{ + return LookupDesc(argv, [](PropertyDescriptor desc) { + if (desc.IsAccessorDescriptor()) { + return desc.GetSetter().GetTaggedValue(); + } + + return JSTaggedValue::Undefined(); + }); +} + +JSTaggedValue BuiltinsObject::CreateRealm(EcmaRuntimeCallInfo *argv) +{ + ASSERT(argv); + JSThread *thread = argv->GetThread(); + [[maybe_unused]] EcmaHandleScope handleScope(thread); + ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); + JSHandle realm = factory->NewJSRealm(); + return realm.GetTaggedValue(); +} + +JSTaggedValue BuiltinsObject::Entries(EcmaRuntimeCallInfo *argv) +{ + ASSERT(argv); + BUILTINS_API_TRACE(argv->GetThread(), Object, ToString); + JSThread *thread = argv->GetThread(); + [[maybe_unused]] EcmaHandleScope handleScope(thread); + + // 1. Let obj be ? ToObject(O). + JSHandle obj = GetCallArg(argv, 0); + JSHandle object = JSTaggedValue::ToObject(thread, obj); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + // 2. Let nameList be ? EnumerableOwnPropertyNames(obj, key+value). + array_size_t length = 0; + JSHandle nameList = + JSObject::EnumerableOwnPropertyNames(thread, object, PropertyKind::KEY_VALUE, &length); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + // 3. Return CreateArrayFromList(nameList). + return JSArray::CreateArrayFromList(thread, nameList, length).GetTaggedValue(); +} + +// ES2021 20.1.2.22 +JSTaggedValue BuiltinsObject::Values(EcmaRuntimeCallInfo *argv) +{ + ASSERT(argv); + BUILTINS_API_TRACE(argv->GetThread(), Object, ToString); + JSThread *thread = argv->GetThread(); + [[maybe_unused]] EcmaHandleScope handleScope(thread); + + // 1. Let obj be ? ToObject(O). + JSHandle obj = GetCallArg(argv, 0); + JSHandle object = JSTaggedValue::ToObject(thread, obj); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + // 2. Let nameList be ? EnumerableOwnPropertyNames(obj, value). + array_size_t length = 0; + JSHandle nameList = JSObject::EnumerableOwnPropertyNames(thread, object, PropertyKind::VALUE, &length); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + // 3. Return CreateArrayFromList(nameList). + return JSArray::CreateArrayFromList(thread, nameList, length).GetTaggedValue(); +} +} // namespace panda::ecmascript::builtins diff --git a/runtime/builtins/builtins_object.h b/runtime/builtins/builtins_object.h new file mode 100644 index 000000000..099170737 --- /dev/null +++ b/runtime/builtins/builtins_object.h @@ -0,0 +1,114 @@ +/* + * Copyright (c) 2021-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_BUILTINS_BUILTINS_OBJECT_H +#define ECMASCRIPT_BUILTINS_BUILTINS_OBJECT_H + +#include "plugins/ecmascript/runtime/ecma_runtime_call_info.h" +#include "plugins/ecmascript/runtime/base/builtins_base.h" +#include "plugins/ecmascript/runtime/js_handle.h" +#include "plugins/ecmascript/runtime/js_hclass.h" + +namespace panda::ecmascript::builtins { +enum class KeyType : uint8_t { + STRING_TYPE = 0, + SYMBOL_TYPE, +}; + +class BuiltinsObject : public ecmascript::base::BuiltinsBase { +public: + // 19.1.1.1Object ( [ value ] ) + static JSTaggedValue ObjectConstructor(EcmaRuntimeCallInfo *argv); + + // 19.1.2.1Object.assign ( target, ...sources ) + static JSTaggedValue Assign(EcmaRuntimeCallInfo *argv); + // 19.1.2.2Object.create ( O [ , Properties ] ) + static JSTaggedValue Create(EcmaRuntimeCallInfo *argv); + // 19.1.2.3Object.defineProperties ( O, Properties ) + static JSTaggedValue DefineProperties(EcmaRuntimeCallInfo *argv); + // 19.1.2.4Object.defineProperty ( O, P, Attributes ) + static JSTaggedValue DefineProperty(EcmaRuntimeCallInfo *argv); + // 19.1.2.5Object.freeze ( O ) + static JSTaggedValue Freeze(EcmaRuntimeCallInfo *argv); + // ES2021 20.1.2.7Object.fromEntries ( iterable ) + static JSTaggedValue FromEntries(EcmaRuntimeCallInfo *argv); + // 19.1.2.6Object.getOwnPropertyDescriptor ( O, P ) + static JSTaggedValue GetOwnPropertyDesciptor(EcmaRuntimeCallInfo *argv); + // ES2021 20.1.2.9 Object.getOwnPropertyDescriptors ( O ) + static JSTaggedValue GetOwnPropertyDesciptors(EcmaRuntimeCallInfo *argv); + // 19.1.2.7Object.getOwnPropertyNames ( O ) + static JSTaggedValue GetOwnPropertyNames(EcmaRuntimeCallInfo *argv); + // 19.1.2.8Object.getOwnPropertySymbols ( O ) + static JSTaggedValue GetOwnPropertySymbols(EcmaRuntimeCallInfo *argv); + // 19.1.2.9Object.getPrototypeOf ( O ) + static JSTaggedValue GetPrototypeOf(EcmaRuntimeCallInfo *argv); + // 19.1.2.10Object.is ( value1, value2 ) + static JSTaggedValue Is(EcmaRuntimeCallInfo *argv); + // 19.1.2.11Object.isExtensible ( O ) + static JSTaggedValue IsExtensible(EcmaRuntimeCallInfo *argv); + // 19.1.2.12Object.isFrozen ( O ) + static JSTaggedValue IsFrozen(EcmaRuntimeCallInfo *argv); + // 19.1.2.13Object.isSealed ( O ) + static JSTaggedValue IsSealed(EcmaRuntimeCallInfo *argv); + // 19.1.2.14 Object.keys(O) + static JSTaggedValue Keys(EcmaRuntimeCallInfo *argv); + // 19.1.2.15 Object.preventExtensions(O) + static JSTaggedValue PreventExtensions(EcmaRuntimeCallInfo *argv); + // 19.1.2.17 Object.seal(O) + static JSTaggedValue Seal(EcmaRuntimeCallInfo *argv); + // 19.1.2.18 Object.setPrototypeOf(O, proto) + static JSTaggedValue SetPrototypeOf(EcmaRuntimeCallInfo *argv); + + // 19.1.3.2 Object.prototype.hasOwnProperty(V) + static JSTaggedValue HasOwnProperty(EcmaRuntimeCallInfo *argv); + // 19.1.3.3 Object.prototype.isPrototypeOf(V) + static JSTaggedValue IsPrototypeOf(EcmaRuntimeCallInfo *argv); + // 19.1.3.4 Object.prototype.propertyIsEnumerable(V) + static JSTaggedValue PropertyIsEnumerable(EcmaRuntimeCallInfo *argv); + // 19.1.3.5 Object.prototype.toLocaleString([reserved1[, reserved2]]) + static JSTaggedValue ToLocaleString(EcmaRuntimeCallInfo *argv); + // 19.1.3.6 Object.prototype.toString() + static JSTaggedValue ToString(EcmaRuntimeCallInfo *argv); + // 19.1.3.7 Object.prototype.valueOf() + static JSTaggedValue ValueOf(EcmaRuntimeCallInfo *argv); + + static JSTaggedValue CreateRealm(EcmaRuntimeCallInfo *argv); + // 20.1.2.5 Object.entries ( O ) + static JSTaggedValue Entries(EcmaRuntimeCallInfo *argv); + + // ES2021 20.1.2.22 + static JSTaggedValue Values(EcmaRuntimeCallInfo *argv); + + // B.2.2.1 Object.prototype.__proto__ + static JSTaggedValue ProtoGetter(EcmaRuntimeCallInfo *argv); + static JSTaggedValue ProtoSetter(EcmaRuntimeCallInfo *argv); + + // B.2.2.2 Object.prototype.__defineGetter__ ( P, getter ) + static JSTaggedValue DefineGetter(EcmaRuntimeCallInfo *argv); + // B.2.2.3 Object.prototype.__defineSetter__ ( P, setter ) + static JSTaggedValue DefineSetter(EcmaRuntimeCallInfo *argv); + // B.2.2.4 Object.prototype.__lookupGetter__ ( P ) + static JSTaggedValue LookupGetter(EcmaRuntimeCallInfo *argv); + // B.2.2.5 Object.prototype.__lookupSetter__ ( P ) + static JSTaggedValue LookupSetter(EcmaRuntimeCallInfo *argv); + +private: + static JSTaggedValue ObjectDefineProperties(JSThread *thread, const JSHandle &obj, + const JSHandle &prop); + static JSTaggedValue GetOwnPropertyKeys(JSThread *thread, const JSHandle &obj, const KeyType &type); + static JSTaggedValue GetBuiltinTag(JSThread *thread, const JSHandle &object); +}; +} // namespace panda::ecmascript::builtins +#endif // ECMASCRIPT_BUILTINS_BUILTINS_OBJECT_H diff --git a/runtime/builtins/builtins_plural_rules.cpp b/runtime/builtins/builtins_plural_rules.cpp new file mode 100644 index 000000000..e50da817b --- /dev/null +++ b/runtime/builtins/builtins_plural_rules.cpp @@ -0,0 +1,130 @@ +/* + * Copyright (c) 2021-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 "plugins/ecmascript/runtime/builtins/builtins_plural_rules.h" + +#include "plugins/ecmascript/runtime/global_env.h" +#include "plugins/ecmascript/runtime/js_locale.h" +#include "plugins/ecmascript/runtime/js_object.h" +#include "plugins/ecmascript/runtime/js_plural_rules.h" +#include "plugins/ecmascript/runtime/object_factory.h" + +namespace panda::ecmascript::builtins { +JSTaggedValue BuiltinsPluralRules::PluralRulesConstructor(EcmaRuntimeCallInfo *argv) +{ + JSThread *thread = argv->GetThread(); + [[maybe_unused]] EcmaHandleScope scope(thread); + EcmaVM *ecmaVm = thread->GetEcmaVM(); + ObjectFactory *factory = ecmaVm->GetFactory(); + + // 1. If NewTarget is undefined, throw a TypeError exception. + JSHandle constructor = GetConstructor(argv); + JSHandle newTarget = GetNewTarget(argv); + if (newTarget->IsUndefined()) { + THROW_TYPE_ERROR_AND_RETURN(thread, "newTarget is undefined", JSTaggedValue::Exception()); + } + + // 2. Let pluralRules be ? OrdinaryCreateFromConstructor(NewTarget, "%PluralRulesPrototype%", + // « [[InitializedPluralRules]], [[Locale]], [[Type]], [[MinimumIntegerDigits]], [[MinimumFractionDigits]], + // [[MaximumFractionDigits]], [[MinimumSignificantDigits]], [[MaximumSignificantDigits]], [[RoundingType]] »). + JSHandle pluralRules = + JSHandle::Cast(factory->NewJSObjectByConstructor(JSHandle(constructor), newTarget)); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + + // 3. Return ? InitializePluralRules(pluralRules, locales, options). + JSHandle locales = GetCallArg(argv, 0); + JSHandle options = GetCallArg(argv, 1); + JSPluralRules::InitializePluralRules(thread, pluralRules, locales, options); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + + return pluralRules.GetTaggedValue(); +} + +JSTaggedValue BuiltinsPluralRules::SupportedLocalesOf(EcmaRuntimeCallInfo *argv) +{ + JSThread *thread = argv->GetThread(); + [[maybe_unused]] EcmaHandleScope scope(thread); + + // 1. Let availableLocales be %PluralRules%.[[AvailableLocales]]. + JSHandle availableLocales = JSPluralRules::GetAvailableLocales(thread); + + // 2. Let requestedLocales be ? CanonicalizeLocaleList(locales). + JSHandle locales = GetCallArg(argv, 0); + JSHandle requestedLocales = JSLocale::CanonicalizeLocaleList(thread, locales); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + + // 3. Return ? SupportedLocales(availableLocales, requestedLocales, options). + JSHandle options = GetCallArg(argv, 1); + JSHandle result = JSLocale::SupportedLocales(thread, availableLocales, requestedLocales, options); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + return result.GetTaggedValue(); +} + +JSTaggedValue BuiltinsPluralRules::Select(EcmaRuntimeCallInfo *argv) +{ + JSThread *thread = argv->GetThread(); + [[maybe_unused]] EcmaHandleScope scope(thread); + + // 1. Let pr be the this value. + JSHandle thisValue = GetThis(argv); + + // 2. Perform ? RequireInternalSlot(pr, [[InitializedPluralRules]]). + if (!thisValue->IsJSPluralRules()) { + THROW_TYPE_ERROR_AND_RETURN(thread, "this is not pr object", JSTaggedValue::Exception()); + } + + // 3. Let n be ? ToNumber(value). + double x = 0.0; + JSHandle value = GetCallArg(argv, 0); + JSTaggedNumber temp = JSTaggedValue::ToNumber(thread, value); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + x = temp.GetNumber(); + + // 4. Return ? ResolvePlural(pr, n). + JSHandle pluralRules = JSHandle::Cast(thisValue); + JSHandle result = JSPluralRules::ResolvePlural(thread, pluralRules, x); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + return result.GetTaggedValue(); +} + +JSTaggedValue BuiltinsPluralRules::ResolvedOptions(EcmaRuntimeCallInfo *argv) +{ + JSThread *thread = argv->GetThread(); + [[maybe_unused]] EcmaHandleScope scope(thread); + + // 1. Let thisValue be the this value; + JSHandle thisValue = GetThis(argv); + + // 2. Perform ? RequireInternalSlot(pr, [[InitializedPluralRules]]). + if (!thisValue->IsJSPluralRules()) { + THROW_TYPE_ERROR_AND_RETURN(thread, "this is not pr object", JSTaggedValue::Exception()); + } + + // 3. Let options be ! ObjectCreate(%ObjectPrototype%). + auto ecmaVm = thread->GetEcmaVM(); + JSHandle env = ecmaVm->GetGlobalEnv(); + ObjectFactory *factory = ecmaVm->GetFactory(); + JSHandle ctor = env->GetObjectFunction(); + JSHandle options(factory->NewJSObjectByConstructor(JSHandle(ctor), ctor)); + + // 4. Perform resolvedOptions + JSHandle pluralRules = JSHandle::Cast(thisValue); + JSPluralRules::ResolvedOptions(thread, pluralRules, options); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + + // 5. Return options. + return options.GetTaggedValue(); +} +} // namespace panda::ecmascript::builtins diff --git a/runtime/builtins/builtins_plural_rules.h b/runtime/builtins/builtins_plural_rules.h new file mode 100644 index 000000000..cf0e91c22 --- /dev/null +++ b/runtime/builtins/builtins_plural_rules.h @@ -0,0 +1,37 @@ +/* + * Copyright (c) 2021-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_BUILTINS_BUILTINS_PLURAL_RULES_H +#define ECMASCRIPT_BUILTINS_BUILTINS_PLURAL_RULES_H + +#include "plugins/ecmascript/runtime/base/builtins_base.h" + +namespace panda::ecmascript::builtins { +class BuiltinsPluralRules : public base::BuiltinsBase { +public: + // 15.2.1 Intl.PluralRules ( [ locales [ , options ] ] ) + static JSTaggedValue PluralRulesConstructor(EcmaRuntimeCallInfo *argv); + + // 15.3.2 Intl.PluralRules.supportedLocalesOf ( locales [, options ] ) + static JSTaggedValue SupportedLocalesOf(EcmaRuntimeCallInfo *argv); + + // 15.4.3 Intl.PluralRules.prototype.select( value ) + static JSTaggedValue Select(EcmaRuntimeCallInfo *argv); + + // 15.4.4 Intl.PluralRules.prototype.resolvedOptions () + static JSTaggedValue ResolvedOptions(EcmaRuntimeCallInfo *argv); +}; +} // namespace panda::ecmascript::builtins +#endif // ECMASCRIPT_BUILTINS_BUILTINS_PLURAL_RULES_H diff --git a/runtime/builtins/builtins_promise.cpp b/runtime/builtins/builtins_promise.cpp new file mode 100644 index 000000000..1c89cd8f7 --- /dev/null +++ b/runtime/builtins/builtins_promise.cpp @@ -0,0 +1,601 @@ +/* + * Copyright (c) 2021-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 "plugins/ecmascript/runtime/builtins/builtins_promise.h" +#include "plugins/ecmascript/runtime/builtins/builtins_promise_handler.h" +#include "plugins/ecmascript/runtime/builtins/builtins_promise_job.h" +#include "plugins/ecmascript/runtime/ecma_runtime_call_info.h" +#include "plugins/ecmascript/runtime/ecma_vm.h" +#include "plugins/ecmascript/runtime/global_env.h" +#include "plugins/ecmascript/runtime/internal_call_params.h" +#include "plugins/ecmascript/runtime/jobs/micro_job_queue.h" +#include "plugins/ecmascript/runtime/js_array.h" +#include "plugins/ecmascript/runtime/js_function.h" +#include "plugins/ecmascript/runtime/js_handle.h" +#include "plugins/ecmascript/runtime/js_iterator.h" +#include "plugins/ecmascript/runtime/js_promise.h" +#include "plugins/ecmascript/runtime/js_tagged_number.h" +#include "plugins/ecmascript/runtime/js_tagged_value-inl.h" +#include "plugins/ecmascript/runtime/js_thread.h" +#include "plugins/ecmascript/runtime/mem/assert_scope-inl.h" +#include "plugins/ecmascript/runtime/object_factory.h" + +namespace panda::ecmascript::builtins { +using BuiltinsPromiseJob = builtins::BuiltinsPromiseJob; +// 25.4.3.1 Promise ( executor ) +JSTaggedValue BuiltinsPromise::PromiseConstructor([[maybe_unused]] EcmaRuntimeCallInfo *argv) +{ + ASSERT(argv); + BUILTINS_API_TRACE(argv->GetThread(), Promise, Constructor); + JSThread *thread = argv->GetThread(); + [[maybe_unused]] EcmaHandleScope handleScope(thread); + EcmaVM *ecmaVm = thread->GetEcmaVM(); + ObjectFactory *factory = ecmaVm->GetFactory(); + const GlobalEnvConstants *globalConst = thread->GlobalConstants(); + // 1. If NewTarget is undefined, throw a TypeError exception. + JSHandle newTarget = GetNewTarget(argv); + if (newTarget->IsUndefined()) { + THROW_TYPE_ERROR_AND_RETURN(thread, "PromiseConstructor: NewTarget is undefined", JSTaggedValue::Exception()); + } + // 2. If IsCallable(executor) is false, throw a TypeError exception. + JSHandle executor = BuiltinsBase::GetCallArg(argv, 0); + if (!executor->IsCallable()) { + THROW_TYPE_ERROR_AND_RETURN(thread, "PromiseConstructor: executor is not callable", JSTaggedValue::Exception()); + } + + // 3. Let promise be OrdinaryCreateFromConstructor(NewTarget, "%PromisePrototype%", + // «[[PromiseState]], [[PromiseResult]], [[PromiseFulfillReactions]], [[PromiseRejectReactions]]» ). + // 4. ReturnIfAbrupt(promise). + JSHandle constructor = GetConstructor(argv); + JSHandle instancePromise = + JSHandle::Cast(factory->NewJSObjectByConstructor(JSHandle(constructor), newTarget)); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + + // 5. Set promise's [[PromiseState]] internal slot to "pending". + // 6. Set promise's [[PromiseFulfillReactions]] internal slot to a new empty List. + // 7. Set promise's [[PromiseRejectReactions]] internal slot to a new empty List. + // 8. Let resolvingFunctions be CreateResolvingFunctions(promise). + JSHandle resolvingFunction = JSPromise::CreateResolvingFunctions(thread, instancePromise); + // 9. Let completion be Call(executor, undefined, «resolvingFunctions.[[Resolve]], resolvingFunctions.[[reject]]) + auto resolveFunc = resolvingFunction->GetResolveFunction(); + auto rejectFunc = resolvingFunction->GetRejectFunction(); + InternalCallParams *arguments = thread->GetInternalCallParams(); + arguments->MakeArgv(resolveFunc, rejectFunc); + JSHandle thisValue = globalConst->GetHandledUndefined(); + JSTaggedValue taggedValue = JSFunction::Call(thread, executor, thisValue, 2, arguments->GetArgv()); // 2: two args + JSHandle completionValue(thread, taggedValue); + + // 10. If completion is an abrupt completion, then + // a. Let status be Call(resolvingFunctions.[[Reject]], undefined, «completion.[[value]]»). + // b. ReturnIfAbrupt(status). + if (thread->HasPendingException()) { + completionValue = JSPromise::IfThrowGetThrowValue(thread); + thread->ClearException(); + JSHandle reject(thread, resolvingFunction->GetRejectFunction()); + arguments->MakeArgv(completionValue); + JSFunction::Call(thread, reject, thisValue, 1, arguments->GetArgv()); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + } + + // 11. Return promise. + return instancePromise.GetTaggedValue(); +} + +// 25.4.4.1 Promise.all ( iterable ) +JSTaggedValue BuiltinsPromise::All(EcmaRuntimeCallInfo *argv) +{ + ASSERT(argv); + BUILTINS_API_TRACE(argv->GetThread(), Promise, All); + JSThread *thread = argv->GetThread(); + [[maybe_unused]] EcmaHandleScope handleScope(thread); + EcmaVM *ecmaVm = thread->GetEcmaVM(); + JSHandle env = ecmaVm->GetGlobalEnv(); + ObjectFactory *factory = ecmaVm->GetFactory(); + + // 1. Let C be the this value. + JSHandle ctor = GetThis(argv); + // 2. If Type(C) is not Object, throw a TypeError exception. + if (!ctor->IsECMAObject()) { + THROW_TYPE_ERROR_AND_RETURN(thread, "Promise ALL: this value is not object", JSTaggedValue::Exception()); + } + // 3. Let S be Get(C, @@species). + // 4. ReturnIfAbrupt(S). + JSHandle speciesSymbol = env->GetSpeciesSymbol(); + JSHandle sctor = JSObject::GetProperty(thread, ctor, speciesSymbol).GetValue(); + RETURN_VALUE_IF_ABRUPT_COMPLETION(thread, sctor.GetTaggedValue()); + + // 5. If S is neither undefined nor null, let C be S. + if (!sctor->IsUndefined() && !sctor->IsNull()) { + ctor = sctor; + } + // 6. Let promiseCapability be NewPromiseCapability(C). + JSHandle capa = JSPromise::NewPromiseCapability(thread, ctor); + // 7. ReturnIfAbrupt(promiseCapability). + RETURN_VALUE_IF_ABRUPT_COMPLETION(thread, capa.GetTaggedValue()); + // 8. Let iterator be GetIterator(iterable). + JSHandle itor = JSIterator::GetIterator(thread, GetCallArg(argv, 0)); + // 9. IfAbruptRejectPromise(iterator, promiseCapability). + if (thread->HasPendingException()) { + itor = JSPromise::IfThrowGetThrowValue(thread); + } + RETURN_REJECT_PROMISE_IF_ABRUPT(thread, itor, capa); + + // 10. Let iteratorRecord be Record {[[iterator]]: iterator, [[done]]: false}. + JSHandle done(thread, JSTaggedValue::False()); + JSHandle itRecord = factory->NewPromiseIteratorRecord(itor, done); + // 11. Let result be PerformPromiseAll(iteratorRecord, C, promiseCapability). + JSHandle result = PerformPromiseAll(thread, itRecord, ctor, capa); + // 12. If result is an abrupt completion, + if (result->IsThrow()) { + thread->ClearException(); + // a. If iteratorRecord.[[done]] is false, let result be IteratorClose(iterator, result). + // b. IfAbruptRejectPromise(result, promiseCapability). + if (itRecord->GetDone().IsFalse()) { + JSHandle closeVal = + JSIterator::IteratorClose(thread, itor, JSHandle::Cast(result)); + if (closeVal.GetTaggedValue().IsRecord()) { + result = JSHandle::Cast(closeVal); + RETURN_REJECT_PROMISE_IF_ABRUPT(thread, result, capa); + return result->GetValue(); + } + } + RETURN_REJECT_PROMISE_IF_ABRUPT(thread, result, capa); + return result->GetValue(); + } + // 13. Return Completion(result). + return result->GetValue(); +} + +// 25.4.4.3 Promise.race ( iterable ) +JSTaggedValue BuiltinsPromise::Race(EcmaRuntimeCallInfo *argv) +{ + ASSERT(argv); + BUILTINS_API_TRACE(argv->GetThread(), Promise, Race); + JSThread *thread = argv->GetThread(); + [[maybe_unused]] EcmaHandleScope handleScope(thread); + EcmaVM *ecmaVm = thread->GetEcmaVM(); + ObjectFactory *factory = ecmaVm->GetFactory(); + JSHandle env = ecmaVm->GetGlobalEnv(); + // 1. Let C be the this value. + // 2. If Type(C) is not Object, throw a TypeError exception. + JSHandle thisValue = GetThis(argv); + if (!thisValue->IsECMAObject()) { + THROW_TYPE_ERROR_AND_RETURN(thread, "Race: this value is not object", JSTaggedValue::Exception()); + } + // 3. Let S be Get(C, @@species). + // 4. ReturnIfAbrupt(S). + // 5. If S is neither undefined nor null, let C be S. + JSHandle speciesSymbol = env->GetSpeciesSymbol(); + JSHandle speciesConstructor = JSObject::GetProperty(thread, thisValue, speciesSymbol).GetValue(); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + if (!(speciesConstructor->IsUndefined() || speciesConstructor->IsNull())) { + thisValue = speciesConstructor; + } + + // 6. Let promiseCapability be NewPromiseCapability(C). + // 7. ReturnIfAbrupt(promiseCapability). + JSHandle promiseCapability = JSPromise::NewPromiseCapability(thread, thisValue); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + + // 8. Let iterator be GetIterator(iterable). + // 9. IfAbruptRejectPromise(iterator, promiseCapability). + JSHandle iterable = GetCallArg(argv, 0); + JSHandle iterator = JSIterator::GetIterator(thread, iterable); + if (thread->HasPendingException()) { + iterator = JSPromise::IfThrowGetThrowValue(thread); + } + RETURN_REJECT_PROMISE_IF_ABRUPT(thread, iterator, promiseCapability); + + // 10. Let iteratorRecord be Record {[[iterator]]: iterator, [[done]]: false}. + JSHandle done(thread, JSTaggedValue::False()); + JSHandle iteratorRecord = factory->NewPromiseIteratorRecord(iterator, done); + + // 11. Let result be PerformPromiseRace(iteratorRecord, promiseCapability, C). + // 12. If result is an abrupt completion, then + // a. If iteratorRecord.[[done]] is false, let result be IteratorClose(iterator,result). + // b. IfAbruptRejectPromise(result, promiseCapability). + // 13. Return Completion(result). + JSHandle result = PerformPromiseRace(thread, iteratorRecord, promiseCapability, thisValue); + if (result->IsThrow()) { + thread->ClearException(); + if (iteratorRecord->GetDone().IsFalse()) { + JSHandle value = + JSIterator::IteratorClose(thread, iterator, JSHandle::Cast(result)); + if (value.GetTaggedValue().IsCompletionRecord()) { + result = JSHandle(value); + RETURN_REJECT_PROMISE_IF_ABRUPT(thread, result, promiseCapability); + return result->GetValue(); + } + } + RETURN_REJECT_PROMISE_IF_ABRUPT(thread, result, promiseCapability); + return result->GetValue(); + } + return result->GetValue(); +} + +// 25.4.4.5 Promise.resolve ( x ) +JSTaggedValue BuiltinsPromise::Resolve(EcmaRuntimeCallInfo *argv) +{ + ASSERT(argv); + BUILTINS_API_TRACE(argv->GetThread(), Promise, Resolve); + JSThread *thread = argv->GetThread(); + [[maybe_unused]] EcmaHandleScope handleScope(thread); + + // 1. Let C be the this value. + JSHandle thisValue = GetThis(argv); + // 2. If Type(C) is not Object, throw a TypeError exception. + if (!thisValue->IsECMAObject()) { + THROW_TYPE_ERROR_AND_RETURN(thread, "Resolve: this value is not object", JSTaggedValue::Exception()); + } + + JSHandle xValue = BuiltinsBase::GetCallArg(argv, 0); + return JSPromise::PromiseResolve(thread, thisValue, xValue); +} + +// 25.4.4.4 Promise.reject ( r ) +JSTaggedValue BuiltinsPromise::Reject(EcmaRuntimeCallInfo *argv) +{ + ASSERT(argv); + BUILTINS_API_TRACE(argv->GetThread(), Promise, Reject); + JSThread *thread = argv->GetThread(); + [[maybe_unused]] EcmaHandleScope handleScope(thread); + const GlobalEnvConstants *globalConst = thread->GlobalConstants(); + + // 1. Let C be the this value. + // 2. If Type(C) is not Object, throw a TypeError exception. + JSHandle thisValue = GetThis(argv); + if (!thisValue->IsECMAObject()) { + THROW_TYPE_ERROR_AND_RETURN(thread, "Reject: this value is not object", JSTaggedValue::Exception()); + } + + // 3. Let promiseCapability be NewPromiseCapability(C). + // 4. ReturnIfAbrupt(promiseCapability). + JSHandle promiseCapability = JSPromise::NewPromiseCapability(thread, thisValue); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + + // 5. Let rejectResult be Call(promiseCapability.[[Reject]], undefined, «r»). + // 6. ReturnIfAbrupt(rejectResult). + JSHandle reason = GetCallArg(argv, 0); + JSHandle reject(thread, promiseCapability->GetReject()); + JSHandle undefined = globalConst->GetHandledUndefined(); + InternalCallParams *arguments = thread->GetInternalCallParams(); + arguments->MakeArgv(reason); + JSFunction::Call(thread, reject, undefined, 1, arguments->GetArgv()); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + + // 7. Return promiseCapability.[[Promise]]. + JSHandle promise(thread, promiseCapability->GetPromise()); + return promise.GetTaggedValue(); +} + +// 25.4.4.6 get Promise [ @@species ] +JSTaggedValue BuiltinsPromise::GetSpecies([[maybe_unused]] EcmaRuntimeCallInfo *argv) +{ + ASSERT(argv); + return JSTaggedValue(GetThis(argv).GetTaggedValue()); +} + +// 25.4.5.1 Promise.prototype.catch ( onRejected ) +JSTaggedValue BuiltinsPromise::Catch(EcmaRuntimeCallInfo *argv) +{ + // 1. Let promise be the this value. + // 2. Return Invoke(promise, "then", «undefined, onRejected»). + ASSERT(argv); + BUILTINS_API_TRACE(argv->GetThread(), Promise, Catch); + JSThread *thread = argv->GetThread(); + [[maybe_unused]] EcmaHandleScope handleScope(thread); + const GlobalEnvConstants *globalConst = thread->GlobalConstants(); + JSHandle promise = GetThis(argv); + JSHandle thenKey = globalConst->GetHandledPromiseThenString(); + JSHandle reject = GetCallArg(argv, 0); + + InternalCallParams *arguments = thread->GetInternalCallParams(); + arguments->MakeArgv(globalConst->GetHandledUndefined(), reject); + return JSFunction::Invoke(thread, promise, thenKey, 2, arguments->GetArgv()); // 2: two args +} + +// 25.4.5.3 Promise.prototype.then ( onFulfilled , onRejected ) +JSTaggedValue BuiltinsPromise::Then(EcmaRuntimeCallInfo *argv) +{ + ASSERT(argv); + BUILTINS_API_TRACE(argv->GetThread(), Promise, Then); + JSThread *thread = argv->GetThread(); + [[maybe_unused]] EcmaHandleScope handleScope(thread); + auto ecmaVm = thread->GetEcmaVM(); + JSHandle env = ecmaVm->GetGlobalEnv(); + + // 1. Let promise be the this value. + JSHandle thisValue = GetThis(argv); + // 2. If IsPromise(promise) is false, throw a TypeError exception. + if (!thisValue->IsJSPromise()) { + THROW_TYPE_ERROR_AND_RETURN(thread, "Then: thisValue is not promise!", JSTaggedValue::Exception()); + } + // 3. Let C be SpeciesConstructor(promise, %Promise%). + // 4. ReturnIfAbrupt(C). + JSHandle promise = JSHandle::Cast(thisValue); + JSHandle defaultFunc = JSHandle::Cast(env->GetPromiseFunction()); + JSHandle constructor = JSObject::SpeciesConstructor(thread, promise, defaultFunc); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + + // 5. Let resultCapability be NewPromiseCapability(C). + // 6. ReturnIfAbrupt(resultCapability). + JSHandle resultCapability = JSPromise::NewPromiseCapability(thread, constructor); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + + JSHandle onFulfilled = BuiltinsBase::GetCallArg(argv, 0); + JSHandle onRejected = BuiltinsBase::GetCallArg(argv, 1); + + // 7. Return PerformPromiseThen(promise, onFulfilled, onRejected, resultCapability). + return PerformPromiseThen(thread, JSHandle::Cast(promise), onFulfilled, onRejected, + JSHandle::Cast(resultCapability)); +} + +JSTaggedValue BuiltinsPromise::PerformPromiseThen(JSThread *thread, const JSHandle &promise, + const JSHandle &onFulfilled, + const JSHandle &onRejected, + const JSHandle &capability) +{ + auto ecmaVm = thread->GetEcmaVM(); + JSHandle job = ecmaVm->GetMicroJobQueue(); + JSHandle env = ecmaVm->GetGlobalEnv(); + ObjectFactory *factory = ecmaVm->GetFactory(); + JSMutableHandle fulfilled(thread, onFulfilled.GetTaggedValue()); + auto globalConst = thread->GlobalConstants(); + if (!fulfilled->IsCallable()) { + fulfilled.Update(globalConst->GetIdentityString()); + } + JSMutableHandle rejected(thread, onRejected.GetTaggedValue()); + if (!rejected->IsCallable()) { + rejected.Update(globalConst->GetThrowerString()); + } + JSHandle fulfillReaction = factory->NewPromiseReaction(); + fulfillReaction->SetPromiseCapability(thread, capability.GetTaggedValue()); + fulfillReaction->SetHandler(thread, fulfilled.GetTaggedValue()); + + JSHandle rejectReaction = factory->NewPromiseReaction(); + rejectReaction->SetPromiseCapability(thread, capability.GetTaggedValue()); + rejectReaction->SetHandler(thread, rejected.GetTaggedValue()); + + if (JSTaggedValue::SameValue(promise->GetPromiseState(), + JSTaggedValue(static_cast(PromiseStatus::PENDING)))) { + JSHandle fulfillReactions(thread, promise->GetPromiseFulfillReactions()); + TaggedQueue *newQueue = + TaggedQueue::Push(thread, fulfillReactions, JSHandle::Cast(fulfillReaction)); + promise->SetPromiseFulfillReactions(thread, JSTaggedValue(newQueue)); + + JSHandle rejectReactions(thread, promise->GetPromiseRejectReactions()); + newQueue = TaggedQueue::Push(thread, rejectReactions, JSHandle::Cast(rejectReaction)); + promise->SetPromiseRejectReactions(thread, JSTaggedValue(newQueue)); + } else if (JSTaggedValue::SameValue(promise->GetPromiseState(), + JSTaggedValue(static_cast(PromiseStatus::FULFILLED)))) { + JSHandle argv = factory->NewTaggedArray(2); // 2: 2 means two args stored in array + argv->Set(thread, 0, fulfillReaction.GetTaggedValue()); + argv->Set(thread, 1, promise->GetPromiseResult()); + + JSHandle promiseReactionsJob(env->GetPromiseReactionJob()); + job::MicroJobQueue::EnqueueJob(thread, job, job::QueueType::QUEUE_PROMISE, promiseReactionsJob, argv); + } else if (JSTaggedValue::SameValue(promise->GetPromiseState(), + JSTaggedValue(static_cast(PromiseStatus::REJECTED)))) { + JSHandle argv = factory->NewTaggedArray(2); // 2: 2 means two args stored in array + argv->Set(thread, 0, rejectReaction.GetTaggedValue()); + argv->Set(thread, 1, promise->GetPromiseResult()); + // When a handler is added to a rejected promise for the first time, it is called with its operation + // argument set to "handle". + if (!promise->GetPromiseIsHandled().ToBoolean()) { + JSHandle reason(thread, JSTaggedValue::Null()); + thread->GetEcmaVM()->PromiseRejectionTracker(promise, reason, ecmascript::PromiseRejectionEvent::HANDLE); + } + JSHandle promiseReactionsJob(env->GetPromiseReactionJob()); + job::MicroJobQueue::EnqueueJob(thread, job, job::QueueType::QUEUE_PROMISE, promiseReactionsJob, argv); + } + + promise->SetPromiseIsHandled(thread, JSTaggedValue::True()); + + if (capability->IsUndefined()) { + return capability.GetTaggedValue(); + } + + return PromiseCapability::Cast(capability->GetHeapObject())->GetPromise(); +} + +JSHandle BuiltinsPromise::PerformPromiseAll(JSThread *thread, + const JSHandle &itRecord, + const JSHandle &ctor, + const JSHandle &capa) +{ + auto ecmaVm = thread->GetEcmaVM(); + const GlobalEnvConstants *globalConst = thread->GlobalConstants(); + ObjectFactory *factory = ecmaVm->GetFactory(); + // 1. Assert: constructor is a constructor function. + ASSERT_PRINT(ctor->IsConstructor(), "PerformPromiseAll is not constructor"); + // 2. Assert: resultCapability is a PromiseCapability record. (not need) + // 3. Let values be a new empty List. + JSHandle values = factory->NewPromiseRecord(); + JSHandle emptyArray = factory->EmptyArray(); + values->SetValue(thread, emptyArray); + // 4. Let remainingElementsCount be a new Record { [[value]]: 1 }. + JSHandle remainCnt = factory->NewPromiseRecord(); + remainCnt->SetValue(thread, JSTaggedNumber(1)); + // 5. Let index be 0. + uint32_t index = 0; + // 6. Repeat + JSHandle itor(thread, itRecord->GetIterator()); + JSMutableHandle next(thread, globalConst->GetUndefined()); + while (true) { + // a. Let next be IteratorStep(iteratorRecord.[[iterator]]). + next.Update(JSIterator::IteratorStep(thread, itor).GetTaggedValue()); + // b. If next is an abrupt completion, set iteratorRecord.[[done]] to true. + if (thread->HasPendingException()) { + itRecord->SetDone(thread, JSTaggedValue::True()); + next.Update(JSPromise::IfThrowGetThrowValue(thread).GetTaggedValue()); + } + // c. ReturnIfAbrupt(next). + RETURN_COMPLETION_IF_ABRUPT(thread, next); + // d. If next is false, + if (next->IsFalse()) { + // i. Set iteratorRecord.[[done]] to true. + itRecord->SetDone(thread, JSTaggedValue::True()); + // ii. Set remainingElementsCount.[[value]] to remainingElementsCount.[[value]] − 1. + remainCnt->SetValue(thread, --JSTaggedNumber(remainCnt->GetValue())); + // iii. If remainingElementsCount.[[value]] is 0, + if (remainCnt->GetValue().IsZero()) { + // 1. Let valuesArray be CreateArrayFromList(values). + JSHandle jsArrayValues = + JSArray::CreateArrayFromList(thread, JSHandle(thread, values->GetValue())); + // 2. Let resolveResult be Call(resultCapability.[[Resolve]], undefined, «valuesArray»). + JSHandle resCapaFunc(thread, capa->GetResolve()); + InternalCallParams *arguments = thread->GetInternalCallParams(); + arguments->MakeArgv(jsArrayValues.GetTaggedValue()); + JSTaggedValue resolveRes = + JSFunction::Call(thread, resCapaFunc, globalConst->GetHandledUndefined(), 1, arguments->GetArgv()); + // 3. ReturnIfAbrupt(resolveResult) + JSHandle resolveAbrupt(thread, resolveRes); + RETURN_COMPLETION_IF_ABRUPT(thread, resolveAbrupt); + } + // iv. Return resultCapability.[[Promise]]. + JSHandle resRecord = factory->NewCompletionRecord( + CompletionRecord::NORMAL, JSHandle(thread, capa->GetPromise())); + return resRecord; + } + // e. Let nextValue be IteratorValue(next). + JSHandle nextVal = JSIterator::IteratorValue(thread, next); + // f. If nextValue is an abrupt completion, set iteratorRecord.[[done]] to true. + if (thread->HasPendingException()) { + itRecord->SetDone(thread, JSTaggedValue::True()); + if (thread->GetException().IsObjectWrapper()) { + JSHandle wrapperVal(thread, thread->GetException()); + JSHandle throwVal(thread, wrapperVal->GetValue()); + nextVal = throwVal; + } else { + nextVal = JSHandle(thread, thread->GetException()); + } + } + + // g. ReturnIfAbrupt(nextValue). + RETURN_COMPLETION_IF_ABRUPT(thread, nextVal); + // h. Append undefined to values. + JSHandle valuesArray = + JSHandle::Cast(JSHandle(thread, values->GetValue())); + valuesArray = TaggedArray::SetCapacity(thread, valuesArray, index + 1); + valuesArray->Set(thread, index, JSTaggedValue::Undefined()); + values->SetValue(thread, valuesArray); + // i. Let nextPromise be Invoke(constructor, "resolve", «‍nextValue»). + JSHandle resolveKey = globalConst->GetHandledPromiseResolveString(); + InternalCallParams *arguments = thread->GetInternalCallParams(); + arguments->MakeArgv(nextVal); + JSTaggedValue taggedNextPromise = JSFunction::Invoke(thread, ctor, resolveKey, 1, arguments->GetArgv()); + // j. ReturnIfAbrupt(nextPromise). + JSHandle nextPromise(thread, taggedNextPromise); + RETURN_COMPLETION_IF_ABRUPT(thread, nextPromise); + // k. Let resolveElement be a new built-in function object as defined in Promise.all + // Resolve Element Functions. + JSHandle resoleveElement = factory->NewJSPromiseAllResolveElementFunction( + reinterpret_cast(BuiltinsPromiseHandler::ResolveElementFunction)); + // l. Set the [[AlreadyCalled]] internal slot of resolveElement to a new Record {[[value]]: false }. + JSHandle falseRecord = factory->NewPromiseRecord(); + falseRecord->SetValue(thread, JSTaggedValue::False()); + resoleveElement->SetAlreadyCalled(thread, falseRecord); + // m. Set the [[Index]] internal slot of resolveElement to index. + resoleveElement->SetIndex(thread, JSTaggedValue(index)); + // n. Set the [[Values]] internal slot of resolveElement to values. + resoleveElement->SetValues(thread, values); + // o. Set the [[Capabilities]] internal slot of resolveElement to resultCapability. + resoleveElement->SetCapabilities(thread, capa); + // p. Set the [[RemainingElements]] internal slot of resolveElement to remainingElementsCount. + resoleveElement->SetRemainingElements(thread, remainCnt); + // q. Set remainingElementsCount.[[value]] to remainingElementsCount.[[value]] + 1. + remainCnt->SetValue(thread, ++JSTaggedNumber(remainCnt->GetValue())); + // r. Let result be Invoke(nextPromise, "then", «‍resolveElement, resultCapability.[[Reject]]»). + JSHandle thenKey = globalConst->GetHandledPromiseThenString(); + arguments->MakeArgv(resoleveElement.GetTaggedValue(), capa->GetReject()); + JSTaggedValue taggedResult = + JSFunction::Invoke(thread, nextPromise, thenKey, 2, arguments->GetArgv()); // 2: two args + + JSHandle result(thread, taggedResult); + // s. ReturnIfAbrupt(result). + RETURN_COMPLETION_IF_ABRUPT(thread, result); + // t. Set index to index + 1. + ++index; + } +} + +JSHandle BuiltinsPromise::PerformPromiseRace(JSThread *thread, + const JSHandle &iteratorRecord, + const JSHandle &capability, + const JSHandle &constructor) +{ + // 1. Repeat + // a. Let next be IteratorStep(iteratorRecord.[[iterator]]). + // b. If next is an abrupt completion, set iteratorRecord.[[done]] to true. + // c. ReturnIfAbrupt(next). + // d. If next is false, then + // i. Set iteratorRecord.[[done]] to true. + // ii. Return promiseCapability.[[Promise]]. + // e. Let nextValue be IteratorValue(next). + // f. If nextValue is an abrupt completion, set iteratorRecord.[[done]] to true. + // g. ReturnIfAbrupt(nextValue). + // h. Let nextPromise be Invoke(C, "resolve", «nextValue»). + // i. ReturnIfAbrupt(nextPromise). + // j. Let result be Invoke(nextPromise, "then", «promiseCapability.[[Resolve]], promiseCapability.[[Reject]]»). + // k. ReturnIfAbrupt(result). + auto ecmaVm = thread->GetEcmaVM(); + const GlobalEnvConstants *globalConst = thread->GlobalConstants(); + ObjectFactory *factory = ecmaVm->GetFactory(); + JSHandle iterator(thread, iteratorRecord->GetIterator()); + JSMutableHandle next(thread, globalConst->GetUndefined()); + while (true) { + next.Update(JSIterator::IteratorStep(thread, iterator).GetTaggedValue()); + if (thread->HasPendingException()) { + iteratorRecord->SetDone(thread, JSTaggedValue::True()); + next.Update(JSPromise::IfThrowGetThrowValue(thread).GetTaggedValue()); + } + RETURN_COMPLETION_IF_ABRUPT(thread, next); + if (next->IsFalse()) { + iteratorRecord->SetDone(thread, JSTaggedValue::True()); + JSHandle promise(thread, capability->GetPromise()); + JSHandle completionRecord = + factory->NewCompletionRecord(CompletionRecord::NORMAL, promise); + return completionRecord; + } + JSHandle nextValue = JSIterator::IteratorValue(thread, next); + if (thread->HasPendingException()) { + iteratorRecord->SetDone(thread, JSTaggedValue::True()); + nextValue = JSPromise::IfThrowGetThrowValue(thread); + } + RETURN_COMPLETION_IF_ABRUPT(thread, nextValue); + JSHandle resolveStr = globalConst->GetHandledPromiseResolveString(); + + InternalCallParams *arguments = thread->GetInternalCallParams(); + arguments->MakeArgv(nextValue); + JSTaggedValue result = JSFunction::Invoke(thread, constructor, resolveStr, 1, arguments->GetArgv()); + + JSHandle nextPromise(thread, result); + if (thread->HasPendingException()) { + nextPromise = JSPromise::IfThrowGetThrowValue(thread); + } + RETURN_COMPLETION_IF_ABRUPT(thread, nextPromise); + + JSHandle thenStr = globalConst->GetHandledPromiseThenString(); + arguments->MakeArgv(capability->GetResolve(), capability->GetReject()); + result = JSFunction::Invoke(thread, nextPromise, thenStr, 2, arguments->GetArgv()); // 2: two args + + JSHandle handleResult(thread, result); + if (thread->HasPendingException()) { + handleResult = JSPromise::IfThrowGetThrowValue(thread); + } + RETURN_COMPLETION_IF_ABRUPT(thread, handleResult); + } +} +} // namespace panda::ecmascript::builtins diff --git a/runtime/builtins/builtins_promise.h b/runtime/builtins/builtins_promise.h new file mode 100644 index 000000000..6d2147d19 --- /dev/null +++ b/runtime/builtins/builtins_promise.h @@ -0,0 +1,63 @@ +/* + * Copyright (c) 2021-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_BUILTINS_BUILTINS_PROMISE_H +#define ECMASCRIPT_BUILTINS_BUILTINS_PROMISE_H + +#include "plugins/ecmascript/runtime/ecma_runtime_call_info.h" +#include "plugins/ecmascript/runtime/base/builtins_base.h" +#include "plugins/ecmascript/runtime/js_handle.h" +#include "plugins/ecmascript/runtime/js_promise.h" +#include "plugins/ecmascript/runtime/js_tagged_value.h" +#include "plugins/ecmascript/runtime/object_factory.h" + +namespace panda::ecmascript::builtins { +class BuiltinsPromise : public ecmascript::base::BuiltinsBase { +public: + // 25.4.3.1 Promise ( executor ) + static JSTaggedValue PromiseConstructor(EcmaRuntimeCallInfo *argv); + // 25.4.4.1 Promise.all ( iterable ) + static JSTaggedValue All(EcmaRuntimeCallInfo *argv); + // 25.4.4.3 Promise.race ( iterable ) + static JSTaggedValue Race(EcmaRuntimeCallInfo *argv); + // 25.4.4.4 Promise.reject ( r ) + static JSTaggedValue Reject(EcmaRuntimeCallInfo *argv); + // 25.4.4.5 Promise.resolve ( x ) + static JSTaggedValue Resolve(EcmaRuntimeCallInfo *argv); + // 25.4.4.6 get Promise [ @@species ] + static JSTaggedValue GetSpecies(EcmaRuntimeCallInfo *argv); + // 25.4.5.1 Promise.prototype.catch ( onRejected ) + static JSTaggedValue Catch(EcmaRuntimeCallInfo *argv); + // 25.4.5.3 Promise.prototype.then ( onFulfilled , onRejected ) + static JSTaggedValue Then(EcmaRuntimeCallInfo *argv); + + static JSTaggedValue PerformPromiseThen(JSThread *thread, const JSHandle &promise, + const JSHandle &onFulfilled, + const JSHandle &onRejected, + const JSHandle &capability); + +private: + static JSHandle PerformPromiseAll(JSThread *thread, + const JSHandle &itRecord, + const JSHandle &ctor, + const JSHandle &capa); + + static JSHandle PerformPromiseRace(JSThread *thread, + const JSHandle &iteratorRecord, + const JSHandle &capability, + const JSHandle &constructor); +}; +} // namespace panda::ecmascript::builtins +#endif // ECMASCRIPT_BUILTINS_BUILTINS_PROMISE_H diff --git a/runtime/builtins/builtins_promise_handler.cpp b/runtime/builtins/builtins_promise_handler.cpp new file mode 100644 index 000000000..bcf66c0c6 --- /dev/null +++ b/runtime/builtins/builtins_promise_handler.cpp @@ -0,0 +1,243 @@ +/* + * Copyright (c) 2021-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 "plugins/ecmascript/runtime/builtins/builtins_promise_handler.h" + +#include "plugins/ecmascript/runtime/global_env.h" +#include "plugins/ecmascript/runtime/internal_call_params.h" +#include "plugins/ecmascript/runtime/jobs/micro_job_queue.h" +#include "plugins/ecmascript/runtime/js_array.h" +#include "plugins/ecmascript/runtime/js_async_function.h" +#include "plugins/ecmascript/runtime/js_handle.h" +#include "plugins/ecmascript/runtime/js_promise.h" +#include "plugins/ecmascript/runtime/js_tagged_value-inl.h" +#include "plugins/ecmascript/runtime/mem/assert_scope-inl.h" + +namespace panda::ecmascript::builtins { +// es6 25.4.1.3.2 Promise Resolve Functions +JSTaggedValue BuiltinsPromiseHandler::Resolve(EcmaRuntimeCallInfo *argv) +{ + ASSERT(argv); + BUILTINS_API_TRACE(argv->GetThread(), PromiseHandler, Resolve); + JSThread *thread = argv->GetThread(); + [[maybe_unused]] EcmaHandleScope handleScope(thread); + ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); + auto ecmaVm = thread->GetEcmaVM(); + JSHandle env = ecmaVm->GetGlobalEnv(); + + // 1. Assert: F has a [[Promise]] internal slot whose value is an Object. + JSHandle resolve = JSHandle::Cast(GetConstructor(argv)); + ASSERT_PRINT(resolve->GetPromise().IsECMAObject(), "Resolve: promise must be js object"); + + // 2. Let promise be the value of F's [[Promise]] internal slot. + // 3. Let alreadyResolved be the value of F's [[AlreadyResolved]] internal slot. + // 4. If alreadyResolved.[[value]] is true, return undefined. + // 5. Set alreadyResolved.[[value]] to true. + JSHandle resolvePromise(thread, resolve->GetPromise()); + JSHandle alreadyResolved(thread, resolve->GetAlreadyResolved()); + if (alreadyResolved->GetValue().IsTrue()) { + return JSTaggedValue::Undefined(); + } + alreadyResolved->SetValue(thread, JSTaggedValue::True()); + + // 6. If SameValue(resolution, promise) is true, then + // a. Let selfResolutionError be a newly created TypeError object. + // b. Return RejectPromise(promise, selfResolutionError). + JSHandle resolution = BuiltinsBase::GetCallArg(argv, 0); + if (JSTaggedValue::SameValue(resolution.GetTaggedValue(), resolvePromise.GetTaggedValue())) { + JSHandle resolutionError = + factory->GetJSError(ErrorType::TYPE_ERROR, "Resolve: The promise and resolution cannot be the same."); + JSPromise::RejectPromise(thread, resolvePromise, JSHandle::Cast(resolutionError)); + return JSTaggedValue::Undefined(); + } + // 7. If Type(resolution) is not Object, then + // a. Return FulfillPromise(promise, resolution). + if (!resolution.GetTaggedValue().IsECMAObject()) { + JSPromise::FulfillPromise(thread, resolvePromise, resolution); + return JSTaggedValue::Undefined(); + } + // 8. Let then be Get(resolution, "then"). + // 9. If then is an abrupt completion, then + // a. Return RejectPromise(promise, then.[[value]]). + JSHandle thenKey(thread->GlobalConstants()->GetHandledPromiseThenString()); + JSHandle thenValue = JSObject::GetProperty(thread, resolution, thenKey).GetValue(); + if (thread->HasPendingException()) { + if (!thenValue->IsJSError()) { + if (thread->GetException().IsObjectWrapper()) { + JSHandle wrapperValue(thread, thread->GetException()); + JSHandle throwValue(thread, wrapperValue->GetValue()); + thenValue = throwValue; + } else { + thenValue = JSHandle(thread, thread->GetException()); + } + } + thread->ClearException(); + return JSPromise::RejectPromise(thread, resolvePromise, thenValue); + } + // 10. Let thenAction be then.[[value]]. + // 11. If IsCallable(thenAction) is false, then + // a. Return FulfillPromise(promise, resolution). + if (!thenValue->IsCallable()) { + JSPromise::FulfillPromise(thread, resolvePromise, resolution); + return JSTaggedValue::Undefined(); + } + // 12. Perform EnqueueJob ("PromiseJobs", PromiseResolveThenableJob, «promise, resolution, thenAction») + JSHandle arguments = factory->NewTaggedArray(3); // 3: 3 means three args stored in array + arguments->Set(thread, 0, resolvePromise); + arguments->Set(thread, 1, resolution); + arguments->Set(thread, 2, thenValue); // 2: 2 means index of array is 2 + + JSHandle promiseResolveThenableJob(env->GetPromiseResolveThenableJob()); + JSHandle job = thread->GetEcmaVM()->GetMicroJobQueue(); + job::MicroJobQueue::EnqueueJob(thread, job, job::QueueType::QUEUE_PROMISE, promiseResolveThenableJob, arguments); + + // 13. Return undefined. + return JSTaggedValue::Undefined(); +} + +// es6 25.4.1.3.1 Promise Reject Functions +JSTaggedValue BuiltinsPromiseHandler::Reject(EcmaRuntimeCallInfo *argv) +{ + ASSERT(argv); + BUILTINS_API_TRACE(argv->GetThread(), PromiseHandler, Reject); + JSThread *thread = argv->GetThread(); + [[maybe_unused]] EcmaHandleScope handleScope(thread); + + // 1. Assert: F has a [[Promise]] internal slot whose value is an Object. + JSHandle reject = JSHandle::Cast(GetConstructor(argv)); + ASSERT_PRINT(reject->GetPromise().IsECMAObject(), "Reject: promise must be js object"); + + // 2. Let promise be the value of F's [[Promise]] internal slot. + // 3. Let alreadyResolved be the value of F's [[AlreadyResolved]] internal slot. + // 4. If alreadyResolved.[[value]] is true, return undefined. + // 5. Set alreadyResolved.[[value]] to true. + JSHandle rejectPromise(thread, reject->GetPromise()); + JSHandle alreadyResolved(thread, reject->GetAlreadyResolved()); + if (alreadyResolved->GetValue().IsTrue()) { + return JSTaggedValue::Undefined(); + } + alreadyResolved->SetValue(thread, JSTaggedValue::True()); + + // 6. Return RejectPromise(promise, reason). + JSHandle reason = GetCallArg(argv, 0); + JSHandle result(thread, JSPromise::RejectPromise(thread, rejectPromise, reason)); + return result.GetTaggedValue(); +} + +// es6 25.4.1.5.1 GetCapabilitiesExecutor Functions +JSTaggedValue BuiltinsPromiseHandler::Executor(EcmaRuntimeCallInfo *argv) +{ + ASSERT(argv); + BUILTINS_API_TRACE(argv->GetThread(), PromiseHandler, Executor); + JSThread *thread = argv->GetThread(); + [[maybe_unused]] EcmaHandleScope handleScope(thread); + + // 1. Assert: F has a [[Capability]] internal slot whose value is a PromiseCapability Record. + JSHandle executor = JSHandle::Cast(GetConstructor(argv)); + ASSERT_PRINT(executor->GetCapability().IsRecord(), + "Executor: F has a [[Capability]] internal slot whose value is a PromiseCapability Record."); + + // 2. Let promiseCapability be the value of F's [[Capability]] internal slot. + // 3. If promiseCapability.[[Resolve]] is not undefined, throw a TypeError exception. + JSHandle promiseCapability(thread, executor->GetCapability()); + if (!promiseCapability->GetResolve().IsUndefined()) { + THROW_TYPE_ERROR_AND_RETURN(thread, "Executor: resolve should be undefine!", JSTaggedValue::Undefined()); + } + // 4. If promiseCapability.[[Reject]] is not undefined, throw a TypeError exception. + if (!promiseCapability->GetReject().IsUndefined()) { + THROW_TYPE_ERROR_AND_RETURN(thread, "Executor: reject should be undefine!", JSTaggedValue::Undefined()); + } + // 5. Set promiseCapability.[[Resolve]] to resolve. + // 6. Set promiseCapability.[[Reject]] to reject. + JSHandle resolve = GetCallArg(argv, 0); + JSHandle reject = GetCallArg(argv, 1); + promiseCapability->SetResolve(thread, resolve); + promiseCapability->SetReject(thread, reject); + // 7. Return undefined. + return JSTaggedValue::Undefined(); +} + +// es6 25.4.4.1.2 Promise.all Resolve Element Functions +JSTaggedValue BuiltinsPromiseHandler::ResolveElementFunction(EcmaRuntimeCallInfo *argv) +{ + ASSERT(argv); + BUILTINS_API_TRACE(argv->GetThread(), PromiseHandler, ResolveElementFunction); + JSThread *thread = argv->GetThread(); + [[maybe_unused]] EcmaHandleScope handleScope(thread); + const GlobalEnvConstants *globalConst = thread->GlobalConstants(); + JSHandle func = + JSHandle::Cast(GetConstructor(argv)); + // 1. Let alreadyCalled be the value of F's [[AlreadyCalled]] internal slot. + JSHandle alreadyCalled = + JSHandle::Cast(JSHandle(thread, func->GetAlreadyCalled())); + // 2. If alreadyCalled.[[value]] is true, return undefined. + if (alreadyCalled->GetValue().IsTrue()) { + return JSTaggedValue::Undefined(); + } + // 3. Set alreadyCalled.[[value]] to true. + alreadyCalled->SetValue(thread, JSTaggedValue::True()); + // 4. Let index be the value of F's [[Index]] internal slot. + JSHandle index(thread, func->GetIndex()); + // 5. Let values be the value of F's [[Values]] internal slot. + JSHandle values = JSHandle::Cast(JSHandle(thread, func->GetValues())); + // 6. Let promiseCapability be the value of F's [[Capabilities]] internal slot. + JSHandle capa = + JSHandle::Cast(JSHandle(thread, func->GetCapabilities())); + // 7. Let remainingElementsCount be the value of F's [[RemainingElements]] internal slot. + JSHandle remainCnt = + JSHandle::Cast(JSHandle(thread, func->GetRemainingElements())); + // 8. Set values[index] to x. + JSHandle arrayValues = + JSHandle::Cast(JSHandle(thread, values->GetValue())); + arrayValues->Set(thread, JSTaggedValue::ToUint32(thread, index), GetCallArg(argv, 0).GetTaggedValue()); + // 9. Set remainingElementsCount.[[value]] to remainingElementsCount.[[value]] - 1. + remainCnt->SetValue(thread, --JSTaggedNumber(remainCnt->GetValue())); + // 10. If remainingElementsCount.[[value]] is 0, + if (remainCnt->GetValue().IsZero()) { + // a. Let valuesArray be CreateArrayFromList(values). + JSHandle jsArrayValues = JSArray::CreateArrayFromList(thread, arrayValues); + // b. Return Call(promiseCapability.[[Resolve]], undefined, «valuesArray»). + JSHandle capaResolve(thread, capa->GetResolve()); + JSHandle undefine = globalConst->GetHandledUndefined(); + InternalCallParams *arguments = thread->GetInternalCallParams(); + arguments->MakeArgv(jsArrayValues); + return JSFunction::Call(thread, capaResolve, undefine, 1, arguments->GetArgv()); + } + // 11. Return undefined. + return JSTaggedValue::Undefined(); +} + +JSTaggedValue BuiltinsPromiseHandler::AsyncAwaitFulfilled(EcmaRuntimeCallInfo *argv) +{ + ASSERT(argv); + BUILTINS_API_TRACE(argv->GetThread(), PromiseHandler, AsyncAwaitFulfilled); + [[maybe_unused]] EcmaHandleScope handleScope(argv->GetThread()); + + JSHandle value = GetCallArg(argv, 0); + JSHandle func(GetConstructor(argv)); + return JSAsyncAwaitStatusFunction::AsyncFunctionAwaitFulfilled(argv->GetThread(), func, value).GetTaggedValue(); +} + +JSTaggedValue BuiltinsPromiseHandler::AsyncAwaitRejected(EcmaRuntimeCallInfo *argv) +{ + ASSERT(argv); + BUILTINS_API_TRACE(argv->GetThread(), PromiseHandler, AsyncAwaitRejected); + [[maybe_unused]] EcmaHandleScope handleScope(argv->GetThread()); + + JSHandle reason = GetCallArg(argv, 0); + JSHandle func(GetConstructor(argv)); + return JSAsyncAwaitStatusFunction::AsyncFunctionAwaitRejected(argv->GetThread(), func, reason).GetTaggedValue(); +} +} // namespace panda::ecmascript::builtins diff --git a/runtime/builtins/builtins_promise_handler.h b/runtime/builtins/builtins_promise_handler.h new file mode 100644 index 000000000..7707f8cc3 --- /dev/null +++ b/runtime/builtins/builtins_promise_handler.h @@ -0,0 +1,44 @@ +/* + * Copyright (c) 2021-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_BUILTINS_BUILTINS_PROMISE_HANDLER_H +#define ECMASCRIPT_BUILTINS_BUILTINS_PROMISE_HANDLER_H + +#include "plugins/ecmascript/runtime/base/builtins_base.h" + +namespace panda::ecmascript::builtins { +class BuiltinsPromiseHandler : public ecmascript::base::BuiltinsBase { +public: + // es6 26.6.1.3.1 Promise Resolve Functions + static JSTaggedValue Resolve(EcmaRuntimeCallInfo *argv); + + // es6 26.6.1.3.2 Promise Reject Functions + static JSTaggedValue Reject(EcmaRuntimeCallInfo *argv); + + // es6 26.6.1.5.1 GetCapabilitiesExecutor Functions + static JSTaggedValue Executor(EcmaRuntimeCallInfo *argv); + + // es2017 25.5.5.4 AsyncFunction Awaited Fulfilled + static JSTaggedValue AsyncAwaitFulfilled(EcmaRuntimeCallInfo *argv); + + // es2017 25.5.5.5 AsyncFunction Awaited Rejected + static JSTaggedValue AsyncAwaitRejected(EcmaRuntimeCallInfo *argv); + + // es6 25.4.4.1.2 Promise.all Resolve Element Functions + static JSTaggedValue ResolveElementFunction(EcmaRuntimeCallInfo *argv); +}; +} // namespace panda::ecmascript::builtins + +#endif // ECMASCRIPT_BUILTINS_BUILTINS_PROMISE_HANDLER_H diff --git a/runtime/builtins/builtins_promise_job.cpp b/runtime/builtins/builtins_promise_job.cpp new file mode 100644 index 000000000..585a5b107 --- /dev/null +++ b/runtime/builtins/builtins_promise_job.cpp @@ -0,0 +1,118 @@ +/* + * Copyright (c) 2021-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 "plugins/ecmascript/runtime/builtins/builtins_promise_job.h" +#include "plugins/ecmascript/runtime/ecma_macros.h" +#include "plugins/ecmascript/runtime/global_env.h" +#include "plugins/ecmascript/runtime/internal_call_params.h" +#include "plugins/ecmascript/runtime/js_function.h" +#include "plugins/ecmascript/runtime/js_handle.h" +#include "plugins/ecmascript/runtime/js_promise.h" +#include "plugins/ecmascript/runtime/js_tagged_value.h" +#include "libpandabase/macros.h" + +namespace panda::ecmascript::builtins { +JSTaggedValue BuiltinsPromiseJob::PromiseReactionJob(EcmaRuntimeCallInfo *argv) +{ + ASSERT(argv); + BUILTINS_API_TRACE(argv->GetThread(), PromiseJob, Reaction); + JSThread *thread = argv->GetThread(); + [[maybe_unused]] EcmaHandleScope handleScope(thread); + // 1. Assert: reaction is a PromiseReaction Record. + JSHandle value = GetCallArg(argv, 0); + ASSERT(value->IsPromiseReaction()); + JSHandle reaction = JSHandle::Cast(value); + JSHandle argument = GetCallArg(argv, 1); + + const GlobalEnvConstants *globalConst = thread->GlobalConstants(); + // 2. Let promiseCapability be reaction.[[Capabilities]]. + JSTaggedValue capabilitiesValue = reaction->GetPromiseCapability(); + // 3. Let handler be reaction.[[Handler]]. + JSHandle handler(thread, reaction->GetHandler()); + JSHandle thisValue = globalConst->GetHandledUndefined(); + + InternalCallParams *result = thread->GetInternalCallParams(); + result->MakeArgv(argument); + + if (capabilitiesValue.IsUndefined()) { + [[maybe_unused]] JSTaggedValue val = JSFunction::Call(thread, handler, thisValue, 1, result->GetArgv()); + return JSTaggedValue::Undefined(); + } + + JSHandle capability(thread, capabilitiesValue); + JSMutableHandle call(thread, capability->GetResolve()); + + if (handler->IsString()) { + // 4. If handler is "Identity", let handlerResult be NormalCompletion(argument). + // 5. Else if handler is "Thrower", let handlerResult be Completion{[[type]]: throw, [[value]]: argument, + // [[target]]: empty}. + + if (EcmaString::StringsAreEqual(handler.GetObject(), + globalConst->GetHandledThrowerString().GetObject())) { + call.Update(capability->GetReject()); + } + } else { + // 6. Else, let handlerResult be Call(handler, undefined, «argument»). + JSTaggedValue taggedValue = JSFunction::Call(thread, handler, thisValue, 1, result->GetArgv()); + result->MakeArgv(taggedValue); + // 7. If handlerResult is an abrupt completion, then + // a. Let status be Call(promiseCapability.[[Reject]], undefined, «handlerResult.[[value]]»). + // b. NextJob Completion(status). + if (thread->HasPendingException()) { + JSHandle throwValue = JSPromise::IfThrowGetThrowValue(thread); + thread->ClearException(); + result->MakeArgv(throwValue); + call.Update(capability->GetReject()); + } + } + // 8. Let status be Call(promiseCapability.[[Resolve]], undefined, «handlerResult.[[value]]»). + return JSFunction::Call(thread, call, thisValue, 1, result->GetArgv()); +} + +JSTaggedValue BuiltinsPromiseJob::PromiseResolveThenableJob(EcmaRuntimeCallInfo *argv) +{ + ASSERT(argv); + BUILTINS_API_TRACE(argv->GetThread(), PromiseJob, ResolveThenableJob); + JSThread *thread = argv->GetThread(); + [[maybe_unused]] EcmaHandleScope handleScope(thread); + const GlobalEnvConstants *globalConst = thread->GlobalConstants(); + JSHandle promise = GetCallArg(argv, 0); + ASSERT(promise->IsJSPromise()); + // 1. Let resolvingFunctions be CreateResolvingFunctions(promiseToResolve). + JSHandle resolvingFunctions = + JSPromise::CreateResolvingFunctions(thread, JSHandle::Cast(promise)); + JSHandle thenable = GetCallArg(argv, 1); + JSHandle then = GetCallArg(argv, BuiltinsBase::ArgsPosition::THIRD); + + // 2. Let thenCallResult be Call(then, thenable, «resolvingFunctions.[[Resolve]], resolvingFunctions.[[Reject]]»). + InternalCallParams *arguments = thread->GetInternalCallParams(); + arguments->MakeArgv(resolvingFunctions->GetResolveFunction(), resolvingFunctions->GetRejectFunction()); + JSTaggedValue result = JSFunction::Call(thread, then, thenable, 2, arguments->GetArgv()); // 2: two args + JSHandle thenResult(thread, result); + // 3. If thenCallResult is an abrupt completion, + // a. Let status be Call(resolvingFunctions.[[Reject]], undefined, «thenCallResult.[[value]]»). + // b. NextJob Completion(status). + if (thread->HasPendingException()) { + thenResult = JSPromise::IfThrowGetThrowValue(thread); + thread->ClearException(); + JSHandle reject(thread, resolvingFunctions->GetRejectFunction()); + JSHandle undefined = globalConst->GetHandledUndefined(); + arguments->MakeArgv(thenResult); + return JSFunction::Call(thread, reject, undefined, 1, arguments->GetArgv()); + } + // 4. NextJob Completion(thenCallResult). + return result; +} +} // namespace panda::ecmascript::builtins diff --git a/runtime/builtins/builtins_promise_job.h b/runtime/builtins/builtins_promise_job.h new file mode 100644 index 000000000..32b9196c9 --- /dev/null +++ b/runtime/builtins/builtins_promise_job.h @@ -0,0 +1,28 @@ +/* + * Copyright (c) 2021-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_JS_PROMISE_JOB_H +#define ECMASCRIPT_JS_PROMISE_JOB_H + +#include "plugins/ecmascript/runtime/base/builtins_base.h" + +namespace panda::ecmascript::builtins { +class BuiltinsPromiseJob : ecmascript::base::BuiltinsBase { +public: + static JSTaggedValue PromiseReactionJob(EcmaRuntimeCallInfo *argv); + static JSTaggedValue PromiseResolveThenableJob(EcmaRuntimeCallInfo *argv); +}; +} // namespace panda::ecmascript::builtins +#endif // ECMASCRIPT_JS_PROMISE_JOB_H diff --git a/runtime/builtins/builtins_proxy.cpp b/runtime/builtins/builtins_proxy.cpp new file mode 100644 index 000000000..e19864896 --- /dev/null +++ b/runtime/builtins/builtins_proxy.cpp @@ -0,0 +1,96 @@ +/* + * Copyright (c) 2021-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 "plugins/ecmascript/runtime/builtins/builtins_proxy.h" +#include "plugins/ecmascript/runtime/ecma_vm.h" +#include "plugins/ecmascript/runtime/global_env.h" +#include "plugins/ecmascript/runtime/js_function.h" +#include "plugins/ecmascript/runtime/object_factory.h" +#include "plugins/ecmascript/runtime/tagged_array-inl.h" + +namespace panda::ecmascript::builtins { +// 26.2.1.1 Proxy( [ value ] ) +JSTaggedValue BuiltinsProxy::ProxyConstructor(EcmaRuntimeCallInfo *argv) +{ + ASSERT(argv); + BUILTINS_API_TRACE(argv->GetThread(), Proxy, Constructor); + [[maybe_unused]] EcmaHandleScope handleScope(argv->GetThread()); + + // 1.If NewTarget is undefined, throw a TypeError exception. + JSHandle newTarget = GetNewTarget(argv); + if (newTarget->IsUndefined()) { + THROW_TYPE_ERROR_AND_RETURN(argv->GetThread(), "ProxyConstructor: NewTarget is undefined", + JSTaggedValue::Exception()); + } + + // 2.Return ProxyCreate(target, handler). + JSHandle proxy = JSProxy::ProxyCreate(argv->GetThread(), GetCallArg(argv, 0), GetCallArg(argv, 1)); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(argv->GetThread()); + return proxy.GetTaggedValue(); +} + +// 26.2.2.1 Proxy.revocable ( target, handler ) +JSTaggedValue BuiltinsProxy::Revocable([[maybe_unused]] EcmaRuntimeCallInfo *argv) +{ + ASSERT(argv); + BUILTINS_API_TRACE(argv->GetThread(), Proxy, Revocable); + JSThread *thread = argv->GetThread(); + [[maybe_unused]] EcmaHandleScope handleScope(thread); + + // 1.Let p be ProxyCreate(target, handler). + JSHandle proxy = JSProxy::ProxyCreate(thread, GetCallArg(argv, 0), GetCallArg(argv, 1)); + + // 2.ReturnIfAbrupt(p). + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + + // 3 ~ 4 new revoker function and set the [[RevocableProxy]] internal slot + JSHandle revoker = thread->GetEcmaVM()->GetFactory()->NewJSProxyRevocFunction( + proxy, reinterpret_cast(InvalidateProxyFunction)); + + // 5.Let result be ObjectCreate(%ObjectPrototype%). + JSHandle env = thread->GetEcmaVM()->GetGlobalEnv(); + JSHandle proto = env->GetObjectFunctionPrototype(); + JSHandle result = thread->GetEcmaVM()->GetFactory()->OrdinaryNewJSObjectCreate(proto); + + // 6.Perform CreateDataProperty(result, "proxy", p). + auto globalConst = thread->GlobalConstants(); + JSHandle proxyKey = globalConst->GetHandledProxyString(); + JSObject::CreateDataProperty(thread, result, proxyKey, JSHandle(proxy)); + + // 7.Perform CreateDataProperty(result, "revoke", revoker). + JSHandle revokeKey = globalConst->GetHandledRevokeString(); + JSObject::CreateDataProperty(thread, result, revokeKey, JSHandle(revoker)); + + // 8.Return result. + return result.GetTaggedValue(); +} + +// A Proxy revocation function to invalidate a specific Proxy object +JSTaggedValue BuiltinsProxy::InvalidateProxyFunction(EcmaRuntimeCallInfo *argv) +{ + ASSERT(argv); + BUILTINS_API_TRACE(argv->GetThread(), Proxy, InvalidateProxyFunction); + JSThread *thread = argv->GetThread(); + [[maybe_unused]] EcmaHandleScope handleScope(thread); + + JSHandle revoke_obj(GetThis(argv)); + JSHandle revokeKey = thread->GlobalConstants()->GetHandledRevokeString(); + + PropertyDescriptor desc(thread); + JSObject::GetOwnProperty(thread, revoke_obj, revokeKey, desc); + JSProxyRevocFunction::ProxyRevocFunctions(thread, JSHandle(desc.GetValue())); + return JSTaggedValue::Undefined(); +} +} // namespace panda::ecmascript::builtins diff --git a/runtime/builtins/builtins_proxy.h b/runtime/builtins/builtins_proxy.h new file mode 100644 index 000000000..38434543f --- /dev/null +++ b/runtime/builtins/builtins_proxy.h @@ -0,0 +1,37 @@ +/* + * Copyright (c) 2021-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_BUILTINS_BUILTINS_PROXY_H +#define ECMASCRIPT_BUILTINS_BUILTINS_PROXY_H + +#include "plugins/ecmascript/runtime/ecma_runtime_call_info.h" +#include "plugins/ecmascript/runtime/base/builtins_base.h" +#include "plugins/ecmascript/runtime/js_handle.h" +#include "plugins/ecmascript/runtime/js_hclass.h" + +namespace panda::ecmascript::builtins { +class BuiltinsProxy : public ecmascript::base::BuiltinsBase { +public: + // 26.2.1.1 Proxy( [ value ] ) + static JSTaggedValue ProxyConstructor(EcmaRuntimeCallInfo *argv); + + // 26.2.2.1 Proxy.revocable ( target, handler ) + static JSTaggedValue Revocable(EcmaRuntimeCallInfo *argv); + + // A Proxy revocation function to invalidate a specific Proxy object + static JSTaggedValue InvalidateProxyFunction(EcmaRuntimeCallInfo *argv); +}; +} // namespace panda::ecmascript::builtins +#endif // ECMASCRIPT_BUILTINS_BUILTINS_PROXYH diff --git a/runtime/builtins/builtins_reflect.cpp b/runtime/builtins/builtins_reflect.cpp new file mode 100644 index 000000000..79782a9e8 --- /dev/null +++ b/runtime/builtins/builtins_reflect.cpp @@ -0,0 +1,308 @@ +/* + * Copyright (c) 2021-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 "plugins/ecmascript/runtime/builtins/builtins_reflect.h" +#include "plugins/ecmascript/runtime/internal_call_params.h" +#include "plugins/ecmascript/runtime/js_tagged_value-inl.h" + +namespace panda::ecmascript::builtins { +// ecma 26.1.1 Reflect.apply (target, thisArgument, argumentsList) +JSTaggedValue BuiltinsReflect::ReflectApply(EcmaRuntimeCallInfo *argv) +{ + ASSERT(argv); + BUILTINS_API_TRACE(argv->GetThread(), Reflect, Apply); + JSThread *thread = argv->GetThread(); + [[maybe_unused]] EcmaHandleScope handleScope(thread); + // 1. If IsCallable(target) is false, throw a TypeError exception. + JSHandle target = GetCallArg(argv, 0); + if (!target->IsCallable()) { + THROW_TYPE_ERROR_AND_RETURN(thread, "Reflect.apply target is not callable", JSTaggedValue::Exception()); + } + // 2. Let args be ? CreateListFromArrayLike(argumentsList). + JSHandle thisArgument = GetCallArg(argv, 1); + JSHandle argumentsList = GetCallArg(argv, BuiltinsBase::ArgsPosition::THIRD); + JSHandle argOrAbrupt = JSObject::CreateListFromArrayLike(thread, argumentsList); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + JSHandle args = JSHandle::Cast(argOrAbrupt); + + // 3. Perform PrepareForTailCall(). + // 4. Return ? Call(target, thisArgument, args). + ecmascript::InternalCallParams *arguments = thread->GetInternalCallParams(); + arguments->MakeArgList(*args); + return JSFunction::Call(thread, target, thisArgument, args->GetLength(), arguments->GetArgv()); +} + +// ecma 26.1.2 Reflect.construct (target, argumentsList [ , newTarget]) +JSTaggedValue BuiltinsReflect::ReflectConstruct(EcmaRuntimeCallInfo *argv) +{ + ASSERT(argv); + BUILTINS_API_TRACE(argv->GetThread(), Reflect, Constructor); + JSThread *thread = argv->GetThread(); + [[maybe_unused]] EcmaHandleScope handleScope(thread); + // 1. If IsConstructor(target) is false, throw a TypeError exception. + JSHandle target = GetCallArg(argv, 0); + if (!target->IsConstructor()) { + THROW_TYPE_ERROR_AND_RETURN(thread, "Reflect.construct target is not constructor", JSTaggedValue::Exception()); + } + // 2. If newTarget is not present, set newTarget to target. + JSHandle newTarget = + argv->GetArgsNumber() > 2 ? GetCallArg(argv, BuiltinsBase::ArgsPosition::THIRD) : target; // 2: num args + // 3. Else if IsConstructor(newTarget) is false, throw a TypeError exception. + if (!newTarget->IsConstructor()) { + THROW_TYPE_ERROR_AND_RETURN(thread, "Reflect.construct newTarget is present, but not constructor", + JSTaggedValue::Exception()); + } + // 4. Let args be ? CreateListFromArrayLike(argumentsList). + JSHandle argumentsList = GetCallArg(argv, 1); + JSHandle argOrAbrupt = JSObject::CreateListFromArrayLike(thread, argumentsList); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + JSHandle args = JSHandle::Cast(argOrAbrupt); + // 5. Return ? Construct(target, args, newTarget). + InternalCallParams *arguments = thread->GetInternalCallParams(); + arguments->MakeArgList(*args); + return JSFunction::Construct(thread, target, args->GetLength(), arguments->GetArgv(), newTarget); +} + +// ecma 26.1.3 Reflect.defineProperty (target, propertyKey, attributes) +JSTaggedValue BuiltinsReflect::ReflectDefineProperty(EcmaRuntimeCallInfo *argv) +{ + ASSERT(argv); + BUILTINS_API_TRACE(argv->GetThread(), Reflect, DefineProperty); + JSThread *thread = argv->GetThread(); + [[maybe_unused]] EcmaHandleScope handleScope(thread); + // 1. If Type(target) is not Object, throw a TypeError exception. + JSHandle target = GetCallArg(argv, 0); + if (!target->IsECMAObject()) { + THROW_TYPE_ERROR_AND_RETURN(thread, "Reflect.defineProperty target is not object", JSTaggedValue::Exception()); + } + // 2. Let key be ? ToPropertyKey(propertyKey). + JSHandle key = JSTaggedValue::ToPropertyKey(thread, GetCallArg(argv, 1)); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + // 3. Let desc be ? ToPropertyDescriptor(attributes). + JSHandle attributes = GetCallArg(argv, BuiltinsBase::ArgsPosition::THIRD); + PropertyDescriptor desc(thread); + JSObject::ToPropertyDescriptor(thread, attributes, desc); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + // 4. Return ? target.[[DefineOwnProperty]](key, desc). + return GetTaggedBoolean(JSTaggedValue::DefineOwnProperty(thread, target, key, desc)); +} + +// ecma 21.1.4 Reflect.deleteProperty (target, propertyKey) +JSTaggedValue BuiltinsReflect::ReflectDeleteProperty(EcmaRuntimeCallInfo *argv) +{ + ASSERT(argv); + BUILTINS_API_TRACE(argv->GetThread(), Reflect, DeleteProperty); + JSThread *thread = argv->GetThread(); + [[maybe_unused]] EcmaHandleScope handleScope(thread); + // 1. If Type(target) is not Object, throw a TypeError exception. + JSHandle target = GetCallArg(argv, 0); + if (!target->IsECMAObject()) { + THROW_TYPE_ERROR_AND_RETURN(thread, "Reflect.deleteProperty target is not object", JSTaggedValue::Exception()); + } + // 2. Let key be ? ToPropertyKey(propertyKey). + JSHandle key = JSTaggedValue::ToPropertyKey(thread, GetCallArg(argv, 1)); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + // 3. Return ? target.[[Delete]](key). + return GetTaggedBoolean(JSTaggedValue::DeleteProperty(thread, target, key)); +} + +// ecma 26.1.5 Reflect.get (target, propertyKey [ , receiver]) +JSTaggedValue BuiltinsReflect::ReflectGet(EcmaRuntimeCallInfo *argv) +{ + ASSERT(argv); + BUILTINS_API_TRACE(argv->GetThread(), Reflect, Get); + JSThread *thread = argv->GetThread(); + [[maybe_unused]] EcmaHandleScope handleScope(thread); + // 1. If Type(target) is not Object, throw a TypeError exception. + JSHandle val = GetCallArg(argv, 0); + if (!val->IsECMAObject()) { + THROW_TYPE_ERROR_AND_RETURN(thread, "Reflect.get target is not object", JSTaggedValue::Exception()); + } + JSHandle target = JSHandle::Cast(val); + // 2. Let key be ? ToPropertyKey(propertyKey). + JSHandle key = JSTaggedValue::ToPropertyKey(thread, GetCallArg(argv, 1)); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + // 3. If receiver is not present, then + // a. Set receiver to target. + // 4. Return ? target.[[Get]](key, receiver). + if (argv->GetArgsNumber() == 2) { // 2: 2 means that there are 2 args in total + return JSObject::GetProperty(thread, target, key).GetValue().GetTaggedValue(); + } + JSHandle receiver = GetCallArg(argv, BuiltinsBase::ArgsPosition::THIRD); + return JSObject::GetProperty(thread, val, key, receiver).GetValue().GetTaggedValue(); +} + +// ecma 26.1.6 Reflect.getOwnPropertyDescriptor ( target, propertyKey ) +JSTaggedValue BuiltinsReflect::ReflectGetOwnPropertyDescriptor(EcmaRuntimeCallInfo *argv) +{ + ASSERT(argv); + BUILTINS_API_TRACE(argv->GetThread(), Reflect, GetOwnPropertyDescriptor); + JSThread *thread = argv->GetThread(); + [[maybe_unused]] EcmaHandleScope handleScope(thread); + // 1. If Type(target) is not Object, throw a TypeError exception. + JSHandle target = GetCallArg(argv, 0); + if (!target->IsECMAObject()) { + THROW_TYPE_ERROR_AND_RETURN(thread, "Reflect.getOwnPropertyDescriptor target is not object", + JSTaggedValue::Exception()); + } + // 2. Let key be ? ToPropertyKey(propertyKey). + JSHandle key = JSTaggedValue::ToPropertyKey(thread, GetCallArg(argv, 1)); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + // 3. Let desc be ? target.[[GetOwnProperty]](key). + PropertyDescriptor desc(thread); + if (!JSTaggedValue::GetOwnProperty(thread, target, key, desc)) { + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + } + // 4. Return FromPropertyDescriptor(desc). + JSHandle res = JSObject::FromPropertyDescriptor(thread, desc); + return res.GetTaggedValue(); +} + +// ecma 21.1.7 Reflect.getPrototypeOf (target) +JSTaggedValue BuiltinsReflect::ReflectGetPrototypeOf(EcmaRuntimeCallInfo *argv) +{ + ASSERT(argv); + BUILTINS_API_TRACE(argv->GetThread(), Reflect, GetPrototypeOf); + JSThread *thread = argv->GetThread(); + [[maybe_unused]] EcmaHandleScope handleScope(thread); + // 1. If Type(target) is not Object, throw a TypeError exception. + JSHandle val = GetCallArg(argv, 0); + if (!val->IsECMAObject()) { + THROW_TYPE_ERROR_AND_RETURN(thread, "Reflect.getPrototypeOf target is not object", JSTaggedValue::Exception()); + } + JSHandle target = JSHandle::Cast(val); + // 2. Return ? target.[[GetPrototypeOf]](). + return target->GetPrototype(thread); +} + +// ecma 26.1.8 Reflect.has (target, propertyKey) +JSTaggedValue BuiltinsReflect::ReflectHas(EcmaRuntimeCallInfo *argv) +{ + ASSERT(argv); + BUILTINS_API_TRACE(argv->GetThread(), Reflect, Has); + JSThread *thread = argv->GetThread(); + [[maybe_unused]] EcmaHandleScope handleScope(thread); + // 1. If Type(target) is not Object, throw a TypeError exception. + JSHandle target = GetCallArg(argv, 0); + if (!target->IsECMAObject()) { + THROW_TYPE_ERROR_AND_RETURN(thread, "Reflect.has target is not object", JSTaggedValue::Exception()); + } + // 2. Let key be ? ToPropertyKey(propertyKey). + JSHandle key = JSTaggedValue::ToPropertyKey(thread, GetCallArg(argv, 1)); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + // 3. Return ? target.[[HasProperty]](key). + return GetTaggedBoolean(JSTaggedValue::HasProperty(thread, target, key)); +} + +// ecma 26.1.9 Reflect.isExtensible (target) +JSTaggedValue BuiltinsReflect::ReflectIsExtensible(EcmaRuntimeCallInfo *argv) +{ + ASSERT(argv); + JSThread *thread = argv->GetThread(); + [[maybe_unused]] EcmaHandleScope handleScope(thread); + // 1. If Type(target) is not Object, throw a TypeError exception. + JSHandle target = GetCallArg(argv, 0); + if (!target->IsECMAObject()) { + THROW_TYPE_ERROR_AND_RETURN(thread, "Reflect.isExtensible target is not object", JSTaggedValue::Exception()); + } + // 2. Return ? target.[[IsExtensible]](). + return GetTaggedBoolean(target->IsExtensible(thread)); +} + +// ecma 26.1.10 Reflect.ownKeys (target) +JSTaggedValue BuiltinsReflect::ReflectOwnKeys(EcmaRuntimeCallInfo *argv) +{ + ASSERT(argv); + BUILTINS_API_TRACE(argv->GetThread(), Reflect, OwnKeys); + JSThread *thread = argv->GetThread(); + [[maybe_unused]] EcmaHandleScope handleScope(thread); + // 1. If Type(target) is not Object, throw a TypeError exception. + JSHandle target = GetCallArg(argv, 0); + if (!target->IsECMAObject()) { + THROW_TYPE_ERROR_AND_RETURN(thread, "Reflect.ownKeys target is not object", JSTaggedValue::Exception()); + } + // 2. Let keys be ? target.[[OwnPropertyKeys]](). + JSHandle keys = JSTaggedValue::GetOwnPropertyKeys(thread, target); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + // 3. Return CreateArrayFromList(keys). + JSHandle result = JSArray::CreateArrayFromList(thread, keys); + return result.GetTaggedValue(); +} + +// ecma 26.1.11 Reflect.preventExtensions (target) +JSTaggedValue BuiltinsReflect::ReflectPreventExtensions(EcmaRuntimeCallInfo *argv) +{ + ASSERT(argv); + BUILTINS_API_TRACE(argv->GetThread(), Reflect, PreventExtensions); + JSThread *thread = argv->GetThread(); + [[maybe_unused]] EcmaHandleScope handleScope(thread); + // 1. If Type(target) is not Object, throw a TypeError exception. + JSHandle target = GetCallArg(argv, 0); + if (!target->IsECMAObject()) { + THROW_TYPE_ERROR_AND_RETURN(thread, "Reflect.preventExtensions target is not object", + JSTaggedValue::Exception()); + } + // 2. Return ? target.[[PreventExtensions]](). + return GetTaggedBoolean(JSTaggedValue::PreventExtensions(thread, target)); +} + +// ecma 26.1.12 Reflect.set (target, propertyKey, V [ , receiver]) +JSTaggedValue BuiltinsReflect::ReflectSet(EcmaRuntimeCallInfo *argv) +{ + ASSERT(argv); + BUILTINS_API_TRACE(argv->GetThread(), Reflect, Set); + JSThread *thread = argv->GetThread(); + [[maybe_unused]] EcmaHandleScope handleScope(thread); + // 1. If Type(target) is not Object, throw a TypeError exception. + JSHandle targetVal = GetCallArg(argv, 0); + if (!targetVal->IsECMAObject()) { + THROW_TYPE_ERROR_AND_RETURN(thread, "Reflect.get target is not object", JSTaggedValue::Exception()); + } + // 2. Let key be ? ToPropertyKey(propertyKey). + JSHandle key = JSTaggedValue::ToPropertyKey(thread, GetCallArg(argv, 1)); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + JSHandle value = GetCallArg(argv, BuiltinsBase::ArgsPosition::THIRD); + // 3. If receiver is not present, then + // a. Set receiver to target. + // 4. Return ? target.[[Set]](key, receiver). + if (argv->GetArgsNumber() == 3) { // 3: 3 means that there are three args in total + return GetTaggedBoolean(JSTaggedValue::SetProperty(thread, targetVal, key, value)); + } + JSHandle receiver = GetCallArg(argv, 3); // 3: 3 means the third arg + return GetTaggedBoolean(JSTaggedValue::SetProperty(thread, targetVal, key, value, receiver)); +} + +// ecma 26.1.13 Reflect.setPrototypeOf (target, proto) +JSTaggedValue BuiltinsReflect::ReflectSetPrototypeOf(EcmaRuntimeCallInfo *argv) +{ + ASSERT(argv); + BUILTINS_API_TRACE(argv->GetThread(), Reflect, SetPrototypeOf); + JSThread *thread = argv->GetThread(); + [[maybe_unused]] EcmaHandleScope handleScope(thread); + // 1. If Type(target) is not Object, throw a TypeError exception. + JSHandle target = GetCallArg(argv, 0); + if (!target->IsECMAObject()) { + THROW_TYPE_ERROR_AND_RETURN(thread, "Reflect.setPrototypeOf target is not object", JSTaggedValue::Exception()); + } + // 2. If Type(proto) is not Object and proto is not null, throw a TypeError exception. + JSHandle proto = GetCallArg(argv, 1); + if (!proto->IsECMAObject() && !proto->IsNull()) { + THROW_TYPE_ERROR_AND_RETURN(thread, "SetPrototypeOf: proto is neither Object nor Null", + JSTaggedValue::Exception()); + } + // 3. Return ? target.[[SetPrototypeOf]](proto). + return GetTaggedBoolean(JSTaggedValue::SetPrototype(thread, target, proto)); +} +} // namespace panda::ecmascript::builtins diff --git a/runtime/builtins/builtins_reflect.h b/runtime/builtins/builtins_reflect.h new file mode 100644 index 000000000..b3e203e20 --- /dev/null +++ b/runtime/builtins/builtins_reflect.h @@ -0,0 +1,66 @@ +/* + * Copyright (c) 2021-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_BUILTINS_BUILTINS_REFLECT_H +#define ECMASCRIPT_BUILTINS_BUILTINS_REFLECT_H + +#include "plugins/ecmascript/runtime/base/builtins_base.h" +#include "plugins/ecmascript/runtime/js_function.h" +#include "plugins/ecmascript/runtime/js_array.h" + +namespace panda::ecmascript::builtins { +class BuiltinsReflect : public ecmascript::base::BuiltinsBase { +public: + // ecma 26.1.1 + static JSTaggedValue ReflectApply(EcmaRuntimeCallInfo *argv); + + // ecma 26.1.2 + static JSTaggedValue ReflectConstruct(EcmaRuntimeCallInfo *argv); + + // ecma 26.1.3 + static JSTaggedValue ReflectDefineProperty(EcmaRuntimeCallInfo *argv); + + // ecma 26.1.4 + static JSTaggedValue ReflectDeleteProperty(EcmaRuntimeCallInfo *argv); + + // ecma 26.1.5 + static JSTaggedValue ReflectGet(EcmaRuntimeCallInfo *argv); + + // ecma 26.1.6 + static JSTaggedValue ReflectGetOwnPropertyDescriptor(EcmaRuntimeCallInfo *argv); + + // ecma 26.1.7 + static JSTaggedValue ReflectGetPrototypeOf(EcmaRuntimeCallInfo *argv); + + // ecma 26.1.8 + static JSTaggedValue ReflectHas(EcmaRuntimeCallInfo *argv); + + // ecma 26.1.9 + static JSTaggedValue ReflectIsExtensible(EcmaRuntimeCallInfo *argv); + + // ecma 26.1.10 + static JSTaggedValue ReflectOwnKeys(EcmaRuntimeCallInfo *argv); + + // ecma 26.1.11 + static JSTaggedValue ReflectPreventExtensions(EcmaRuntimeCallInfo *argv); + + // ecma 26.1.12 + static JSTaggedValue ReflectSet(EcmaRuntimeCallInfo *argv); + + // ecma 26.1.13 + static JSTaggedValue ReflectSetPrototypeOf(EcmaRuntimeCallInfo *argv); +}; +} // namespace panda::ecmascript::builtins +#endif // ECMASCRIPT_BUILTINS_BUILTINS_REFLECT_H diff --git a/runtime/builtins/builtins_regexp.cpp b/runtime/builtins/builtins_regexp.cpp new file mode 100644 index 000000000..4698b4f80 --- /dev/null +++ b/runtime/builtins/builtins_regexp.cpp @@ -0,0 +1,1834 @@ +/* + * Copyright (c) 2021-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 "plugins/ecmascript/runtime/builtins/builtins_regexp.h" +#include +#include "plugins/ecmascript/runtime/ecma_string-inl.h" +#include "plugins/ecmascript/runtime/ecma_vm.h" +#include "plugins/ecmascript/runtime/global_env.h" +#include "plugins/ecmascript/runtime/internal_call_params.h" +#include "plugins/ecmascript/runtime/interpreter/fast_runtime_stub-inl.h" +#include "plugins/ecmascript/runtime/js_array.h" +#include "plugins/ecmascript/runtime/js_function.h" +#include "plugins/ecmascript/runtime/js_hclass.h" +#include "plugins/ecmascript/runtime/js_invoker.h" +#include "plugins/ecmascript/runtime/js_object-inl.h" +#include "plugins/ecmascript/runtime/js_regexp.h" +#include "plugins/ecmascript/runtime/js_tagged_value-inl.h" +#include "plugins/ecmascript/runtime/mem/assert_scope-inl.h" +#include "plugins/ecmascript/runtime/mem/c_containers.h" +#include "plugins/ecmascript/runtime/mem/chunk_containers.h" +#include "plugins/ecmascript/runtime/object_factory.h" +#include "plugins/ecmascript/runtime/regexp/regexp_parser_cache.h" +#include "plugins/ecmascript/runtime/tagged_array-inl.h" + +namespace panda::ecmascript::builtins { +// 21.2.3.1 +JSTaggedValue BuiltinsRegExp::RegExpConstructor(EcmaRuntimeCallInfo *argv) +{ + ASSERT(argv); + BUILTINS_API_TRACE(argv->GetThread(), RegExp, Constructor); + JSThread *thread = argv->GetThread(); + [[maybe_unused]] EcmaHandleScope handleScope(thread); + JSHandle newTargetTemp = GetNewTarget(argv); + JSHandle pattern = GetCallArg(argv, 0); + JSHandle flags = GetCallArg(argv, 1); + ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); + // 1. Let patternIsRegExp be IsRegExp(pattern). + bool patternIsRegExp = JSObject::IsRegExp(thread, pattern); + // 2. ReturnIfAbrupt(patternIsRegExp). + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + // 3. If NewTarget is not undefined, let newTarget be NewTarget. + JSHandle newTarget; + if (!newTargetTemp->IsUndefined()) { + newTarget = newTargetTemp; + } else { + auto ecmaVm = thread->GetEcmaVM(); + JSHandle env = ecmaVm->GetGlobalEnv(); + const GlobalEnvConstants *globalConst = thread->GlobalConstants(); + // disable gc + [[maybe_unused]] DisallowGarbageCollection no_gc; + // 4.a Let newTarget be the active function object. + newTarget = env->GetRegExpFunction(); + JSHandle constructorString = globalConst->GetHandledConstructorString(); + // 4.b If patternIsRegExp is true and flags is undefined + if (patternIsRegExp && flags->IsUndefined()) { + // 4.b.i Let patternConstructor be Get(pattern, "constructor"). + JSTaggedValue patternConstructor = FastRuntimeStub::FastGetPropertyByName( + thread, pattern.GetTaggedValue(), constructorString.GetTaggedValue()); + // 4.b.ii ReturnIfAbrupt(patternConstructor). + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + // 4.b.iii If SameValue(newTarget, patternConstructor) is true, return pattern. + if (JSTaggedValue::SameValue(newTarget.GetTaggedValue(), patternConstructor)) { + return pattern.GetTaggedValue(); + } + } + } + // 5. If Type(pattern) is Object and pattern has a [[RegExpMatcher]] internal slot + bool isJsReg = false; + if (pattern->IsECMAObject()) { + JSHandle patternObj = JSHandle::Cast(pattern); + isJsReg = patternObj->IsJSRegExp(); + } + JSHandle patternTemp; + JSHandle flagsTemp; + if (isJsReg) { + JSHandle patternReg(thread, JSRegExp::Cast(pattern->GetTaggedObject())); + // 5.a Let P be the value of pattern’s [[OriginalSource]] internal slot. + patternTemp = JSHandle(thread, patternReg->GetOriginalSource()); + if (flags->IsUndefined()) { + // 5.b If flags is undefined, let F be the value of pattern’s [[OriginalFlags]] internal slot. + flagsTemp = JSHandle(thread, patternReg->GetOriginalFlags()); + } else { + // 5.c Else, let F be flags. + flagsTemp = JSHandle(thread, *JSTaggedValue::ToString(thread, flags)); + } + // 6. Else if patternIsRegExp is true + } else if (patternIsRegExp) { + JSHandle sourceString(factory->NewFromCanBeCompressString("source")); + JSHandle flagsString(factory->NewFromCanBeCompressString("flags")); + // disable gc + [[maybe_unused]] DisallowGarbageCollection noGc; + // 6.a Let P be Get(pattern, "source"). + patternTemp = JSObject::GetProperty(thread, pattern, sourceString).GetValue(); + // 6.b ReturnIfAbrupt(P). + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + // 6.c If flags is undefined + if (flags->IsUndefined()) { + // 6.c.i Let F be Get(pattern, "flags"). + flagsTemp = JSObject::GetProperty(thread, pattern, flagsString).GetValue(); + // 6.c.ii ReturnIfAbrupt(F). + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + } else { + // 6.d Else, let F be flags. + flagsTemp = JSHandle(thread, *JSTaggedValue::ToString(thread, flags)); + } + } else { + // 7.a Let P be pattern. + patternTemp = pattern; + // 7.b Let F be flags. + if (flags->IsUndefined()) { + flagsTemp = flags; + } else { + flagsTemp = JSHandle(thread, *JSTaggedValue::ToString(thread, flags)); + } + } + // 8. Let O be RegExpAlloc(newTarget). + JSHandle object(thread, RegExpAlloc(thread, newTarget)); + // 9. ReturnIfAbrupt(O). + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + // 10. Return RegExpInitialize(O, P, F). + JSTaggedValue result = RegExpInitialize(thread, object, patternTemp, flagsTemp); + return JSTaggedValue(result); +} + +// prototype +// 20.2.5.2 +JSTaggedValue BuiltinsRegExp::Exec(EcmaRuntimeCallInfo *argv) +{ + ASSERT(argv); + BUILTINS_API_TRACE(argv->GetThread(), RegExp, Exec); + JSThread *thread = argv->GetThread(); + [[maybe_unused]] EcmaHandleScope handleScope(thread); + // 1. Let R be the this value. + JSHandle thisObj = GetThis(argv); + // 4. Let S be ToString(string). + JSHandle inputStr = GetCallArg(argv, 0); + JSHandle stringHandle = JSTaggedValue::ToString(thread, inputStr); + // 5. ReturnIfAbrupt(S). + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + JSHandle string = JSHandle::Cast(stringHandle); + // 2. If Type(R) is not Object, throw a TypeError exception. + if (!thisObj->IsECMAObject()) { + // throw a TypeError exception. + THROW_TYPE_ERROR_AND_RETURN(thread, "this is not Object", JSTaggedValue::Exception()); + } + // 3. If R does not have a [[RegExpMatcher]] internal slot, throw a TypeError exception. + if (!thisObj->IsJSRegExp()) { + // throw a TypeError exception. + THROW_TYPE_ERROR_AND_RETURN(thread, "this does not have [[RegExpMatcher]]", JSTaggedValue::Exception()); + } + + bool isCached = true; + JSHandle cacheTable(thread->GetEcmaVM()->GetRegExpCache()); + if (cacheTable->GetLargeStrCount() == 0U || cacheTable->GetConflictCount() != 0U) { + isCached = false; + } + // 6. Return RegExpBuiltinExec(R, S). + JSTaggedValue result = RegExpBuiltinExec(thread, thisObj, string, isCached); + return JSTaggedValue(result); +} + +// 20.2.5.13 +JSTaggedValue BuiltinsRegExp::Test(EcmaRuntimeCallInfo *argv) +{ + ASSERT(argv); + BUILTINS_API_TRACE(argv->GetThread(), RegExp, Test); + JSThread *thread = argv->GetThread(); + [[maybe_unused]] EcmaHandleScope handleScope(thread); + // 1. Let R be the this value. + JSHandle thisObj = GetThis(argv); + JSHandle inputStr = GetCallArg(argv, 0); + // 3. Let string be ToString(S). + // 4. ReturnIfAbrupt(string). + JSHandle stringHandle = JSTaggedValue::ToString(thread, inputStr); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + JSHandle string = JSHandle::Cast(stringHandle); + // 2. If Type(R) is not Object, throw a TypeError exception. + if (!thisObj->IsECMAObject()) { + // throw a TypeError exception. + THROW_TYPE_ERROR_AND_RETURN(thread, "this is not Object", JSTaggedValue::Exception()); + } + + // 5. Let match be RegExpExec(R, string). + JSTaggedValue matchResult = RegExpExec(thread, thisObj, string, false); + // 6. ReturnIfAbrupt(match). + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + // 7. If match is not null, return true; else return false. + return GetTaggedBoolean(!matchResult.IsNull()); +} + +// 20.2.5.14 +JSTaggedValue BuiltinsRegExp::ToString(EcmaRuntimeCallInfo *argv) +{ + ASSERT(argv); + BUILTINS_API_TRACE(argv->GetThread(), RegExp, ToString); + JSThread *thread = argv->GetThread(); + [[maybe_unused]] EcmaHandleScope handleScope(thread); + // 1. Let R be the this value. + JSHandle thisObj = GetThis(argv); + auto ecmaVm = thread->GetEcmaVM(); + // 2. If Type(R) is not Object, throw a TypeError exception. + if (!thisObj->IsECMAObject()) { + THROW_TYPE_ERROR_AND_RETURN(thread, "this is not Object", JSTaggedValue::Exception()); + } + ObjectFactory *factory = ecmaVm->GetFactory(); + JSHandle sourceString(factory->NewFromCanBeCompressString("source")); + JSHandle flagsString(factory->NewFromCanBeCompressString("flags")); + // 3. Let pattern be ToString(Get(R, "source")). + JSHandle getSource(JSObject::GetProperty(thread, thisObj, sourceString).GetValue()); + JSHandle getFlags(JSObject::GetProperty(thread, thisObj, flagsString).GetValue()); + JSHandle sourceStrHandle = JSTaggedValue::ToString(thread, getSource); + // 4. ReturnIfAbrupt(pattern). + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + // 5. Let flags be ToString(Get(R, "flags")). + JSHandle flagsStrHandle = JSTaggedValue::ToString(thread, getFlags); + // 4. ReturnIfAbrupt(flags). + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + JSHandle slashStr = factory->NewFromCanBeCompressString("/"); + // 7. Let result be the String value formed by concatenating "/", pattern, and "/", and flags. + JSHandle tempStr = factory->ConcatFromString(slashStr, sourceStrHandle); + JSHandle resultTemp = factory->ConcatFromString(tempStr, slashStr); + return factory->ConcatFromString(resultTemp, flagsStrHandle).GetTaggedValue(); +} + +JSHandle ConcatFlags(JSThread *thread, const JSHandle &obj, + const JSHandle &string, const char *name) +{ + auto ecmaVm = thread->GetEcmaVM(); + ObjectFactory *factory = ecmaVm->GetFactory(); + + JSHandle nameString(factory->NewFromString(name)); + bool exist = JSObject::GetProperty(thread, obj, nameString).GetValue()->ToBoolean(); + // ReturnIfAbrupt + RETURN_HANDLE_IF_ABRUPT_COMPLETION(JSTaggedValue, thread); + if (exist) { + JSHandle temp = factory->GetEmptyString(); + if (CString("global") == name) { + temp = factory->NewFromString("g"); + } else if (CString("ignoreCase") == name) { + temp = factory->NewFromString("i"); + } else if (CString("multiline") == name) { + temp = factory->NewFromString("m"); + } else if (CString("dotAll") == name) { + temp = factory->NewFromString("s"); + } else if (CString("unicode") == name) { + temp = factory->NewFromString("u"); + } else if (CString("sticky") == name) { + temp = factory->NewFromString("y"); + } + JSHandle thisString(string); + return JSHandle(factory->ConcatFromString(thisString, temp)); + } + return JSHandle(string); +} + +// 20.2.5.3 +JSTaggedValue BuiltinsRegExp::GetFlags(EcmaRuntimeCallInfo *argv) +{ + ASSERT(argv); + BUILTINS_API_TRACE(argv->GetThread(), RegExp, GetFlags); + JSThread *thread = argv->GetThread(); + [[maybe_unused]] EcmaHandleScope handleScope(thread); + // 1. Let R be the this value. + JSHandle thisObj = GetThis(argv); + // 2. If Type(R) is not Object, throw a TypeError exception. + if (!thisObj->IsECMAObject()) { + THROW_TYPE_ERROR_AND_RETURN(thread, "this is not Object", JSTaggedValue::Exception()); + } + // 3. Let result be the empty String. + // 4. ~ 19. + if (JSHandle::Cast(thisObj)->IsJSRegExp()) { + uint8_t flagsBits = + static_cast(JSRegExp::Cast(thisObj->GetTaggedObject())->GetOriginalFlags().GetInt()); + return FlagsBitsToString(thread, flagsBits); + } + + ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); + JSHandle result(factory->GetEmptyString()); + result = ConcatFlags(thread, thisObj, result, "global"); + result = ConcatFlags(thread, thisObj, result, "ignoreCase"); + result = ConcatFlags(thread, thisObj, result, "multiline"); + result = ConcatFlags(thread, thisObj, result, "dotaAll"); + result = ConcatFlags(thread, thisObj, result, "unicode"); + result = ConcatFlags(thread, thisObj, result, "sticky"); + return JSTaggedValue(static_cast(result->GetHeapObject())); +} + +// 20.2.5.4 +JSTaggedValue BuiltinsRegExp::GetGlobal(EcmaRuntimeCallInfo *argv) +{ + ASSERT(argv); + JSThread *thread = argv->GetThread(); + [[maybe_unused]] EcmaHandleScope handleScope(thread); + JSHandle thisObj = GetThis(argv); + bool result = GetFlagsInternal(thread, thisObj, RegExpParser::FLAG_GLOBAL); + return GetTaggedBoolean(result); +} + +// 20.2.5.5 +JSTaggedValue BuiltinsRegExp::GetIgnoreCase(EcmaRuntimeCallInfo *argv) +{ + ASSERT(argv); + JSThread *thread = argv->GetThread(); + [[maybe_unused]] EcmaHandleScope handleScope(thread); + JSHandle thisObj = GetThis(argv); + bool result = GetFlagsInternal(thread, thisObj, RegExpParser::FLAG_IGNORECASE); + return GetTaggedBoolean(result); +} + +// 20.2.5.7 +JSTaggedValue BuiltinsRegExp::GetMultiline(EcmaRuntimeCallInfo *argv) +{ + ASSERT(argv); + JSThread *thread = argv->GetThread(); + [[maybe_unused]] EcmaHandleScope handleScope(thread); + JSHandle thisObj = GetThis(argv); + bool result = GetFlagsInternal(thread, thisObj, RegExpParser::FLAG_MULTILINE); + return GetTaggedBoolean(result); +} + +JSTaggedValue BuiltinsRegExp::GetDotAll(EcmaRuntimeCallInfo *argv) +{ + ASSERT(argv); + JSThread *thread = argv->GetThread(); + [[maybe_unused]] EcmaHandleScope handleScope(thread); + JSHandle thisObj = GetThis(argv); + bool result = GetFlagsInternal(thread, thisObj, RegExpParser::FLAG_DOTALL); + return GetTaggedBoolean(result); +} + +// 20.2.5.10 +JSTaggedValue BuiltinsRegExp::GetSource(EcmaRuntimeCallInfo *argv) +{ + ASSERT(argv); + JSThread *thread = argv->GetThread(); + [[maybe_unused]] EcmaHandleScope handleScope(thread); + // 1. Let R be the this value. + JSHandle thisObj = GetThis(argv); + // 2. If Type(R) is not Object, throw a TypeError exception. + // 3. If R does not have an [[OriginalSource]] internal slot, throw a TypeError exception. + // 4. If R does not have an [[OriginalFlags]] internal slot, throw a TypeError exception. + if (!thisObj->IsECMAObject()) { + // throw a TypeError exception. + THROW_TYPE_ERROR_AND_RETURN(thread, "this is not Object", JSTaggedValue::Exception()); + } + if (!thisObj->IsJSRegExp()) { + // throw a TypeError exception. + THROW_TYPE_ERROR_AND_RETURN(thread, "this does not have [[OriginalSource]]", JSTaggedValue::Exception()); + } + // 5. Let src be the value of R’s [[OriginalSource]] internal slot. + JSHandle regexpObj(thread, JSRegExp::Cast(thisObj->GetTaggedObject())); + JSHandle source(thread, regexpObj->GetOriginalSource()); + // 6. Let flags be the value of R’s [[OriginalFlags]] internal slot. + uint8_t flagsBits = static_cast(regexpObj->GetOriginalFlags().GetInt()); + JSHandle flags(thread, FlagsBitsToString(thread, flagsBits)); + // 7. Return EscapeRegExpPattern(src, flags). + return JSTaggedValue(EscapeRegExpPattern(thread, source, flags)); +} + +// 20.2.5.12 +JSTaggedValue BuiltinsRegExp::GetSticky(EcmaRuntimeCallInfo *argv) +{ + ASSERT(argv); + JSThread *thread = argv->GetThread(); + [[maybe_unused]] EcmaHandleScope handleScope(thread); + JSHandle thisObj = GetThis(argv); + bool result = GetFlagsInternal(thread, thisObj, RegExpParser::FLAG_STICKY); + return GetTaggedBoolean(result); +} + +// 20.2.5.15 +JSTaggedValue BuiltinsRegExp::GetUnicode(EcmaRuntimeCallInfo *argv) +{ + ASSERT(argv); + JSThread *thread = argv->GetThread(); + [[maybe_unused]] EcmaHandleScope handleScope(thread); + JSHandle thisObj = GetThis(argv); + bool result = GetFlagsInternal(thread, thisObj, RegExpParser::FLAG_UTF16); + return GetTaggedBoolean(result); +} + +// 21.2.4.2 +JSTaggedValue BuiltinsRegExp::GetSpecies(EcmaRuntimeCallInfo *argv) +{ + ASSERT(argv); + return GetThis(argv).GetTaggedValue(); +} + +// 21.2.5.6 +JSTaggedValue BuiltinsRegExp::Match(EcmaRuntimeCallInfo *argv) +{ + ASSERT(argv); + BUILTINS_API_TRACE(argv->GetThread(), RegExp, Match); + JSThread *thread = argv->GetThread(); + [[maybe_unused]] EcmaHandleScope handleScope(thread); + // 1. Let rx be the this value. + JSHandle thisObj = GetThis(argv); + // 3. Let S be ToString(string) + JSHandle inputString = GetCallArg(argv, 0); + JSHandle stringHandle = JSTaggedValue::ToString(thread, inputString); + bool isCached = true; + JSHandle cacheTable(thread->GetEcmaVM()->GetRegExpCache()); + if (cacheTable->GetLargeStrCount() == 0U || (cacheTable->GetConflictCount()) != 0U) { + isCached = false; + } + // 4. ReturnIfAbrupt(string). + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + JSHandle string = JSHandle::Cast(stringHandle); + if (!thisObj->IsECMAObject()) { + // 2. If Type(rx) is not Object, throw a TypeError exception. + THROW_TYPE_ERROR_AND_RETURN(thread, "this is not Object", JSTaggedValue::Exception()); + } + // 5. Let global be ToBoolean(Get(rx, "global")). + const GlobalEnvConstants *globalConst = thread->GlobalConstants(); + JSHandle global = globalConst->GetHandledGlobalString(); + JSTaggedValue globalValue = + FastRuntimeStub::FastGetPropertyByName(thread, thisObj.GetTaggedValue(), global.GetTaggedValue()); + // 6. ReturnIfAbrupt(global). + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + bool isGlobal = globalValue.ToBoolean(); + // 7. If global is false, then + if (!isGlobal) { + // a. Return RegExpExec(rx, S). + JSTaggedValue result = RegExpExec(thread, thisObj, string, isCached); + return JSTaggedValue(result); + } + JSHandle regexpObj(thisObj); + JSMutableHandle pattern(thread, JSTaggedValue::Undefined()); + JSMutableHandle flags(thread, JSTaggedValue::Undefined()); + if (thisObj->IsJSRegExp()) { + pattern.Update(regexpObj->GetOriginalSource()); + flags.Update(regexpObj->GetOriginalFlags()); + } + if (isCached) { + JSTaggedValue cacheResult = cacheTable->FindCachedResult(thread, pattern, flags, inputString, + RegExpExecResultCache::MATCH_TYPE, thisObj); + if (cacheResult != JSTaggedValue::Undefined()) { + return cacheResult; + } + } + // 8. Else global is true + // a. Let fullUnicode be ToBoolean(Get(rx, "unicode")). + JSHandle unicode = globalConst->GetHandledUnicodeString(); + JSTaggedValue uincodeValue = + FastRuntimeStub::FastGetProperty(thread, thisObj.GetTaggedValue(), unicode.GetTaggedValue()); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + bool fullUnicode = uincodeValue.ToBoolean(); + // b. ReturnIfAbrupt(fullUnicode) + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + // c. Let setStatus be Set(rx, "lastIndex", 0, true). + JSHandle lastIndexString(globalConst->GetHandledLastIndexString()); + JSHandle value(thread, JSTaggedValue(0)); + FastRuntimeStub::FastSetProperty(thread, thisObj.GetTaggedValue(), lastIndexString.GetTaggedValue(), + JSTaggedValue(0), true); + JSObject::SetProperty(thread, thisObj, lastIndexString, value, true); + // d. ReturnIfAbrupt(setStatus). + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + // e. Let A be ArrayCreate(0). + JSHandle array(JSArray::ArrayCreate(thread, JSTaggedNumber(0))); + // f. Let n be 0. + int resultNum = 0; + JSMutableHandle result(thread, JSTaggedValue(0)); + // g. Repeat, + while (true) { + // i. Let result be RegExpExec(rx, S). + result.Update(RegExpExec(thread, thisObj, string, isCached)); + // ii. ReturnIfAbrupt(result). + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + // iii. If result is null, then + if (result->IsNull()) { + // 1. If n=0, return null. + if (resultNum == 0) { + return JSTaggedValue::Null(); + } + if (isCached) { + RegExpExecResultCache::AddResultInCache(thread, cacheTable, pattern, flags, inputString, + JSHandle(array), + RegExpExecResultCache::MATCH_TYPE, 0); + } + // 2. Else, return A. + return array.GetTaggedValue(); + } + // iv. Else result is not null, + // 1. Let matchStr be ToString(Get(result, "0")). + JSHandle zeroString = globalConst->GetHandledZeroString(); + JSHandle matchStr( + thread, FastRuntimeStub::FastGetProperty(thread, result.GetTaggedValue(), zeroString.GetTaggedValue())); + JSHandle matchString = JSTaggedValue::ToString(thread, matchStr); + // 2. ReturnIfAbrupt(matchStr). + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + JSHandle matchValue = JSHandle::Cast(matchString); + // 3. Let status be CreateDataProperty(A, ToString(n), matchStr). + JSObject::CreateDataProperty(thread, array, resultNum, matchValue); + // 5. If matchStr is the empty String, then + if (JSTaggedValue::ToString(thread, matchValue)->GetLength() == 0) { + // a. Let thisIndex be ToLength(Get(rx, "lastIndex")). + JSHandle lastIndexHandle( + thread, + FastRuntimeStub::FastGetProperty(thread, thisObj.GetTaggedValue(), lastIndexString.GetTaggedValue())); + JSTaggedNumber thisIndex = JSTaggedValue::ToLength(thread, lastIndexHandle); + // b. ReturnIfAbrupt(thisIndex). + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + // c. Let nextIndex be AdvanceStringIndex(S, thisIndex, fullUnicode). + // d. Let setStatus be Set(rx, "lastIndex", nextIndex, true). + auto nextIndex = JSTaggedValue(AdvanceStringIndex(thread, string, thisIndex.GetNumber(), fullUnicode)); + FastRuntimeStub::FastSetProperty(thread, thisObj.GetTaggedValue(), lastIndexString.GetTaggedValue(), + nextIndex, true); + // e. ReturnIfAbrupt(setStatus). + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + } + // 6. Increment n. + resultNum++; + } +} + +JSTaggedValue BuiltinsRegExp::RegExpReplaceFast(JSThread *thread, JSHandle ®exp, + JSHandle inputString, uint32_t inputLength) +{ + ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); + // get bytecode + JSTaggedValue bufferData = JSRegExp::Cast(regexp->GetTaggedObject())->GetByteCodeBuffer(); + void *dynBuf = JSNativePointer::Cast(bufferData.GetTaggedObject())->GetExternalPointer(); + // get flags + auto bytecodeBuffer = reinterpret_cast(dynBuf); + // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-pointer-arithmetic) + uint32_t flags = *reinterpret_cast(bytecodeBuffer + RegExpParser::FLAGS_OFFSET); + JSHandle lastIndexHandle(thread->GlobalConstants()->GetHandledLastIndexString()); + uint32_t lastIndex; + JSHandle regexpHandle(regexp); + bool isCached = false; + if ((flags & (RegExpParser::FLAG_STICKY | RegExpParser::FLAG_GLOBAL)) == 0) { + lastIndex = 0; + } else { + JSTaggedValue thisIndex = + FastRuntimeStub::FastGetProperty(thread, regexp.GetTaggedValue(), lastIndexHandle.GetTaggedValue()); + if (thisIndex.IsInt()) { + lastIndex = thisIndex.GetInt(); + } else { + JSHandle thisIndexHandle(thread, thisIndex); + lastIndex = JSTaggedValue::ToLength(thread, thisIndexHandle).GetNumber(); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + } + } + + JSHandle tagInputString = JSHandle::Cast(inputString); + JSMutableHandle pattern(thread, JSTaggedValue::Undefined()); + JSMutableHandle flagsBits(thread, JSTaggedValue::Undefined()); + if (regexp->IsJSRegExp()) { + pattern.Update(regexpHandle->GetOriginalSource()); + flagsBits.Update(regexpHandle->GetOriginalFlags()); + } + JSHandle cacheTable(thread->GetEcmaVM()->GetRegExpCache()); + uint32_t length = inputString->GetLength(); + uint32_t largeStrCount = cacheTable->GetLargeStrCount(); + if (largeStrCount != 0) { + if (length > MIN_REPLACE_STRING_LENGTH) { + cacheTable->SetLargeStrCount(thread, --largeStrCount); + } + } else { + cacheTable->SetStrLenThreshold(thread, MIN_REPLACE_STRING_LENGTH); + } + if (lastIndex == 0 && length > cacheTable->GetStrLenThreshold()) { + isCached = true; + } + if (isCached) { + JSTaggedValue cacheResult = cacheTable->FindCachedResult(thread, pattern, flagsBits, tagInputString, + RegExpExecResultCache::REPLACE_TYPE, regexp); + if (cacheResult != JSTaggedValue::Undefined()) { + return cacheResult; + } + } + + std::string resultString; + uint32_t nextPosition = 0; + JSMutableHandle lastIndexValue(thread, JSTaggedValue(lastIndex)); + + // 12. Let done be false. + // 13. Repeat, while done is false + for (;;) { + if (lastIndex > inputLength) { + break; + } + + bool isUtf16 = inputString->IsUtf16(); + const uint8_t *strBuffer; + CVector u8Buffer; + CVector u16Buffer; + if (isUtf16) { + u16Buffer = CVector(inputLength); + inputString->CopyDataUtf16(u16Buffer.data(), inputLength); + strBuffer = reinterpret_cast(u16Buffer.data()); + } else { + u8Buffer = CVector(inputLength + 1); + inputString->CopyDataUtf8(u8Buffer.data(), inputLength + 1); + strBuffer = u8Buffer.data(); + } + + RegExpExecutor::MatchResult matchResult = Matcher(thread, regexp, strBuffer, inputLength, lastIndex, isUtf16); + if (!matchResult.isSuccess_) { + if ((flags & (RegExpParser::FLAG_STICKY | RegExpParser::FLAG_GLOBAL)) != 0U) { + lastIndex = 0; + lastIndexValue.Update(JSTaggedValue(lastIndex)); + JSObject::SetProperty(thread, regexp, lastIndexHandle, lastIndexValue, true); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + } + break; + } + uint32_t startIndex = matchResult.index_; + uint32_t endIndex = matchResult.endIndex_; + lastIndex = endIndex; + if (nextPosition < startIndex) { + resultString += base::StringHelper::SubString(inputString, nextPosition, startIndex - nextPosition); + } + nextPosition = endIndex; + if ((flags & RegExpParser::FLAG_GLOBAL) == 0U) { + // a. Let setStatus be Set(R, "lastIndex", e, true). + lastIndexValue.Update(JSTaggedValue(lastIndex)); + JSObject::SetProperty(thread, regexp, lastIndexHandle, lastIndexValue, true); + // b. ReturnIfAbrupt(setStatus). + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + break; + } + if (endIndex == startIndex) { + bool unicode = inputString->IsUtf16() && ((flags & RegExpParser::FLAG_UTF16) != 0U); + endIndex = AdvanceStringIndex(thread, tagInputString, endIndex, unicode); + } + lastIndex = endIndex; + } + resultString += base::StringHelper::SubString(inputString, nextPosition, inputLength - nextPosition); + auto resultValue = factory->NewFromStdString(resultString); + if (isCached) { + RegExpExecResultCache::AddResultInCache(thread, cacheTable, pattern, flagsBits, tagInputString, + JSHandle(resultValue), + RegExpExecResultCache::REPLACE_TYPE, lastIndex); + } + return resultValue.GetTaggedValue(); +} + +// 21.2.5.8 +// NOLINTNEXTLINE(readability-function-size) +JSTaggedValue BuiltinsRegExp::Replace(EcmaRuntimeCallInfo *argv) +{ + ASSERT(argv); + BUILTINS_API_TRACE(argv->GetThread(), RegExp, Replace); + JSThread *thread = argv->GetThread(); + [[maybe_unused]] EcmaHandleScope handleScope(thread); + // 1. Let rx be the this value. + JSHandle thisObj = GetThis(argv); + if (!thisObj->IsECMAObject()) { + // 2. If Type(rx) is not Object, throw a TypeError exception. + THROW_TYPE_ERROR_AND_RETURN(thread, "this is not Object", JSTaggedValue::Exception()); + } + // 3. Let S be ToString(string). + JSHandle string = GetCallArg(argv, 0); + JSHandle inputReplaceValue = GetCallArg(argv, 1); + JSHandle srcString = JSTaggedValue::ToString(thread, string); + const GlobalEnvConstants *globalConst = thread->GlobalConstants(); + + // 4. ReturnIfAbrupt(S). + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + JSHandle inputStr = JSHandle::Cast(srcString); + // 5. Let lengthS be the number of code unit elements in S. + uint32_t length = srcString->GetLength(); + // 6. Let functionalReplace be IsCallable(replaceValue). + bool functionalReplace = inputReplaceValue->IsCallable(); + JSHandle replaceValueHandle; + if (!functionalReplace) { + replaceValueHandle = JSTaggedValue::ToString(thread, inputReplaceValue); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + } + JSHandle lastIndex = globalConst->GetHandledLastIndexString(); + // 8. Let global be ToBoolean(Get(rx, "global")). + ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); + JSHandle global = globalConst->GetHandledGlobalString(); + JSTaggedValue globalValue = + FastRuntimeStub::FastGetProperty(thread, thisObj.GetTaggedValue(), global.GetTaggedValue()); + // 9. ReturnIfAbrupt(global). + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + bool isGlobal = globalValue.ToBoolean(); + // 10. If global is true, then + bool fullUnicode = false; + if (isGlobal) { + // a. Let fullUnicode be ToBoolean(Get(rx, "unicode")). + JSHandle unicode = globalConst->GetHandledUnicodeString(); + JSTaggedValue fullUnicodeTag = + FastRuntimeStub::FastGetProperty(thread, thisObj.GetTaggedValue(), unicode.GetTaggedValue()); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + fullUnicode = fullUnicodeTag.ToBoolean(); + // b. ReturnIfAbrupt(fullUnicode). + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + // c. Let setStatus be Set(rx, "lastIndex", 0, true). + FastRuntimeStub::FastSetProperty(thread, thisObj.GetTaggedValue(), lastIndex.GetTaggedValue(), JSTaggedValue(0), + true); + // d. ReturnIfAbrupt(setStatus). + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + } + + if (isGlobal && !functionalReplace && (replaceValueHandle->GetLength() == 0) && thisObj->IsJSRegExp()) { + JSHClass *hclass = JSHandle::Cast(thisObj)->GetJSHClass(); + JSHClass *originHClass = JSHClass::Cast(globalConst->GetJSRegExpClass().GetTaggedObject()); + if (hclass == originHClass) { + return RegExpReplaceFast(thread, thisObj, srcString, length); + } + } + + JSHandle matchedStr = globalConst->GetHandledZeroString(); + // 11. Let results be a new empty List. + JSHandle resultsList(JSArray::ArrayCreate(thread, JSTaggedNumber(0))); + int resultsIndex = 0; + // 12. Let done be false. + // 13. Repeat, while done is false + JSMutableHandle nextIndexHandle(thread, JSTaggedValue(0)); + JSMutableHandle execResult(thread, JSTaggedValue(0)); + for (;;) { + // a. Let result be RegExpExec(rx, S). + execResult.Update(RegExpExec(thread, thisObj, inputStr, false)); + // b. ReturnIfAbrupt(result). + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + // c. If result is null, set done to true. + if (execResult->IsNull()) { + break; + } + // d. Else result is not null, i. Append result to the end of results. + JSObject::CreateDataProperty(thread, resultsList, resultsIndex, execResult); + resultsIndex++; + // ii. If global is false, set done to true. + if (!isGlobal) { + break; + } + // iii. Else, 1. Let matchStr be ToString(Get(result, "0")). + JSHandle getMatch( + thread, FastRuntimeStub::FastGetProperty(thread, execResult.GetTaggedValue(), matchedStr.GetTaggedValue())); + JSHandle matchString = JSTaggedValue::ToString(thread, getMatch); + // 2. ReturnIfAbrupt(matchStr). + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + // 3. If matchStr is the empty String, then + if (matchString->GetLength() == 0) { + // a. Let thisIndex be ToLength(Get(rx, "lastIndex")). + JSHandle thisIndexHandle( + thread, FastRuntimeStub::FastGetProperty(thread, thisObj.GetTaggedValue(), lastIndex.GetTaggedValue())); + uint32_t thisIndex = 0; + if (thisIndexHandle->IsInt()) { + thisIndex = 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(thread, inputStr, thisIndex, fullUnicode); + nextIndexHandle.Update(JSTaggedValue(nextIndex)); + // d. Let setStatus be Set(rx, "lastIndex", nextIndex, true). + FastRuntimeStub::FastSetProperty(thread, thisObj.GetTaggedValue(), lastIndex.GetTaggedValue(), + nextIndexHandle.GetTaggedValue(), true); + // e. ReturnIfAbrupt(setStatus). + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + } + } + // 14. Let accumulatedResult be the empty String value. + std::string accumulatedResult; + // 15. Let nextSourcePosition be 0. + uint32_t nextSourcePosition = 0; + JSHandle getMatchString; + JSMutableHandle resultValues(thread, JSTaggedValue(0)); + JSMutableHandle ncapturesHandle(thread, JSTaggedValue(0)); + JSMutableHandle capN(thread, JSTaggedValue(0)); + // 16. Repeat, for each result in results, + for (int i = 0; i < resultsIndex; i++) { + resultValues.Update(FastRuntimeStub::FastGetPropertyByIndex(thread, resultsList.GetTaggedValue(), i)); + // a. Let nCaptures be ToLength(Get(result, "length")). + JSHandle lengthHandle = globalConst->GetHandledLengthString(); + ncapturesHandle.Update( + FastRuntimeStub::FastGetProperty(thread, resultValues.GetTaggedValue(), lengthHandle.GetTaggedValue())); + uint32_t ncaptures = JSTaggedValue::ToUint32(thread, ncapturesHandle); + // b. ReturnIfAbrupt(nCaptures). + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + // c. Let nCaptures be max(nCaptures − 1, 0). + ncaptures = std::max((ncaptures - 1), 0); + // d. Let matched be ToString(Get(result, "0")). + getMatchString = JSObject::GetProperty(thread, resultValues, matchedStr).GetValue(); + JSHandle matchString = JSTaggedValue::ToString(thread, getMatchString); + // e. ReturnIfAbrupt(matched). + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + // f. Let matchLength be the number of code units in matched. + uint32_t matchLength = matchString->GetLength(); + // g. Let position be ToInteger(Get(result, "index")). + JSHandle resultIndex(factory->NewFromCanBeCompressString("index")); + JSHandle positionHandle = JSObject::GetProperty(thread, resultValues, resultIndex).GetValue(); + uint32_t position = 0; + if (positionHandle->IsInt()) { + position = positionHandle->GetInt(); + } else { + position = JSTaggedValue::ToUint32(thread, positionHandle); + // h. ReturnIfAbrupt(position). + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + } + // i. Let position be max(min(position, lengthS), 0). + position = std::max(std::min(position, length), 0); + // j. Let n be 1. + uint32_t index = 1; + // k. Let captures be an empty List. + JSHandle capturesList = factory->NewTaggedArray(ncaptures); + // l. Repeat while n ≤ nCaptures + while (index <= ncaptures) { + // i. Let capN be Get(result, ToString(n)). + capN.Update(FastRuntimeStub::FastGetPropertyByIndex(thread, resultValues.GetTaggedValue(), index)); + // ii. ReturnIfAbrupt(capN). + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + // iii. If capN is not undefined, then + if (!capN->IsUndefined()) { + // 1. Let capN be ToString(capN). + JSHandle capNStr = JSTaggedValue::ToString(thread, capN); + // 2. ReturnIfAbrupt(capN). + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + JSHandle capnStr = JSHandle::Cast(capNStr); + capturesList->Set(thread, index - 1, capnStr); + } else { + // iv. Append capN as the last element of captures. + capturesList->Set(thread, index - 1, capN); + } + // v. Let n be n+1 + ++index; + } + // m. If functionalReplace is true, then + CString replacement; + JSHandle replacerArgs = + factory->NewTaggedArray(3 + capturesList->GetLength()); // 3: «matched, pos, and string» + if (functionalReplace) { + // i. Let replacerArgs be «matched». + replacerArgs->Set(thread, 0, getMatchString.GetTaggedValue()); + // ii. Append in list order the elements of captures to the end of the List replacerArgs. + // iii. Append position and S as the last two elements of replacerArgs. + index = 0; + while (index < capturesList->GetLength()) { + replacerArgs->Set(thread, index + 1, capturesList->Get(index)); + ++index; + } + replacerArgs->Set(thread, index + 1, JSTaggedValue(position)); + replacerArgs->Set(thread, index + 2, inputStr.GetTaggedValue()); // 2: position of string + // iv. Let replValue be Call(replaceValue, undefined, replacerArgs). + JSHandle undefined = globalConst->GetHandledUndefined(); + ecmascript::InternalCallParams *args = thread->GetInternalCallParams(); + args->MakeArgList(*replacerArgs); + JSTaggedValue replaceResult = + JSFunction::Call(thread, inputReplaceValue, undefined, replacerArgs->GetLength(), args->GetArgv()); + JSHandle replValue(thread, replaceResult); + // v. Let replacement be ToString(replValue). + JSHandle replacementString = JSTaggedValue::ToString(thread, replValue); + // o. ReturnIfAbrupt(replacement). + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + replacement = ConvertToString(*replacementString, StringConvertedUsage::LOGICOPERATION); + } else { + // n. Else, + JSHandle replacementHandle( + thread, BuiltinsString::GetSubstitution(thread, matchString, srcString, position, capturesList, + replaceValueHandle)); + replacement = ConvertToString(EcmaString::Cast(replacementHandle->GetTaggedObject()), + StringConvertedUsage::LOGICOPERATION); + } + // p. If position ≥ nextSourcePosition, then + if (position >= nextSourcePosition) { + // ii. Let accumulatedResult be the String formed by concatenating the code units of the current value + // of accumulatedResult with the substring of S consisting of the code units from nextSourcePosition + // (inclusive) up to position (exclusive) and with the code units of replacement. + accumulatedResult += ecmascript::base::StringHelper::SubString( + JSHandle::Cast(inputStr), nextSourcePosition, (position - nextSourcePosition)); + accumulatedResult += replacement; + // iii. Let nextSourcePosition be position + matchLength. + nextSourcePosition = position + matchLength; + } + } + // 17. If nextSourcePosition ≥ lengthS, return accumulatedResult. + if (nextSourcePosition >= length) { + return factory->NewFromStdString(accumulatedResult).GetTaggedValue(); + } + // 18. Return the String formed by concatenating the code units of accumulatedResult with the substring of S + // consisting of the code units from nextSourcePosition (inclusive) up through the final code unit of S(inclusive). + accumulatedResult += ecmascript::base::StringHelper::SubString(JSHandle::Cast(inputStr), + nextSourcePosition, (length - nextSourcePosition)); + return factory->NewFromStdString(accumulatedResult).GetTaggedValue(); +} + +// 21.2.5.9 +JSTaggedValue BuiltinsRegExp::Search(EcmaRuntimeCallInfo *argv) +{ + ASSERT(argv); + BUILTINS_API_TRACE(argv->GetThread(), RegExp, Search); + JSThread *thread = argv->GetThread(); + [[maybe_unused]] EcmaHandleScope handleScope(thread); + // 1. Let rx be the this value. + JSHandle thisObj = GetThis(argv); + // 3. Let S be ToString(string). + JSHandle inputStr = GetCallArg(argv, 0); + JSHandle stringHandle = JSTaggedValue::ToString(thread, inputStr); + + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + JSHandle string = JSHandle::Cast(stringHandle); + if (!thisObj->IsECMAObject()) { + // 2. If Type(rx) is not Object, throw a TypeError exception. + THROW_TYPE_ERROR_AND_RETURN(thread, "this is not Object", JSTaggedValue::Exception()); + } + // 4. Let previousLastIndex be ? Get(rx, "lastIndex"). + JSHandle lastIndexString(thread->GlobalConstants()->GetHandledLastIndexString()); + JSHandle previousLastIndex = JSObject::GetProperty(thread, thisObj, lastIndexString).GetValue(); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + // 5. If SameValue(previousLastIndex, 0) is false, then + // Perform ? Set(rx, "lastIndex", 0, true). + if (!JSTaggedValue::SameValue(previousLastIndex.GetTaggedValue(), JSTaggedValue(0))) { + JSHandle value(thread, JSTaggedValue(0)); + JSObject::SetProperty(thread, thisObj, lastIndexString, value, true); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + } + // 6. Let result be ? RegExpExec(rx, S). + JSHandle result(thread, RegExpExec(thread, thisObj, string, false)); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + // 7. Let currentLastIndex be ? Get(rx, "lastIndex"). + JSHandle currentLastIndex = JSObject::GetProperty(thread, thisObj, lastIndexString).GetValue(); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + // 8. If SameValue(currentLastIndex, previousLastIndex) is false, then + // Perform ? Set(rx, "lastIndex", previousLastIndex, true). + if (!JSTaggedValue::SameValue(previousLastIndex.GetTaggedValue(), currentLastIndex.GetTaggedValue())) { + JSObject::SetProperty(thread, thisObj, lastIndexString, previousLastIndex, true); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + } + // 9. If result is null, return -1. + if (result->IsNull()) { + return JSTaggedValue(-1); + } + // 10. Return ? Get(result, "index"). + ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); + JSHandle index(factory->NewFromCanBeCompressString("index")); + return JSObject::GetProperty(thread, result, index).GetValue().GetTaggedValue(); +} + +// 21.2.5.11 +// NOLINTNEXTLINE(readability-function-size) +JSTaggedValue BuiltinsRegExp::Split(EcmaRuntimeCallInfo *argv) +{ + ASSERT(argv); + BUILTINS_API_TRACE(argv->GetThread(), RegExp, Split); + JSThread *thread = argv->GetThread(); + [[maybe_unused]] EcmaHandleScope handleScope(thread); + bool isCached = false; + // 1. Let rx be the this value. + JSHandle thisObj = GetThis(argv); + auto ecmaVm = thread->GetEcmaVM(); + // 3. Let S be ToString(string). + JSHandle inputString = GetCallArg(argv, 0); + JSHandle limit = GetCallArg(argv, 1); + JSHandle stringHandle = JSTaggedValue::ToString(thread, inputString); + + // 4. ReturnIfAbrupt(string). + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + JSHandle jsString = JSHandle::Cast(stringHandle); + if (!thisObj->IsECMAObject()) { + // 2. If Type(rx) is not Object, throw a TypeError exception. + THROW_TYPE_ERROR_AND_RETURN(thread, "this is not Object", JSTaggedValue::Exception()); + } + // 5. Let C be SpeciesConstructor(rx, %RegExp%). + JSHandle defaultConstructor = ecmaVm->GetGlobalEnv()->GetRegExpFunction(); + JSHandle objHandle(thisObj); + JSHandle constructor = JSObject::SpeciesConstructor(thread, objHandle, defaultConstructor); + // 6. ReturnIfAbrupt(C). + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + // 7. Let flags be ToString(Get(rx, "flags")). + ObjectFactory *factory = ecmaVm->GetFactory(); + JSHandle flagsString(factory->NewFromCanBeCompressString("flags")); + JSHandle taggedFlags = JSObject::GetProperty(thread, thisObj, flagsString).GetValue(); + JSHandle flags; + + if (taggedFlags->IsUndefined()) { + flags = factory->GetEmptyString(); + } else { + flags = JSTaggedValue::ToString(thread, taggedFlags); + } + // 8. ReturnIfAbrupt(flags). + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + // 9. If flags contains "u", let unicodeMatching be true. + // 10. Else, let unicodeMatching be false. + JSHandle uStringHandle(factory->NewFromCanBeCompressString("u")); + bool unicodeMatching = ecmascript::base::StringHelper::Contains(*flags, *uStringHandle); + // 11. If flags contains "y", let newFlags be flags. + JSHandle newFlagsHandle; + JSHandle yStringHandle(factory->NewFromCanBeCompressString("y")); + if (ecmascript::base::StringHelper::Contains(*flags, *yStringHandle)) { + newFlagsHandle = flags; + } else { + // 12. Else, let newFlags be the string that is the concatenation of flags and "y". + JSHandle yStr = factory->NewFromCanBeCompressString("y"); + newFlagsHandle = factory->ConcatFromString(flags, yStr); + } + + // 17. If limit is undefined, let lim be 2^32–1; else let lim be ToUint32(limit). + uint32_t lim; + if (limit->IsUndefined()) { + lim = MAX_SPLIT_LIMIT; + } else { + lim = JSTaggedValue::ToUint32(thread, limit); + // 18. ReturnIfAbrupt(lim). + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + } + + if (lim == MAX_SPLIT_LIMIT) { + isCached = true; + } + + JSHandle regexpHandle(thisObj); + JSMutableHandle pattern(thread, JSTaggedValue::Undefined()); + JSMutableHandle flagsBits(thread, JSTaggedValue::Undefined()); + if (thisObj->IsJSRegExp()) { + pattern.Update(regexpHandle->GetOriginalSource()); + flagsBits.Update(regexpHandle->GetOriginalFlags()); + } + JSHandle cacheTable(thread->GetEcmaVM()->GetRegExpCache()); + if (isCached) { + JSTaggedValue cacheResult = cacheTable->FindCachedResult(thread, pattern, flagsBits, inputString, + RegExpExecResultCache::SPLIT_TYPE, thisObj); + if (cacheResult != JSTaggedValue::Undefined()) { + return cacheResult; + } + } + + // 13. Let splitter be Construct(C, «rx, newFlags»). + JSHandle globalObject(thread, thread->GetEcmaVM()->GetGlobalEnv()->GetGlobalObject()); + JSHandle undefined(thread, JSTaggedValue::Undefined()); + InternalCallParams *arguments = thread->GetInternalCallParams(); + arguments->MakeArgv(thisObj, newFlagsHandle); + JSTaggedValue taggedSplitter = + JSFunction::Construct(thread, constructor, 2, arguments->GetArgv(), undefined); // 2: two args + // 14. ReturnIfAbrupt(splitter). + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + + JSHandle splitter(thread, taggedSplitter); + // 15. Let A be ArrayCreate(0). + JSHandle array(JSArray::ArrayCreate(thread, JSTaggedNumber(0))); + // 16. Let lengthA be 0. + uint32_t aLength = 0; + + // 19. Let size be the number of elements in S. + uint32_t size = static_cast(jsString->GetTaggedObject())->GetLength(); + // 20. Let p be 0. + uint32_t startIndex = 0; + // 21. If lim = 0, return A. + if (lim == 0) { + return JSTaggedValue(static_cast(array.GetTaggedValue().GetTaggedObject())); + } + // 22. If size = 0, then + if (size == 0) { + // a. Let z be RegExpExec(splitter, S). + JSHandle execResult(thread, RegExpExec(thread, splitter, jsString, isCached)); + // b. ReturnIfAbrupt(z). + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + // c. If z is not null, return A. + if (!execResult->IsNull()) { + return JSTaggedValue(static_cast(array.GetTaggedValue().GetTaggedObject())); + } + // d. Assert: The following call will never result in an abrupt completion. + // e. Perform CreateDataProperty(A, "0", S). + JSObject::CreateDataProperty(thread, array, 0, jsString); + // f. Return A. + return JSTaggedValue(static_cast(array.GetTaggedValue().GetTaggedObject())); + } + // 23. Let q be p. + uint32_t endIndex = startIndex; + JSMutableHandle lastIndexvalue(thread, JSTaggedValue(endIndex)); + // 24. Repeat, while q < size + JSHandle lastIndexString(thread->GlobalConstants()->GetHandledLastIndexString()); + while (endIndex < size) { + // a. Let setStatus be Set(splitter, "lastIndex", q, true). + lastIndexvalue.Update(JSTaggedValue(endIndex)); + JSObject::SetProperty(thread, splitter, lastIndexString, lastIndexvalue, true); + // b. ReturnIfAbrupt(setStatus). + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + JSHandle execResult(thread, RegExpExec(thread, splitter, jsString, isCached)); + // d. ReturnIfAbrupt(z). + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + // e. If z is null, let q be AdvanceStringIndex(S, q, unicodeMatching). + if (execResult->IsNull()) { + endIndex = AdvanceStringIndex(thread, jsString, endIndex, unicodeMatching); + } else { + // f. Else z is not null, + // i. Let e be ToLength(Get(splitter, "lastIndex")). + JSHandle lastIndexHandle = + JSObject::GetProperty(thread, splitter, lastIndexString).GetValue(); + JSTaggedNumber lastIndexNumber = JSTaggedValue::ToLength(thread, lastIndexHandle); + // ii. ReturnIfAbrupt(e). + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + uint32_t lastIndex = lastIndexNumber.GetNumber(); + // iii. If e = p, let q be AdvanceStringIndex(S, q, unicodeMatching). + if (lastIndex == startIndex) { + endIndex = AdvanceStringIndex(thread, jsString, endIndex, unicodeMatching); + } else { + // iv. Else e != p, + // 1. Let T be a String value equal to the substring of S consisting of the elements at indices p + // (inclusive) through q (exclusive). + std::string stdStrT = ecmascript::base::StringHelper::SubString( + JSHandle::Cast(jsString), startIndex, (endIndex - startIndex)); + // 2. Assert: The following call will never result in an abrupt completion. + // 3. Perform CreateDataProperty(A, ToString(lengthA), T). + JSHandle tValue(factory->NewFromStdString(stdStrT)); + JSObject::CreateDataProperty(thread, array, aLength, tValue); + // 4. Let lengthA be lengthA +1. + ++aLength; + // 5. If lengthA = lim, return A. + if (aLength == lim) { + if (isCached) { + RegExpExecResultCache::AddResultInCache(thread, cacheTable, pattern, flagsBits, inputString, + JSHandle(array), + RegExpExecResultCache::SPLIT_TYPE, lastIndex); + } + return array.GetTaggedValue(); + } + // 6. Let p be e. + startIndex = lastIndex; + // 7. Let numberOfCaptures be ToLength(Get(z, "length")). + JSHandle lengthString(factory->NewFromCanBeCompressString("length")); + JSHandle capturesHandle = + JSObject::GetProperty(thread, execResult, lengthString).GetValue(); + JSTaggedNumber numberOfCapturesNumber = JSTaggedValue::ToLength(thread, capturesHandle); + // 8. ReturnIfAbrupt(numberOfCaptures). + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + uint32_t numberOfCaptures = numberOfCapturesNumber.GetNumber(); + // 9. Let numberOfCaptures be max(numberOfCaptures-1, 0). + numberOfCaptures = (numberOfCaptures == 0) ? 0 : numberOfCaptures - 1; + // 10. Let i be 1. + uint32_t i = 1; + // 11. Repeat, while i ≤ numberOfCaptures. + while (i <= numberOfCaptures) { + // a. Let nextCapture be Get(z, ToString(i)). + JSHandle nextCapture = JSObject::GetProperty(thread, execResult, i).GetValue(); + // b. ReturnIfAbrupt(nextCapture). + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + // c. Perform CreateDataProperty(A, ToString(lengthA), nextCapture). + JSObject::CreateDataProperty(thread, array, aLength, nextCapture); + // d. Let i be i + 1. + ++i; + // e. Let lengthA be lengthA +1. + ++aLength; + // f. If lengthA = lim, return A. + if (aLength == lim) { + if (isCached) { + RegExpExecResultCache::AddResultInCache(thread, cacheTable, pattern, flagsBits, inputString, + JSHandle(array), + RegExpExecResultCache::SPLIT_TYPE, lastIndex); + } + return array.GetTaggedValue(); + } + } + // 12. Let q be p. + endIndex = startIndex; + } + } + } + // 25. Let T be a String value equal to the substring of S consisting of the elements at indices p (inclusive) + // through size (exclusive). + std::string stdStrT = ecmascript::base::StringHelper::SubString(JSHandle::Cast(jsString), + startIndex, (size - startIndex)); + // 26. Assert: The following call will never result in an abrupt completion. + // 27. Perform CreateDataProperty(A, ToString(lengthA), t). + JSHandle tValue(factory->NewFromStdString(stdStrT)); + JSObject::CreateDataProperty(thread, array, aLength, tValue); + if (lim == MAX_SPLIT_LIMIT) { + RegExpExecResultCache::AddResultInCache(thread, cacheTable, pattern, flagsBits, inputString, + JSHandle(array), RegExpExecResultCache::SPLIT_TYPE, + endIndex); + } + // 28. Return A. + return array.GetTaggedValue(); +} + +// NOLINTNEXTLINE(readability-non-const-parameter) +RegExpExecutor::MatchResult BuiltinsRegExp::Matcher(JSThread *thread, const JSHandle ®exp, + const uint8_t *buffer, size_t length, int32_t lastIndex, + bool isUtf16) +{ + // get bytecode + JSTaggedValue bufferData = JSRegExp::Cast(regexp->GetTaggedObject())->GetByteCodeBuffer(); + void *dynBuf = JSNativePointer::Cast(bufferData.GetTaggedObject())->GetExternalPointer(); + auto bytecodeBuffer = reinterpret_cast(dynBuf); + // execute + Chunk chunk(thread->GetRegionFactory()); + RegExpExecutor executor(&chunk); + if (lastIndex < 0) { + lastIndex = 0; + } + bool ret = executor.Execute(buffer, lastIndex, length, bytecodeBuffer, isUtf16); + RegExpExecutor::MatchResult result = executor.GetResult(thread, ret); + return result; +} + +uint32_t BuiltinsRegExp::AdvanceStringIndex([[maybe_unused]] JSThread *thread, const JSHandle &inputStr, + uint32_t index, bool unicode) +{ + // 1. Assert: Type(S) is String. + ASSERT(inputStr->IsString()); + // 2. Assert: index is an integer such that 0≤index≤2^53 - 1 + ASSERT(index <= pow(2, 53) - 1); + // 3. Assert: Type(unicode) is Boolean. + // 4. If unicode is false, return index+1. + if (!unicode) { + return index + 1; + } + // 5. Let length be the number of code units in S. + uint32_t length = static_cast(inputStr->GetTaggedObject())->GetLength(); + // 6. If index+1 ≥ length, return index+1. + if (index + 1 >= length) { + return index + 1; + } + // 7. Let first be the code unit value at index index in S. + uint16_t first = static_cast(inputStr->GetTaggedObject())->At(index); + // 8. If first < 0xD800 or first > 0xDFFF, return index+1. + if (first < 0xD800 || first > 0xDFFF) { // NOLINT(readability-magic-numbers) + return index + 1; + } + // 9. Let second be the code unit value at index index+1 in S. + uint16_t second = static_cast(inputStr->GetTaggedObject())->At(index + 1); + // 10. If second < 0xDC00 or second > 0xDFFF, return index+1. + if (second < 0xDC00 || second > 0xDFFF) { // NOLINT(readability-magic-numbers) + return index + 1; + } + // 11. Return index + 2. + return index + 2; +} + +bool BuiltinsRegExp::GetFlagsInternal(JSThread *thread, const JSHandle &obj, const uint8_t mask) +{ + // 1. Let R be the this value. + // 2. If Type(R) is not Object, throw a TypeError exception. + if (!obj->IsECMAObject()) { + // throw a TypeError exception. + THROW_TYPE_ERROR_AND_RETURN(thread, "this is not Object", false); + } + // 3. If R does not have an [[OriginalFlags]] internal slot, throw a TypeError exception. + JSHandle patternObj = JSHandle::Cast(obj); + if (!patternObj->IsJSRegExp()) { + // throw a TypeError exception. + THROW_TYPE_ERROR_AND_RETURN(thread, "this does not have [[OriginalFlags]]", false); + } + // 4. Let flags be the value of R’s [[OriginalFlags]] internal slot. + JSHandle regexpObj(thread, JSRegExp::Cast(obj->GetTaggedObject())); + // 5. If flags contains the code unit "[flag]", return true. + // 6. Return false. + uint8_t flags = static_cast(regexpObj->GetOriginalFlags().GetInt()); + return (flags & mask) != 0U; +} + +// 21.2.5.2.2 +JSTaggedValue BuiltinsRegExp::RegExpBuiltinExec(JSThread *thread, const JSHandle ®exp, + const JSHandle &inputStr, bool isCached) +{ + ASSERT(JSObject::IsRegExp(thread, regexp)); + ASSERT(inputStr->IsString()); + int32_t length = static_cast(inputStr->GetTaggedObject())->GetLength(); + + const GlobalEnvConstants *globalConst = thread->GlobalConstants(); + JSHandle lastIndexHandle = globalConst->GetHandledLastIndexString(); + JSTaggedValue result = + FastRuntimeStub::FastGetProperty(thread, regexp.GetTaggedValue(), lastIndexHandle.GetTaggedValue()); + int32_t lastIndex = 0; + if (result.IsInt()) { + lastIndex = result.GetInt(); + } else { + JSHandle lastIndexResult(thread, result); + JSTaggedNumber lastIndexNumber = JSTaggedValue::ToLength(thread, lastIndexResult); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + lastIndex = lastIndexNumber.GetNumber(); + } + JSHandle globalHandle = globalConst->GetHandledGlobalString(); + bool global = + FastRuntimeStub::FastGetProperty(thread, regexp.GetTaggedValue(), globalHandle.GetTaggedValue()).ToBoolean(); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + JSHandle stickyHandle = globalConst->GetHandledStickyString(); + bool sticky = + FastRuntimeStub::FastGetProperty(thread, regexp.GetTaggedValue(), stickyHandle.GetTaggedValue()).ToBoolean(); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + if (!global && !sticky) { + lastIndex = 0; + } + JSHandle regexpObj(regexp); + JSMutableHandle pattern(thread, JSTaggedValue::Undefined()); + JSMutableHandle flags(thread, JSTaggedValue::Undefined()); + if (regexp->IsJSRegExp()) { + pattern.Update(regexpObj->GetOriginalSource()); + flags.Update(regexpObj->GetOriginalFlags()); + } + JSHandle cacheTable(thread->GetEcmaVM()->GetRegExpCache()); + if (lastIndex == 0 && isCached) { + JSTaggedValue cacheResult = + cacheTable->FindCachedResult(thread, pattern, flags, inputStr, RegExpExecResultCache::EXEC_TYPE, regexp); + if (cacheResult != JSTaggedValue::Undefined()) { + return cacheResult; + } + } + uint8_t flagsBits = static_cast(regexpObj->GetOriginalFlags().GetInt()); + JSHandle flagsValue(thread, FlagsBitsToString(thread, flagsBits)); + JSHandle flagsStr = JSTaggedValue::ToString(thread, flagsValue); + JSHandle uString(globalConst->GetHandledUString()); + [[maybe_unused]] bool fullUnicode = base::StringHelper::Contains(*flagsStr, *uString); + if (lastIndex > length) { + FastRuntimeStub::FastSetPropertyByValue(thread, regexp.GetTaggedValue(), lastIndexHandle.GetTaggedValue(), + JSTaggedValue(0)); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + return JSTaggedValue::Null(); + } + JSHandle inputString = JSTaggedValue::ToString(thread, inputStr); + bool isUtf16 = inputString->IsUtf16(); + const uint8_t *strBuffer; + size_t stringLength = inputString->GetLength(); + CVector u8Buffer; + CVector u16Buffer; + if (isUtf16) { + u16Buffer = CVector(stringLength); + inputString->CopyDataUtf16(u16Buffer.data(), stringLength); + strBuffer = reinterpret_cast(u16Buffer.data()); + } else { + u8Buffer = CVector(stringLength + 1); + inputString->CopyDataUtf8(u8Buffer.data(), stringLength + 1); + strBuffer = u8Buffer.data(); + } + RegExpExecutor::MatchResult matchResult = Matcher(thread, regexp, strBuffer, stringLength, lastIndex, isUtf16); + if (!matchResult.isSuccess_) { + if (global || sticky) { + JSHandle lastIndexValue(thread, JSTaggedValue(0)); + FastRuntimeStub::FastSetPropertyByValue(thread, regexp.GetTaggedValue(), lastIndexHandle.GetTaggedValue(), + JSTaggedValue(0)); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + } + return JSTaggedValue::Null(); + } + uint32_t endIndex = matchResult.endIndex_; + if (global || sticky) { + // a. Let setStatus be Set(R, "lastIndex", e, true). + FastRuntimeStub::FastSetPropertyByValue(thread, regexp.GetTaggedValue(), lastIndexHandle.GetTaggedValue(), + JSTaggedValue(endIndex)); + // b. ReturnIfAbrupt(setStatus). + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + } + uint32_t capturesSize = matchResult.captures_.size(); + JSHandle results(JSArray::ArrayCreate(thread, JSTaggedNumber(capturesSize))); + uint32_t matchIndex = matchResult.index_; + // 24. Perform CreateDataProperty(A, "index", matchIndex). + JSHandle indexKey = globalConst->GetHandledIndexString(); + JSHandle indexValue(thread, JSTaggedValue(matchIndex)); + 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); + JSObject::CreateDataProperty(thread, results, 0, zeroValue); + // 28. For each integer i such that i > 0 and i <= n + for (uint32_t i = 1; i < capturesSize; i++) { + // a. Let capture_i be ith element of r's captures List + JSTaggedValue capturedValue; + if (matchResult.captures_[i].first) { + capturedValue = JSTaggedValue::Undefined(); + } else { + capturedValue = matchResult.captures_[i].second.GetTaggedValue(); + } + JSHandle iValue(thread, capturedValue); + JSObject::CreateDataProperty(thread, results, i, iValue); + } + if (lastIndex == 0 && isCached) { + RegExpExecResultCache::AddResultInCache(thread, cacheTable, pattern, flags, inputStr, + JSHandle(results), RegExpExecResultCache::EXEC_TYPE, + endIndex); + } + // 29. Return A. + return results.GetTaggedValue(); +} + +// 21.2.5.2.1 +JSTaggedValue BuiltinsRegExp::RegExpExec(JSThread *thread, const JSHandle ®exp, + const JSHandle &inputString, bool isCached) +{ + // 1. Assert: Type(R) is Object. + ASSERT(regexp->IsECMAObject()); + // 2. Assert: Type(S) is String. + ASSERT(inputString->IsString()); + // 3. Let exec be Get(R, "exec"). + JSHandle thisObj(thread, regexp->GetTaggedObject()); + JSHandle inputStr = JSTaggedValue::ToString(thread, inputString); + + JSHandle execHandle(thread->GlobalConstants()->GetHandledExecString()); + JSHandle exec( + thread, FastRuntimeStub::FastGetProperty(thread, thisObj.GetTaggedValue(), execHandle.GetTaggedValue())); + // 4. ReturnIfAbrupt(exec). + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + // 5. If IsCallable(exec) is true, then + if (exec->IsCallable()) { + JSHClass *hclass = JSHandle::Cast(regexp)->GetJSHClass(); + JSHClass *originHClass = JSHClass::Cast(thread->GlobalConstants()->GetJSRegExpClass().GetTaggedObject()); + if (hclass == originHClass) { + // 7. Return RegExpBuiltinExec(R, S). + return RegExpBuiltinExec(thread, regexp, inputString, isCached); + } + JSHandle obj = JSHandle::Cast(thisObj); + InternalCallParams *arguments = thread->GetInternalCallParams(); + arguments->MakeArgv(inputStr.GetTaggedValue()); + JSTaggedValue result = JSFunction::Call(thread, exec, obj, 1, arguments->GetArgv()); + // b. ReturnIfAbrupt(result). + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + if (!result.IsECMAObject() && !result.IsNull()) { + // throw a TypeError exception. + THROW_TYPE_ERROR_AND_RETURN(thread, "exec result is null or is not Object", JSTaggedValue::Exception()); + } + return result; + } + // 6. If R does not have a [[RegExpMatcher]] internal slot, throw a TypeError exception. + if (!thisObj->IsJSRegExp()) { + // throw a TypeError exception. + THROW_TYPE_ERROR_AND_RETURN(thread, "this does not have a [[RegExpMatcher]]", JSTaggedValue::Exception()); + } + // 7. Return RegExpBuiltinExec(R, S). + return RegExpBuiltinExec(thread, regexp, inputString, isCached); +} + +// 21.2.3.2.1 +JSTaggedValue BuiltinsRegExp::RegExpAlloc(JSThread *thread, const JSHandle &newTarget) +{ + /** + * 1. Let obj be OrdinaryCreateFromConstructor(newTarget, "%RegExpPrototype%", + * «[[RegExpMatcher]],[[OriginalSource]], [[OriginalFlags]]»). + * */ + ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); + JSHandle env = thread->GetEcmaVM()->GetGlobalEnv(); + JSHandle func = env->GetRegExpFunction(); + JSHandle obj(factory->NewJSObjectByConstructor(JSHandle(func), newTarget)); + // 2. ReturnIfAbrupt(obj). + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + // 5. Return obj. + return obj.GetTaggedValue(); +} + +uint32_t BuiltinsRegExp::UpdateExpressionFlags(JSThread *thread, const CString &checkStr) +{ + uint32_t flagsBits = 0; + uint32_t flagsBitsTemp = 0; + for (char i : checkStr) { + switch (i) { + case 'g': + flagsBitsTemp = RegExpParser::FLAG_GLOBAL; + break; + case 'i': + flagsBitsTemp = RegExpParser::FLAG_IGNORECASE; + break; + case 'm': + flagsBitsTemp = RegExpParser::FLAG_MULTILINE; + break; + case 's': + flagsBitsTemp = RegExpParser::FLAG_DOTALL; + break; + case 'u': + flagsBitsTemp = RegExpParser::FLAG_UTF16; + break; + case 'y': + flagsBitsTemp = RegExpParser::FLAG_STICKY; + break; + default: { + ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); + JSHandle syntaxError = + factory->GetJSError(ecmascript::base::ErrorType::SYNTAX_ERROR, "invalid regular expression flags"); + THROW_NEW_ERROR_AND_RETURN_VALUE(thread, syntaxError.GetTaggedValue(), 0); + } + } + if ((flagsBits & flagsBitsTemp) != 0) { + ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); + JSHandle syntaxError = + factory->GetJSError(ecmascript::base::ErrorType::SYNTAX_ERROR, "invalid regular expression flags"); + THROW_NEW_ERROR_AND_RETURN_VALUE(thread, syntaxError.GetTaggedValue(), 0); + } + flagsBits |= flagsBitsTemp; + } + return flagsBits; +} + +JSTaggedValue BuiltinsRegExp::FlagsBitsToString(JSThread *thread, uint8_t flags) +{ + ASSERT((flags & static_cast(0xC0)) == 0); // 0xC0: first 2 bits of flags must be 0 + + auto *flagsStr = new uint8_t[7]; // 7: maximum 6 flags + '\0' + size_t flagsLen = 0; + if ((flags & RegExpParser::FLAG_GLOBAL) != 0U) { + // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-pointer-arithmetic) + flagsStr[flagsLen] = 'g'; + flagsLen++; + } + if ((flags & RegExpParser::FLAG_IGNORECASE) != 0U) { + // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-pointer-arithmetic) + flagsStr[flagsLen] = 'i'; + flagsLen++; + } + if ((flags & RegExpParser::FLAG_MULTILINE) != 0U) { + // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-pointer-arithmetic) + flagsStr[flagsLen] = 'm'; + flagsLen++; + } + if ((flags & RegExpParser::FLAG_DOTALL) != 0U) { + // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-pointer-arithmetic) + flagsStr[flagsLen] = 's'; + flagsLen++; + } + if ((flags & RegExpParser::FLAG_UTF16) != 0U) { + // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-pointer-arithmetic) + flagsStr[flagsLen] = 'u'; + flagsLen++; + } + if ((flags & RegExpParser::FLAG_STICKY) != 0U) { + // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-pointer-arithmetic) + flagsStr[flagsLen] = 'y'; + flagsLen++; + } + // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-pointer-arithmetic) + flagsStr[flagsLen] = '\0'; + JSHandle flagsString = thread->GetEcmaVM()->GetFactory()->NewFromUtf8(flagsStr, flagsLen); + delete[] flagsStr; + + return flagsString.GetTaggedValue(); +} + +// 21.2.3.2.2 +JSTaggedValue BuiltinsRegExp::RegExpInitialize(JSThread *thread, const JSHandle &obj, + const JSHandle &pattern, + const JSHandle &flags) +{ + ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); + JSHandle patternStrHandle; + uint8_t flagsBits = 0; + // 1. If pattern is undefined, let P be the empty String. + if (pattern->IsUndefined()) { + patternStrHandle = factory->GetEmptyString(); + } else { + // 2. Else, let P be ToString(pattern). + patternStrHandle = JSTaggedValue::ToString(thread, pattern); + // 3. ReturnIfAbrupt(P). + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + } + // 4. If flags is undefined, let F be the empty String. + if (flags->IsUndefined()) { + flagsBits = 0; + } else if (flags->IsInt()) { + flagsBits = static_cast(flags->GetInt()); + } else { + // 5. Else, let F be ToString(flags). + JSHandle flagsStrHandle = JSTaggedValue::ToString(thread, flags); + // 6. ReturnIfAbrupt(F). + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + /** + * 7. If F contains any code unit other than "g", "i", "m", "u", or "y" or if it contains the same code + * unit more than once, throw a SyntaxError exception. + **/ + CString checkStr = ConvertToString(*flagsStrHandle, StringConvertedUsage::LOGICOPERATION); + flagsBits = static_cast(UpdateExpressionFlags(thread, checkStr)); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + } + // String -> CString + CString patternStdStr = ConvertToString(*patternStrHandle, StringConvertedUsage::LOGICOPERATION); + // 9. 10. + Chunk chunk(thread->GetRegionFactory()); + RegExpParser parser = RegExpParser(&chunk); + RegExpParserCache *regExpParserCache = thread->GetEcmaVM()->GetRegExpParserCache(); + auto getCache = regExpParserCache->GetCache(*patternStrHandle, flagsBits); + if (getCache.first == JSTaggedValue::Hole()) { + parser.Init(const_cast(reinterpret_cast(patternStdStr.c_str())), patternStdStr.size(), + flagsBits); + parser.Parse(); + if (parser.IsError()) { + JSHandle syntaxError = + factory->GetJSError(base::ErrorType::SYNTAX_ERROR, parser.GetErrorMsg().c_str()); + THROW_NEW_ERROR_AND_RETURN_VALUE(thread, syntaxError.GetTaggedValue(), JSTaggedValue::Exception()); + } + } + JSHandle regexp(thread, JSRegExp::Cast(obj->GetTaggedObject())); + // 11. Set the value of obj’s [[OriginalSource]] internal slot to P. + regexp->SetOriginalSource(thread, patternStrHandle.GetTaggedValue()); + // 12. Set the value of obj’s [[OriginalFlags]] internal slot to F. + regexp->SetOriginalFlags(thread, JSTaggedValue(flagsBits)); + // 13. Set obj’s [[RegExpMatcher]] internal slot. + if (getCache.first == JSTaggedValue::Hole()) { + auto bufferSize = parser.GetOriginBufferSize(); + auto buffer = parser.GetOriginBuffer(); + factory->NewJSRegExpByteCodeData(regexp, buffer, bufferSize); + regExpParserCache->SetCache(*patternStrHandle, flagsBits, regexp->GetByteCodeBuffer(), bufferSize); + } else { + regexp->SetByteCodeBuffer(thread, getCache.first); + regexp->SetLength(thread, JSTaggedValue(static_cast(getCache.second))); + } + // 14. Let setStatus be Set(obj, "lastIndex", 0, true). + JSHandle lastIndexString = thread->GlobalConstants()->GetHandledLastIndexString(); + FastRuntimeStub::FastSetProperty(thread, obj.GetTaggedValue(), lastIndexString.GetTaggedValue(), JSTaggedValue(0), + true); + // 15. ReturnIfAbrupt(setStatus). + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + // 16. Return obj. + return obj.GetTaggedValue(); +} + +JSTaggedValue BuiltinsRegExp::RegExpCreate(JSThread *thread, const JSHandle &pattern, + const JSHandle &flags) +{ + BUILTINS_API_TRACE(thread, RegExp, Create); + auto ecmaVm = thread->GetEcmaVM(); + JSHandle env = ecmaVm->GetGlobalEnv(); + JSHandle newTarget = env->GetRegExpFunction(); + // 1. Let obj be RegExpAlloc(%RegExp%). + JSHandle object(thread, RegExpAlloc(thread, newTarget)); + // 2. ReturnIfAbrupt(obj). + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + // 3. Return RegExpInitialize(obj, P, F). + return RegExpInitialize(thread, object, pattern, flags); +} + +// 21.2.3.2.4 +EcmaString *BuiltinsRegExp::EscapeRegExpPattern(JSThread *thread, const JSHandle &src, + const JSHandle &flags) +{ + // String -> CString + JSHandle srcStr(thread, static_cast(src->GetTaggedObject())); + JSHandle flagsStr(thread, static_cast(flags->GetTaggedObject())); + CString srcStdStr = ConvertToString(*srcStr, StringConvertedUsage::LOGICOPERATION); + ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); + // "" -> (?:) + if (srcStdStr.empty()) { + srcStdStr = "(?:)"; + } + // "/" -> "\/" + srcStdStr = ecmascript::base::StringHelper::RepalceAll(srcStdStr, "/", "\\/"); + // "\\" -> "\" + srcStdStr = ecmascript::base::StringHelper::RepalceAll(srcStdStr, "\\", "\\"); + + return *factory->NewFromString(srcStdStr); +} + +JSTaggedValue RegExpExecResultCache::CreateCacheTable(JSThread *thread) +{ + int length = CACHE_TABLE_HEADER_SIZE + INITIAL_CACHE_NUMBER * ENTRY_SIZE; + + auto table = static_cast( + *thread->GetEcmaVM()->GetFactory()->NewTaggedArray(length, JSTaggedValue::Undefined())); + table->SetLargeStrCount(thread, DEFAULT_LARGE_STRING_COUNT); + table->SetConflictCount(thread, DEFAULT_CONFLICT_COUNT); + table->SetStrLenThreshold(thread, 0); + table->SetHitCount(thread, 0); + table->SetCacheCount(thread, 0); + table->SetCacheLength(thread, INITIAL_CACHE_NUMBER); + return JSTaggedValue(table); +} + +JSTaggedValue RegExpExecResultCache::FindCachedResult(JSThread *thread, const JSHandle &pattern, + const JSHandle &flags, + const JSHandle &input, CacheType type, + const JSHandle ®exp) +{ + JSTaggedValue patternValue = pattern.GetTaggedValue(); + JSTaggedValue flagsValue = flags.GetTaggedValue(); + JSTaggedValue inputValue = input.GetTaggedValue(); + + if (!pattern->IsString() || !flags->IsInt() || !input->IsString()) { + return JSTaggedValue::Undefined(); + } + + uint32_t hash = pattern->GetKeyHashCode() + flags->GetInt() + input->GetKeyHashCode(); + uint32_t entry = hash & static_cast((GetCacheLength() - 1)); + if (!Match(entry, patternValue, flagsValue, inputValue)) { + uint32_t entry2 = (entry + 1) & static_cast((GetCacheLength() - 1)); + if (!Match(entry2, patternValue, flagsValue, inputValue)) { + return JSTaggedValue::Undefined(); + } + entry = entry2; + } + uint32_t index = CACHE_TABLE_HEADER_SIZE + entry * ENTRY_SIZE; + JSTaggedValue result; + switch (type) { + case REPLACE_TYPE: + result = Get(index + RESULT_REPLACE_INDEX); + break; + case SPLIT_TYPE: + result = Get(index + RESULT_SPLIT_INDEX); + break; + case MATCH_TYPE: + result = Get(index + RESULT_MATCH_INDEX); + break; + case EXEC_TYPE: + result = Get(index + RESULT_EXEC_INDEX); + break; + default: + UNREACHABLE(); + break; + } + SetHitCount(thread, GetHitCount() + 1); + JSHandle lastIndexHandle = thread->GlobalConstants()->GetHandledLastIndexString(); + FastRuntimeStub::FastSetPropertyByValue(thread, regexp.GetTaggedValue(), lastIndexHandle.GetTaggedValue(), + Get(index + LAST_INDEX_INDEX)); + return result; +} + +void RegExpExecResultCache::AddResultInCache(JSThread *thread, JSHandle cache, + const JSHandle &pattern, + const JSHandle &flags, const JSHandle &input, + const JSHandle &resultArray, CacheType type, + uint32_t lastIndex) +{ + if (!pattern->IsString() || !flags->IsInt() || !input->IsString()) { + return; + } + + JSTaggedValue patternValue = pattern.GetTaggedValue(); + JSTaggedValue flagsValue = flags.GetTaggedValue(); + JSTaggedValue inputValue = input.GetTaggedValue(); + JSTaggedValue lastIndexValue(lastIndex); + + uint32_t hash = pattern->GetKeyHashCode() + flags->GetInt() + input->GetKeyHashCode(); + uint32_t entry = hash & static_cast((cache->GetCacheLength() - 1)); + uint32_t index = CACHE_TABLE_HEADER_SIZE + entry * ENTRY_SIZE; + if (cache->Get(index) == JSTaggedValue::Undefined()) { + cache->SetCacheCount(thread, cache->GetCacheCount() + 1); + cache->SetEntry(thread, entry, patternValue, flagsValue, inputValue, lastIndexValue); + cache->UpdateResultArray(thread, entry, resultArray.GetTaggedValue(), type); + } else if (cache->Match(entry, patternValue, flagsValue, inputValue)) { + cache->UpdateResultArray(thread, entry, resultArray.GetTaggedValue(), type); + } else { + uint32_t entry2 = (entry + 1) & static_cast((cache->GetCacheLength() - 1)); + uint32_t index2 = CACHE_TABLE_HEADER_SIZE + entry2 * ENTRY_SIZE; + if (cache->GetCacheLength() < DEFAULT_CACHE_NUMBER) { + GrowRegexpCache(thread, cache); + // update value after gc. + patternValue = pattern.GetTaggedValue(); + flagsValue = flags.GetTaggedValue(); + inputValue = input.GetTaggedValue(); + + cache->SetCacheLength(thread, DEFAULT_CACHE_NUMBER); + entry2 = hash & static_cast((cache->GetCacheLength() - 1)); + index2 = CACHE_TABLE_HEADER_SIZE + entry2 * ENTRY_SIZE; + } + if (cache->Get(index2) == JSTaggedValue::Undefined()) { + cache->SetCacheCount(thread, cache->GetCacheCount() + 1); + cache->SetEntry(thread, entry2, patternValue, flagsValue, inputValue, lastIndexValue); + cache->UpdateResultArray(thread, entry2, resultArray.GetTaggedValue(), type); + } else if (cache->Match(entry2, patternValue, flagsValue, inputValue)) { + cache->UpdateResultArray(thread, entry2, resultArray.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); + cache->UpdateResultArray(thread, entry, resultArray.GetTaggedValue(), type); + } + } +} + +void RegExpExecResultCache::GrowRegexpCache(JSThread *thread, JSHandle cache) +{ + int length = CACHE_TABLE_HEADER_SIZE + DEFAULT_CACHE_NUMBER * ENTRY_SIZE; + auto factory = thread->GetEcmaVM()->GetFactory(); + auto newCache = factory->ExtendArray(JSHandle(cache), length, JSTaggedValue::Undefined()); + thread->GetEcmaVM()->SetRegExpCache(newCache.GetTaggedValue()); +} + +void RegExpExecResultCache::SetEntry(JSThread *thread, int entry, JSTaggedValue &pattern, JSTaggedValue &flags, + JSTaggedValue &input, JSTaggedValue &lastIndexValue) +{ + int index = CACHE_TABLE_HEADER_SIZE + entry * ENTRY_SIZE; + Set(thread, index + PATTERN_INDEX, pattern); + Set(thread, index + FLAG_INDEX, flags); + Set(thread, index + INPUT_STRING_INDEX, input); + Set(thread, index + LAST_INDEX_INDEX, lastIndexValue); +} + +void RegExpExecResultCache::UpdateResultArray(JSThread *thread, int entry, JSTaggedValue resultArray, CacheType type) +{ + int index = CACHE_TABLE_HEADER_SIZE + entry * ENTRY_SIZE; + switch (type) { + break; + case REPLACE_TYPE: + Set(thread, index + RESULT_REPLACE_INDEX, resultArray); + break; + case SPLIT_TYPE: + Set(thread, index + RESULT_SPLIT_INDEX, resultArray); + break; + case MATCH_TYPE: + Set(thread, index + RESULT_MATCH_INDEX, resultArray); + break; + case EXEC_TYPE: + Set(thread, index + RESULT_EXEC_INDEX, resultArray); + break; + default: + UNREACHABLE(); + break; + } +} + +void RegExpExecResultCache::ClearEntry(JSThread *thread, int entry) +{ + int index = CACHE_TABLE_HEADER_SIZE + entry * ENTRY_SIZE; + JSTaggedValue undefined = JSTaggedValue::Undefined(); + for (int i = 0; i < ENTRY_SIZE; i++) { + Set(thread, index + i, undefined); + } +} +bool RegExpExecResultCache::Match(int entry, JSTaggedValue &pattern, JSTaggedValue &flags, JSTaggedValue &input) +{ + int index = CACHE_TABLE_HEADER_SIZE + entry * ENTRY_SIZE; + JSTaggedValue keyPattern = Get(index + PATTERN_INDEX); + JSTaggedValue keyFlags = Get(index + FLAG_INDEX); + JSTaggedValue keyInput = Get(index + INPUT_STRING_INDEX); + + if (keyPattern == JSTaggedValue::Undefined()) { + return false; + } + + EcmaString *patternStr = EcmaString::Cast(pattern.GetTaggedObject()); + auto flagsBits = static_cast(flags.GetInt()); + EcmaString *inputStr = EcmaString::Cast(input.GetTaggedObject()); + EcmaString *keyPatternStr = EcmaString::Cast(keyPattern.GetTaggedObject()); + auto keyFlagsBits = static_cast(keyFlags.GetInt()); + EcmaString *keyInputStr = EcmaString::Cast(keyInput.GetTaggedObject()); + return EcmaString::StringsAreEqual(patternStr, keyPatternStr) && flagsBits == keyFlagsBits && + EcmaString::StringsAreEqual(inputStr, keyInputStr); +} +} // namespace panda::ecmascript::builtins diff --git a/runtime/builtins/builtins_regexp.h b/runtime/builtins/builtins_regexp.h new file mode 100644 index 000000000..b5980cf07 --- /dev/null +++ b/runtime/builtins/builtins_regexp.h @@ -0,0 +1,213 @@ +/* + * Copyright (c) 2021-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_BUILTINS_BUILTINS_REGEXP_H +#define ECMASCRIPT_BUILTINS_BUILTINS_REGEXP_H + +#include "plugins/ecmascript/runtime/base/builtins_base.h" +#include "plugins/ecmascript/runtime/builtins/builtins_string.h" +#include "plugins/ecmascript/runtime/ecma_runtime_call_info.h" +#include "plugins/ecmascript/runtime/js_tagged_value.h" +#include "plugins/ecmascript/runtime/regexp/regexp_executor.h" +#include "plugins/ecmascript/runtime/regexp/regexp_parser.h" + +namespace panda::ecmascript::builtins { +class BuiltinsRegExp : public ecmascript::base::BuiltinsBase { +public: + // 21.2.3.1 RegExp ( pattern, flags ) + static JSTaggedValue RegExpConstructor(EcmaRuntimeCallInfo *argv); + + // prototype + // 21.2.5.2 RegExp.prototype.exec ( string ) + static JSTaggedValue Exec(EcmaRuntimeCallInfo *argv); + // 21.2.5.13 RegExp.prototype.test( S ) + static JSTaggedValue Test(EcmaRuntimeCallInfo *argv); + // 21.2.5.14 RegExp.prototype.toString ( ) + static JSTaggedValue ToString(EcmaRuntimeCallInfo *argv); + // 21.2.5.3 get RegExp.prototype.flags + static JSTaggedValue GetFlags(EcmaRuntimeCallInfo *argv); + // 21.2.5.4 get RegExp.prototype.global + static JSTaggedValue GetGlobal(EcmaRuntimeCallInfo *argv); + // 21.2.5.5 get RegExp.prototype.ignoreCase + static JSTaggedValue GetIgnoreCase(EcmaRuntimeCallInfo *argv); + // 21.2.5.7 get RegExp.prototype.multiline + static JSTaggedValue GetMultiline(EcmaRuntimeCallInfo *argv); + static JSTaggedValue GetDotAll(EcmaRuntimeCallInfo *argv); + // 21.2.5.10 get RegExp.prototype.source + static JSTaggedValue GetSource(EcmaRuntimeCallInfo *argv); + // 21.2.5.12 get RegExp.prototype.sticky + static JSTaggedValue GetSticky(EcmaRuntimeCallInfo *argv); + // 21.2.5.15 get RegExp.prototype.unicode + static JSTaggedValue GetUnicode(EcmaRuntimeCallInfo *argv); + // 21.2.4.2 get RegExp [ @@species ] + static JSTaggedValue GetSpecies(EcmaRuntimeCallInfo *argv); + // 21.2.5.6 RegExp.prototype [ @@match ] ( string ) + static JSTaggedValue Match(EcmaRuntimeCallInfo *argv); + // 21.2.5.8 RegExp.prototype [ @@replace ] ( string, replaceValue ) + static JSTaggedValue Replace(EcmaRuntimeCallInfo *argv); + // 21.2.5.9 RegExp.prototype [ @@search ] ( string ) + static JSTaggedValue Search(EcmaRuntimeCallInfo *argv); + // 21.2.5.11 RegExp.prototype [ @@split ] ( string, limit ) + static JSTaggedValue Split(EcmaRuntimeCallInfo *argv); + // 21.2.3.2.3 Runtime Semantics: RegExpCreate ( P, F ) + static JSTaggedValue RegExpCreate(JSThread *thread, const JSHandle &pattern, + const JSHandle &flags); + static JSTaggedValue FlagsBitsToString(JSThread *thread, uint8_t flags); + +private: + static constexpr uint32_t MIN_REPLACE_STRING_LENGTH = 1000; + static constexpr uint32_t MAX_SPLIT_LIMIT = 0xFFFFFFFFU; + + static RegExpExecutor::MatchResult Matcher(JSThread *thread, const JSHandle ®exp, + const uint8_t *buffer, size_t length, int32_t lastindex, bool isUtf16); + // 21.2.5.2.3 AdvanceStringIndex ( S, index, unicode ) + static uint32_t AdvanceStringIndex(JSThread *thread, const JSHandle &inputStr, uint32_t index, + bool unicode); + + static bool GetFlagsInternal(JSThread *thread, const JSHandle &obj, uint8_t mask); + // 21.2.5.2.2 Runtime Semantics: RegExpBuiltinExec ( R, S ) + static JSTaggedValue RegExpBuiltinExec(JSThread *thread, const JSHandle ®exp, + const JSHandle &inputStr, bool isCached); + // 21.2.5.2.1 Runtime Semantics: RegExpExec ( R, S ) + static JSTaggedValue RegExpExec(JSThread *thread, const JSHandle ®exp, + const JSHandle &inputString, bool isCached); + // 21.2.3.2.1 Runtime Semantics: RegExpAlloc ( newTarget ) + static JSTaggedValue RegExpAlloc(JSThread *thread, const JSHandle &newTarget); + + static uint32_t UpdateExpressionFlags(JSThread *thread, const CString &checkStr); + + // 21.2.3.2.2 Runtime Semantics: RegExpInitialize ( obj, pattern, flags ) + static JSTaggedValue RegExpInitialize(JSThread *thread, const JSHandle &obj, + const JSHandle &pattern, const JSHandle &flags); + // 21.2.3.2.4 Runtime Semantics: EscapeRegExpPattern ( P, F ) + static EcmaString *EscapeRegExpPattern(JSThread *thread, const JSHandle &src, + const JSHandle &flags); + static JSTaggedValue RegExpReplaceFast(JSThread *thread, JSHandle ®exp, + JSHandle inputString, uint32_t inputLength); +}; + +class RegExpExecResultCache : public TaggedArray { +public: + enum CacheType { REPLACE_TYPE, SPLIT_TYPE, MATCH_TYPE, EXEC_TYPE }; + static RegExpExecResultCache *Cast(TaggedObject *object) + { + return reinterpret_cast(object); + } + static JSTaggedValue CreateCacheTable(JSThread *thread); + JSTaggedValue FindCachedResult(JSThread *thread, const JSHandle &pattern, + const JSHandle &flags, const JSHandle &input, + CacheType type, const JSHandle ®exp); + static void AddResultInCache(JSThread *thread, JSHandle cache, + const JSHandle &pattern, const JSHandle &flags, + const JSHandle &input, const JSHandle &resultArray, + CacheType type, uint32_t lastIndex); + + static void GrowRegexpCache(JSThread *thread, JSHandle cache); + + void ClearEntry(JSThread *thread, int entry); + void SetEntry(JSThread *thread, int entry, JSTaggedValue &pattern, JSTaggedValue &flags, JSTaggedValue &input, + JSTaggedValue &lastIndexValue); + void UpdateResultArray(JSThread *thread, int entry, JSTaggedValue resultArray, CacheType type); + bool Match(int entry, JSTaggedValue &pattern, JSTaggedValue &flagsStr, JSTaggedValue &inputStr); + inline void SetHitCount(JSThread *thread, int hitCount) + { + Set(thread, CACHE_HIT_COUNT_INDEX, JSTaggedValue(hitCount)); + } + + inline int GetHitCount() + { + return Get(CACHE_HIT_COUNT_INDEX).GetInt(); + } + + inline void SetCacheCount(JSThread *thread, int hitCount) + { + Set(thread, CACHE_COUNT_INDEX, JSTaggedValue(hitCount)); + } + + inline int GetCacheCount() + { + return Get(CACHE_COUNT_INDEX).GetInt(); + } + + void Print() + { + std::cout << "cache count: " << GetCacheCount() << std::endl; + std::cout << "cache hit count: " << GetHitCount() << std::endl; + } + + inline void SetLargeStrCount(JSThread *thread, uint32_t newCount) + { + Set(thread, LARGE_STRING_COUNT_INDEX, JSTaggedValue(newCount)); + } + + inline void SetConflictCount(JSThread *thread, uint32_t newCount) + { + Set(thread, CONFLICT_COUNT_INDEX, JSTaggedValue(newCount)); + } + + inline void SetStrLenThreshold(JSThread *thread, uint32_t newThreshold) + { + Set(thread, STRING_LENGTH_THRESHOLD_INDEX, JSTaggedValue(newThreshold)); + } + + inline uint32_t GetLargeStrCount() + { + return Get(LARGE_STRING_COUNT_INDEX).GetInt(); + } + + inline uint32_t GetConflictCount() + { + return Get(CONFLICT_COUNT_INDEX).GetInt(); + } + + inline uint32_t GetStrLenThreshold() + { + return Get(STRING_LENGTH_THRESHOLD_INDEX).GetInt(); + } + + inline void SetCacheLength(JSThread *thread, int length) + { + Set(thread, CACHE_LENGTH_INDEX, JSTaggedValue(length)); + } + + inline int GetCacheLength() + { + return Get(CACHE_LENGTH_INDEX).GetInt(); + } + +private: + static constexpr int DEFAULT_LARGE_STRING_COUNT = 10; + static constexpr int DEFAULT_CONFLICT_COUNT = 100; + static constexpr int INITIAL_CACHE_NUMBER = 0x10; + static constexpr int DEFAULT_CACHE_NUMBER = 0x1000; + static constexpr int CACHE_COUNT_INDEX = 0; + static constexpr int CACHE_HIT_COUNT_INDEX = 1; + static constexpr int LARGE_STRING_COUNT_INDEX = 2; + static constexpr int CONFLICT_COUNT_INDEX = 3; + static constexpr int STRING_LENGTH_THRESHOLD_INDEX = 4; + static constexpr int CACHE_LENGTH_INDEX = 5; + static constexpr int CACHE_TABLE_HEADER_SIZE = 6; + 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 ENTRY_SIZE = 8; +}; +} // namespace panda::ecmascript::builtins +#endif // ECMASCRIPT_BUILTINS_BUILTINS_REGEXP_H diff --git a/runtime/builtins/builtins_relative_time_format.cpp b/runtime/builtins/builtins_relative_time_format.cpp new file mode 100644 index 000000000..99e7fd216 --- /dev/null +++ b/runtime/builtins/builtins_relative_time_format.cpp @@ -0,0 +1,174 @@ +/* + * Copyright (c) 2021-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 "builtins_relative_time_format.h" + +namespace panda::ecmascript::builtins { +JSTaggedValue BuiltinsRelativeTimeFormat::RelativeTimeFormatConstructor(EcmaRuntimeCallInfo *argv) +{ + JSThread *thread = argv->GetThread(); + [[maybe_unused]] EcmaHandleScope scope(thread); + EcmaVM *ecmaVm = thread->GetEcmaVM(); + JSHandle env = ecmaVm->GetGlobalEnv(); + ObjectFactory *factory = ecmaVm->GetFactory(); + + // 1. If NewTarget is undefined, throw a TypeError exception. + JSHandle newTarget = GetNewTarget(argv); + if (newTarget->IsUndefined()) { + THROW_TYPE_ERROR_AND_RETURN(thread, "newTarget is undefined", JSTaggedValue::Exception()); + } + + // 2. Let relativeTimeFormat be ? OrdinaryCreateFromConstructor + // (NewTarget, "%RelativeTimeFormatPrototype%", « [[InitializedRelativeTimeFormat]], + // [[Locale]], [[DataLocale]], [[Style]], [[Numeric]], [[NumberFormat]], [[NumberingSystem]], [[PluralRules]] »). + JSHandle constructor = GetConstructor(argv); + JSHandle relativeTimeFormat = JSHandle::Cast( + factory->NewJSObjectByConstructor(JSHandle(constructor), newTarget)); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + + // 3. Perform ? InitializeRelativeTimeFormat(relativeTimeFormat, locales, options). + JSHandle locales = GetCallArg(argv, 0); + JSHandle options = GetCallArg(argv, 1); + JSRelativeTimeFormat::InitializeRelativeTimeFormat(thread, relativeTimeFormat, locales, options); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + + // 4. Intl.RelativeTimeFormat.prototype[ @@toStringTag ] + // This property has the attributes { [[Writable]]: false, [[Enumerable]]: false, [[Configurable]]: true }. + JSHandle thisValue = GetThis(argv); + bool isInstanceOf = JSObject::InstanceOf(thread, thisValue, env->GetRelativeTimeFormatFunction()); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + if (newTarget->IsUndefined() && thisValue->IsJSObject() && isInstanceOf) { + PropertyDescriptor descriptor(thread, JSHandle::Cast(relativeTimeFormat), false, false, true); + JSHandle key(thread, JSHandle::Cast(env->GetIntlFunction())->GetFallbackSymbol()); + JSTaggedValue::DefinePropertyOrThrow(thread, thisValue, key, descriptor); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + return thisValue.GetTaggedValue(); + } + + return relativeTimeFormat.GetTaggedValue(); +} + +JSTaggedValue BuiltinsRelativeTimeFormat::SupportedLocalesOf(EcmaRuntimeCallInfo *argv) +{ + JSThread *thread = argv->GetThread(); + [[maybe_unused]] EcmaHandleScope scope(thread); + + // 1. Let availableLocales be %RelativeTimeFormat%.[[AvailableLocales]]. + JSHandle availableLocales = JSLocale::GetAvailableLocales(thread, "calendar", nullptr); + + // 2. Let requestedLocales be ? CanonicalizeLocaleList(locales). + JSHandle locales = GetCallArg(argv, 0); + JSHandle requestedLocales = JSLocale::CanonicalizeLocaleList(thread, locales); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + + // 3. Return ? SupportedLocales(availableLocales, requestedLocales, options). + JSHandle options = GetCallArg(argv, 1); + JSHandle result = JSLocale::SupportedLocales(thread, availableLocales, requestedLocales, options); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + return result.GetTaggedValue(); +} + +JSTaggedValue BuiltinsRelativeTimeFormat::Format(EcmaRuntimeCallInfo *argv) +{ + JSThread *thread = argv->GetThread(); + [[maybe_unused]] EcmaHandleScope scope(thread); + + // 1. Let relativeTimeFormat be the this value. + JSHandle thisValue = GetThis(argv); + + // 2. Perform ? RequireInternalSlot(relativeTimeFormat, [[InitializedRelativeTimeFormat]]). + if (!thisValue->IsJSRelativeTimeFormat()) { + THROW_TYPE_ERROR_AND_RETURN(thread, "this is not rtf object", JSTaggedValue::Exception()); + } + + // 3. Let value be ? ToNumber(value). + double x = 0.0; + JSHandle value = GetCallArg(argv, 0); + JSTaggedNumber temp = JSTaggedValue::ToNumber(thread, value); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + x = temp.GetNumber(); + + // 4. Let unit be ? ToString(unit). + JSHandle unitValue = GetCallArg(argv, 1); + JSHandle unit = JSTaggedValue::ToString(thread, unitValue); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + + // 5. Return ? FormatRelativeTime(relativeTimeFormat, value, unit). + JSHandle relativeTimeFormat = JSHandle::Cast(thisValue); + JSHandle result = JSRelativeTimeFormat::Format(thread, x, unit, relativeTimeFormat); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + return result.GetTaggedValue(); +} + +JSTaggedValue BuiltinsRelativeTimeFormat::FormatToParts(EcmaRuntimeCallInfo *argv) +{ + JSThread *thread = argv->GetThread(); + [[maybe_unused]] EcmaHandleScope scope(thread); + + // 1. Let relativeTimeFormat be the this value. + JSHandle thisValue = GetThis(argv); + + // 2. Perform ? RequireInternalSlot(relativeTimeFormat, [[InitializedRelativeTimeFormat]]). + if (!thisValue->IsJSRelativeTimeFormat()) { + THROW_TYPE_ERROR_AND_RETURN(thread, "this is not rtf object", JSTaggedValue::Exception()); + } + + // 3. Let value be ? ToNumber(value). + double x = 0.0; + JSHandle value = GetCallArg(argv, 0); + JSTaggedNumber temp = JSTaggedValue::ToNumber(thread, value); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + x = temp.GetNumber(); + + // 4. Let unit be ? ToString(unit). + JSHandle unitValue = GetCallArg(argv, 1); + JSHandle unit = JSTaggedValue::ToString(thread, unitValue); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + + // 5. Return ? FormatRelativeTime(relativeTimeFormat, value, unit). + JSHandle relativeTimeFormat = JSHandle::Cast(thisValue); + JSHandle result = JSRelativeTimeFormat::FormatToParts(thread, x, unit, relativeTimeFormat); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + return result.GetTaggedValue(); +} + +JSTaggedValue BuiltinsRelativeTimeFormat::ResolvedOptions(EcmaRuntimeCallInfo *argv) +{ + JSThread *thread = argv->GetThread(); + [[maybe_unused]] EcmaHandleScope scope(thread); + + // 1. Let relativeTimeFormat be the this value. + JSHandle thisValue = GetThis(argv); + + // 2. Perform ? RequireInternalSlot(relativeTimeFormat, [[InitializedRelativeTimeFormat]]). + if (!thisValue->IsJSRelativeTimeFormat()) { + THROW_TYPE_ERROR_AND_RETURN(thread, "this is not rtf object", JSTaggedValue::Exception()); + } + + // 3. Let options be ! ObjectCreate(%ObjectPrototype%). + auto ecmaVm = thread->GetEcmaVM(); + JSHandle env = ecmaVm->GetGlobalEnv(); + ObjectFactory *factory = ecmaVm->GetFactory(); + JSHandle ctor = env->GetObjectFunction(); + JSHandle options(factory->NewJSObjectByConstructor(JSHandle(ctor), ctor)); + + // 4. perform resolvedOptions + JSHandle relativeTimeFormat = JSHandle::Cast(thisValue); + JSRelativeTimeFormat::ResolvedOptions(thread, relativeTimeFormat, options); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + // 5. Return options. + return options.GetTaggedValue(); +} +} // namespace panda::ecmascript::builtins \ No newline at end of file diff --git a/runtime/builtins/builtins_relative_time_format.h b/runtime/builtins/builtins_relative_time_format.h new file mode 100644 index 000000000..da57c03a2 --- /dev/null +++ b/runtime/builtins/builtins_relative_time_format.h @@ -0,0 +1,52 @@ +/* + * Copyright (c) 2021-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_BUILTINS_BUILTINS_RELATIVE_TIME_FORMAT_H +#define ECMASCRIPT_BUILTINS_BUILTINS_RELATIVE_TIME_FORMAT_H + +#include "plugins/ecmascript/runtime/base/builtins_base.h" +#include "plugins/ecmascript/runtime/ecma_runtime_call_info.h" +#include "plugins/ecmascript/runtime/ecma_vm.h" +#include "plugins/ecmascript/runtime/global_env.h" +#include "plugins/ecmascript/runtime/js_array.h" +#include "plugins/ecmascript/runtime/js_date.h" +#include "plugins/ecmascript/runtime/js_hclass.h" +#include "plugins/ecmascript/runtime/js_intl.h" +#include "plugins/ecmascript/runtime/js_locale.h" +#include "plugins/ecmascript/runtime/js_object.h" +#include "plugins/ecmascript/runtime/js_relative_time_format.h" +#include "plugins/ecmascript/runtime/object_factory.h" + +namespace panda::ecmascript::builtins { +class BuiltinsRelativeTimeFormat : public ecmascript::base::BuiltinsBase { +public: + // 14.2.1 Intl.RelativeTimeFormat ([ locales [ , options ]]) + static JSTaggedValue RelativeTimeFormatConstructor(EcmaRuntimeCallInfo *argv); + + // 14.3.1 Intl.RelativeTimeFormat.supportedLocalesOf ( locales [ , options ] ) + static JSTaggedValue SupportedLocalesOf(EcmaRuntimeCallInfo *argv); + + // 14.4.3 Intl.RelativeTimeFormat.prototype.format( value, unit ) + static JSTaggedValue Format(EcmaRuntimeCallInfo *argv); + + // 14.4.4 Intl.RelativeTimeFormat.prototype.formatToParts( value, unit ) + static JSTaggedValue FormatToParts(EcmaRuntimeCallInfo *argv); + + // 14.4.5 Intl.RelativeTimeFormat.prototype.resolvedOptions () + static JSTaggedValue ResolvedOptions(EcmaRuntimeCallInfo *argv); +}; +} // namespace panda::ecmascript::builtins + +#endif // ECMASCRIPT_BUILTINS_BUILTINS_RELATIVE_TIME_FORMAT_H diff --git a/runtime/builtins/builtins_set.cpp b/runtime/builtins/builtins_set.cpp new file mode 100644 index 000000000..fc3f90bdc --- /dev/null +++ b/runtime/builtins/builtins_set.cpp @@ -0,0 +1,262 @@ +/* + * Copyright (c) 2021-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 "builtins_set.h" +#include "plugins/ecmascript/runtime/ecma_vm.h" +#include "plugins/ecmascript/runtime/global_env.h" +#include "plugins/ecmascript/runtime/internal_call_params.h" +#include "plugins/ecmascript/runtime/js_invoker.h" +#include "plugins/ecmascript/runtime/js_set.h" +#include "plugins/ecmascript/runtime/js_set_iterator.h" +#include "plugins/ecmascript/runtime/linked_hash_table-inl.h" +#include "plugins/ecmascript/runtime/object_factory.h" +#include "plugins/ecmascript/runtime/tagged_array-inl.h" + +namespace panda::ecmascript::builtins { +JSTaggedValue BuiltinsSet::SetConstructor(EcmaRuntimeCallInfo *argv) +{ + ASSERT(argv); + BUILTINS_API_TRACE(argv->GetThread(), Set, Constructor); + JSThread *thread = argv->GetThread(); + [[maybe_unused]] EcmaHandleScope handleScope(thread); + ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); + // 1.If NewTarget is undefined, throw a TypeError exception + JSHandle newTarget = GetNewTarget(argv); + if (newTarget->IsUndefined()) { + // throw type error + THROW_TYPE_ERROR_AND_RETURN(thread, "new target can't be undefined", JSTaggedValue::Exception()); + } + // 2.Let set be OrdinaryCreateFromConstructor(NewTarget, "%SetPrototype%", «‍[[SetData]]» ). + JSHandle constructor = GetConstructor(argv); + JSHandle obj = factory->NewJSObjectByConstructor(JSHandle(constructor), newTarget); + // 3.returnIfAbrupt() + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + JSHandle set = JSHandle::Cast(obj); + // 3.ReturnIfAbrupt(set). + // 4.Set set’s [[SetData]] internal slot to a new empty List. + JSHandle linkedSet = LinkedHashSet::Create(thread); + set->SetLinkedSet(thread, linkedSet); + + // add data into set from iterable + // 5.If iterable is not present, let iterable be undefined. + // 6.If iterable is either undefined or null, let iter be undefined. + JSHandle iterable(GetCallArg(argv, 0)); + // 8.If iter is undefined, return set + if (iterable->IsUndefined() || iterable->IsNull()) { + return set.GetTaggedValue(); + } + // Let adder be Get(set, "add"). + JSHandle adderKey(factory->NewFromCanBeCompressString("add")); + JSHandle setHandle(set); + JSHandle adder = JSObject::GetProperty(thread, setHandle, adderKey).GetValue(); + // ReturnIfAbrupt(adder). + RETURN_VALUE_IF_ABRUPT_COMPLETION(thread, adder.GetTaggedValue()); + // If IsCallable(adder) is false, throw a TypeError exception + if (!adder->IsCallable()) { + THROW_TYPE_ERROR_AND_RETURN(thread, "adder is not callable", adder.GetTaggedValue()); + } + // Let iter be GetIterator(iterable). + JSHandle iter(JSIterator::GetIterator(thread, iterable)); + // ReturnIfAbrupt(iter). + RETURN_VALUE_IF_ABRUPT_COMPLETION(thread, iter.GetTaggedValue()); + // values in iterator_result may be a JSArray, values[0] = key values[1]=value, used valueIndex to get value from + // jsarray + JSHandle valueIndex(thread, JSTaggedValue(1)); + JSHandle next = JSIterator::IteratorStep(thread, iter); + InternalCallParams *arguments = thread->GetInternalCallParams(); + while (!next->IsFalse()) { + // ReturnIfAbrupt(next). + RETURN_VALUE_IF_ABRUPT_COMPLETION(thread, next.GetTaggedValue()); + // Let nextValue be IteratorValue(next). + JSHandle nextValue(JSIterator::IteratorValue(thread, next)); + // ReturnIfAbrupt(nextValue). + RETURN_VALUE_IF_ABRUPT_COMPLETION(thread, nextValue.GetTaggedValue()); + arguments->MakeArgv(nextValue); + if (nextValue->IsArray(thread)) { + auto prop = JSObject::GetProperty(thread, nextValue, valueIndex).GetValue(); + arguments->MakeArgv(prop); + } + JSTaggedValue ret = JSFunction::Call(thread, adder, JSHandle(set), 1, arguments->GetArgv()); + // Let status be Call(adder, set, «nextValue.[[value]]»). + JSHandle status(thread, ret); + + if (thread->HasPendingException()) { + return JSIterator::IteratorCloseAndReturn(thread, iter, status); + } + // Let next be IteratorStep(iter). + next = JSIterator::IteratorStep(thread, iter); + } + return set.GetTaggedValue(); +} + +JSTaggedValue BuiltinsSet::Add(EcmaRuntimeCallInfo *argv) +{ + ASSERT(argv); + BUILTINS_API_TRACE(argv->GetThread(), Set, Add); + JSThread *thread = argv->GetThread(); + [[maybe_unused]] EcmaHandleScope handleScope(thread); + JSHandle self = GetThis(argv); + + // 2.If Type(S) is not Object, throw a TypeError exception. + // 3.If S does not have a [[SetData]] internal slot, throw a TypeError exception. + if (!self->IsJSSet()) { + THROW_TYPE_ERROR_AND_RETURN(thread, "obj is not JSSet", JSTaggedValue::Exception()); + } + + JSHandle value(GetCallArg(argv, 0)); + JSHandle set(JSTaggedValue::ToObject(thread, self)); + + JSSet::Add(thread, set, value); + return set.GetTaggedValue(); +} + +JSTaggedValue BuiltinsSet::Clear(EcmaRuntimeCallInfo *argv) +{ + ASSERT(argv); + BUILTINS_API_TRACE(argv->GetThread(), Set, Clear); + JSThread *thread = argv->GetThread(); + [[maybe_unused]] EcmaHandleScope handleScope(thread); + JSHandle self = GetThis(argv); + + // 2.If Type(S) is not Object, throw a TypeError exception. + // 3.If S does not have a [[SetData]] internal slot, throw a TypeError exception. + if (!self->IsJSSet()) { + THROW_TYPE_ERROR_AND_RETURN(thread, "obj is not JSSet", JSTaggedValue::Exception()); + } + JSHandle set(thread, JSSet::Cast(*JSTaggedValue::ToObject(thread, self))); + JSSet::Clear(thread, set); + return JSTaggedValue::Undefined(); +} + +JSTaggedValue BuiltinsSet::Delete(EcmaRuntimeCallInfo *argv) +{ + ASSERT(argv); + BUILTINS_API_TRACE(argv->GetThread(), Set, Delete); + JSThread *thread = argv->GetThread(); + [[maybe_unused]] EcmaHandleScope handleScope(thread); + JSHandle self = GetThis(argv); + // 2.If Type(S) is not Object, throw a TypeError exception. + // 3.If S does not have a [[SetData]] internal slot, throw a TypeError exception. + if (!self->IsJSSet()) { + THROW_TYPE_ERROR_AND_RETURN(thread, "obj is not JSSet", JSTaggedValue::Exception()); + } + + JSHandle set(thread, JSSet::Cast(*JSTaggedValue::ToObject(thread, self))); + JSHandle value = GetCallArg(argv, 0); + bool flag = JSSet::Delete(thread, set, value); + return GetTaggedBoolean(flag); +} + +JSTaggedValue BuiltinsSet::Has(EcmaRuntimeCallInfo *argv) +{ + ASSERT(argv); + BUILTINS_API_TRACE(argv->GetThread(), Set, Has); + JSThread *thread = argv->GetThread(); + [[maybe_unused]] EcmaHandleScope handleScope(thread); + JSHandle self = GetThis(argv); + // 2.If Type(S) is not Object, throw a TypeError exception. + // 3.If S does not have a [[SetData]] internal slot, throw a TypeError exception. + if (!self->IsJSSet()) { + THROW_TYPE_ERROR_AND_RETURN(thread, "obj is not JSSet", JSTaggedValue::Exception()); + } + JSSet *jsSet = JSSet::Cast(*JSTaggedValue::ToObject(thread, self)); + JSHandle value = GetCallArg(argv, 0); + bool flag = jsSet->Has(value.GetTaggedValue()); + return GetTaggedBoolean(flag); +} + +JSTaggedValue BuiltinsSet::ForEach([[maybe_unused]] EcmaRuntimeCallInfo *argv) +{ + JSThread *thread = argv->GetThread(); + [[maybe_unused]] EcmaHandleScope handleScope(thread); + ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); + JSHandle self = GetThis(argv); + // 2.If Type(S) is not Object, throw a TypeError exception. + // 3.If S does not have a [[SetData]] internal slot, throw a TypeError exception. + if (!self->IsJSSet()) { + THROW_TYPE_ERROR_AND_RETURN(thread, "obj is not JSSet", JSTaggedValue::Exception()); + } + JSHandle set(thread, JSSet::Cast(*JSTaggedValue::ToObject(thread, self))); + + // 4.If IsCallable(callbackfn) is false, throw a TypeError exception. + JSHandle func(GetCallArg(argv, 0)); + if (!func->IsCallable()) { + THROW_TYPE_ERROR_AND_RETURN(thread, "callbackfn is not callable", JSTaggedValue::Exception()); + } + + // 5.If thisArg was supplied, let T be thisArg; else let T be undefined. + JSHandle thisArg = GetCallArg(argv, 1); + + // composed arguments + JSHandle iter(factory->NewJSSetIterator(set, IterationKind::KEY)); + JSHandle result = JSIterator::IteratorStep(thread, iter); + InternalCallParams *arguments = thread->GetInternalCallParams(); + while (!result->IsFalse()) { + RETURN_VALUE_IF_ABRUPT_COMPLETION(thread, result.GetTaggedValue()); + JSHandle value = JSIterator::IteratorValue(thread, result); + // Let funcResult be Call(callbackfn, T, «e, e, S»). + arguments->MakeArgv(value, value, JSHandle(set)); + JSTaggedValue ret = JSFunction::Call(thread, func, thisArg, 3, arguments->GetArgv()); // 3: three args + // returnIfAbrupt + RETURN_VALUE_IF_ABRUPT_COMPLETION(thread, ret); + result = JSIterator::IteratorStep(thread, iter); + } + return JSTaggedValue::Undefined(); +} + +JSTaggedValue BuiltinsSet::Species([[maybe_unused]] EcmaRuntimeCallInfo *argv) +{ + return GetThis(argv).GetTaggedValue(); +} + +JSTaggedValue BuiltinsSet::GetSize(EcmaRuntimeCallInfo *argv) +{ + ASSERT(argv); + BUILTINS_API_TRACE(argv->GetThread(), Set, GetSize); + JSThread *thread = argv->GetThread(); + [[maybe_unused]] EcmaHandleScope handleScope(thread); + JSHandle self(GetThis(argv)); + // 2.If Type(S) is not Object, throw a TypeError exception. + // 3.If S does not have a [[SetData]] internal slot, throw a TypeError exception. + if (!self->IsJSSet()) { + THROW_TYPE_ERROR_AND_RETURN(thread, "obj is not JSSet", JSTaggedValue::Exception()); + } + JSSet *jsSet = JSSet::Cast(*JSTaggedValue::ToObject(thread, self)); + int count = jsSet->GetSize(); + return JSTaggedValue(count); +} + +JSTaggedValue BuiltinsSet::Entries(EcmaRuntimeCallInfo *argv) +{ + ASSERT(argv); + BUILTINS_API_TRACE(argv->GetThread(), Set, Entries); + JSThread *thread = argv->GetThread(); + [[maybe_unused]] EcmaHandleScope handleScope(thread); + JSHandle self = GetThis(argv); + JSHandle iter = JSSetIterator::CreateSetIterator(thread, self, IterationKind::KEY_AND_VALUE); + return iter.GetTaggedValue(); +} + +JSTaggedValue BuiltinsSet::Values(EcmaRuntimeCallInfo *argv) +{ + ASSERT(argv); + BUILTINS_API_TRACE(argv->GetThread(), Set, Values); + JSThread *thread = argv->GetThread(); + [[maybe_unused]] EcmaHandleScope handleScope(thread); + JSHandle self = GetThis(argv); + JSHandle iter = JSSetIterator::CreateSetIterator(thread, self, IterationKind::VALUE); + return iter.GetTaggedValue(); +} +} // namespace panda::ecmascript::builtins diff --git a/runtime/builtins/builtins_set.h b/runtime/builtins/builtins_set.h new file mode 100644 index 000000000..51d893c77 --- /dev/null +++ b/runtime/builtins/builtins_set.h @@ -0,0 +1,47 @@ +/* + * Copyright (c) 2021-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_BUILTINS_BUILTINS_SET_H +#define ECMASCRIPT_BUILTINS_BUILTINS_SET_H + +#include "plugins/ecmascript/runtime/base/builtins_base.h" +#include "plugins/ecmascript/runtime/ecma_runtime_call_info.h" + +namespace panda::ecmascript::builtins { +class BuiltinsSet : public ecmascript::base::BuiltinsBase { +public: + // 23.2.1.1 + static JSTaggedValue SetConstructor(EcmaRuntimeCallInfo *argv); + // 23.2.2.2 + static JSTaggedValue Species(EcmaRuntimeCallInfo *argv); + // 23.2.3.1 + static JSTaggedValue Add(EcmaRuntimeCallInfo *argv); + // 23.2.3.2 + static JSTaggedValue Clear(EcmaRuntimeCallInfo *argv); + // 23.2.3.4 + static JSTaggedValue Delete(EcmaRuntimeCallInfo *argv); + // 23.2.3.5 + static JSTaggedValue Entries(EcmaRuntimeCallInfo *argv); + // 23.2.3.6 + static JSTaggedValue ForEach(EcmaRuntimeCallInfo *argv); + // 23.2.3.7 + static JSTaggedValue Has(EcmaRuntimeCallInfo *argv); + // 23.2.3.9 + static JSTaggedValue GetSize(EcmaRuntimeCallInfo *argv); + // 23.2.3.10 + static JSTaggedValue Values(EcmaRuntimeCallInfo *argv); +}; +} // namespace panda::ecmascript::builtins +#endif // ECMASCRIPT_BUILTINS_BUILTINS_SET_H diff --git a/runtime/builtins/builtins_string.cpp b/runtime/builtins/builtins_string.cpp new file mode 100644 index 000000000..639841097 --- /dev/null +++ b/runtime/builtins/builtins_string.cpp @@ -0,0 +1,1883 @@ +/* + * Copyright (c) 2021-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 "plugins/ecmascript/runtime/builtins/builtins_string.h" + +#include + +#include "plugins/ecmascript/runtime/base/number_helper.h" +#include "plugins/ecmascript/runtime/base/string_helper.h" +#include "plugins/ecmascript/runtime/builtins/builtins_json.h" +#include "plugins/ecmascript/runtime/builtins/builtins_regexp.h" +#include "plugins/ecmascript/runtime/builtins/builtins_symbol.h" +#include "plugins/ecmascript/runtime/ecma_runtime_call_info.h" +#include "plugins/ecmascript/runtime/ecma_string-inl.h" +#include "plugins/ecmascript/runtime/ecma_vm.h" +#include "plugins/ecmascript/runtime/global_env.h" +#include "plugins/ecmascript/runtime/internal_call_params.h" +#include "plugins/ecmascript/runtime/interpreter/fast_runtime_stub-inl.h" +#include "plugins/ecmascript/runtime/js_array.h" +#include "plugins/ecmascript/runtime/js_hclass.h" +#include "plugins/ecmascript/runtime/js_invoker.h" +#include "plugins/ecmascript/runtime/js_locale.h" +#include "plugins/ecmascript/runtime/js_object-inl.h" +#include "plugins/ecmascript/runtime/js_primitive_ref.h" +#include "plugins/ecmascript/runtime/js_string_iterator.h" +#include "plugins/ecmascript/runtime/js_tagged_value-inl.h" +#include "plugins/ecmascript/runtime/mem/c_containers.h" +#include "plugins/ecmascript/runtime/object_factory.h" +#include "plugins/ecmascript/runtime/tagged_array-inl.h" +#include "plugins/ecmascript/runtime/tagged_array.h" +#include "unicode/normalizer2.h" +#include "unicode/normlzr.h" +#include "unicode/unistr.h" + +namespace panda::ecmascript::builtins { +using ObjectFactory = ecmascript::ObjectFactory; +using JSArray = ecmascript::JSArray; + +// 21.1.1.1 String(value) +JSTaggedValue BuiltinsString::StringConstructor(EcmaRuntimeCallInfo *argv) +{ + ASSERT(argv); + BUILTINS_API_TRACE(argv->GetThread(), String, Constructor); + JSThread *thread = argv->GetThread(); + [[maybe_unused]] EcmaHandleScope handleScope(thread); + ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); + JSHandle newTarget = GetNewTarget(argv); + if (argv->GetArgsNumber() > 0) { + JSHandle valTagNew = GetCallArg(argv, 0); + if (newTarget->IsUndefined() && valTagNew->IsSymbol()) { + return BuiltinsSymbol::SymbolDescriptiveString(thread, valTagNew.GetTaggedValue()); + } + JSHandle str = JSTaggedValue::ToString(thread, valTagNew); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + if (newTarget->IsUndefined()) { + return str.GetTaggedValue(); + } + JSHandle strTag(str); + return JSPrimitiveRef::StringCreate(thread, strTag).GetTaggedValue(); + } + JSHandle val = factory->GetEmptyString(); + JSHandle valTag(val); + if (newTarget->IsUndefined()) { + return factory->GetEmptyString().GetTaggedValue(); + } + return JSPrimitiveRef::StringCreate(thread, valTag).GetTaggedValue(); +} + +// 21.1.2.1 +JSTaggedValue BuiltinsString::FromCharCode(EcmaRuntimeCallInfo *argv) +{ + ASSERT(argv); + BUILTINS_API_TRACE(argv->GetThread(), String, FromCharCode); + JSThread *thread = argv->GetThread(); + [[maybe_unused]] EcmaHandleScope handleScope(thread); + ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); + int32_t argLength = argv->GetArgsNumber(); + if (argLength == 0) { + return factory->GetEmptyString().GetTaggedValue(); + } + JSHandle codePointTag = BuiltinsString::GetCallArg(argv, 0); + uint16_t codePointValue = JSTaggedValue::ToUint16(thread, codePointTag); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + JSHandle strHandle = factory->NewFromUtf16Literal(&codePointValue, 1); + if (argLength == 1) { + return strHandle.GetTaggedValue(); + } + std::u16string u16str = ecmascript::base::StringHelper::Utf16ToU16String(&codePointValue, 1); + CVector valueTable; + valueTable.reserve(argLength - 1); + for (int32_t i = 1; i < argLength; i++) { + JSHandle nextCp = BuiltinsString::GetCallArg(argv, i); + uint16_t nextCv = JSTaggedValue::ToUint16(thread, nextCp); + valueTable.emplace_back(nextCv); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + } + std::u16string nextU16str = base::StringHelper::Utf16ToU16String(valueTable.data(), argLength - 1); + u16str = base::StringHelper::Append(u16str, nextU16str); + const char16_t *constChar16tData = u16str.data(); + auto *char16tData = const_cast(constChar16tData); + auto *uint16tData = reinterpret_cast(char16tData); + int32_t u16strSize = u16str.size(); + return factory->NewFromUtf16Literal(uint16tData, u16strSize).GetTaggedValue(); +} + +// 21.1.2.2 +JSTaggedValue BuiltinsString::FromCodePoint(EcmaRuntimeCallInfo *argv) +{ + ASSERT(argv); + BUILTINS_API_TRACE(argv->GetThread(), String, FromCodePoint); + JSThread *thread = argv->GetThread(); + [[maybe_unused]] EcmaHandleScope handleScope(thread); + ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); + int32_t argLength = argv->GetArgsNumber(); + if (argLength == 0) { + return factory->GetEmptyString().GetTaggedValue(); + } + std::u16string u16str; + int32_t u16strSize = argLength; + for (int i = 0; i < argLength; i++) { + JSHandle nextCpTag = BuiltinsString::GetCallArg(argv, i); + JSTaggedNumber nextCpVal = JSTaggedValue::ToNumber(thread, nextCpTag); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + if (!nextCpVal.IsInteger()) { + THROW_RANGE_ERROR_AND_RETURN(thread, "is not integer", JSTaggedValue::Exception()); + } + int32_t cp = nextCpVal.ToInt32(); + if (cp < 0 || cp > ENCODE_MAX_UTF16) { + THROW_RANGE_ERROR_AND_RETURN(thread, "CodePoint < 0 or CodePoint > 0x10FFFF", JSTaggedValue::Exception()); + } + if (cp == 0) { + CVector data {0x00}; + return factory->NewFromUtf16Literal(data.data(), 1).GetTaggedValue(); + } + if (cp > UINT16_MAX) { + uint16_t cu1 = std::floor((cp - ENCODE_SECOND_FACTOR) / ENCODE_FIRST_FACTOR) + ENCODE_LEAD_LOW; + uint16_t cu2 = ((cp - ENCODE_SECOND_FACTOR) % ENCODE_FIRST_FACTOR) + ENCODE_TRAIL_LOW; + std::u16string nextU16str1 = ecmascript::base::StringHelper::Utf16ToU16String(&cu1, 1); + std::u16string nextU16str2 = ecmascript::base::StringHelper::Utf16ToU16String(&cu2, 1); + u16str = ecmascript::base::StringHelper::Append(u16str, nextU16str1); + u16str = ecmascript::base::StringHelper::Append(u16str, nextU16str2); + u16strSize++; + } else { + auto u16tCp = static_cast(cp); + std::u16string nextU16str = ecmascript::base::StringHelper::Utf16ToU16String(&u16tCp, 1); + u16str = ecmascript::base::StringHelper::Append(u16str, nextU16str); + } + } + const char16_t *constChar16tData = u16str.data(); + auto *char16tData = const_cast(constChar16tData); + auto *uint16tData = reinterpret_cast(char16tData); + return factory->NewFromUtf16Literal(uint16tData, u16strSize).GetTaggedValue(); +} + +// 21.1.2.4 +JSTaggedValue BuiltinsString::Raw(EcmaRuntimeCallInfo *argv) +{ + ASSERT(argv); + BUILTINS_API_TRACE(argv->GetThread(), String, Raw); + JSThread *thread = argv->GetThread(); + [[maybe_unused]] EcmaHandleScope handleScope(thread); + ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); + // Let cooked be ToObject(template). + JSHandle cooked = JSTaggedValue::ToObject(thread, BuiltinsString::GetCallArg(argv, 0)); + // ReturnIfAbrupt(cooked). + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + // Let raw be ToObject(Get(cooked, "raw")). + JSHandle rawKey(factory->NewFromCanBeCompressString("raw")); + JSHandle rawTag = + JSObject::GetProperty(thread, JSHandle::Cast(cooked), rawKey).GetValue(); + JSHandle rawObj = JSTaggedValue::ToObject(thread, rawTag); + // ReturnIfAbrupt(rawObj). + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + JSHandle lengthKey = thread->GlobalConstants()->GetHandledLengthString(); + JSHandle rawLen = + JSObject::GetProperty(thread, JSHandle::Cast(rawObj), lengthKey).GetValue(); + // ReturnIfAbrupt(rawLen). + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + JSTaggedNumber lengthNumber = JSTaggedValue::ToLength(thread, rawLen); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + int length = lengthNumber.ToUint32(); + if (length <= 0) { + return factory->GetEmptyString().GetTaggedValue(); + } + + std::u16string u16str; + int argc = static_cast(argv->GetArgsNumber()) - 1; + bool canBeCompress = true; + for (int i = 0, argsI = 1; i < length; ++i, ++argsI) { + // Let nextSeg be ToString(Get(raw, nextKey)). + JSHandle elementString = + JSObject::GetProperty(thread, JSHandle::Cast(rawObj), i).GetValue(); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + EcmaString *nextSeg = *JSTaggedValue::ToString(thread, elementString); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + if (nextSeg->IsUtf16()) { + u16str += ecmascript::base::StringHelper::Utf16ToU16String(nextSeg->GetDataUtf16(), nextSeg->GetLength()); + canBeCompress = false; + } else { + u16str += ecmascript::base::StringHelper::Utf8ToU16String(nextSeg->GetDataUtf8(), nextSeg->GetLength()); + } + if (i + 1 == length) { + break; + } + if (argsI <= argc) { + EcmaString *nextSub = *JSTaggedValue::ToString(thread, GetCallArg(argv, argsI)); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + if (nextSub->IsUtf16()) { + u16str += + ecmascript::base::StringHelper::Utf16ToU16String(nextSub->GetDataUtf16(), nextSub->GetLength()); + canBeCompress = false; + } else { + u16str += ecmascript::base::StringHelper::Utf8ToU16String(nextSub->GetDataUtf8(), nextSub->GetLength()); + } + } + } + // return the result string + auto *uint16tData = reinterpret_cast(const_cast(u16str.data())); + return factory->NewFromUtf16LiteralUnCheck(uint16tData, u16str.size(), canBeCompress).GetTaggedValue(); +} + +// 21.1.3.1 +JSTaggedValue BuiltinsString::CharAt(EcmaRuntimeCallInfo *argv) +{ + ASSERT(argv); + BUILTINS_API_TRACE(argv->GetThread(), String, CharAt); + JSThread *thread = argv->GetThread(); + [[maybe_unused]] EcmaHandleScope handleScope(thread); + ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); + JSHandle thisTag(JSTaggedValue::RequireObjectCoercible(thread, GetThis(argv))); + JSHandle thisHandle = JSTaggedValue::ToString(thread, thisTag); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + int32_t thisLen = thisHandle->GetLength(); + JSHandle posTag = BuiltinsString::GetCallArg(argv, 0); + int32_t pos; + if (posTag->IsUndefined()) { + pos = 0; + } else { + JSTaggedNumber posVal = JSTaggedValue::ToInteger(thread, posTag); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + pos = posVal.ToInt32(); + } + if (pos < 0 || pos >= thisLen) { + return factory->GetEmptyString().GetTaggedValue(); + } + uint16_t res = thisHandle->At(pos); + return factory->NewFromUtf16Literal(&res, 1).GetTaggedValue(); +} + +// 21.1.3.2 +JSTaggedValue BuiltinsString::CharCodeAt(EcmaRuntimeCallInfo *argv) +{ + ASSERT(argv); + BUILTINS_API_TRACE(argv->GetThread(), String, CharCodeAt); + JSThread *thread = argv->GetThread(); + [[maybe_unused]] EcmaHandleScope handleScope(thread); + JSHandle thisTag(JSTaggedValue::RequireObjectCoercible(thread, GetThis(argv))); + JSHandle thisHandle = JSTaggedValue::ToString(thread, thisTag); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + int32_t thisLen = thisHandle->GetLength(); + JSHandle posTag = BuiltinsString::GetCallArg(argv, 0); + int32_t pos; + if (posTag->IsUndefined()) { + pos = 0; + } else { + JSTaggedNumber posVal = JSTaggedValue::ToInteger(thread, posTag); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + pos = posVal.ToInt32(); + } + if (pos < 0 || pos >= thisLen) { + return GetTaggedDouble(ecmascript::base::NAN_VALUE); + } + uint16_t ret = thisHandle->At(pos); + return GetTaggedInt(ret); +} + +// 21.1.3.3 +JSTaggedValue BuiltinsString::CodePointAt(EcmaRuntimeCallInfo *argv) +{ + ASSERT(argv); + BUILTINS_API_TRACE(argv->GetThread(), String, CodePointAt); + JSThread *thread = argv->GetThread(); + [[maybe_unused]] EcmaHandleScope handleScope(thread); + JSHandle thisTag(JSTaggedValue::RequireObjectCoercible(thread, GetThis(argv))); + JSHandle thisHandle = JSTaggedValue::ToString(thread, thisTag); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + JSHandle posTag = BuiltinsString::GetCallArg(argv, 0); + + JSTaggedNumber posVal = JSTaggedValue::ToNumber(thread, posTag); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + int32_t pos = ecmascript::base::NumberHelper::DoubleInRangeInt32(posVal.GetNumber()); + int32_t thisLen = thisHandle->GetLength(); + if (pos < 0 || pos >= thisLen) { + return JSTaggedValue::Undefined(); + } + uint16_t first = thisHandle->At(pos); + if (first < ecmascript::base::utf_helper::DECODE_LEAD_LOW || + first > ecmascript::base::utf_helper::DECODE_LEAD_HIGH || pos + 1 == thisLen) { + return GetTaggedInt(first); + } + uint16_t second = thisHandle->At(pos + 1); + if (second < ecmascript::base::utf_helper::DECODE_TRAIL_LOW || + second > ecmascript::base::utf_helper::DECODE_TRAIL_HIGH) { + return GetTaggedInt(first); + } + uint32_t res = ecmascript::base::utf_helper::UTF16Decode(first, second); + return GetTaggedInt(res); +} + +// 21.1.3.4 +JSTaggedValue BuiltinsString::Concat(EcmaRuntimeCallInfo *argv) +{ + ASSERT(argv); + BUILTINS_API_TRACE(argv->GetThread(), String, Concat); + JSThread *thread = argv->GetThread(); + [[maybe_unused]] EcmaHandleScope handleScope(thread); + ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); + JSHandle thisTag(JSTaggedValue::RequireObjectCoercible(thread, GetThis(argv))); + JSHandle thisHandle = JSTaggedValue::ToString(thread, thisTag); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + int32_t thisLen = thisHandle->GetLength(); + int32_t argLength = argv->GetArgsNumber(); + if (argLength == 0) { + return thisHandle.GetTaggedValue(); + } + std::u16string u16strThis; + std::u16string u16strNext; + bool canBeCompress = true; + if (thisHandle->IsUtf16()) { + u16strThis = ecmascript::base::StringHelper::Utf16ToU16String(thisHandle->GetDataUtf16(), thisLen); + canBeCompress = false; + } else { + u16strThis = ecmascript::base::StringHelper::Utf8ToU16String(thisHandle->GetDataUtf8(), thisLen); + } + for (int i = 0; i < argLength; i++) { + JSHandle nextTag = BuiltinsString::GetCallArg(argv, i); + JSHandle nextHandle = JSTaggedValue::ToString(thread, nextTag); + int32_t nextLen = nextHandle->GetLength(); + if (nextHandle->IsUtf16()) { + u16strNext = ecmascript::base::StringHelper::Utf16ToU16String(nextHandle->GetDataUtf16(), nextLen); + canBeCompress = false; + } else { + u16strNext = ecmascript::base::StringHelper::Utf8ToU16String(nextHandle->GetDataUtf8(), nextLen); + } + u16strThis = ecmascript::base::StringHelper::Append(u16strThis, u16strNext); + } + const char16_t *constChar16tData = u16strThis.data(); + auto *char16tData = const_cast(constChar16tData); + auto *uint16tData = reinterpret_cast(char16tData); + int32_t u16strSize = u16strThis.size(); + return factory->NewFromUtf16LiteralUnCheck(uint16tData, u16strSize, canBeCompress).GetTaggedValue(); +} + +// 21.1.3.5 String.prototype.constructor +// 21.1.3.6 +JSTaggedValue BuiltinsString::EndsWith(EcmaRuntimeCallInfo *argv) +{ + ASSERT(argv); + BUILTINS_API_TRACE(argv->GetThread(), String, EndsWith); + JSThread *thread = argv->GetThread(); + [[maybe_unused]] EcmaHandleScope handleScope(thread); + JSHandle thisTag(JSTaggedValue::RequireObjectCoercible(thread, GetThis(argv))); + JSHandle searchTag = BuiltinsString::GetCallArg(argv, 0); + JSHandle thisHandle = JSTaggedValue::ToString(thread, thisTag); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + bool isRegexp = JSObject::IsRegExp(thread, searchTag); + if (isRegexp) { + THROW_TYPE_ERROR_AND_RETURN(thread, "is regexp", JSTaggedValue::Exception()); + } + JSHandle searchHandle = JSTaggedValue::ToString(thread, searchTag); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + int32_t thisLen = thisHandle->GetLength(); + int32_t searchLen = searchHandle->GetLength(); + int32_t pos; + JSHandle posTag = BuiltinsString::GetCallArg(argv, 1); + if (posTag->IsUndefined()) { + pos = thisLen; + } else { + JSTaggedNumber posVal = JSTaggedValue::ToInteger(thread, posTag); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + pos = posVal.ToInt32(); + } + int32_t end = std::min(std::max(pos, 0), thisLen); + int32_t start = end - searchLen; + if (start < 0) { + return BuiltinsString::GetTaggedBoolean(false); + } + std::u16string u16strThis; + std::u16string u16strSearch; + if (thisHandle->IsUtf16()) { + u16strThis = ecmascript::base::StringHelper::Utf16ToU16String(thisHandle->GetDataUtf16(), thisLen); + } else { + const uint8_t *uint8This = thisHandle->GetDataUtf8(); + u16strThis = ecmascript::base::StringHelper::Utf8ToU16String(uint8This, thisLen); + } + if (searchHandle->IsUtf16()) { + u16strSearch = ecmascript::base::StringHelper::Utf16ToU16String(searchHandle->GetDataUtf16(), searchLen); + } else { + const uint8_t *uint8Search = searchHandle->GetDataUtf8(); + u16strSearch = ecmascript::base::StringHelper::Utf8ToU16String(uint8Search, searchLen); + } + int32_t idx = ecmascript::base::StringHelper::Find(u16strThis, u16strSearch, start); + if (idx == start) { + return BuiltinsString::GetTaggedBoolean(true); + } + return BuiltinsString::GetTaggedBoolean(false); +} + +// 21.1.3.7 +JSTaggedValue BuiltinsString::Includes(EcmaRuntimeCallInfo *argv) +{ + ASSERT(argv); + BUILTINS_API_TRACE(argv->GetThread(), String, Includes); + JSThread *thread = argv->GetThread(); + [[maybe_unused]] EcmaHandleScope handleScope(thread); + JSHandle searchTag = BuiltinsString::GetCallArg(argv, 0); + JSHandle thisTag(JSTaggedValue::RequireObjectCoercible(thread, GetThis(argv))); + JSHandle thisHandle = JSTaggedValue::ToString(thread, thisTag); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + bool isRegexp = JSObject::IsRegExp(thread, searchTag); + if (isRegexp) { + THROW_TYPE_ERROR_AND_RETURN(thread, "is regexp", JSTaggedValue::Exception()); + } + JSHandle searchHandle = JSTaggedValue::ToString(thread, searchTag); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + int32_t thisLen = thisHandle->GetLength(); + int32_t searchLen = searchHandle->GetLength(); + int32_t pos = 0; + JSHandle posTag = BuiltinsBase::GetCallArg(argv, 1); + if (argv->GetArgsNumber() == 1) { + pos = 0; + } else { + JSTaggedNumber posVal = JSTaggedValue::ToNumber(thread, posTag); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + pos = ecmascript::base::NumberHelper::DoubleInRangeInt32(posVal.GetNumber()); + } + int32_t start = std::min(std::max(pos, 0), thisLen); + std::u16string u16strThis; + std::u16string u16strSearch; + if (thisHandle->IsUtf16()) { + u16strThis = ecmascript::base::StringHelper::Utf16ToU16String(thisHandle->GetDataUtf16(), thisLen); + } else { + const uint8_t *uint8This = thisHandle->GetDataUtf8(); + u16strThis = ecmascript::base::StringHelper::Utf8ToU16String(uint8This, thisLen); + } + if (searchHandle->IsUtf16()) { + u16strSearch = ecmascript::base::StringHelper::Utf16ToU16String(searchHandle->GetDataUtf16(), searchLen); + } else { + const uint8_t *uint8Search = searchHandle->GetDataUtf8(); + u16strSearch = ecmascript::base::StringHelper::Utf8ToU16String(uint8Search, searchLen); + } + int32_t idx = ecmascript::base::StringHelper::Find(u16strThis, u16strSearch, start); + if (idx < 0 || idx > thisLen) { + return BuiltinsString::GetTaggedBoolean(false); + } + return BuiltinsString::GetTaggedBoolean(true); +} + +// 21.1.3.8 +JSTaggedValue BuiltinsString::IndexOf(EcmaRuntimeCallInfo *argv) +{ + ASSERT(argv); + BUILTINS_API_TRACE(argv->GetThread(), String, IndexOf); + JSThread *thread = argv->GetThread(); + [[maybe_unused]] EcmaHandleScope handleScope(thread); + JSHandle searchTag = BuiltinsString::GetCallArg(argv, 0); + JSHandle thisTag(JSTaggedValue::RequireObjectCoercible(thread, GetThis(argv))); + JSHandle thisHandle = JSTaggedValue::ToString(thread, thisTag); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + int32_t thisLen = thisHandle->GetLength(); + JSHandle searchHandle = JSTaggedValue::ToString(thread, searchTag); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + int32_t searchLen = searchHandle->GetLength(); + int32_t pos; + if (argv->GetArgsNumber() == 1) { + pos = 0; + } else { + JSHandle posTag = BuiltinsString::GetCallArg(argv, 1); + JSTaggedNumber posVal = JSTaggedValue::ToInteger(thread, posTag); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + pos = posVal.ToInt32(); + } + pos = std::min(std::max(pos, 0), thisLen); + if (thisHandle->IsUtf8() && searchHandle->IsUtf8()) { + std::string thisString = base::StringHelper::Utf8ToString(thisHandle->GetDataUtf8(), thisLen); + std::string searchString = base::StringHelper::Utf8ToString(searchHandle->GetDataUtf8(), searchLen); + int32_t res = base::StringHelper::Find(thisString, searchString, pos); + if (res >= 0 && res < thisLen) { + return GetTaggedInt(res); + } + return GetTaggedInt(-1); + } + std::u16string u16strThis; + std::u16string u16strSearch; + if (thisHandle->IsUtf16()) { + u16strThis = ecmascript::base::StringHelper::Utf16ToU16String(thisHandle->GetDataUtf16(), thisLen); + } else { + const uint8_t *uint8This = thisHandle->GetDataUtf8(); + u16strThis = ecmascript::base::StringHelper::Utf8ToU16String(uint8This, thisLen); + } + if (searchHandle->IsUtf16()) { + u16strSearch = ecmascript::base::StringHelper::Utf16ToU16String(searchHandle->GetDataUtf16(), searchLen); + } else { + const uint8_t *uint8Search = searchHandle->GetDataUtf8(); + u16strSearch = ecmascript::base::StringHelper::Utf8ToU16String(uint8Search, searchLen); + } + int32_t res = ecmascript::base::StringHelper::Find(u16strThis, u16strSearch, pos); + if (res >= 0 && res < thisLen) { + return GetTaggedInt(res); + } + return GetTaggedInt(-1); +} + +// 21.1.3.9 +JSTaggedValue BuiltinsString::LastIndexOf(EcmaRuntimeCallInfo *argv) +{ + ASSERT(argv); + BUILTINS_API_TRACE(argv->GetThread(), String, LastIndexOf); + JSThread *thread = argv->GetThread(); + [[maybe_unused]] EcmaHandleScope handleScope(thread); + JSHandle searchTag = BuiltinsString::GetCallArg(argv, 0); + JSHandle thisTag(JSTaggedValue::RequireObjectCoercible(thread, GetThis(argv))); + JSHandle thisHandle = JSTaggedValue::ToString(thread, thisTag); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + int32_t thisLen = thisHandle->GetLength(); + JSHandle searchHandle = JSTaggedValue::ToString(thread, searchTag); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + int32_t searchLen = searchHandle->GetLength(); + int32_t pos; + if (argv->GetArgsNumber() == 1) { + pos = thisLen; + } else { + JSHandle posTag = BuiltinsString::GetCallArg(argv, 1); + JSTaggedNumber posVal = JSTaggedValue::ToInteger(thread, posTag); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + if (std::isnan(JSTaggedValue::ToNumber(thread, posTag).GetNumber())) { + pos = thisLen; + } else { + pos = posVal.ToInt32(); + } + } + pos = std::min(std::max(pos, 0), thisLen); + std::u16string u16strThis; + std::u16string u16strSearch; + if (thisHandle->IsUtf16()) { + u16strThis = ecmascript::base::StringHelper::Utf16ToU16String(thisHandle->GetDataUtf16(), thisLen); + } else { + const uint8_t *uint8This = thisHandle->GetDataUtf8(); + u16strThis = ecmascript::base::StringHelper::Utf8ToU16String(uint8This, thisLen); + } + if (searchHandle->IsUtf16()) { + u16strSearch = ecmascript::base::StringHelper::Utf16ToU16String(searchHandle->GetDataUtf16(), searchLen); + } else { + const uint8_t *uint8Search = searchHandle->GetDataUtf8(); + u16strSearch = ecmascript::base::StringHelper::Utf8ToU16String(uint8Search, searchLen); + } + int32_t res = ecmascript::base::StringHelper::RFind(u16strThis, u16strSearch, pos); + if (res >= 0 && res < thisLen) { + return GetTaggedInt(res); + } + res = -1; + return GetTaggedInt(res); +} + +// 21.1.3.10 +JSTaggedValue BuiltinsString::LocaleCompare(EcmaRuntimeCallInfo *argv) +{ + ASSERT(argv); + BUILTINS_API_TRACE(argv->GetThread(), String, LocaleCompare); + JSThread *thread = argv->GetThread(); + [[maybe_unused]] EcmaHandleScope handleScope(thread); + JSHandle that_tag = BuiltinsString::GetCallArg(argv, 0); + JSHandle thisTag(JSTaggedValue::RequireObjectCoercible(thread, GetThis(argv))); + JSHandle thisHandle = JSTaggedValue::ToString(thread, thisTag); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + JSHandle thatHandle = JSTaggedValue::ToString(thread, that_tag); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + int32_t res = thisHandle->Compare(*thatHandle); + return GetTaggedInt(res); +} + +// 21.1.3.11 +JSTaggedValue BuiltinsString::Match(EcmaRuntimeCallInfo *argv) +{ + ASSERT(argv); + BUILTINS_API_TRACE(argv->GetThread(), String, Match); + JSThread *thread = argv->GetThread(); + [[maybe_unused]] EcmaHandleScope handleScope(thread); + const GlobalEnvConstants *globalConst = thread->GlobalConstants(); + JSHandle thisTag(JSTaggedValue::RequireObjectCoercible(thread, GetThis(argv))); + JSHandle regexp = BuiltinsString::GetCallArg(argv, 0); + JSHandle matchTag = thread->GetEcmaVM()->GetGlobalEnv()->GetMatchSymbol(); + if (!regexp->IsUndefined() && !regexp->IsNull()) { + if (regexp->IsECMAObject()) { + JSHandle matcher = JSObject::GetMethod(thread, regexp, matchTag); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + if (!matcher->IsUndefined()) { + ASSERT(matcher->IsJSFunction()); + InternalCallParams *arguments = thread->GetInternalCallParams(); + arguments->MakeArgv(thisTag); + return JSFunction::Call(thread, matcher, regexp, 1, arguments->GetArgv()); + } + } + } + JSHandle thisVal = JSTaggedValue::ToString(thread, thisTag); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + JSHandle undifinedHandle = globalConst->GetHandledUndefined(); + JSHandle rx(thread, BuiltinsRegExp::RegExpCreate(thread, regexp, undifinedHandle)); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + InternalCallParams *arguments = thread->GetInternalCallParams(); + arguments->MakeArgv(thisVal.GetTaggedValue()); + return JSFunction::Invoke(thread, rx, matchTag, 1, arguments->GetArgv()); +} + +// 21.1.3.12 +JSTaggedValue BuiltinsString::Normalize(EcmaRuntimeCallInfo *argv) +{ + ASSERT(argv); + BUILTINS_API_TRACE(argv->GetThread(), String, Normalize); + JSThread *thread = argv->GetThread(); + [[maybe_unused]] EcmaHandleScope handleScope(thread); + ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); + JSHandle thisTag(JSTaggedValue::RequireObjectCoercible(thread, GetThis(argv))); + JSHandle thisHandle = JSTaggedValue::ToString(thread, thisTag); + RETURN_VALUE_IF_ABRUPT_COMPLETION(thread, JSTaggedValue::Exception()); + JSHandle formValue; + if (argv->GetArgsNumber() == 0) { + formValue = factory->NewFromString("NFC"); + } else { + JSHandle formTag = BuiltinsString::GetCallArg(argv, 0); + if (formTag->IsUndefined()) { + formValue = factory->NewFromString("NFC"); + } else { + formValue = JSTaggedValue::ToString(thread, formTag); + RETURN_VALUE_IF_ABRUPT_COMPLETION(thread, JSTaggedValue::Exception()); + } + } + JSHandle nfc = factory->NewFromString("NFC"); + JSHandle nfd = factory->NewFromString("NFD"); + JSHandle nfkc = factory->NewFromString("NFKC"); + JSHandle nfkd = factory->NewFromString("NFKD"); + if (formValue->Compare(*nfc) != 0 && formValue->Compare(*nfd) != 0 && formValue->Compare(*nfkc) != 0 && + formValue->Compare(*nfkd) != 0) { + THROW_RANGE_ERROR_AND_RETURN(thread, "compare not equal", JSTaggedValue::Exception()); + } + std::u16string u16strThis; + if (thisHandle->IsUtf16()) { + u16strThis = + ecmascript::base::StringHelper::Utf16ToU16String(thisHandle->GetDataUtf16(), thisHandle->GetLength()); + } else { + const uint8_t *uint8This = thisHandle->GetDataUtf8(); + u16strThis = ecmascript::base::StringHelper::Utf8ToU16String(uint8This, thisHandle->GetLength()); + } + const char16_t *constChar16tData = u16strThis.data(); + icu::UnicodeString src(constChar16tData); + icu::UnicodeString res; + UErrorCode errorCode = U_ZERO_ERROR; + UNormalizationMode uForm; + int32_t option = 0; + if (formValue->Compare(*nfc) == 0) { + uForm = UNORM_NFC; + } else if (formValue->Compare(*nfd) == 0) { + uForm = UNORM_NFD; + } else if (formValue->Compare(*nfkc) == 0) { + uForm = UNORM_NFKC; + } else if (formValue->Compare(*nfkd) == 0) { + uForm = UNORM_NFKD; + } else { + UNREACHABLE(); + } + + icu::Normalizer::normalize(src, uForm, option, res, errorCode); + JSHandle str = JSLocale::IcuToString(thread, res); + return JSTaggedValue(*str); +} + +// ES2021 22.1.3.14 +JSTaggedValue BuiltinsString::padEnd(EcmaRuntimeCallInfo *argv) +{ + // 1. Let O be ? RequireObjectCoercible(this value). + JSThread *thread = argv->GetThread(); + JSHandle thisTag(JSTaggedValue::RequireObjectCoercible(thread, GetThis(argv))); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + + // 2. Return ? StringPad(O, maxLength, fillString, start). + return base::StringHelper::StringPad(thread, thisTag, BuiltinsString::GetCallArg(argv, 0), + BuiltinsString::GetCallArg(argv, 1), base::PadPlacement::END); +} + +// ES2021 22.1.3.15 +JSTaggedValue BuiltinsString::padStart(EcmaRuntimeCallInfo *argv) +{ + // 1. Let O be ? RequireObjectCoercible(this value). + JSThread *thread = argv->GetThread(); + JSHandle thisTag(JSTaggedValue::RequireObjectCoercible(thread, GetThis(argv))); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + + // 2. Return ? StringPad(O, maxLength, fillString, start). + return base::StringHelper::StringPad(thread, thisTag, BuiltinsString::GetCallArg(argv, 0), + BuiltinsString::GetCallArg(argv, 1), base::PadPlacement::START); +} + +// 21.1.3.13 +JSTaggedValue BuiltinsString::Repeat(EcmaRuntimeCallInfo *argv) +{ + ASSERT(argv); + BUILTINS_API_TRACE(argv->GetThread(), String, Repeat); + JSThread *thread = argv->GetThread(); + [[maybe_unused]] EcmaHandleScope handleScope(thread); + JSHandle thisTag(JSTaggedValue::RequireObjectCoercible(thread, GetThis(argv))); + JSHandle thisHandle = JSTaggedValue::ToString(thread, thisTag); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + int32_t thisLen = thisHandle->GetLength(); + JSHandle countTag = BuiltinsString::GetCallArg(argv, 0); + JSTaggedNumber num = JSTaggedValue::ToInteger(thread, countTag); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + double d = num.GetNumber(); + if (d < 0) { + THROW_RANGE_ERROR_AND_RETURN(thread, "less than 0", JSTaggedValue::Exception()); + } + if (d == ecmascript::base::POSITIVE_INFINITY) { + THROW_RANGE_ERROR_AND_RETURN(thread, "is infinity", JSTaggedValue::Exception()); + } + int32_t count = ecmascript::base::NumberHelper::DoubleInRangeInt32(d); + std::u16string u16strThis; + bool canBeCompress = true; + if (thisHandle->IsUtf16()) { + u16strThis = ecmascript::base::StringHelper::Utf16ToU16String(thisHandle->GetDataUtf16(), thisLen); + canBeCompress = false; + } else { + const uint8_t *uint8This = thisHandle->GetDataUtf8(); + u16strThis = ecmascript::base::StringHelper::Utf8ToU16String(uint8This, thisLen); + } + if (thisLen == 0) { + return thisHandle.GetTaggedValue(); + } + + EcmaString *res = ecmascript::base::StringHelper::Repeat(thread, u16strThis, count, canBeCompress); + return JSTaggedValue(res); +} + +// 21.1.3.14 +JSTaggedValue BuiltinsString::Replace(EcmaRuntimeCallInfo *argv) +{ + ASSERT(argv); + BUILTINS_API_TRACE(argv->GetThread(), String, Replace); + JSThread *thread = argv->GetThread(); + [[maybe_unused]] EcmaHandleScope handleScope(thread); + JSHandle thisTag = JSTaggedValue::RequireObjectCoercible(thread, BuiltinsString::GetThis(argv)); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + + auto ecmaVm = thread->GetEcmaVM(); + JSHandle env = ecmaVm->GetGlobalEnv(); + const GlobalEnvConstants *globalConst = thread->GlobalConstants(); + JSHandle searchTag = BuiltinsString::GetCallArg(argv, 0); + JSHandle replaceTag = BuiltinsString::GetCallArg(argv, 1); + + ObjectFactory *factory = ecmaVm->GetFactory(); + + // If searchValue is neither undefined nor null, then + if (searchTag->IsECMAObject()) { + JSHandle replaceKey = env->GetReplaceSymbol(); + // Let replacer be GetMethod(searchValue, @@replace). + JSHandle replaceMethod = JSObject::GetMethod(thread, searchTag, replaceKey); + // ReturnIfAbrupt(replacer). + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + // If replacer is not undefined, then + if (!replaceMethod->IsUndefined()) { + // Return Call(replacer, searchValue, «O, replaceValue»). + InternalCallParams *arguments = thread->GetInternalCallParams(); + arguments->MakeArgv(thisTag, replaceTag); + return JSFunction::Call(thread, replaceMethod, searchTag, 2, arguments->GetArgv()); // 2: two args + } + } + + // Let string be ToString(O). + JSHandle thisString = JSTaggedValue::ToString(thread, thisTag); + // ReturnIfAbrupt(string). + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + // Let searchString be ToString(searchValue). + JSHandle searchString = JSTaggedValue::ToString(thread, searchTag); + // ReturnIfAbrupt(searchString). + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + // Let functionalReplace be IsCallable(replaceValue). + if (!replaceTag->IsCallable()) { + // If functionalReplace is false, then + // Let replaceValue be ToString(replaceValue). + // ReturnIfAbrupt(replaceValue) + replaceTag = JSHandle(JSTaggedValue::ToString(thread, replaceTag)); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + } + // Search string for the first occurrence of searchString and let pos be the index within string of the first code + // unit of the matched substring and let matched be searchString. If no occurrences of searchString were found, + // return string. + int32_t pos = thisString->IndexOf(*searchString); + if (pos == -1) { + return thisString.GetTaggedValue(); + } + + JSMutableHandle replHandle(thread, factory->GetEmptyString().GetTaggedValue()); + // If functionalReplace is true, then + if (replaceTag->IsCallable()) { + // Let replValue be Call(replaceValue, undefined,«matched, pos, and string»). + InternalCallParams *arguments = thread->GetInternalCallParams(); + arguments->MakeArgv(JSHandle(searchString), JSHandle(thread, JSTaggedValue(pos)), + JSHandle(thisString)); + JSTaggedValue replStrDeocodeValue = JSFunction::Call(thread, replaceTag, globalConst->GetHandledUndefined(), 3, + arguments->GetArgv()); // 3: «matched, pos, and string» + replHandle.Update(replStrDeocodeValue); + } else { + // Let captures be an empty List. + JSHandle capturesList = factory->NewTaggedArray(0); + ASSERT_PRINT(replaceTag->IsString(), "replace must be string"); + JSHandle replacement(thread, replaceTag->GetTaggedObject()); + // Let replStr be GetSubstitution(matched, string, pos, captures, replaceValue) + replHandle.Update(GetSubstitution(thread, searchString, thisString, pos, capturesList, replacement)); + } + JSHandle realReplaceStr = JSTaggedValue::ToString(thread, replHandle); + // Let tailPos be pos + the number of code units in matched. + int32_t tailPos = pos + searchString->GetLength(); + // Let newString be the String formed by concatenating the first pos code units of string, replStr, and the trailing + // substring of string starting at index tailPos. If pos is 0, the first element of the concatenation will be the + // empty String. + // Return newString. + JSHandle prefixString(thread, EcmaString::FastSubString(thisString, 0, pos, ecmaVm)); + JSHandle suffixString( + thread, EcmaString::FastSubString(thisString, tailPos, thisString->GetLength() - tailPos, ecmaVm)); + std::u16string stringBuilder; + bool canBeCompress = true; + if (prefixString->IsUtf16()) { + const uint16_t *data = prefixString->GetDataUtf16(); + stringBuilder += ecmascript::base::StringHelper::Utf16ToU16String(data, prefixString->GetLength()); + canBeCompress = false; + } else { + const uint8_t *data = prefixString->GetDataUtf8(); + stringBuilder += ecmascript::base::StringHelper::Utf8ToU16String(data, prefixString->GetLength()); + } + + if (realReplaceStr->IsUtf16()) { + const uint16_t *data = realReplaceStr->GetDataUtf16(); + stringBuilder += ecmascript::base::StringHelper::Utf16ToU16String(data, realReplaceStr->GetLength()); + canBeCompress = false; + } else { + const uint8_t *data = realReplaceStr->GetDataUtf8(); + stringBuilder += ecmascript::base::StringHelper::Utf8ToU16String(data, realReplaceStr->GetLength()); + } + + if (suffixString->IsUtf16()) { + const uint16_t *data = suffixString->GetDataUtf16(); + stringBuilder += ecmascript::base::StringHelper::Utf16ToU16String(data, suffixString->GetLength()); + canBeCompress = false; + } else { + const uint8_t *data = suffixString->GetDataUtf8(); + stringBuilder += ecmascript::base::StringHelper::Utf8ToU16String(data, suffixString->GetLength()); + } + + auto *char16tData = const_cast(stringBuilder.c_str()); + auto *uint16tData = reinterpret_cast(char16tData); + return factory->NewFromUtf16LiteralUnCheck(uint16tData, stringBuilder.size(), canBeCompress).GetTaggedValue(); +} + +// ES2021 22.1.3.18 +// NOLINTNEXTLINE(readability-function-size) +JSTaggedValue BuiltinsString::ReplaceAll(EcmaRuntimeCallInfo *argv) +{ + ASSERT(argv); + BUILTINS_API_TRACE(argv->GetThread(), String, ReplaceAll); + JSThread *thread = argv->GetThread(); + [[maybe_unused]] EcmaHandleScope handleScope(thread); + // 1. Let O be ? RequireObjectCoercible(this value). + JSHandle thisTag = JSTaggedValue::RequireObjectCoercible(thread, BuiltinsString::GetThis(argv)); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + + auto ecmaVm = thread->GetEcmaVM(); + ObjectFactory *factory = ecmaVm->GetFactory(); + JSHandle env = ecmaVm->GetGlobalEnv(); + + JSHandle searchValue = BuiltinsString::GetCallArg(argv, 0); + JSHandle replaceValue = BuiltinsString::GetCallArg(argv, 1); + + // 2. If searchValue is neither undefined nor null, then + if (searchValue->IsECMAObject()) { + // a. Let isRegExp be ? IsRegExp(searchValue). + bool isRegExp = JSObject::IsRegExp(thread, searchValue); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + + // b. If isRegExp is true, then + if (isRegExp) { + // i. Let flags be ? Get(searchValue, "flags"). + JSHandle flagsString(factory->NewFromString("flags")); + JSHandle flags(JSObject::GetProperty(thread, searchValue, flagsString).GetValue()); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + + // ii. Perform ? RequireObjectCoercible(flags). + JSTaggedValue::RequireObjectCoercible(thread, flags); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + + // iii. If ? ToString(flags) does not contain "g", throw a TypeError exception. + JSHandle flags_str = JSTaggedValue::ToString(thread, flags); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + std::string flags_std_str = base::StringHelper::ToStdString(flags_str.GetObject()); + if (flags_std_str.find('g') == std::string::npos) { + THROW_TYPE_ERROR_AND_RETURN(thread, "replaceAll must be called with a global RegExp", + JSTaggedValue::Exception()); + } + } + + // c. Let replacer be ? GetMethod(searchValue, @@replace). + JSHandle replaceKey = env->GetReplaceSymbol(); + JSHandle replacer = JSObject::GetMethod(thread, searchValue, replaceKey); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + + // d. If replacer is not undefined, then + if (!replacer->IsUndefined()) { + // i. Return ? Call(replacer, searchValue, « O, replaceValue »). + InternalCallParams *arguments = thread->GetInternalCallParams(); // 2: « O, replaceValue » + arguments->MakeArgv(thisTag, replaceValue); + JSTaggedValue result = JSFunction::Call(thread, replacer, searchValue, 2U, arguments->GetArgv()); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + return result; + } + } + + // 3. Let string be ? ToString(O). + JSHandle thisString = JSTaggedValue::ToString(thread, thisTag); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + + // 4. Let searchString be ? ToString(searchValue). + JSHandle searchString = JSTaggedValue::ToString(thread, searchValue); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + + // 5. Let functionalReplace be IsCallable(replaceValue). + // 6. If functionalReplace is false, then + if (!replaceValue->IsCallable()) { + // a. Set replaceValue to ? ToString(replaceValue). + replaceValue = JSHandle(JSTaggedValue::ToString(thread, replaceValue)); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + } + + // 7. Let searchLength be the length of searchString. + uint32_t searchLength = searchString->GetLength(); + + // 8. Let advanceBy be max(1, searchLength). + uint32_t advanceBy = searchLength > 1 ? searchLength : 1; + + // 9. Let matchPositions be a new empty List. + JSHandle matchPositions = factory->NewTaggedArray(thisString->GetLength() + 1); + + // 10. Let position be ! StringIndexOf(string, searchString, 0). + int32_t position = thisString->IndexOf(*searchString); + if (position == -1) { + return thisString.GetTaggedValue(); + } + + // 11. Repeat, while position is not -1, + uint32_t index = 0; + while (position != -1) { + // a. Append position to the end of matchPositions. + matchPositions->Set(thread, index++, JSTaggedValue(position)); + + // b. Set position to ! StringIndexOf(string, searchString, position + advanceBy). + position = thisString->IndexOf(*searchString, position + static_cast(advanceBy)); + } + size_t num_matches = index; + + // 12. Let endOfLastMatch be 0. + uint32_t endOfLastMatch = 0; + + // 13. Let result be the empty String. + JSHandle result = factory->NewFromString(""); + JSMutableHandle preserved(thread, JSTaggedValue::Undefined()); + + // 14. For each element p of matchPositions, do + for (size_t pos = 0; pos < num_matches; pos++) { + index = matchPositions->Get(pos).GetInt(); + // a. Let preserved be the substring of string from endOfLastMatch to p. + preserved.Update( + JSTaggedValue(EcmaString::FastSubString(thisString, endOfLastMatch, index - endOfLastMatch, ecmaVm))); + + // b. If functionalReplace is true, then + JSTaggedValue replacement; + if (replaceValue->IsCallable()) { + // i. Let replacement be ? ToString(? Call(replaceValue, undefined, « searchString, F(p), string »)). + + InternalCallParams *arguments = thread->GetInternalCallParams(); + arguments->MakeArgv(searchString.GetTaggedValue(), JSTaggedValue(index), + thisString.GetTaggedValue()); // 3: « searchString, F(p), string » + JSTaggedValue result = + JSFunction::Call(thread, replaceValue, JSHandle(thread, JSTaggedValue::Undefined()), 3U, + arguments->GetArgv()); + + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + + replacement = JSTaggedValue::ToString(thread, JSHandle(thread, result)).GetTaggedValue(); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + } else { + // i. Assert: Type(replaceValue) is String. + ASSERT(replaceValue->IsString()); + + // ii. Let captures be a new empty List. + JSHandle captures = factory->NewTaggedArray(0); + + // iii. Let replacement be ! GetSubstitution(searchString, string, p, captures, undefined, replaceValue). + replacement = + GetSubstitution(thread, searchString, thisString, index, captures, JSHandle(replaceValue)); + } + + // d. Set result to the string-concatenation of result, preserved, and replacement. + std::u16string stringBuilder; + if (result->IsUtf16()) { + const uint16_t *data = result->GetDataUtf16(); + stringBuilder += ecmascript::base::StringHelper::Utf16ToU16String(data, result->GetLength()); + } else { + const uint8_t *data = result->GetDataUtf8(); + stringBuilder += ecmascript::base::StringHelper::Utf8ToU16String(data, result->GetLength()); + } + + if (preserved->IsUtf16()) { + const uint16_t *data = preserved->GetDataUtf16(); + stringBuilder += ecmascript::base::StringHelper::Utf16ToU16String(data, preserved->GetLength()); + } else { + const uint8_t *data = preserved->GetDataUtf8(); + stringBuilder += ecmascript::base::StringHelper::Utf8ToU16String(data, preserved->GetLength()); + } + + JSHandle replacementHandle = JSHandle(thread, replacement); + if (replacementHandle->IsUtf16()) { + const uint16_t *data = replacementHandle->GetDataUtf16(); + stringBuilder += ecmascript::base::StringHelper::Utf16ToU16String(data, replacementHandle->GetLength()); + } else { + const uint8_t *data = replacementHandle->GetDataUtf8(); + stringBuilder += ecmascript::base::StringHelper::Utf8ToU16String(data, replacementHandle->GetLength()); + } + + auto *char16tData = const_cast(stringBuilder.c_str()); + auto *uint16tData = reinterpret_cast(char16tData); + result = factory->NewFromUtf16Literal(uint16tData, stringBuilder.size()); + + // e. Set endOfLastMatch to p + searchLength. + endOfLastMatch = index + searchLength; + } + + // 15. If endOfLastMatch < the length of string, then + if (endOfLastMatch < thisString->GetLength()) { + // a. Set result to the string-concatenation of result and the substring of string from endOfLastMatch. + std::u16string stringBuilder; + if (result->IsUtf16()) { + const uint16_t *data = result->GetDataUtf16(); + stringBuilder += ecmascript::base::StringHelper::Utf16ToU16String(data, result->GetLength()); + } else { + const uint8_t *data = result->GetDataUtf8(); + stringBuilder += ecmascript::base::StringHelper::Utf8ToU16String(data, result->GetLength()); + } + + EcmaString *sub = + EcmaString::FastSubString(thisString, endOfLastMatch, thisString->GetLength() - endOfLastMatch, ecmaVm); + + if (sub->IsUtf16()) { + const uint16_t *data = sub->GetDataUtf16(); + stringBuilder += ecmascript::base::StringHelper::Utf16ToU16String(data, sub->GetLength()); + } else { + const uint8_t *data = sub->GetDataUtf8(); + stringBuilder += ecmascript::base::StringHelper::Utf8ToU16String(data, sub->GetLength()); + } + + auto *char16tData = const_cast(stringBuilder.c_str()); + auto *uint16tData = reinterpret_cast(char16tData); + result = factory->NewFromUtf16Literal(uint16tData, stringBuilder.size()); + } + + // 16. Return result. + return result.GetTaggedValue(); +} + +JSTaggedValue BuiltinsString::GetSubstitution(JSThread *thread, const JSHandle &matched, + const JSHandle &srcString, int position, + const JSHandle &captureList, + const JSHandle &replacement) +{ + BUILTINS_API_TRACE(thread, String, GetSubstitution); + auto ecmaVm = thread->GetEcmaVM(); + ObjectFactory *factory = ecmaVm->GetFactory(); + JSHandle dollarString = factory->NewFromCanBeCompressString("$"); + int32_t replaceLength = replacement->GetLength(); + int32_t tailPos = position + matched->GetLength(); + + int32_t nextDollarIndex = replacement->IndexOf(*dollarString, 0); + if (nextDollarIndex < 0) { + return replacement.GetTaggedValue(); + } + + std::u16string stringBuilder; + bool canBeCompress = true; + if (nextDollarIndex > 0) { + if (replacement->IsUtf16()) { + const uint16_t *data = replacement->GetDataUtf16(); + stringBuilder += ecmascript::base::StringHelper::Utf16ToU16String(data, nextDollarIndex); + canBeCompress = false; + } else { + const uint8_t *data = replacement->GetDataUtf8(); + stringBuilder += ecmascript::base::StringHelper::Utf8ToU16String(data, nextDollarIndex); + } + } + + while (true) { + int peekIndex = nextDollarIndex + 1; + if (peekIndex >= replaceLength) { + stringBuilder += '$'; + auto *char16tData = const_cast(stringBuilder.c_str()); + auto *uint16tData = reinterpret_cast(char16tData); + return factory->NewFromUtf16LiteralUnCheck(uint16tData, stringBuilder.length(), canBeCompress) + .GetTaggedValue(); + } + int continueFromIndex = -1; + uint16_t peek = replacement->At(peekIndex); + switch (peek) { + case '$': // $$ + stringBuilder += '$'; + continueFromIndex = peekIndex + 1; + break; + case '&': // $& - match + if (matched->IsUtf16()) { + const uint16_t *data = matched->GetDataUtf16(); + stringBuilder += ecmascript::base::StringHelper::Utf16ToU16String(data, matched->GetLength()); + canBeCompress = false; + } else { + const uint8_t *data = matched->GetDataUtf8(); + stringBuilder += ecmascript::base::StringHelper::Utf8ToU16String(data, matched->GetLength()); + } + continueFromIndex = peekIndex + 1; + break; + case '`': // $` - prefix + if (position > 0) { + EcmaString *prefix = EcmaString::FastSubString(srcString, 0, position, ecmaVm); + if (prefix->IsUtf16()) { + const uint16_t *data = prefix->GetDataUtf16(); + stringBuilder += ecmascript::base::StringHelper::Utf16ToU16String(data, prefix->GetLength()); + canBeCompress = false; + } else { + const uint8_t *data = prefix->GetDataUtf8(); + stringBuilder += ecmascript::base::StringHelper::Utf8ToU16String(data, prefix->GetLength()); + } + } + continueFromIndex = peekIndex + 1; + break; + case '\'': { + // $' - suffix + int32_t srcLength = srcString->GetLength(); + if (tailPos < srcLength) { + EcmaString *sufffix = EcmaString::FastSubString(srcString, tailPos, srcLength - tailPos, ecmaVm); + if (sufffix->IsUtf16()) { + const uint16_t *data = sufffix->GetDataUtf16(); + stringBuilder += ecmascript::base::StringHelper::Utf16ToU16String(data, sufffix->GetLength()); + canBeCompress = false; + } else { + const uint8_t *data = sufffix->GetDataUtf8(); + stringBuilder += ecmascript::base::StringHelper::Utf8ToU16String(data, sufffix->GetLength()); + } + } + continueFromIndex = peekIndex + 1; + break; + } + case '0': + case '1': + case '2': + case '3': + case '4': + case '5': + case '6': + case '7': + case '8': + case '9': { + int capturesLength = captureList->GetLength(); + // Valid indices are $1 .. $9, $01 .. $09 and $10 .. $99 + int32_t scaledIndex = (peek - '0'); + int32_t advance = 1; + if (peekIndex + 1 < replaceLength) { + uint16_t nextPeek = replacement->At(peekIndex + 1); + if (nextPeek >= '0' && nextPeek <= '9') { + constexpr int32_t TEN_BASE = 10; + int32_t newScaledIndex = scaledIndex * TEN_BASE + (nextPeek - '0'); + if (newScaledIndex <= capturesLength) { + scaledIndex = newScaledIndex; + advance = 2; // 2: 2 means from index needs to add two. + } + } + } + + if (scaledIndex == 0 || scaledIndex > capturesLength) { + stringBuilder += '$'; + continueFromIndex = peekIndex; + break; + } + + JSTaggedValue capturesVal(captureList->Get(scaledIndex - 1)); + if (!capturesVal.IsUndefined()) { + EcmaString *captureString = EcmaString::Cast(capturesVal.GetTaggedObject()); + if (captureString->IsUtf16()) { + const uint16_t *data = captureString->GetDataUtf16(); + stringBuilder += + ecmascript::base::StringHelper::Utf16ToU16String(data, captureString->GetLength()); + canBeCompress = false; + } else { + const uint8_t *data = captureString->GetDataUtf8(); + stringBuilder += + ecmascript::base::StringHelper::Utf8ToU16String(data, captureString->GetLength()); + } + } + continueFromIndex = peekIndex + advance; + break; + } + default: + stringBuilder += '$'; + continueFromIndex = peekIndex; + break; + } + // Go the the next $ in the replacement. + nextDollarIndex = replacement->IndexOf(*dollarString, continueFromIndex); + if (nextDollarIndex < 0) { + if (continueFromIndex < replaceLength) { + EcmaString *nextAppend = EcmaString::FastSubString(replacement, continueFromIndex, + replaceLength - continueFromIndex, ecmaVm); + if (nextAppend->IsUtf16()) { + const uint16_t *data = nextAppend->GetDataUtf16(); + stringBuilder += ecmascript::base::StringHelper::Utf16ToU16String(data, nextAppend->GetLength()); + canBeCompress = false; + } else { + const uint8_t *data = nextAppend->GetDataUtf8(); + stringBuilder += ecmascript::base::StringHelper::Utf8ToU16String(data, nextAppend->GetLength()); + } + } + auto *char16tData = const_cast(stringBuilder.c_str()); + auto *uint16tData = reinterpret_cast(char16tData); + return factory->NewFromUtf16LiteralUnCheck(uint16tData, stringBuilder.length(), canBeCompress) + .GetTaggedValue(); + } + // Append substring between the previous and the next $ character. + if (nextDollarIndex > continueFromIndex) { + EcmaString *nextAppend = + EcmaString::FastSubString(replacement, continueFromIndex, nextDollarIndex - continueFromIndex, ecmaVm); + if (nextAppend->IsUtf16()) { + const uint16_t *data = nextAppend->GetDataUtf16(); + stringBuilder += ecmascript::base::StringHelper::Utf16ToU16String(data, nextAppend->GetLength()); + canBeCompress = false; + } else { + const uint8_t *data = nextAppend->GetDataUtf8(); + stringBuilder += ecmascript::base::StringHelper::Utf8ToU16String(data, nextAppend->GetLength()); + } + } + } + UNREACHABLE(); +} + +// 21.1.3.15 +JSTaggedValue BuiltinsString::Search(EcmaRuntimeCallInfo *argv) +{ + ASSERT(argv); + BUILTINS_API_TRACE(argv->GetThread(), String, Search); + JSThread *thread = argv->GetThread(); + [[maybe_unused]] EcmaHandleScope handleScope(thread); + const GlobalEnvConstants *globalConst = thread->GlobalConstants(); + JSHandle thisTag(JSTaggedValue::RequireObjectCoercible(thread, GetThis(argv))); + JSHandle regexp = BuiltinsString::GetCallArg(argv, 0); + JSHandle searchTag = thread->GetEcmaVM()->GetGlobalEnv()->GetSearchSymbol(); + if (!regexp->IsUndefined() && !regexp->IsNull()) { + if (regexp->IsECMAObject()) { + JSHandle searcher = JSObject::GetMethod(thread, regexp, searchTag); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + if (!searcher->IsUndefined()) { + ASSERT(searcher->IsJSFunction()); + InternalCallParams *arguments = thread->GetInternalCallParams(); + arguments->MakeArgv(thisTag); + return JSFunction::Call(thread, searcher, regexp, 1, arguments->GetArgv()); + } + } + } + JSHandle thisVal = JSTaggedValue::ToString(thread, thisTag); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + JSHandle undifinedHandle = globalConst->GetHandledUndefined(); + JSHandle rx(thread, BuiltinsRegExp::RegExpCreate(thread, regexp, undifinedHandle)); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + InternalCallParams *arguments = thread->GetInternalCallParams(); + arguments->MakeArgv(thisVal.GetTaggedValue()); + return JSFunction::Invoke(thread, rx, searchTag, 1, arguments->GetArgv()); +} + +// 21.1.3.16 +JSTaggedValue BuiltinsString::Slice(EcmaRuntimeCallInfo *argv) +{ + ASSERT(argv); + BUILTINS_API_TRACE(argv->GetThread(), String, Slice); + JSThread *thread = argv->GetThread(); + [[maybe_unused]] EcmaHandleScope handleScope(thread); + + JSHandle thisTag(JSTaggedValue::RequireObjectCoercible(thread, GetThis(argv))); + JSHandle thisHandle = JSTaggedValue::ToString(thread, thisTag); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + int32_t thisLen = thisHandle->GetLength(); + JSHandle startTag = BuiltinsString::GetCallArg(argv, 0); + JSTaggedNumber startVal = JSTaggedValue::ToInteger(thread, startTag); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + int32_t start = ConvertDoubleToInt(startVal.GetNumber()); + int32_t end; + JSHandle endTag = BuiltinsString::GetCallArg(argv, 1); + if (endTag->IsUndefined()) { + end = thisLen; + } else { + JSTaggedNumber endVal = JSTaggedValue::ToInteger(thread, endTag); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + end = ConvertDoubleToInt(endVal.GetNumber()); + } + int32_t from; + int32_t to; + if (start < 0) { + from = std::max(start + thisLen, 0); + } else { + from = std::min(start, thisLen); + } + if (end < 0) { + to = std::max(end + thisLen, 0); + } else { + to = std::min(end, thisLen); + } + int32_t len = std::max(to - from, 0); + return JSTaggedValue(EcmaString::FastSubString(thisHandle, from, len, thread->GetEcmaVM())); +} + +// 21.1.3.17 +JSTaggedValue BuiltinsString::Split(EcmaRuntimeCallInfo *argv) +{ + ASSERT(argv); + BUILTINS_API_TRACE(argv->GetThread(), String, Split); + JSThread *thread = argv->GetThread(); + [[maybe_unused]] EcmaHandleScope handleScope(thread); + auto ecmaVm = thread->GetEcmaVM(); + JSHandle env = ecmaVm->GetGlobalEnv(); + + // Let O be RequireObjectCoercible(this value). + JSHandle thisTag = JSTaggedValue::RequireObjectCoercible(thread, GetThis(argv)); + JSHandle thisObj(thisTag); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + JSHandle seperatorTag = BuiltinsString::GetCallArg(argv, 0); + JSHandle limitTag = BuiltinsString::GetCallArg(argv, 1); + // If separator is neither undefined nor null, then + if (seperatorTag->IsECMAObject()) { + JSHandle splitKey = env->GetSplitSymbol(); + // Let splitter be GetMethod(separator, @@split). + JSHandle splitter = JSObject::GetMethod(thread, seperatorTag, splitKey); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + if (!splitter->IsUndefined()) { + // Return Call(splitter, separator, «‍O, limit»). + InternalCallParams *arguments = thread->GetInternalCallParams(); + arguments->MakeArgv(thisTag, limitTag); + return JSFunction::Call(thread, splitter, seperatorTag, 2, arguments->GetArgv()); // 2: two args + } + } + // Let S be ToString(O). + JSHandle thisString = JSTaggedValue::ToString(thread, thisTag); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + // Let A be ArrayCreate(0). + JSHandle resultArray(JSArray::ArrayCreate(thread, JSTaggedNumber(0))); + uint32_t arrayLength = 0; + // If limit is undefined, let lim = 2^53–1; else let lim = ToLength(limit). + uint32_t lim; + if (limitTag->IsUndefined()) { + lim = UINT32_MAX - 1; + } else { + lim = JSTaggedValue::ToInteger(thread, limitTag).ToUint32(); + } + // ReturnIfAbrupt(lim). + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + // If lim = 0, return A. + if (lim == 0) { + return resultArray.GetTaggedValue(); + } + // Let s be the number of elements in S. + int32_t thisLength = thisString->GetLength(); + JSHandle seperatorString = JSTaggedValue::ToString(thread, seperatorTag); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + if (seperatorTag->IsUndefined()) { + // Perform CreateDataProperty(A, "0", S). + JSObject::CreateDataProperty(thread, resultArray, 0, JSHandle(thisString)); + ASSERT_PRINT(!thread->HasPendingException(), "CreateDataProperty(A, \"0\", S) can't throw exception"); + return resultArray.GetTaggedValue(); + } + // If S.length = 0, then + if (thisLength == 0) { + if (SplitMatch(thisString, 0, seperatorString) != -1) { + return resultArray.GetTaggedValue(); + } + JSObject::CreateDataProperty(thread, resultArray, 0, JSHandle(thisString)); + ASSERT_PRINT(!thread->HasPendingException(), "CreateDataProperty(A, \"0\", S) can't throw exception"); + return resultArray.GetTaggedValue(); + } + + // Let q = p. + // Repeat, while q ≠ s + int32_t p = 0; + int32_t q = p; + while (q != thisLength) { + int32_t matchedIndex = SplitMatch(thisString, q, seperatorString); + if (matchedIndex == -1) { + q = q + 1; + } else { + if (matchedIndex == p) { + q = q + 1; + } else { + EcmaString *elementString = EcmaString::FastSubString(thisString, p, q - p, ecmaVm); + JSHandle elementTag(thread, elementString); + JSObject::CreateDataProperty(thread, resultArray, arrayLength, elementTag); + ASSERT_PRINT(!thread->HasPendingException(), "CreateDataProperty can't throw exception"); + ++arrayLength; + if (arrayLength == lim) { + return resultArray.GetTaggedValue(); + } + p = matchedIndex; + q = p; + } + } + } + EcmaString *elementString = EcmaString::FastSubString(thisString, p, thisLength - p, ecmaVm); + JSHandle elementTag(thread, elementString); + JSObject::CreateDataProperty(thread, resultArray, arrayLength, elementTag); + ASSERT_PRINT(!thread->HasPendingException(), "CreateDataProperty can't throw exception"); + return resultArray.GetTaggedValue(); +} + +int32_t BuiltinsString::SplitMatch(const JSHandle &str, int32_t q, const JSHandle ®) +{ + int32_t s = str->GetLength(); + int32_t r = reg->GetLength(); + if (q + r > s) { + return -1; + } + int32_t i = 0; + for (i = 0; i < r; i++) { + if (str->At(q + i) != reg->At(i)) { + return -1; + } + } + return q + r; +} + +// 21.1.3.18 +JSTaggedValue BuiltinsString::StartsWith(EcmaRuntimeCallInfo *argv) +{ + ASSERT(argv); + BUILTINS_API_TRACE(argv->GetThread(), String, StartsWith); + JSThread *thread = argv->GetThread(); + [[maybe_unused]] EcmaHandleScope handleScope(thread); + JSHandle searchTag = BuiltinsString::GetCallArg(argv, 0); + + JSHandle thisTag(JSTaggedValue::RequireObjectCoercible(thread, GetThis(argv))); + JSHandle thisHandle = JSTaggedValue::ToString(thread, thisTag); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + bool isRegexp = JSObject::IsRegExp(thread, searchTag); + if (isRegexp) { + THROW_TYPE_ERROR_AND_RETURN(thread, "is regexp", JSTaggedValue::Exception()); + } + + JSHandle searchHandle = JSTaggedValue::ToString(thread, searchTag); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + int32_t thisLen = thisHandle->GetLength(); + int32_t searchLen = searchHandle->GetLength(); + int32_t pos; + JSHandle posTag = BuiltinsString::GetCallArg(argv, 1); + if (posTag->IsUndefined()) { + pos = 0; + } else { + JSTaggedNumber posVal = JSTaggedValue::ToInteger(thread, posTag); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + pos = posVal.ToInt32(); + } + pos = std::min(std::max(pos, 0), thisLen); + if (pos + searchLen > thisLen) { + return BuiltinsString::GetTaggedBoolean(false); + } + std::u16string u16strThis; + std::u16string u16strSearch; + if (thisHandle->IsUtf16()) { + u16strThis = ecmascript::base::StringHelper::Utf16ToU16String(thisHandle->GetDataUtf16(), thisLen); + } else { + const uint8_t *uint8This = thisHandle->GetDataUtf8(); + u16strThis = ecmascript::base::StringHelper::Utf8ToU16String(uint8This, thisLen); + } + if (searchHandle->IsUtf16()) { + u16strSearch = ecmascript::base::StringHelper::Utf16ToU16String(searchHandle->GetDataUtf16(), searchLen); + } else { + const uint8_t *uint8Search = searchHandle->GetDataUtf8(); + u16strSearch = ecmascript::base::StringHelper::Utf8ToU16String(uint8Search, searchLen); + } + int32_t idx = ecmascript::base::StringHelper::Find(u16strThis, u16strSearch, pos); + if (idx == pos) { + return BuiltinsString::GetTaggedBoolean(true); + } + return BuiltinsString::GetTaggedBoolean(false); +} + +// 21.1.3.19 +JSTaggedValue BuiltinsString::Substring(EcmaRuntimeCallInfo *argv) +{ + ASSERT(argv); + BUILTINS_API_TRACE(argv->GetThread(), String, Substring); + JSThread *thread = argv->GetThread(); + [[maybe_unused]] EcmaHandleScope handleScope(thread); + + JSHandle thisTag(JSTaggedValue::RequireObjectCoercible(thread, GetThis(argv))); + JSHandle thisHandle = JSTaggedValue::ToString(thread, thisTag); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + int32_t thisLen = thisHandle->GetLength(); + JSHandle startTag = BuiltinsString::GetCallArg(argv, 0); + JSTaggedNumber startVal = JSTaggedValue::ToInteger(thread, startTag); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + int32_t start = ConvertDoubleToInt(startVal.GetNumber()); + int32_t end; + JSHandle endTag = BuiltinsString::GetCallArg(argv, 1); + if (endTag->IsUndefined()) { + end = thisLen; + } else { + JSTaggedNumber endVal = JSTaggedValue::ToInteger(thread, endTag); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + end = ConvertDoubleToInt(endVal.GetNumber()); + } + start = std::min(std::max(start, 0), thisLen); + end = std::min(std::max(end, 0), thisLen); + int32_t from = std::min(start, end); + int32_t to = std::max(start, end); + int32_t len = to - from; + return JSTaggedValue(EcmaString::FastSubString(thisHandle, from, len, thread->GetEcmaVM())); +} + +// 21.1.3.20 +JSTaggedValue BuiltinsString::ToLocaleLowerCase(EcmaRuntimeCallInfo *argv) +{ + ASSERT(argv); + BUILTINS_API_TRACE(argv->GetThread(), String, ToLocaleLowerCase); + JSThread *thread = argv->GetThread(); + EcmaVM *ecmaVm = thread->GetEcmaVM(); + ObjectFactory *factory = ecmaVm->GetFactory(); + [[maybe_unused]] EcmaHandleScope handleScope(thread); + + // Let O be RequireObjectCoercible(this value). + JSHandle obj(JSTaggedValue::RequireObjectCoercible(thread, GetThis(argv))); + + // Let S be ? ToString(O). + JSHandle string = JSTaggedValue::ToString(thread, obj); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + + // Let requestedLocales be ? CanonicalizeLocaleList(locales). + JSHandle locales = GetCallArg(argv, 0); + JSHandle requestedLocales = JSLocale::CanonicalizeLocaleList(thread, locales); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + + // If requestedLocales is not an empty List, then Let requestedLocale be requestedLocales[0]. + // Else, Let requestedLocale be DefaultLocale(). + JSHandle requestedLocale = JSLocale::DefaultLocale(thread); + if (requestedLocales->GetLength() != 0) { + requestedLocale = JSHandle(thread, requestedLocales->Get(0)); + } + + // Let noExtensionsLocale be the String value that is requestedLocale with all Unicode locale extension sequences + // removed. + JSLocale::ParsedLocale noExtensionsLocale = JSLocale::HandleLocale(requestedLocale); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + + // Let availableLocales be a List with language tags that includes the languages for which the Unicode Character + // Database contains language sensitive case mappings. Implementations may add additional language tags + // if they support case mapping for additional locales. + JSHandle availableLocales = JSLocale::GetAvailableLocales(thread, nullptr, nullptr); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + + // Let locale be BestAvailableLocale(availableLocales, noExtensionsLocale). + std::string locale = JSLocale::BestAvailableLocale(thread, availableLocales, noExtensionsLocale.base); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + + // If locale is undefined, let locale be "und". + if (locale.empty()) { + locale = "und"; + } + + // Let uString be a List containing in order the code points of S as defined in ES2020, 6.1.4, + // starting at the first element of S. + // Transform those elements in uString to the to the Unicode Default Case Conversion algorithm + icu::Locale icuLocale = icu::Locale::createFromName(locale.c_str()); + std::u16string utf16String; + if (string->IsUtf16()) { + utf16String = + ecmascript::base::StringHelper::Utf16ToU16String(string->GetDataUtf16(), string->GetUtf16Length()); + } else { + const uint8_t *uint8This = string->GetDataUtf8(); + utf16String = ecmascript::base::StringHelper::Utf8ToU16String(uint8This, string->GetLength()); + } + icu::UnicodeString uString(utf16String.data()); + icu::UnicodeString res = uString.toLower(icuLocale); + std::string CSLower; + res.toUTF8String(CSLower); + JSHandle result = factory->NewFromStdString(CSLower); + return result.GetTaggedValue(); +} + +// 21.1.3.21 +JSTaggedValue BuiltinsString::ToLocaleUpperCase(EcmaRuntimeCallInfo *argv) +{ + ASSERT(argv); + BUILTINS_API_TRACE(argv->GetThread(), String, ToLocaleLowerCase); + JSThread *thread = argv->GetThread(); + EcmaVM *ecmaVm = thread->GetEcmaVM(); + ObjectFactory *factory = ecmaVm->GetFactory(); + [[maybe_unused]] EcmaHandleScope handleScope(thread); + + // Let O be RequireObjectCoercible(this value). + JSHandle obj(JSTaggedValue::RequireObjectCoercible(thread, GetThis(argv))); + + // Let S be ? ToString(O). + JSHandle string = JSTaggedValue::ToString(thread, obj); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + + // Let requestedLocales be ? CanonicalizeLocaleList(locales). + JSHandle locales = GetCallArg(argv, 0); + JSHandle requestedLocales = JSLocale::CanonicalizeLocaleList(thread, locales); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + + // If requestedLocales is not an empty List, then Let requestedLocale be requestedLocales[0]. + // Else, Let requestedLocale be DefaultLocale(). + JSHandle requestedLocale = JSLocale::DefaultLocale(thread); + if (requestedLocales->GetLength() != 0) { + requestedLocale = JSHandle(thread, requestedLocales->Get(0)); + } + + // Let noExtensionsLocale be the String value that is requestedLocale with all Unicode locale extension sequences + // removed. + JSLocale::ParsedLocale noExtensionsLocale = JSLocale::HandleLocale(requestedLocale); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + + // Let availableLocales be a List with language tags that includes the languages for which the Unicode Character + // Database contains language sensitive case mappings. Implementations may add additional language tags + // if they support case mapping for additional locales. + JSHandle availableLocales = JSLocale::GetAvailableLocales(thread, nullptr, nullptr); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + + // Let locale be BestAvailableLocale(availableLocales, noExtensionsLocale). + std::string locale = JSLocale::BestAvailableLocale(thread, availableLocales, noExtensionsLocale.base); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + + // If locale is undefined, let locale be "und". + if (locale.empty()) { + locale = "und"; + } + + // Let uString be a List containing in order the code points of S as defined in ES2020, 6.1.4, + // starting at the first element of S. + // Transform those elements in uString to the to the Unicode Default Case Conversion algorithm + icu::Locale icuLocale = icu::Locale::createFromName(locale.c_str()); + std::u16string utf16String; + if (string->IsUtf16()) { + utf16String = + ecmascript::base::StringHelper::Utf16ToU16String(string->GetDataUtf16(), string->GetUtf16Length()); + } else { + const uint8_t *uint8This = string->GetDataUtf8(); + utf16String = ecmascript::base::StringHelper::Utf8ToU16String(uint8This, string->GetLength()); + } + icu::UnicodeString uString(utf16String.data()); + icu::UnicodeString res = uString.toUpper(icuLocale); + std::string CSUpper; + res.toUTF8String(CSUpper); + JSHandle result = factory->NewFromStdString(CSUpper); + return result.GetTaggedValue(); +} + +static std::u16string ToU16String(EcmaRuntimeCallInfo *argv) +{ + JSThread *thread = argv->GetThread(); + [[maybe_unused]] EcmaHandleScope handleScope(thread); + JSHandle thisTag( + JSTaggedValue::RequireObjectCoercible(thread, ecmascript::base::BuiltinsBase::GetThis(argv))); + JSHandle thisHandle = JSTaggedValue::ToString(thread, thisTag); + if (thread->HasPendingException()) { + return std::u16string(); + } + + int32_t thisLen = thisHandle->GetLength(); + if (thisHandle->IsUtf16()) { + return ecmascript::base::StringHelper::Utf16ToU16String(thisHandle->GetDataUtf16(), thisLen); + } + + const uint8_t *uint8This = thisHandle->GetDataUtf8(); + return ecmascript::base::StringHelper::Utf8ToU16String(uint8This, thisLen); +} + +// 21.1.3.22 +JSTaggedValue BuiltinsString::ToLowerCase(EcmaRuntimeCallInfo *argv) +{ + ASSERT(argv); + BUILTINS_API_TRACE(argv->GetThread(), String, ToLowerCase); + JSThread *thread = argv->GetThread(); + std::u16string u16strThis = ToU16String(argv); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + return JSTaggedValue(ecmascript::base::StringHelper::ToLower(thread, u16strThis)); +} + +// 21.1.3.23 +JSTaggedValue BuiltinsString::ToString(EcmaRuntimeCallInfo *argv) +{ + ASSERT(argv); + return ThisStringValue(argv->GetThread(), GetThis(argv).GetTaggedValue()); +} + +// 21.1.3.24 +JSTaggedValue BuiltinsString::ToUpperCase(EcmaRuntimeCallInfo *argv) +{ + ASSERT(argv); + BUILTINS_API_TRACE(argv->GetThread(), String, ToUpperCase); + JSThread *thread = argv->GetThread(); + std::u16string u16strThis = ToU16String(argv); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + return JSTaggedValue(ecmascript::base::StringHelper::ToUpper(thread, u16strThis)); +} + +// 21.1.3.25 +JSTaggedValue BuiltinsString::Trim(EcmaRuntimeCallInfo *argv) +{ + ASSERT(argv); + BUILTINS_API_TRACE(argv->GetThread(), String, Trim); + JSThread *thread = argv->GetThread(); + std::u16string u16strThis = ToU16String(argv); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + EcmaString *str = ecmascript::base::StringHelper::Trim(thread, u16strThis, base::TrimKind::TRIM_START_END); + return JSTaggedValue(str); +} + +// ES2021 22.1.3.30 +JSTaggedValue BuiltinsString::TrimEnd(EcmaRuntimeCallInfo *argv) +{ + ASSERT(argv); + BUILTINS_API_TRACE(argv->GetThread(), String, Trim); + JSThread *thread = argv->GetThread(); + std::u16string u16strThis = ToU16String(argv); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + EcmaString *str = ecmascript::base::StringHelper::Trim(thread, u16strThis, base::TrimKind::TRIM_END); + return JSTaggedValue(str); +} + +// ES2021 22.1.3.31 +JSTaggedValue BuiltinsString::TrimStart(EcmaRuntimeCallInfo *argv) +{ + ASSERT(argv); + BUILTINS_API_TRACE(argv->GetThread(), String, Trim); + JSThread *thread = argv->GetThread(); + std::u16string u16strThis = ToU16String(argv); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + EcmaString *str = ecmascript::base::StringHelper::Trim(thread, u16strThis, base::TrimKind::TRIM_START); + return JSTaggedValue(str); +} + +// 21.1.3.26 +JSTaggedValue BuiltinsString::ValueOf(EcmaRuntimeCallInfo *argv) +{ + ASSERT(argv); + return ThisStringValue(argv->GetThread(), GetThis(argv).GetTaggedValue()); +} + +// 21.1.3.27 +JSTaggedValue BuiltinsString::GetStringIterator(EcmaRuntimeCallInfo *argv) +{ + ASSERT(argv); + BUILTINS_API_TRACE(argv->GetThread(), String, GetStringIterator); + JSThread *thread = argv->GetThread(); + [[maybe_unused]] EcmaHandleScope handleScope(thread); + // 1. Let O be RequireObjectCoercible(this value). + JSHandle current(JSTaggedValue::RequireObjectCoercible(thread, GetThis(argv))); + // Let S be ToString(O). + + JSHandle string = JSTaggedValue::ToString(thread, current); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(argv->GetThread()); + // Return CreateStringIterator(S). + return JSStringIterator::CreateStringIterator(thread, string).GetTaggedValue(); +} + +// B.2.3.1 +JSTaggedValue BuiltinsString::SubStr(EcmaRuntimeCallInfo *argv) +{ + ASSERT(argv); + BUILTINS_API_TRACE(argv->GetThread(), String, SubStr); + JSThread *thread = argv->GetThread(); + + [[maybe_unused]] EcmaHandleScope handleScope(thread); + + // 1. Let O be RequireObjectCoercible(this value). + // 2. Let S be ToString(O). + + JSHandle thisTag(JSTaggedValue::RequireObjectCoercible(thread, GetThis(argv))); + JSHandle thisString = JSTaggedValue::ToString(thread, thisTag); + + // 3. ReturnIfAbrupt(S). + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + JSHandle intStart = GetCallArg(argv, 0); + // 4. Let intStart be ToInteger(start). + JSTaggedNumber numStart = JSTaggedValue::ToInteger(thread, intStart); + // 5. ReturnIfAbrupt(intStart). + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + int32_t start = numStart.ToInt32(); + JSHandle lengthTag = GetCallArg(argv, 1); + // 6. If length is undefined, let end be +; otherwise let end be ToInteger(length). + int32_t end; + if (lengthTag->IsUndefined()) { + end = INT_MAX; + } else { + JSTaggedNumber lengthNumber = JSTaggedValue::ToInteger(thread, lengthTag); + // 7. ReturnIfAbrupt(end). + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + end = lengthNumber.ToInt32(); + } + // 8. Let size be the number of code units in S. + int32_t size = thisString->GetLength(); + // 9. If intStart < 0, let intStart be max(size + intStart,0). + if (start < 0) { + start = std::max(size + start, 0); + } + // 10. Let resultLength be min(max(end,0), size – intStart). + int32_t resultLength = std::min(std::max(end, 0), size - start); + ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); + // 11. If resultLength  0, return the empty String "". + if (resultLength <= 0) { + return factory->GetEmptyString().GetTaggedValue(); + } + return JSTaggedValue(EcmaString::FastSubString(thisString, start, resultLength, thread->GetEcmaVM())); +} + +JSTaggedValue BuiltinsString::GetLength(EcmaRuntimeCallInfo *argv) +{ + ASSERT(argv); + JSThread *thread = argv->GetThread(); + [[maybe_unused]] EcmaHandleScope handleScope(thread); + JSHandle thisHandle = GetThis(argv); + + JSHandle thisString = JSTaggedValue::ToString(thread, thisHandle); + return GetTaggedInt(thisString->GetLength()); +} + +// 21.1.3 +JSTaggedValue BuiltinsString::ThisStringValue(JSThread *thread, JSTaggedValue value) +{ + if (value.IsString()) { + return value; + } + if (value.IsECMAObject()) { + auto jshClass = value.GetTaggedObject()->GetClass(); + if (jshClass->GetObjectType() == JSType::JS_PRIMITIVE_REF) { + JSTaggedValue primitive = JSPrimitiveRef::Cast(value.GetTaggedObject())->GetValue(); + if (primitive.IsString()) { + return primitive; + } + } + } + THROW_TYPE_ERROR_AND_RETURN(thread, "can not convert to String", JSTaggedValue::Exception()); +} + +int32_t BuiltinsString::ConvertDoubleToInt(double d) +{ + if (std::isnan(d) || d == -ecmascript::base::POSITIVE_INFINITY) { + return 0; + } + if (d >= static_cast(INT_MAX)) { + return INT_MAX; + } + if (d <= static_cast(INT_MIN)) { + return INT_MIN; + } + return ecmascript::base::NumberHelper::DoubleToInt(d, ecmascript::base::INT32_BITS); +} +} // namespace panda::ecmascript::builtins diff --git a/runtime/builtins/builtins_string.h b/runtime/builtins/builtins_string.h new file mode 100644 index 000000000..7d3ac3e90 --- /dev/null +++ b/runtime/builtins/builtins_string.h @@ -0,0 +1,128 @@ +/* + * Copyright (c) 2021-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_BUILTINS_BUILTINS_STRING_H +#define ECMASCRIPT_BUILTINS_BUILTINS_STRING_H + +#include "plugins/ecmascript/runtime/base/builtins_base.h" +#include "plugins/ecmascript/runtime/ecma_runtime_call_info.h" +#include "plugins/ecmascript/runtime/js_tagged_value.h" + +namespace panda::ecmascript::builtins { +constexpr int32_t ENCODE_MAX_UTF16 = 0X10FFFF; +constexpr uint16_t ENCODE_LEAD_LOW = 0xD800; +constexpr uint16_t ENCODE_TRAIL_LOW = 0xDC00; +constexpr uint32_t ENCODE_FIRST_FACTOR = 0x400; +constexpr uint32_t ENCODE_SECOND_FACTOR = 0x10000; +constexpr double DOUBLE_INT_MAX = static_cast(INT_MAX); +constexpr double DOUBLE_INT_MIN = static_cast(INT_MIN); + +class BuiltinsString : public ecmascript::base::BuiltinsBase { +public: + // 21.1.1.1 + static JSTaggedValue StringConstructor(EcmaRuntimeCallInfo *argv); + // 21.1.2.1 + static JSTaggedValue FromCharCode(EcmaRuntimeCallInfo *argv); + // 21.1.2.2 + static JSTaggedValue FromCodePoint(EcmaRuntimeCallInfo *argv); + // 21.1.2.4 + static JSTaggedValue Raw(EcmaRuntimeCallInfo *argv); + // 21.1.3.14.1 Runtime Semantics: GetSubstitution() + static JSTaggedValue GetSubstitution(JSThread *thread, const JSHandle &matched, + const JSHandle &srcString, int position, + const JSHandle &captureList, + const JSHandle &replacement); + // 21.1.3.1 + static JSTaggedValue CharAt(EcmaRuntimeCallInfo *argv); + // 21.1.3.2 + static JSTaggedValue CharCodeAt(EcmaRuntimeCallInfo *argv); + // 21.1.3.3 + static JSTaggedValue CodePointAt(EcmaRuntimeCallInfo *argv); + // 21.1.3.4 + static JSTaggedValue Concat(EcmaRuntimeCallInfo *argv); + // 21.1.3.5 String.prototype.constructor + // 21.1.3.6 + static JSTaggedValue EndsWith(EcmaRuntimeCallInfo *argv); + // 21.1.3.7 + static JSTaggedValue Includes(EcmaRuntimeCallInfo *argv); + // 21.1.3.8 + static JSTaggedValue IndexOf(EcmaRuntimeCallInfo *argv); + // 21.1.3.9 + static JSTaggedValue LastIndexOf(EcmaRuntimeCallInfo *argv); + // 21.1.3.10 + static JSTaggedValue LocaleCompare(EcmaRuntimeCallInfo *argv); + // 21.1.3.11 + static JSTaggedValue Match(EcmaRuntimeCallInfo *argv); + // 21.1.3.12 + static JSTaggedValue Normalize(EcmaRuntimeCallInfo *argv); + // ES2021 22.1.3.14 + static JSTaggedValue padEnd(EcmaRuntimeCallInfo *argv); + // ES2021 22.1.3.15 + static JSTaggedValue padStart(EcmaRuntimeCallInfo *argv); + // 21.1.3.13 + static JSTaggedValue Repeat(EcmaRuntimeCallInfo *argv); + // 21.1.3.14 + static JSTaggedValue Replace(EcmaRuntimeCallInfo *argv); + // ES2021 22.1.3.18 + static JSTaggedValue ReplaceAll(EcmaRuntimeCallInfo *argv); + // 21.1.3.15 + static JSTaggedValue Search(EcmaRuntimeCallInfo *argv); + // 21.1.3.16 + static JSTaggedValue Slice(EcmaRuntimeCallInfo *argv); + // 21.1.3.17 + static JSTaggedValue Split(EcmaRuntimeCallInfo *argv); + // 21.1.3.17.1 Runtime Semantics: SplitMatch + // 21.1.3.18 + static JSTaggedValue StartsWith(EcmaRuntimeCallInfo *argv); + // 21.1.3.19 + static JSTaggedValue Substring(EcmaRuntimeCallInfo *argv); + // 21.1.3.20 + static JSTaggedValue ToLocaleLowerCase(EcmaRuntimeCallInfo *argv); + // 21.1.3.21 + static JSTaggedValue ToLocaleUpperCase(EcmaRuntimeCallInfo *argv); + // 21.1.3.22 + static JSTaggedValue ToLowerCase(EcmaRuntimeCallInfo *argv); + // 21.1.3.23 + static JSTaggedValue ToString(EcmaRuntimeCallInfo *argv); + // 21.1.3.24 + static JSTaggedValue ToUpperCase(EcmaRuntimeCallInfo *argv); + // 21.1.3.25 + static JSTaggedValue Trim(EcmaRuntimeCallInfo *argv); + // ES2021 22.1.3.30 + static JSTaggedValue TrimEnd(EcmaRuntimeCallInfo *argv); + // ES2021 22.1.3.31 + static JSTaggedValue TrimStart(EcmaRuntimeCallInfo *argv); + // 21.1.3.26 + static JSTaggedValue ValueOf(EcmaRuntimeCallInfo *argv); + // 21.1.3.27 + static JSTaggedValue GetStringIterator(EcmaRuntimeCallInfo *argv); + // 21.1.3 + static JSTaggedValue ThisStringValue(JSThread *thread, JSTaggedValue value); + // 21.1.2.27 + static JSTaggedValue CreateIterator(EcmaRuntimeCallInfo *argv); + // 10.1.2 + static uint16_t UTF16Decode(uint16_t lead, uint16_t trail); + // annexB B.2.3.1 + static JSTaggedValue SubStr(EcmaRuntimeCallInfo *argv); + + static JSTaggedValue GetLength(EcmaRuntimeCallInfo *argv); + +private: + static int32_t ConvertDoubleToInt(double d); + // 21.1.3.17.1 + static int32_t SplitMatch(const JSHandle &str, int32_t q, const JSHandle ®); +}; +} // namespace panda::ecmascript::builtins +#endif // ECMASCRIPT_BUILTINS_BUILTINS_STRING_H diff --git a/runtime/builtins/builtins_string_iterator.cpp b/runtime/builtins/builtins_string_iterator.cpp new file mode 100644 index 000000000..1eecb7082 --- /dev/null +++ b/runtime/builtins/builtins_string_iterator.cpp @@ -0,0 +1,95 @@ +/* + * Copyright (c) 2021-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 "builtins_string_iterator.h" +#include "plugins/ecmascript/runtime/base/string_helper.h" +#include "plugins/ecmascript/runtime/ecma_macros.h" +#include "plugins/ecmascript/runtime/js_iterator.h" +#include "plugins/ecmascript/runtime/js_string_iterator.h" +#include "plugins/ecmascript/runtime/js_tagged_number.h" +#include "plugins/ecmascript/runtime/js_tagged_value.h" +#include "plugins/ecmascript/runtime/object_factory.h" + +namespace panda::ecmascript::builtins { +JSTaggedValue BuiltinsStringIterator::Next(EcmaRuntimeCallInfo *argv) +{ + ASSERT(argv); + BUILTINS_API_TRACE(argv->GetThread(), StringIterator, Next); + JSThread *thread = argv->GetThread(); + [[maybe_unused]] EcmaHandleScope handleScope(thread); + // 1. Let O be the this value. + JSHandle thisValue = GetThis(argv); + // 2. If Type(O) is not Object, throw a TypeError exception. + // 3. If O does not have all of the internal slots of an String Iterator Instance (21.1.5.3), + // throw a TypeError exception. + if (!thisValue->IsStringIterator()) { + THROW_TYPE_ERROR_AND_RETURN(thread, "is not StringIterator", JSTaggedValue::Exception()); + } + // 4. Let s be the value of the [[IteratedString]] internal slot of O. + JSHandle string(thread, thisValue.GetObject()->GetIteratedString()); + // 5. If s is undefined, return CreateIterResultObject(undefined, true). + if (string->IsUndefined()) { + return JSIterator::CreateIterResultObject(thread, string, true).GetTaggedValue(); + } + // 6. Let position be the value of the [[StringIteratorNextIndex]] internal slot of O. + double position = JSTaggedNumber(thisValue.GetObject()->GetStringIteratorNextIndex()).GetNumber(); + + // 7. Let len be the number of elements in s. + uint32_t len = string.GetObject()->GetLength(); + // If position ≥ len, then + // a. Set the value of the [[IteratedString]] internal slot of O to + // b. Return CreateIterResultObject(undefined, true). + if (position >= len) { + thisValue.GetObject()->SetIteratedString(thread, JSTaggedValue::Undefined()); + JSHandle result(thread, JSTaggedValue::Undefined()); + return JSIterator::CreateIterResultObject(thread, result, true).GetTaggedValue(); + } + + // 9. Let first be the code unit value at index position in s. + uint16_t first = string.GetObject()->At(position); + JSMutableHandle result(thread, JSTaggedValue::Undefined()); + uint32_t resultSize = 1; + // 10. If first < 0xD800 or first > 0xDBFF or position+1 = len, let resultString be the string consisting of the + // single code unit first. + ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); + if (position + 1 == len || first < ecmascript::base::utf_helper::DECODE_LEAD_LOW || + first > ecmascript::base::utf_helper::DECODE_LEAD_HIGH) { + std::vector resultString {first, 0x0}; + result.Update(factory->NewFromUtf16UnCheck(resultString.data(), 1, true).GetTaggedValue()); + } else { + // 11. Else, + // a. Let second be the code unit value at index position+1 in the String S. + // b. If second < 0xDC00 or second > 0xDFFF, let resultString be the string consisting of the single code unit + // first. + // c. Else, let resultString be the string consisting of the code unit first followed by the code unit second. + uint16_t second = string.GetObject()->At(position + 1); + if (second < ecmascript::base::utf_helper::DECODE_TRAIL_LOW || second > ecmascript::base::utf_helper::DECODE_TRAIL_HIGH) { + std::vector resultString {first, 0x0}; + result.Update(factory->NewFromUtf16UnCheck(resultString.data(), 1, false).GetTaggedValue()); + } else { + std::vector resultString {first, second, 0x0}; + result.Update( + factory->NewFromUtf16UnCheck(resultString.data(), 2, false).GetTaggedValue()); // 2: two bytes + resultSize = 2; // 2: 2 means that two bytes represent a character string + } + } + // 12. Let resultSize be the number of code units in resultString. + // 13. Set the value of the [[StringIteratorNextIndex]] internal slot of O to position+ resultSize. + thisValue.GetObject()->SetStringIteratorNextIndex(thread, JSTaggedValue(position + resultSize)); + + // 14. Return CreateIterResultObject(resultString, false). + return JSIterator::CreateIterResultObject(thread, result, false).GetTaggedValue(); +} +} // namespace panda::ecmascript::builtins diff --git a/runtime/builtins/builtins_string_iterator.h b/runtime/builtins/builtins_string_iterator.h new file mode 100644 index 000000000..815313e22 --- /dev/null +++ b/runtime/builtins/builtins_string_iterator.h @@ -0,0 +1,28 @@ +/* + * Copyright (c) 2021-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_BUILTINS_BUILTINS_STRING_ITERATOR_H +#define ECMASCRIPT_BUILTINS_BUILTINS_STRING_ITERATOR_H + +#include "plugins/ecmascript/runtime/base/builtins_base.h" + +namespace panda::ecmascript::builtins { +class BuiltinsStringIterator : public ecmascript::base::BuiltinsBase { +public: + // 21.1.5.2.1 + static JSTaggedValue Next(EcmaRuntimeCallInfo *argv); +}; +} // namespace panda::ecmascript::builtins +#endif // ECMASCRIPT_BUILTINS_BUILTINS_STRING_ITERATOR_H diff --git a/runtime/builtins/builtins_symbol.cpp b/runtime/builtins/builtins_symbol.cpp new file mode 100644 index 000000000..2de586040 --- /dev/null +++ b/runtime/builtins/builtins_symbol.cpp @@ -0,0 +1,262 @@ +/* + * Copyright (c) 2021-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 "plugins/ecmascript/runtime/builtins/builtins_symbol.h" +#include "plugins/ecmascript/runtime/ecma_runtime_call_info.h" +#include "plugins/ecmascript/runtime/ecma_string.h" +#include "plugins/ecmascript/runtime/global_env.h" +#include "plugins/ecmascript/runtime/js_primitive_ref.h" +#include "plugins/ecmascript/runtime/js_symbol.h" +#include "plugins/ecmascript/runtime/js_tagged_value.h" +#include "plugins/ecmascript/runtime/object_factory.h" +#include "plugins/ecmascript/runtime/symbol_table-inl.h" +#include "utils/bit_utils.h" + +namespace panda::ecmascript::builtins { +// prototype +// 19.4.3.1 +// constructor +JSTaggedValue BuiltinsSymbol::SymbolConstructor(EcmaRuntimeCallInfo *argv) +{ + ASSERT(argv); + BUILTINS_API_TRACE(argv->GetThread(), Symbol, Constructor); + JSThread *thread = argv->GetThread(); + [[maybe_unused]] EcmaHandleScope handleScope(thread); + // 1.If NewTarget is not undefined, throw a TypeError exception. + JSHandle key = GetCallArg(argv, 0); + JSHandle newTarget = GetNewTarget(argv); + if (!newTarget->IsUndefined()) { + THROW_TYPE_ERROR_AND_RETURN(thread, "SymbolConstructor: NewTarget is not undefined", + JSTaggedValue::Exception()); + } + // 2.If description is undefined, let descString be undefined. + ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); + if (key->IsUndefined()) { + JSHandle jsSymbol = factory->NewJSSymbol(); + return jsSymbol.GetTaggedValue(); + } + // 3.Else, let descString be ToString(description). + JSHandle descHandle = JSHandle::Cast(JSTaggedValue::ToString(thread, key)); + // 4.ReturnIfAbrupt(descString). + RETURN_VALUE_IF_ABRUPT_COMPLETION(thread, JSTaggedValue::Exception()); + // 5.Return a new unique Symbol value whose [[Description]] value is descString. + JSHandle jsSymbol = factory->NewPublicSymbol(descHandle); + return jsSymbol.GetTaggedValue(); +} + +// 19.4.3.2 Symbol.prototype.toString() +JSTaggedValue BuiltinsSymbol::ToString(EcmaRuntimeCallInfo *argv) +{ + ASSERT(argv); + BUILTINS_API_TRACE(argv->GetThread(), Symbol, ToString); + JSThread *thread = argv->GetThread(); + [[maybe_unused]] EcmaHandleScope handleScope(thread); + // 1.Let s be the this value. + JSHandle valueHandle = GetThis(argv); + // 2.If Type(s) is Symbol, let sym be s. + JSTaggedValue sym = valueHandle.GetTaggedValue(); + // 3.Else + if (!valueHandle->IsSymbol()) { + if (valueHandle->IsObject()) { + if (!valueHandle->IsJSPrimitiveRef()) { + // If s does not have a [[SymbolData]] internal slot, throw a TypeError exception. + THROW_TYPE_ERROR_AND_RETURN(thread, "ToString: no [[SymbolData]]", JSTaggedValue::Exception()); + } + // Let sym be the value of s's [[SymbolData]] internal slot. + JSTaggedValue primitive = JSPrimitiveRef::Cast(valueHandle->GetTaggedObject())->GetValue(); + ASSERT(primitive.IsSymbol()); + sym = primitive; + } else { + // If Type(s) is not Object, throw a TypeError exception. + THROW_TYPE_ERROR_AND_RETURN(thread, "ToString: s is not Object", JSTaggedValue::Exception()); + } + } + // Return SymbolDescriptiveString(sym). + return SymbolDescriptiveString(thread, sym); +} + +JSTaggedValue BuiltinsSymbol::SymbolDescriptiveString(JSThread *thread, JSTaggedValue sym) +{ + BUILTINS_API_TRACE(thread, Symbol, SymbolDescriptiveString); + // Assert: Type(sym) is Symbol. + ASSERT(sym.IsSymbol()); + ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); + + // Let desc be sym’s [[Description]] value. + auto symbolObject = reinterpret_cast(sym.GetTaggedObject()); + JSHandle descHandle(thread, symbolObject->GetDescription()); + + // If desc is undefined, let desc be the empty string. + + if (descHandle->IsUndefined()) { + JSHandle leftHandle(factory->NewFromCanBeCompressString("Symbol(")); + JSHandle rightHandle(factory->NewFromCanBeCompressString(")")); + JSHandle str = factory->ConcatFromString(leftHandle, rightHandle); + return str.GetTaggedValue(); + } + // Assert: Type(desc) is String. + ASSERT(descHandle->IsString()); + // Return the result of concatenating the strings "Symbol(", desc, and ")". + JSHandle leftHandle(factory->NewFromCanBeCompressString("Symbol(")); + JSHandle rightHandle(factory->NewFromCanBeCompressString(")")); + JSHandle stringLeft = + factory->ConcatFromString(leftHandle, JSTaggedValue::ToString(thread, descHandle)); + JSHandle str = factory->ConcatFromString(stringLeft, rightHandle); + return str.GetTaggedValue(); +} + +// 19.4.3.3 +JSTaggedValue BuiltinsSymbol::ValueOf(EcmaRuntimeCallInfo *argv) +{ + ASSERT(argv); + BUILTINS_API_TRACE(argv->GetThread(), Symbol, ValueOf); + JSThread *thread = argv->GetThread(); + [[maybe_unused]] EcmaHandleScope handleScope(thread); + // Let s be the this value. + JSHandle valueHandle = GetThis(argv); + // If Type(s) is Symbol, return s. + if (valueHandle->IsSymbol()) { + return valueHandle.GetTaggedValue(); + } + // If Type(s) is not Object, throw a TypeError exception. + if (!valueHandle->IsObject()) { + // return TypeError + THROW_TYPE_ERROR_AND_RETURN(thread, "ValueOf: s is not Object", JSTaggedValue::Exception()); + } + // If s does not have a [[SymbolData]] internal slot, throw a TypeError exception. + if (!valueHandle->IsJSPrimitiveRef()) { + // If s does not have a [[SymbolData]] internal slot, throw a TypeError exception. + THROW_TYPE_ERROR_AND_RETURN(thread, "ValueOf: no [[SymbolData]]", JSTaggedValue::Exception()); + } + JSTaggedValue primitive = JSPrimitiveRef::Cast(valueHandle->GetTaggedObject())->GetValue(); + ASSERT(primitive.IsSymbol()); + // Return the value of s's [[SymbolData]] internal slot. + return primitive; +} + +// 19.4.2.1 Symbol.for (key) +JSTaggedValue BuiltinsSymbol::For(EcmaRuntimeCallInfo *argv) +{ + ASSERT(argv); + BUILTINS_API_TRACE(argv->GetThread(), Symbol, For); + JSThread *thread = argv->GetThread(); + [[maybe_unused]] EcmaHandleScope handleScope(thread); + // 1.Let stringKey be ToString(key). + JSHandle key = BuiltinsSymbol::GetCallArg(argv, 0); + JSHandle stringHandle = JSHandle::Cast(JSTaggedValue::ToString(thread, key)); + // 2.ReturnIfAbrupt + RETURN_VALUE_IF_ABRUPT_COMPLETION(thread, JSTaggedValue::Exception()); + + // 3.For each element e of the GlobalSymbolRegistry List, + // If SameValue(e.[[key]], stringKey) is true, return e.[[symbol]]. + // 4.Assert: GlobalSymbolRegistry does not currently contain an entry for stringKey. + // 5.Let newSymbol be a new unique Symbol value whose [[Description]] value is stringKey. + // 6.Append the record { [[key]]: stringKey, [[symbol]]: newSymbol } to the GlobalSymbolRegistry List. + // 7.Return newSymbol. + ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); + JSHandle symbol = factory->NewSymbolWithTable(stringHandle); + return symbol.GetTaggedValue(); +} + +// 19.4.2.5 Symbol.keyFor (sym) +JSTaggedValue BuiltinsSymbol::KeyFor(EcmaRuntimeCallInfo *argv) +{ + ASSERT(argv); + BUILTINS_API_TRACE(argv->GetThread(), Symbol, KeyFor); + JSThread *thread = argv->GetThread(); + [[maybe_unused]] EcmaHandleScope handleScope(thread); + // 1.If Type(sym) is not Symbol, throw a TypeError exception. + JSHandle sym = BuiltinsSymbol::GetCallArg(argv, 0); + if (!sym->IsSymbol()) { + // return typeError + THROW_TYPE_ERROR_AND_RETURN(thread, "KeyFor: sym is not Symbol", JSTaggedValue::Exception()); + } + // 2.For each element e of the GlobalSymbolRegistry List, + // If SameValue(e.[[symbol]], sym) is true, return e.[[key]]. + // 3.Assert: GlobalSymbolRegistry does not currently contain an entry for sym. + // 4.Return undefined. + JSHandle env = thread->GetEcmaVM()->GetGlobalEnv(); + auto *table = env->GetRegisterSymbols().GetObject(); + JSTaggedValue key = table->FindSymbol(thread, sym.GetTaggedValue()); + if (key.IsUndefined()) { + return JSTaggedValue::Undefined(); + } + return JSTaggedValue::ToString(thread, JSHandle(thread, key)).GetTaggedValue(); +} + +// 19.4.3.4 Symbol.prototype [ @@toPrimitive ] ( hint ) +JSTaggedValue BuiltinsSymbol::ToPrimitive(EcmaRuntimeCallInfo *argv) +{ + // The allowed values for hint are "default", "number", and "string". + ASSERT(argv); + BUILTINS_API_TRACE(argv->GetThread(), Symbol, ToPrimitive); + JSThread *thread = argv->GetThread(); + [[maybe_unused]] EcmaHandleScope handleScope(thread); + // 1.Let s be the this value. + JSHandle sym = GetThis(argv); + // 2.If Type(s) is Symbol, return s. + if (sym->IsSymbol()) { + return sym.GetTaggedValue(); + } + // 3.If Type(s) is not Object, throw a TypeError exception. + if (!sym->IsObject()) { + // return TypeError + THROW_TYPE_ERROR_AND_RETURN(thread, "ToPrimitive: s is not Object", JSTaggedValue::Exception()); + } + ASSERT(sym->IsObject()); + // 4.If s does not have a [[SymbolData]] internal slot, throw a TypeError exception. + // 5.Return the value of s's [[SymbolData]] internal slot. + if (!sym->IsJSPrimitiveRef()) { + // If s does not have a [[SymbolData]] internal slot, throw a TypeError exception. + THROW_TYPE_ERROR_AND_RETURN(thread, "ToPrimitive: no [[SymbolData]]", JSTaggedValue::Exception()); + } + // Let sym be the value of s's [[SymbolData]] internal slot. + JSTaggedValue primitive = JSPrimitiveRef::Cast(sym->GetTaggedObject())->GetValue(); + ASSERT(primitive.IsSymbol()); + return primitive; +} + +JSTaggedValue BuiltinsSymbol::DescriptionGetter(EcmaRuntimeCallInfo *argv) +{ + ASSERT(argv); + BUILTINS_API_TRACE(argv->GetThread(), Symbol, DescriptionGetter); + // 1.Let s be the this value. + JSThread *thread = argv->GetThread(); + [[maybe_unused]] EcmaHandleScope handleScope(thread); + JSHandle s = GetThis(argv); + // 2.Let sym be ? thisSymbolValue(s). + // 3.Return sym.[[Description]]. + return ThisSymbolValue(thread, s); +} + +JSTaggedValue BuiltinsSymbol::ThisSymbolValue(JSThread *thread, const JSHandle &value) +{ + BUILTINS_API_TRACE(thread, Symbol, ThisSymbolValue); + if (value->IsSymbol()) { + JSTaggedValue desValue = JSSymbol::Cast(value->GetTaggedObject())->GetDescription(); + return desValue; + } + + // If s does not have a [[SymbolData]] internal slot, throw a TypeError exception. + if (!value->IsJSPrimitiveRef()) { + THROW_TYPE_ERROR_AND_RETURN(thread, "ThisSymbolValue: no SymbolData", JSTaggedValue::Exception()); + } + JSTaggedValue primitive = JSPrimitiveRef::Cast(value->GetTaggedObject())->GetValue(); + ASSERT(primitive.IsSymbol()); + // Return the value of s's [[SymbolData]] internal slot. + JSTaggedValue primitiveDesValue = JSSymbol::Cast(primitive.GetTaggedObject())->GetDescription(); + return primitiveDesValue; +} +} // namespace panda::ecmascript::builtins diff --git a/runtime/builtins/builtins_symbol.h b/runtime/builtins/builtins_symbol.h new file mode 100644 index 000000000..9cf538c12 --- /dev/null +++ b/runtime/builtins/builtins_symbol.h @@ -0,0 +1,50 @@ +/* + * Copyright (c) 2021-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_BUILTINS_BUILTINS_SYMBOL_H +#define ECMASCRIPT_BUILTINS_BUILTINS_SYMBOL_H + +#include "plugins/ecmascript/runtime/base/builtins_base.h" +#include "plugins/ecmascript/runtime/ecma_runtime_call_info.h" +#include "plugins/ecmascript/runtime/js_tagged_value.h" + +namespace panda::ecmascript::builtins { +class BuiltinsSymbol : public ecmascript::base::BuiltinsBase { +public: + // 19.4.1 + static JSTaggedValue SymbolConstructor(EcmaRuntimeCallInfo *argv); + + // prototype + // 19.4.3.2 + static JSTaggedValue ToString(EcmaRuntimeCallInfo *argv); + // 19.4.3.3 + static JSTaggedValue ValueOf(EcmaRuntimeCallInfo *argv); + // 19.4.2.1 Symbol.for (key) + static JSTaggedValue For(EcmaRuntimeCallInfo *argv); + // 19.4.2.5 Symbol.keyFor (sym) + static JSTaggedValue KeyFor(EcmaRuntimeCallInfo *argv); + + // 19.4.3.2 get Symbol.prototype.description + static JSTaggedValue DescriptionGetter(EcmaRuntimeCallInfo *argv); + + static JSTaggedValue ThisSymbolValue(JSThread *thread, const JSHandle &value); + + // 19.4.3.4 + static JSTaggedValue ToPrimitive(EcmaRuntimeCallInfo *argv); + + static JSTaggedValue SymbolDescriptiveString(JSThread *thread, JSTaggedValue sym); +}; +} // namespace panda::ecmascript::builtins +#endif // ECMASCRIPT_BUILTINS_BUILTINS_SYMBOL_H diff --git a/runtime/builtins/builtins_typedarray.cpp b/runtime/builtins/builtins_typedarray.cpp new file mode 100644 index 000000000..8e8a93df6 --- /dev/null +++ b/runtime/builtins/builtins_typedarray.cpp @@ -0,0 +1,1438 @@ +/* + * Copyright (c) 2021-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 "plugins/ecmascript/runtime/builtins/builtins_typedarray.h" +#include +#include "plugins/ecmascript/runtime/base/typed_array_helper-inl.h" +#include "plugins/ecmascript/runtime/base/typed_array_helper.h" +#include "plugins/ecmascript/runtime/builtins/builtins_array.h" +#include "plugins/ecmascript/runtime/builtins/builtins_arraybuffer.h" +#include "plugins/ecmascript/runtime/ecma_runtime_call_info.h" +#include "plugins/ecmascript/runtime/ecma_string.h" +#include "plugins/ecmascript/runtime/global_env.h" +#include "plugins/ecmascript/runtime/internal_call_params.h" +#include "plugins/ecmascript/runtime/js_array.h" +#include "plugins/ecmascript/runtime/js_array_iterator.h" +#include "plugins/ecmascript/runtime/js_function.h" +#include "plugins/ecmascript/runtime/js_handle.h" +#include "plugins/ecmascript/runtime/js_iterator.h" +#include "plugins/ecmascript/runtime/js_tagged_number.h" +#include "plugins/ecmascript/runtime/js_tagged_value-inl.h" +#include "plugins/ecmascript/runtime/js_typed_array.h" +#include "plugins/ecmascript/runtime/object_factory.h" + +namespace panda::ecmascript::builtins { +using TypedArrayHelper = ecmascript::base::TypedArrayHelper; +using BuiltinsArray = builtins::BuiltinsArray; +using BuiltinsArrayBuffer = builtins::BuiltinsArrayBuffer; + +// 22.2.1 +JSTaggedValue BuiltinsTypedArray::TypedArrayBaseConstructor(EcmaRuntimeCallInfo *argv) +{ + ASSERT(argv); + BUILTINS_API_TRACE(argv->GetThread(), TypedArray, BaseConstructor); + THROW_TYPE_ERROR_AND_RETURN(argv->GetThread(), "TypedArray Constructor cannot be called.", + JSTaggedValue::Exception()); +} + +JSTaggedValue BuiltinsTypedArray::Int8ArrayConstructor(EcmaRuntimeCallInfo *argv) +{ + ASSERT(argv); + JSThread *thread = argv->GetThread(); + return TypedArrayHelper::TypedArrayConstructor(argv, thread->GlobalConstants()->GetHandledInt8ArrayString()); +} + +JSTaggedValue BuiltinsTypedArray::Uint8ArrayConstructor(EcmaRuntimeCallInfo *argv) +{ + ASSERT(argv); + JSThread *thread = argv->GetThread(); + return TypedArrayHelper::TypedArrayConstructor(argv, thread->GlobalConstants()->GetHandledUint8ArrayString()); +} + +JSTaggedValue BuiltinsTypedArray::Uint8ClampedArrayConstructor(EcmaRuntimeCallInfo *argv) +{ + ASSERT(argv); + JSThread *thread = argv->GetThread(); + return TypedArrayHelper::TypedArrayConstructor(argv, + thread->GlobalConstants()->GetHandledUint8ClampedArrayString()); +} + +JSTaggedValue BuiltinsTypedArray::Int16ArrayConstructor(EcmaRuntimeCallInfo *argv) +{ + ASSERT(argv); + JSThread *thread = argv->GetThread(); + return TypedArrayHelper::TypedArrayConstructor(argv, thread->GlobalConstants()->GetHandledInt16ArrayString()); +} + +JSTaggedValue BuiltinsTypedArray::Uint16ArrayConstructor(EcmaRuntimeCallInfo *argv) +{ + ASSERT(argv); + JSThread *thread = argv->GetThread(); + return TypedArrayHelper::TypedArrayConstructor(argv, thread->GlobalConstants()->GetHandledUint16ArrayString()); +} + +JSTaggedValue BuiltinsTypedArray::Int32ArrayConstructor(EcmaRuntimeCallInfo *argv) +{ + ASSERT(argv); + JSThread *thread = argv->GetThread(); + return TypedArrayHelper::TypedArrayConstructor(argv, thread->GlobalConstants()->GetHandledInt32ArrayString()); +} + +JSTaggedValue BuiltinsTypedArray::Uint32ArrayConstructor(EcmaRuntimeCallInfo *argv) +{ + ASSERT(argv); + JSThread *thread = argv->GetThread(); + return TypedArrayHelper::TypedArrayConstructor(argv, thread->GlobalConstants()->GetHandledUint32ArrayString()); +} + +JSTaggedValue BuiltinsTypedArray::Float32ArrayConstructor(EcmaRuntimeCallInfo *argv) +{ + ASSERT(argv); + JSThread *thread = argv->GetThread(); + return TypedArrayHelper::TypedArrayConstructor(argv, thread->GlobalConstants()->GetHandledFloat32ArrayString()); +} + +JSTaggedValue BuiltinsTypedArray::Float64ArrayConstructor(EcmaRuntimeCallInfo *argv) +{ + ASSERT(argv); + JSThread *thread = argv->GetThread(); + return TypedArrayHelper::TypedArrayConstructor(argv, thread->GlobalConstants()->GetHandledFloat64ArrayString()); +} + +// 22.2.2.1 %TypedArray%.from ( source [ , mapfn [ , thisArg ] ] ) +JSTaggedValue BuiltinsTypedArray::From(EcmaRuntimeCallInfo *argv) +{ + ASSERT(argv); + BUILTINS_API_TRACE(argv->GetThread(), TypedArray, From); + JSThread *thread = argv->GetThread(); + [[maybe_unused]] EcmaHandleScope handleScope(thread); + JSHandle env = thread->GetEcmaVM()->GetGlobalEnv(); + // 1. Let C be the this value. + // 2. If IsConstructor(C) is false, throw a TypeError exception. + JSHandle thisHandle = GetThis(argv); + if (!thisHandle->IsConstructor()) { + THROW_TYPE_ERROR_AND_RETURN(thread, "the this value is not a Constructor.", JSTaggedValue::Exception()); + } + + JSHandle thisArgHandle = GetCallArg(argv, BuiltinsBase::ArgsPosition::THIRD); + // 3. If mapfn is undefined, let mapping be false. + // 4. Else, + // a. If IsCallable(mapfn) is false, throw a TypeError exception. + // b. Let mapping be true. + bool mapping = false; + JSHandle mapfn = GetCallArg(argv, 1); + if (!mapfn->IsUndefined()) { + if (!mapfn->IsCallable()) { + THROW_TYPE_ERROR_AND_RETURN(thread, "the mapfn is not callable.", JSTaggedValue::Exception()); + } + mapping = true; + } + // 5. Let usingIterator be ? GetMethod(source, @@iterator). + JSHandle source = GetCallArg(argv, 0); + if (!source->IsECMAObject()) { + THROW_TYPE_ERROR_AND_RETURN(thread, "the source is not an object.", JSTaggedValue::Exception()); + } + JSHandle sourceObj(source); + JSHandle iteratorSymbol = env->GetIteratorSymbol(); + JSHandle usingIterator = + JSObject::GetMethod(thread, JSHandle::Cast(sourceObj), iteratorSymbol); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + + // 6. If usingIterator is not undefined, then + // a. Let values be ? IterableToList(source, usingIterator). + // b. Let len be the number of elements in values. + // c. Let targetObj be ? TypedArrayCreate(C, « len »). + if (!usingIterator->IsUndefined()) { + CVector> vec; + JSHandle iterator = JSIterator::GetIterator(thread, source, usingIterator); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + JSHandle next(thread, JSTaggedValue::True()); + while (!next->IsFalse()) { + next = JSIterator::IteratorStep(thread, iterator); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + if (!next->IsFalse()) { + JSHandle nextValue = JSIterator::IteratorValue(thread, next); + RETURN_VALUE_IF_ABRUPT_COMPLETION(thread, nextValue.GetTaggedValue()); + vec.push_back(nextValue); + } + } + int32_t len = vec.size(); + InternalCallParams *arguments = thread->GetInternalCallParams(); + arguments->MakeArgv(JSTaggedValue(len)); + JSHandle targetObj = TypedArrayHelper::TypedArrayCreate(thread, thisHandle, 1, arguments->GetArgv()); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + // d. Let k be 0. + // e. Repeat, while k < len + // i. Let Pk be ! ToString(k). + // ii. Let kValue be the first element of values and remove that element from values. + // iii. If mapping is true, then + // 1. Let mappedValue be ? Call(mapfn, thisArg, « kValue, k »). + // iv. Else, let mappedValue be kValue. + // v. Perform ? Set(targetObj, Pk, mappedValue, true). + // vi. Set k to k + 1. + JSMutableHandle tKey(thread, JSTaggedValue::Undefined()); + JSMutableHandle mapValue(thread, JSTaggedValue::Undefined()); + double k = 0; + while (k < len) { + tKey.Update(JSTaggedValue(k)); + JSHandle kValue = vec[k]; + if (mapping) { + arguments->MakeArgv(kValue, tKey); + JSTaggedValue callResult = + JSFunction::Call(thread, mapfn, thisArgHandle, 2, arguments->GetArgv()); // 2: two args + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + mapValue.Update(callResult); + } else { + mapValue.Update(kValue.GetTaggedValue()); + } + JSHandle kKey(JSTaggedValue::ToString(thread, tKey)); + JSTaggedValue::SetProperty(thread, JSHandle::Cast(targetObj), kKey, mapValue, true); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + k++; + } + // f. Assert: values is now an empty List. + // g. Return targetObj. + return targetObj.GetTaggedValue(); + } + + // 7. NOTE: source is not an Iterable so assume it is already an array-like object. + // 8. Let arrayLike be ! ToObject(source). + JSHandle arrayLikeObj = JSTaggedValue::ToObject(thread, source); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + JSHandle arrayLike(arrayLikeObj); + // 9. Let len be ? LengthOfArrayLike(arrayLike). + JSHandle lengthKey = thread->GlobalConstants()->GetHandledLengthString(); + JSHandle lenResult = JSTaggedValue::GetProperty(thread, arrayLike, lengthKey).GetValue(); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + JSTaggedNumber tLen = JSTaggedValue::ToLength(thread, lenResult); + // 6. ReturnIfAbrupt(relativeTarget). + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + double len = tLen.GetNumber(); + + // 10. Let targetObj be ? TypedArrayCreate(C, « len »). + InternalCallParams *arguments = thread->GetInternalCallParams(); + arguments->MakeArgv(JSTaggedValue(len)); + JSHandle targetObj = TypedArrayHelper::TypedArrayCreate(thread, thisHandle, 1, arguments->GetArgv()); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + // 11. Let k be 0. + // 12. Repeat, while k < len + // a. Let Pk be ! ToString(k). + // b. Let kValue be ? Get(arrayLike, Pk). + // c. If mapping is true, then + // i. Let mappedValue be ? Call(mapfn, thisArg, « kValue, k »). + // d. Else, let mappedValue be kValue. + // e. Perform ? Set(targetObj, Pk, mappedValue, true). + // f. Set k to k + 1. + JSMutableHandle tKey(thread, JSTaggedValue::Undefined()); + double k = 0; + while (k < len) { + tKey.Update(JSTaggedValue(k)); + JSHandle kKey(JSTaggedValue::ToString(thread, tKey)); + JSHandle kValue = JSTaggedValue::GetProperty(thread, arrayLike, kKey).GetValue(); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + JSHandle mapValue; + if (mapping) { + arguments->MakeArgv(kValue, tKey); + JSTaggedValue callResult = + JSFunction::Call(thread, mapfn, thisArgHandle, 2, arguments->GetArgv()); // 2: two args + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + mapValue = JSHandle(thread, callResult); + } else { + mapValue = kValue; + } + JSTaggedValue::SetProperty(thread, JSHandle::Cast(targetObj), kKey, mapValue, true); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + k++; + } + // 13. Return targetObj. + return targetObj.GetTaggedValue(); +} + +// 22.2.2.2 %TypedArray%.of ( ...items ) +JSTaggedValue BuiltinsTypedArray::Of(EcmaRuntimeCallInfo *argv) +{ + ASSERT(argv); + BUILTINS_API_TRACE(argv->GetThread(), TypedArray, Of); + JSThread *thread = argv->GetThread(); + [[maybe_unused]] EcmaHandleScope handleScope(thread); + // 1. Let len be the actual number of arguments passed to this function. + uint32_t len = argv->GetArgsNumber(); + // 2. Let items be the List of arguments passed to this function. + // 3. Let C be the this value. + JSHandle thisHandle = GetThis(argv); + // 4. If IsConstructor(C) is false, throw a TypeError exception. + if (!thisHandle->IsConstructor()) { + THROW_TYPE_ERROR_AND_RETURN(thread, "the this value is not a Constructor.", JSTaggedValue::Exception()); + } + // 5. Let newObj be TypedArrayCreate(C, « len »). + InternalCallParams *arguments = thread->GetInternalCallParams(); + arguments->MakeArgv(JSTaggedValue(len)); + JSHandle newObj = TypedArrayHelper::TypedArrayCreate(thread, thisHandle, 1, arguments->GetArgv()); + // 6. ReturnIfAbrupt(newObj). + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + // 7. Let k be 0. + // 8. Repeat, while k < len + // a. Let kValue be items[k]. + // b. Let Pk be ! ToString(k). + // c. Perform ? Set(newObj, Pk, kValue, true). + // d. ReturnIfAbrupt(status). + // e. Set k to k + 1. + JSMutableHandle tKey(thread, JSTaggedValue::Undefined()); + double k = 0; + while (k < len) { + tKey.Update(JSTaggedValue(k)); + JSHandle kKey(JSTaggedValue::ToString(thread, tKey)); + JSHandle kValue = GetCallArg(argv, k); + JSTaggedValue::SetProperty(thread, JSHandle::Cast(newObj), kKey, kValue, true); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + k++; + } + // 9. Return newObj. + return newObj.GetTaggedValue(); +} + +// 22.2.2.4 +JSTaggedValue BuiltinsTypedArray::Species(EcmaRuntimeCallInfo *argv) +{ + ASSERT(argv); + // 1. Return the this value. + return GetThis(argv).GetTaggedValue(); +} + +// prototype +// 22.2.3.1 get %TypedArray%.prototype.buffer +JSTaggedValue BuiltinsTypedArray::GetBuffer(EcmaRuntimeCallInfo *argv) +{ + ASSERT(argv); + BUILTINS_API_TRACE(argv->GetThread(), TypedArray, GetBuffer); + JSThread *thread = argv->GetThread(); + [[maybe_unused]] EcmaHandleScope handleScope(thread); + // 1. Let O be the this value. + JSHandle thisHandle = GetThis(argv); + // 2. If Type(O) is not Object, throw a TypeError exception. + if (!thisHandle->IsECMAObject()) { + THROW_TYPE_ERROR_AND_RETURN(thread, "This value is not an object.", JSTaggedValue::Exception()); + } + // 3. If O does not have a [[ViewedArrayBuffer]] internal slot, throw a TypeError exception. + if (!thisHandle->IsTypedArray()) { + THROW_TYPE_ERROR_AND_RETURN(thread, "This value does not have a [[ViewedArrayBuffer]] internal slot.", + JSTaggedValue::Exception()); + } + // 4. Let buffer be the value of O’s [[ViewedArrayBuffer]] internal slot. + JSTaggedValue buffer = JSHandle::Cast(thisHandle)->GetViewedArrayBuffer(); + // 5. Return buffer. + return buffer; +} + +// 22.2.3.2 +JSTaggedValue BuiltinsTypedArray::GetByteLength(EcmaRuntimeCallInfo *argv) +{ + ASSERT(argv); + BUILTINS_API_TRACE(argv->GetThread(), TypedArray, GetByteLength); + JSThread *thread = argv->GetThread(); + [[maybe_unused]] EcmaHandleScope handleScope(thread); + // 1. Let O be the this value. + JSHandle thisHandle = GetThis(argv); + // 2. If Type(O) is not Object, throw a TypeError exception. + if (!thisHandle->IsECMAObject()) { + THROW_TYPE_ERROR_AND_RETURN(thread, "This value is not an object.", JSTaggedValue::Exception()); + } + // 3. If O does not have a [[ViewedArrayBuffer]] internal slot, throw a TypeError exception. + if (!thisHandle->IsTypedArray()) { + THROW_TYPE_ERROR_AND_RETURN(thread, "This value does not have a [[ViewedArrayBuffer]] internal slot.", + JSTaggedValue::Exception()); + } + // 4. Let buffer be the value of O’s [[ViewedArrayBuffer]] internal slot. + JSTaggedValue buffer = JSHandle::Cast(thisHandle)->GetViewedArrayBuffer(); + // 5. If IsDetachedBuffer(buffer) is true, return 0. + if (BuiltinsArrayBuffer::IsDetachedBuffer(buffer)) { + return JSTaggedValue(0); + } + // 6. Let size be the value of O’s [[ByteLength]] internal slot. + // 7. Return size. + return JSHandle(thisHandle)->GetByteLength(); +} + +// 22.2.3.3 +JSTaggedValue BuiltinsTypedArray::GetByteOffset(EcmaRuntimeCallInfo *argv) +{ + ASSERT(argv); + BUILTINS_API_TRACE(argv->GetThread(), TypedArray, GetByteOffset); + JSThread *thread = argv->GetThread(); + [[maybe_unused]] EcmaHandleScope handleScope(thread); + // 1. Let O be the this value. + JSHandle thisHandle = GetThis(argv); + // 2. If Type(O) is not Object, throw a TypeError exception. + if (!thisHandle->IsECMAObject()) { + THROW_TYPE_ERROR_AND_RETURN(thread, "This value is not an object.", JSTaggedValue::Exception()); + } + // 3. If O does not have a [[ViewedArrayBuffer]] internal slot, throw a TypeError exception. + if (!thisHandle->IsTypedArray()) { + THROW_TYPE_ERROR_AND_RETURN(thread, "This value does not have a [[ViewedArrayBuffer]] internal slot.", + JSTaggedValue::Exception()); + } + // 4. Let buffer be the value of O’s [[ViewedArrayBuffer]] internal slot. + JSTaggedValue buffer = JSHandle::Cast(thisHandle)->GetViewedArrayBuffer(); + // 5. If IsDetachedBuffer(buffer) is true, return 0. + if (BuiltinsArrayBuffer::IsDetachedBuffer(buffer)) { + return JSTaggedValue(0); + } + // 6. Let offset be the value of O’s [[ByteOffset]] internal slot. + int32_t offset = TypedArrayHelper::GetByteOffset(thread, JSHandle(thisHandle)); + // 7. Return offset. + return JSTaggedValue(offset); +} + +// 22.2.3.5 +JSTaggedValue BuiltinsTypedArray::CopyWithin(EcmaRuntimeCallInfo *argv) +{ + ASSERT(argv); + BUILTINS_API_TRACE(argv->GetThread(), TypedArray, CopyWithin); + if (!GetThis(argv)->IsTypedArray()) { + THROW_TYPE_ERROR_AND_RETURN(argv->GetThread(), "This is not a TypedArray.", JSTaggedValue::Exception()); + } + return BuiltinsArray::CopyWithin(argv); +} + +// 22.2.3.6 +JSTaggedValue BuiltinsTypedArray::Entries(EcmaRuntimeCallInfo *argv) +{ + ASSERT(argv); + BUILTINS_API_TRACE(argv->GetThread(), TypedArray, Entries); + JSThread *thread = argv->GetThread(); + [[maybe_unused]] EcmaHandleScope handleScope(thread); + // 1. Let O be the this value. + JSHandle thisHandle = GetThis(argv); + // 2. Let valid be ValidateTypedArray(O). + TypedArrayHelper::ValidateTypedArray(thread, thisHandle); + // 3. ReturnIfAbrupt(valid). + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(argv->GetThread()); + JSHandle self(thisHandle); + ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); + // 4. Return CreateArrayIterator(O, "key+value"). + JSHandle iter(factory->NewJSArrayIterator(self, IterationKind::KEY_AND_VALUE)); + return iter.GetTaggedValue(); +} + +// 22.2.3.7 +JSTaggedValue BuiltinsTypedArray::Every(EcmaRuntimeCallInfo *argv) +{ + ASSERT(argv); + BUILTINS_API_TRACE(argv->GetThread(), TypedArray, Every); + JSThread *thread = argv->GetThread(); + [[maybe_unused]] EcmaHandleScope handleScope(thread); + + // 1. Let O be ToObject(this value). + JSHandle thisHandle = GetThis(argv); + if (!thisHandle->IsTypedArray()) { + THROW_TYPE_ERROR_AND_RETURN(argv->GetThread(), "This is not a TypedArray.", JSTaggedValue::Exception()); + } + JSHandle thisObjHandle = JSTaggedValue::ToObject(thread, thisHandle); + // 2. ReturnIfAbrupt(O). + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + JSHandle thisObjVal(thisObjHandle); + + // 3. Let len be ToLength(Get(O, "length")). + int32_t len = TypedArrayHelper::GetArrayLength(thread, thisObjHandle); + // 4. ReturnIfAbrupt(len). + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + + // 5. If IsCallable(callbackfn) is false, throw a TypeError exception. + JSHandle callbackFnHandle = GetCallArg(argv, 0); + if (!callbackFnHandle->IsCallable()) { + THROW_TYPE_ERROR_AND_RETURN(thread, "the callbackfun is not callable.", JSTaggedValue::Exception()); + } + + // 6. If thisArg was supplied, let T be thisArg; else let T be undefined. + JSHandle thisArgHandle = GetCallArg(argv, 1); + + // 7. Let k be 0. + // 8. Repeat, while k < len + // a. Let Pk be ToString(k). + // b. Let kPresent be HasProperty(O, Pk). + // c. ReturnIfAbrupt(kPresent). + // d. If kPresent is true, then + // i. Let kValue be Get(O, Pk). + // ii. ReturnIfAbrupt(kValue). + // iii. Let testResult be ToBoolean(Call(callbackfn, T, «kValue, k, O»)). + // iv. ReturnIfAbrupt(testResult). + // v. If testResult is false, return false. + // e. Increase k by 1. + JSMutableHandle key(thread, JSTaggedValue::Undefined()); + InternalCallParams *arguments = thread->GetInternalCallParams(); + int32_t k = 0; + while (k < len) { + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + JSHandle kValue = JSTaggedValue::GetProperty(thread, thisObjVal, k).GetValue(); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + key.Update(JSTaggedValue(k)); + arguments->MakeArgv(kValue, key, thisObjVal); + JSTaggedValue callResult = + JSFunction::Call(thread, callbackFnHandle, thisArgHandle, 3, arguments->GetArgv()); // 3: three args + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + bool boolResult = callResult.ToBoolean(); + if (!boolResult) { + return GetTaggedBoolean(false); + } + k++; + } + + // 9. Return true. + return GetTaggedBoolean(true); +} + +// 22.2.3.8 +JSTaggedValue BuiltinsTypedArray::Fill(EcmaRuntimeCallInfo *argv) +{ + ASSERT(argv); + if (!GetThis(argv)->IsTypedArray()) { + THROW_TYPE_ERROR_AND_RETURN(argv->GetThread(), "This is not a TypedArray.", JSTaggedValue::Exception()); + } + return BuiltinsArray::Fill(argv); +} + +// 22.2.3.9 %TypedArray%.prototype.filter ( callbackfn [ , thisArg ] ) +JSTaggedValue BuiltinsTypedArray::Filter(EcmaRuntimeCallInfo *argv) +{ + ASSERT(argv); + BUILTINS_API_TRACE(argv->GetThread(), TypedArray, Filter); + JSThread *thread = argv->GetThread(); + [[maybe_unused]] EcmaHandleScope handleScope(thread); + ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); + // 1. Let O be the this value. + JSHandle thisHandle = GetThis(argv); + // 2. Let valid be ValidateTypedArray(O). + TypedArrayHelper::ValidateTypedArray(thread, thisHandle); + // 3. ReturnIfAbrupt(valid). + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + + JSHandle thisObj(thisHandle); + // 4. Let len be the value of O’s [[ArrayLength]] internal slot. + int32_t len = TypedArrayHelper::GetArrayLength(thread, thisObj); + // 5. If IsCallable(callbackfn) is false, throw a TypeError exception. + JSHandle callbackFnHandle = GetCallArg(argv, 0); + if (!callbackFnHandle->IsCallable()) { + THROW_TYPE_ERROR_AND_RETURN(thread, "the callbackfun is not callable.", JSTaggedValue::Exception()); + } + // 6. If thisArg was supplied, let T be thisArg; else let T be undefined. + JSHandle thisArgHandle = GetCallArg(argv, 1); + + // 10. Let kept be a new empty List. + JSHandle kept(factory->NewTaggedArray(len)); + + // 11. Let k be 0. + // 12. Let captured be 0. + // 13. Repeat, while k < len + // a. Let Pk be ToString(k). + // b. Let kValue be Get(O, Pk). + // c. ReturnIfAbrupt(kValue). + // d. Let selected be ToBoolean(Call(callbackfn, T, «kValue, k, O»)). + // e. ReturnIfAbrupt(selected). + // f. If selected is true, then + // i. Append kValue to the end of kept. + // ii. Increase captured by 1. + // g. Increase k by 1. + int32_t captured = 0; + JSMutableHandle tKey(thread, JSTaggedValue::Undefined()); + InternalCallParams *arguments = thread->GetInternalCallParams(); + for (int32_t k = 0; k < len; k++) { + tKey.Update(JSTaggedValue(k)); + JSHandle kKey(JSTaggedValue::ToString(thread, tKey)); + JSHandle kValue = JSTaggedValue::GetProperty(thread, thisHandle, kKey).GetValue(); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + arguments->MakeArgv(kValue, tKey, thisHandle); + JSTaggedValue callResult = + JSFunction::Call(thread, callbackFnHandle, thisArgHandle, 3, arguments->GetArgv()); // 3: three args + bool testResult = callResult.ToBoolean(); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + if (testResult) { + kept->Set(thread, captured, kValue); + captured++; + } + } + // es11 9. Let A be ? TypedArraySpeciesCreate(O, « captured »). + arguments->MakeArgv(JSTaggedValue(captured)); + JSHandle newArrObj = TypedArrayHelper::TypedArraySpeciesCreate(thread, thisObj, 1, arguments->GetArgv()); + // 15. ReturnIfAbrupt(A). + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + // 16. Let n be 0. + // 17. For each element e of kept + // a. Let status be Set(A, ToString(n), e, true ). + // b. ReturnIfAbrupt(status). + // c. Increment n by 1. + JSMutableHandle valueHandle(thread, JSTaggedValue::Undefined()); + JSMutableHandle ntKey(thread, JSTaggedValue::Undefined()); + for (int32_t n = 0; n < captured; n++) { + valueHandle.Update(kept->Get(n)); + ntKey.Update(JSTaggedValue(n)); + JSHandle nKey(JSTaggedValue::ToString(thread, ntKey)); + JSTaggedValue::SetProperty(thread, JSHandle(newArrObj), nKey, valueHandle, true); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + } + // 18. Return A. + return newArrObj.GetTaggedValue(); +} + +// 22.2.3.10 +JSTaggedValue BuiltinsTypedArray::Find(EcmaRuntimeCallInfo *argv) +{ + ASSERT(argv); + if (!GetThis(argv)->IsTypedArray()) { + THROW_TYPE_ERROR_AND_RETURN(argv->GetThread(), "This is not a TypedArray.", JSTaggedValue::Exception()); + } + return BuiltinsArray::Find(argv); +} + +// 22.2.3.11 +JSTaggedValue BuiltinsTypedArray::FindIndex(EcmaRuntimeCallInfo *argv) +{ + ASSERT(argv); + if (!GetThis(argv)->IsTypedArray()) { + THROW_TYPE_ERROR_AND_RETURN(argv->GetThread(), "This is not a TypedArray.", JSTaggedValue::Exception()); + } + return BuiltinsArray::FindIndex(argv); +} + +// 22.2.3.12 +JSTaggedValue BuiltinsTypedArray::ForEach(EcmaRuntimeCallInfo *argv) +{ + ASSERT(argv); + BUILTINS_API_TRACE(argv->GetThread(), TypedArray, ForEach); + JSThread *thread = argv->GetThread(); + [[maybe_unused]] EcmaHandleScope handleScope(thread); + + // 1. Let O be ToObject(this value). + JSHandle thisHandle = GetThis(argv); + if (!thisHandle->IsTypedArray()) { + THROW_TYPE_ERROR_AND_RETURN(argv->GetThread(), "This is not a TypedArray.", JSTaggedValue::Exception()); + } + JSHandle thisObjHandle = JSTaggedValue::ToObject(thread, thisHandle); + // 2. ReturnIfAbrupt(O). + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + JSHandle thisObjVal(thisObjHandle); + + // 3. Let len be ToLength(Get(O, "length")). + int32_t len = TypedArrayHelper::GetArrayLength(thread, thisObjHandle); + // 4. ReturnIfAbrupt(len). + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + + // 5. If IsCallable(callbackfn) is false, throw a TypeError exception. + JSHandle callbackFnHandle = GetCallArg(argv, 0); + if (!callbackFnHandle->IsCallable()) { + THROW_TYPE_ERROR_AND_RETURN(thread, "the callbackfun is not callable.", JSTaggedValue::Exception()); + } + + // 6. If thisArg was supplied, let T be thisArg; else let T be undefined. + JSHandle thisArgHandle = GetCallArg(argv, 1); + + // 7. Let k be 0. + // 8. Repeat, while k < len + // a. Let Pk be ToString(k). + // b. Let kPresent be HasProperty(O, Pk). + // c. ReturnIfAbrupt(kPresent). + // d. If kPresent is true, then + // i. Let kValue be Get(O, Pk). + // ii. ReturnIfAbrupt(kValue). + // iii. Let funcResult be Call(callbackfn, T, «kValue, k, O»). + // iv. ReturnIfAbrupt(funcResult). + // e. Increase k by 1. + JSMutableHandle key(thread, JSTaggedValue::Undefined()); + InternalCallParams *arguments = thread->GetInternalCallParams(); + int32_t k = 0; + while (k < len) { + JSHandle kValue = JSTaggedValue::GetProperty(thread, thisObjVal, k).GetValue(); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + key.Update(JSTaggedValue(k)); + arguments->MakeArgv(kValue, key, thisObjVal); + JSTaggedValue funcResult = + JSFunction::Call(thread, callbackFnHandle, thisArgHandle, 3, arguments->GetArgv()); // 3: three args + RETURN_VALUE_IF_ABRUPT_COMPLETION(thread, funcResult); + k++; + } + + // 9. Return undefined. + return JSTaggedValue::Undefined(); +} + +// ES2021 23.2.3.13 +JSTaggedValue BuiltinsTypedArray::Includes(EcmaRuntimeCallInfo *argv) +{ + ASSERT(argv); + BUILTINS_API_TRACE(argv->GetThread(), TypedArray, Includes); + 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); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + + // 3. Let len be ToLength(Get(O, "length")). + JSHandle thisObj(thisHandle); + int32_t len = TypedArrayHelper::GetArrayLength(thread, thisObj); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + + // 3. If len is 0, return false. + if (len == 0) { + return GetTaggedBoolean(false); + } + + array_size_t argc = argv->GetArgsNumber(); + double fromIndex = 0; + + if (argc > 1) { + // 4-5. If argument fromIndex was passed let n be ToInteger(fromIndex); else let n be 0. + JSHandle msg1 = GetCallArg(argv, 1); + JSTaggedNumber fromIndexTemp = JSTaggedValue::ToNumber(thread, msg1); + + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + fromIndex = ecmascript::base::NumberHelper::TruncateDouble(fromIndexTemp.GetNumber()); + + // 6. If n is positiveInfinity, return false. + if (JSTaggedValue::Equal(thread, msg1, + JSHandle(thread, JSTaggedValue(base::POSITIVE_INFINITY)))) { + return GetTaggedBoolean(false); + } + + // 7. Else if n is negativeInfinity, set n to 0. + if (JSTaggedValue::Equal(thread, msg1, + JSHandle(thread, JSTaggedValue(-base::POSITIVE_INFINITY)))) { + fromIndex = 0; + } + } + + // 8. If n ≥ 0, then + // a. Let k be n. + // 9. Else, + // a. Let k be len + n. + // b. If k < 0, set k to 0. + double from = (fromIndex >= 0) ? fromIndex : ((len + fromIndex) >= 0 ? len + fromIndex : 0); + + // 10. + const JSHandle searchElement = GetCallArg(argv, 0); + while (from < len) { + // a) + JSHandle indexHandle(thread, JSTaggedValue(from)); + JSHandle handle = + JSHandle(thread, JSTaggedValue::ToString(thread, indexHandle).GetTaggedValue()); + JSHandle element = JSTaggedValue::GetProperty(thread, thisHandle, handle).GetValue(); + + // b) + if (JSTaggedValue::SameValueZero(searchElement.GetTaggedValue(), element.GetTaggedValue())) { + return GetTaggedBoolean(true); + } + + // c) + from++; + } + + return GetTaggedBoolean(false); +} +// 22.2.3.13 +JSTaggedValue BuiltinsTypedArray::IndexOf(EcmaRuntimeCallInfo *argv) +{ + ASSERT(argv); + if (!GetThis(argv)->IsTypedArray()) { + THROW_TYPE_ERROR_AND_RETURN(argv->GetThread(), "This is not a TypedArray.", JSTaggedValue::Exception()); + } + return BuiltinsArray::IndexOf(argv); +} + +// 22.2.3.14 +JSTaggedValue BuiltinsTypedArray::Join(EcmaRuntimeCallInfo *argv) +{ + ASSERT(argv); + if (!GetThis(argv)->IsTypedArray()) { + THROW_TYPE_ERROR_AND_RETURN(argv->GetThread(), "This is not a TypedArray.", JSTaggedValue::Exception()); + } + return BuiltinsArray::Join(argv); +} + +// 22.2.3.15 +JSTaggedValue BuiltinsTypedArray::Keys(EcmaRuntimeCallInfo *argv) +{ + ASSERT(argv); + BUILTINS_API_TRACE(argv->GetThread(), TypedArray, Keys); + JSThread *thread = argv->GetThread(); + [[maybe_unused]] EcmaHandleScope handleScope(thread); + // 1. Let O be the this value. + JSHandle thisHandle = GetThis(argv); + // 2. Let valid be ValidateTypedArray(O). + TypedArrayHelper::ValidateTypedArray(thread, thisHandle); + // 3. ReturnIfAbrupt(valid). + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(argv->GetThread()); + JSHandle self(thisHandle); + ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); + // 4. Return CreateArrayIterator(O, "key"). + JSHandle iter(factory->NewJSArrayIterator(self, IterationKind::KEY)); + return iter.GetTaggedValue(); +} + +// 22.2.3.16 +JSTaggedValue BuiltinsTypedArray::LastIndexOf(EcmaRuntimeCallInfo *argv) +{ + ASSERT(argv); + if (!GetThis(argv)->IsTypedArray()) { + THROW_TYPE_ERROR_AND_RETURN(argv->GetThread(), "This is not a TypedArray.", JSTaggedValue::Exception()); + } + return BuiltinsArray::LastIndexOf(argv); +} + +// 22.2.3.17 +JSTaggedValue BuiltinsTypedArray::GetLength(EcmaRuntimeCallInfo *argv) +{ + ASSERT(argv); + BUILTINS_API_TRACE(argv->GetThread(), TypedArray, GetLength); + JSThread *thread = argv->GetThread(); + [[maybe_unused]] EcmaHandleScope handleScope(thread); + // 1. Let O be the this value. + JSHandle thisHandle = GetThis(argv); + // 2. If Type(O) is not Object, throw a TypeError exception. + if (!thisHandle->IsECMAObject()) { + THROW_TYPE_ERROR_AND_RETURN(thread, "This value is not an object.", JSTaggedValue::Exception()); + } + // 3. If O does not have a [[TypedArrayName]] internal slot, throw a TypeError exception. + if (!thisHandle->IsTypedArray()) { + THROW_TYPE_ERROR_AND_RETURN(thread, "This value does not have a [[TypedArrayName]] internal slot.", + JSTaggedValue::Exception()); + } + // 4. Assert: O has [[ViewedArrayBuffer]] and [[ArrayLength]] internal slots. + // 5. Let buffer be the value of O’s [[ViewedArrayBuffer]] internal slot. + JSTaggedValue buffer = JSHandle::Cast(thisHandle)->GetViewedArrayBuffer(); + // 6. If IsDetachedBuffer(buffer) is true, return 0. + if (BuiltinsArrayBuffer::IsDetachedBuffer(buffer)) { + return JSTaggedValue(0); + } + // 7. Let length be the value of O’s [[ArrayLength]] internal slot. + int32_t length = TypedArrayHelper::GetArrayLength(thread, JSHandle(thisHandle)); + // 8. Return length. + return JSTaggedValue(length); +} + +// 22.2.3.18 %TypedArray%.prototype.map ( callbackfn [ , thisArg ] ) +JSTaggedValue BuiltinsTypedArray::Map(EcmaRuntimeCallInfo *argv) +{ + ASSERT(argv); + BUILTINS_API_TRACE(argv->GetThread(), TypedArray, Map); + JSThread *thread = argv->GetThread(); + [[maybe_unused]] EcmaHandleScope handleScope(thread); + // 1. Let O be the this value. + JSHandle thisHandle = GetThis(argv); + // 2. Let valid be ValidateTypedArray(O). + TypedArrayHelper::ValidateTypedArray(thread, thisHandle); + // 3. ReturnIfAbrupt(valid). + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + + JSHandle thisObj(thisHandle); + // 4. Let len be the value of O’s [[ArrayLength]] internal slot. + int32_t len = TypedArrayHelper::GetArrayLength(thread, thisObj); + // 5. If IsCallable(callbackfn) is false, throw a TypeError exception. + JSHandle callbackfnHandle = GetCallArg(argv, 0); + if (!callbackfnHandle->IsCallable()) { + THROW_TYPE_ERROR_AND_RETURN(thread, "the callbackfun is not callable.", JSTaggedValue::Exception()); + } + // 6. If thisArg was supplied, let T be thisArg; else let T be undefined. + JSHandle thisArgHandle = GetCallArg(argv, 1); + // es11 5. Let A be ? TypedArraySpeciesCreate(O, « len »). + InternalCallParams *arguments = thread->GetInternalCallParams(); + arguments->MakeArgv(JSTaggedValue(len)); + JSHandle newArrObj = TypedArrayHelper::TypedArraySpeciesCreate(thread, thisObj, 1, arguments->GetArgv()); + // 11. ReturnIfAbrupt(A). + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + + // 12. Let k be 0. + // 13. Repeat, while k < len + // a. Let Pk be ToString(k). + // b. Let kValue be Get(O, Pk). + // c. ReturnIfAbrupt(kValue). + // d. Let mappedValue be Call(callbackfn, T, «kValue, k, O»). + // e. ReturnIfAbrupt(mappedValue). + // f. Let status be Set(A, Pk, mappedValue, true ). + // g. ReturnIfAbrupt(status). + // h. Increase k by 1. + JSMutableHandle key(thread, JSTaggedValue::Undefined()); + JSMutableHandle mapValue(thread, JSTaggedValue::Undefined()); + for (int32_t k = 0; k < len; k++) { + key.Update(JSTaggedValue(k)); + JSHandle kValue = JSTaggedValue::GetProperty(thread, thisHandle, key).GetValue(); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + arguments->MakeArgv(kValue, key, thisHandle); + JSTaggedValue callResult = + JSFunction::Call(thread, callbackfnHandle, thisArgHandle, 3, arguments->GetArgv()); // 3: three args + mapValue.Update(callResult); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + JSTaggedValue::SetProperty(thread, JSHandle(newArrObj), key, mapValue, true); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + } + + // 14. Return A. + return newArrObj.GetTaggedValue(); +} + +// 22.2.3.19 +JSTaggedValue BuiltinsTypedArray::Reduce(EcmaRuntimeCallInfo *argv) +{ + ASSERT(argv); + if (!GetThis(argv)->IsTypedArray()) { + THROW_TYPE_ERROR_AND_RETURN(argv->GetThread(), "This is not a TypedArray.", JSTaggedValue::Exception()); + } + return BuiltinsArray::Reduce(argv); +} + +// 22.2.3.20 +JSTaggedValue BuiltinsTypedArray::ReduceRight(EcmaRuntimeCallInfo *argv) +{ + ASSERT(argv); + if (!GetThis(argv)->IsTypedArray()) { + THROW_TYPE_ERROR_AND_RETURN(argv->GetThread(), "This is not a TypedArray.", JSTaggedValue::Exception()); + } + return BuiltinsArray::ReduceRight(argv); +} + +// 22.2.3.21 +JSTaggedValue BuiltinsTypedArray::Reverse(EcmaRuntimeCallInfo *argv) +{ + ASSERT(argv); + if (!GetThis(argv)->IsTypedArray()) { + THROW_TYPE_ERROR_AND_RETURN(argv->GetThread(), "This is not a TypedArray.", JSTaggedValue::Exception()); + } + return BuiltinsArray::Reverse(argv); +} + +// 22.2.3.22 %TypedArray%.prototype.set ( overloaded [ , offset ]) +JSTaggedValue BuiltinsTypedArray::Set(EcmaRuntimeCallInfo *argv) +{ + ASSERT(argv); + BUILTINS_API_TRACE(argv->GetThread(), TypedArray, Set); + JSThread *thread = argv->GetThread(); + [[maybe_unused]] EcmaHandleScope handleScope(thread); + JSHandle env = thread->GetEcmaVM()->GetGlobalEnv(); + // 1. Assert: array is any ECMAScript language value other than an Object with a [[TypedArrayName]] internal slot. + // If it is such an Object, the definition in 22.2.3.22.2 applies. + // 2. Let target be the this value. + JSHandle target = GetThis(argv); + // 3. If Type(target) is not Object, throw a TypeError exception. + if (!target->IsECMAObject()) { + THROW_TYPE_ERROR_AND_RETURN(thread, "This value is not an object.", JSTaggedValue::Exception()); + } + JSHandle targetObj(target); + // 4. If target does not have a [[TypedArrayName]] internal slot, throw a TypeError exception. + if (!target->IsTypedArray()) { + THROW_TYPE_ERROR_AND_RETURN(thread, "This value does not have a [[TypedArrayName]] internal slot.", + JSTaggedValue::Exception()); + } + + // 5. Assert: target has a [[ViewedArrayBuffer]] internal slot. + // 6. Let targetOffset be ToInteger (offset). + JSTaggedNumber tTargetOffset = JSTaggedValue::ToInteger(thread, GetCallArg(argv, 1)); + // 7. ReturnIfAbrupt(targetOffset). + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + double targetOffset = tTargetOffset.GetNumber(); + // 8. If targetOffset < 0, throw a RangeError exception. + if (targetOffset < 0) { + THROW_RANGE_ERROR_AND_RETURN(thread, "The targetOffset of This value is less than 0.", + JSTaggedValue::Exception()); + } + // 9. Let targetBuffer be the value of target’s [[ViewedArrayBuffer]] internal slot. + JSHandle targetBuffer(thread, JSTypedArray::Cast(*targetObj)->GetViewedArrayBuffer()); + // 10. If IsDetachedBuffer(targetBuffer) is true, throw a TypeError exception. + if (BuiltinsArrayBuffer::IsDetachedBuffer(targetBuffer.GetTaggedValue())) { + THROW_TYPE_ERROR_AND_RETURN(thread, "The targetBuffer of This value is detached buffer.", + JSTaggedValue::Exception()); + } + // 11. Let targetLength be the value of target’s [[ArrayLength]] internal slot. + // 12. Let targetName be the String value of target’s [[TypedArrayName]] internal slot. + // 13. Let targetElementSize be the Number value of the Element Size value specified in Table 49 for targetName. + // 14. Let targetType be the String value of the Element Type value in Table 49 for targetName. + // 15. Let targetByteOffset be the value of target’s [[ByteOffset]] internal slot. + int32_t targetLength = TypedArrayHelper::GetArrayLength(thread, targetObj); + JSHandle targetName(thread, JSTypedArray::Cast(*targetObj)->GetTypedArrayName()); + int32_t targetElementSize = TypedArrayHelper::GetSizeFromName(thread, targetName); + DataViewType targetType = TypedArrayHelper::GetTypeFromName(thread, targetName); + int32_t targetByteOffset = TypedArrayHelper::GetByteOffset(thread, targetObj); + + JSHandle argArray = GetCallArg(argv, 0); + + // 22.2.3.22.1 %TypedArray%.prototype.set (array [ , offset ] ) + if (!argArray->IsTypedArray()) { + // 16. Let src be ToObject(array). + JSHandle src = JSTaggedValue::ToObject(thread, argArray); + // 17. ReturnIfAbrupt(src). + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + // 18. Let srcLength be ToLength(Get(src, "length")). + JSHandle lengthKey = thread->GlobalConstants()->GetHandledLengthString(); + JSHandle lenResult = + JSTaggedValue::GetProperty(thread, JSHandle::Cast(src), lengthKey).GetValue(); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + JSTaggedNumber tSrcLen = JSTaggedValue::ToLength(thread, lenResult); + // 19. ReturnIfAbrupt(srcLength). + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + double srcLen = tSrcLen.GetNumber(); + // 20. If srcLength + targetOffset > targetLength, throw a RangeError exception. + if (srcLen + targetOffset > targetLength) { + THROW_RANGE_ERROR_AND_RETURN(thread, "The sum of srcLength and targetOffset is greater than targetLength.", + JSTaggedValue::Exception()); + } + // 21. Let targetByteIndex be targetOffset × targetElementSize + targetByteOffset. + int32_t targetByteIndex = targetOffset * targetElementSize + targetByteOffset; + // 22. Let k be 0. + // 23. Let limit be targetByteIndex + targetElementSize × srcLength. + int32_t k = 0; + int32_t limit = targetByteIndex + targetElementSize * srcLen; + // 24. Repeat, while targetByteIndex < limit + // a. Let Pk be ToString(k). + // b. Let kNumber be ToNumber(Get(src, Pk)). + // c. ReturnIfAbrupt(kNumber). + // d. If IsDetachedBuffer(targetBuffer) is true, throw a TypeError exception. + // e. Perform SetValueInBuffer(targetBuffer, targetByteIndex, targetType, kNumber). + // f. Set k to k + 1. + // g. Set targetByteIndex to targetByteIndex + targetElementSize. + JSMutableHandle tKey(thread, JSTaggedValue::Undefined()); + while (targetByteIndex < limit) { + tKey.Update(JSTaggedValue(k)); + JSHandle kKey(JSTaggedValue::ToString(thread, tKey)); + JSHandle kValue = + JSTaggedValue::GetProperty(thread, JSHandle::Cast(src), kKey).GetValue(); + JSTaggedNumber kNumber = JSTaggedValue::ToNumber(thread, kValue); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + if (BuiltinsArrayBuffer::IsDetachedBuffer(targetBuffer.GetTaggedValue())) { + THROW_TYPE_ERROR_AND_RETURN(thread, "The targetBuffer of This value is detached buffer.", + JSTaggedValue::Exception()); + } + BuiltinsArrayBuffer::SetValueInBuffer(targetBuffer.GetTaggedValue(), targetByteIndex, targetType, kNumber, + true); + k++; + targetByteIndex = targetByteIndex + targetElementSize; + } + // 25. Return undefined. + return JSTaggedValue::Undefined(); + } + + // 22.2.3.22.2 %TypedArray%.prototype.set(typedArray [, offset ] ) + JSHandle typedArray(argArray); + // 12. Let srcBuffer be the value of typedArray’s [[ViewedArrayBuffer]] internal slot. + // 13. If IsDetachedBuffer(srcBuffer) is true, throw a TypeError exception. + JSTaggedValue srcBuffer = JSTypedArray::Cast(*typedArray)->GetViewedArrayBuffer(); + if (BuiltinsArrayBuffer::IsDetachedBuffer(srcBuffer)) { + THROW_TYPE_ERROR_AND_RETURN(thread, "The ArrayBuffer of typedArray is detached buffer.", + JSTaggedValue::Exception()); + } + // 18. Let srcName be the String value of typedArray’s [[TypedArrayName]] internal slot. + // 19. Let srcType be the String value of the Element Type value in Table 49 for srcName . + // 20. Let srcElementSize be the Number value of the Element Size value specified in Table 49 for srcName. + // 21. Let srcLength be the value of typedArray’s [[ArrayLength]] internal slot. + // 22. Let srcByteOffset be the value of typedArray’s [[ByteOffset]] internal slot. + JSHandle srcName(thread, JSTypedArray::Cast(*typedArray)->GetTypedArrayName()); + DataViewType srcType = TypedArrayHelper::GetTypeFromName(thread, srcName); + int32_t srcElementSize = TypedArrayHelper::GetSizeFromName(thread, srcName); + int32_t srcLength = TypedArrayHelper::GetArrayLength(thread, typedArray); + int32_t srcByteOffset = TypedArrayHelper::GetByteOffset(thread, typedArray); + // 23. If srcLength + targetOffset > targetLength, throw a RangeError exception. + if (srcLength + targetOffset > targetLength) { + THROW_RANGE_ERROR_AND_RETURN(thread, "The sum of srcLength and targetOffset is greater than targetLength.", + JSTaggedValue::Exception()); + } + // 24. If SameValue(srcBuffer, targetBuffer) is true, then + // a. Let srcBuffer be CloneArrayBuffer(targetBuffer, srcByteOffset, %ArrayBuffer%). + // b. NOTE: %ArrayBuffer% is used to clone targetBuffer because is it known to not have any observable + // side-effects. + // c. ReturnIfAbrupt(srcBuffer). + // d. Let srcByteIndex be 0. + // 25. Else, let srcByteIndex be srcByteOffset. + int32_t srcByteIndex; + if (JSTaggedValue::SameValue(srcBuffer, targetBuffer.GetTaggedValue())) { + srcBuffer = + BuiltinsArrayBuffer::CloneArrayBuffer(thread, targetBuffer, srcByteOffset, env->GetArrayBufferFunction()); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + srcByteIndex = 0; + } else { + srcByteIndex = srcByteOffset; + } + // 26. Let targetByteIndex be targetOffset × targetElementSize + targetByteOffset. + int32_t targetByteIndex = targetOffset * targetElementSize + targetByteOffset; + // 27. Let limit be targetByteIndex + targetElementSize × srcLength. + int32_t limit = targetByteIndex + targetElementSize * srcLength; + // 28. If SameValue(srcType, targetType) is false, then + // a. Repeat, while targetByteIndex < limit + // i. Let value be GetValueFromBuffer(srcBuffer, srcByteIndex, srcType). + // ii. Perform SetValueInBuffer (targetBuffer, targetByteIndex, targetType, value). + // iii. Set srcByteIndex to srcByteIndex + srcElementSize. + // iv. Set targetByteIndex to targetByteIndex + targetElementSize. + JSMutableHandle value(thread, JSTaggedValue::Undefined()); + if (srcType != targetType) { + while (targetByteIndex < limit) { + JSTaggedValue taggedData = BuiltinsArrayBuffer::GetValueFromBuffer(srcBuffer, srcByteIndex, srcType, true); + value.Update(taggedData); + JSTaggedNumber kNumber = JSTaggedValue::ToNumber(thread, value); + BuiltinsArrayBuffer::SetValueInBuffer(targetBuffer.GetTaggedValue(), targetByteIndex, targetType, kNumber, + true); + srcByteIndex = srcByteIndex + srcElementSize; + targetByteIndex = targetByteIndex + targetElementSize; + } + } else { + // 29. Else, + // a. NOTE: If srcType and targetType are the same the transfer must be performed in a manner that preserves + // the bit-level encoding of the source data. + // b. Repeat, while targetByteIndex < limit + // i. Let value be GetValueFromBuffer(srcBuffer, srcByteIndex, "Uint8"). + // ii. Perform SetValueInBuffer (targetBuffer, targetByteIndex, "Uint8", value). + // iii. Set srcByteIndex to srcByteIndex + 1. + // iv. Set targetByteIndex to targetByteIndex + 1. + while (targetByteIndex < limit) { + JSTaggedValue taggedData = + BuiltinsArrayBuffer::GetValueFromBuffer(srcBuffer, srcByteIndex, DataViewType::UINT8, true); + value.Update(taggedData); + JSTaggedNumber kNumber = JSTaggedValue::ToNumber(thread, value); + BuiltinsArrayBuffer::SetValueInBuffer(targetBuffer.GetTaggedValue(), targetByteIndex, DataViewType::UINT8, + kNumber, true); + srcByteIndex = srcByteIndex + 1; + targetByteIndex = targetByteIndex + 1; + } + } + // 30. Return undefined. + return JSTaggedValue::Undefined(); +} // namespace panda::ecmascript::builtins + +// 22.2.3.23 %TypedArray%.prototype.slice ( start, end ) +JSTaggedValue BuiltinsTypedArray::Slice(EcmaRuntimeCallInfo *argv) +{ + ASSERT(argv); + BUILTINS_API_TRACE(argv->GetThread(), TypedArray, Slice); + JSThread *thread = argv->GetThread(); + [[maybe_unused]] EcmaHandleScope handleScope(thread); + // 1. Let O be the this value. + JSHandle thisHandle = GetThis(argv); + // 2. Let valid be ValidateTypedArray(O). + TypedArrayHelper::ValidateTypedArray(thread, thisHandle); + // 3. ReturnIfAbrupt(valid). + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + + JSHandle thisObj(thisHandle); + // 4. Let len be the value of O’s [[ArrayLength]] internal slot. + int32_t len = TypedArrayHelper::GetArrayLength(thread, thisObj); + + double k; + // 5. Let relativeStart be ToInteger(start). + JSTaggedNumber tRelativeStart = JSTaggedValue::ToInteger(thread, GetCallArg(argv, 0)); + // 6. ReturnIfAbrupt(relativeStart). + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + double relativeStart = tRelativeStart.GetNumber(); + // 7. If relativeStart < 0, let k be max((len + relativeStart),0); else let k be min(relativeStart, len). + if (relativeStart < 0) { + k = relativeStart + len > 0 ? relativeStart + len : 0; + } else { + k = relativeStart < len ? relativeStart : len; + } + // 8. If end is undefined, let relativeEnd be len; else let relativeEnd be ToInteger(end). + double relativeEnd = len; + JSHandle end = GetCallArg(argv, 1); + if (!end->IsUndefined()) { + JSTaggedNumber tRelativeEnd = JSTaggedValue::ToInteger(thread, end); + // 9. ReturnIfAbrupt(relativeEnd). + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + relativeEnd = tRelativeEnd.GetNumber(); + } + + // 10. If relativeEnd < 0, let final be max((len + relativeEnd),0); else let final be min(relativeEnd, len). + double final = 0; + if (relativeEnd < 0) { + final = relativeEnd + len > 0 ? relativeEnd + len : 0; + } else { + final = relativeEnd < len ? relativeEnd : len; + } + // 11. Let count be max(final – k, 0). + double count = (final - k) > 0 ? (final - k) : 0; + // es11 9. Let A be ? TypedArraySpeciesCreate(O, « count »). + InternalCallParams *arguments = thread->GetInternalCallParams(); + arguments->MakeArgv(JSTaggedValue(count)); + JSHandle newArrObj = TypedArrayHelper::TypedArraySpeciesCreate(thread, thisObj, 1, arguments->GetArgv()); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + // 17. Let srcName be the String value of O’s [[TypedArrayName]] internal slot. + // 18. Let srcType be the String value of the Element Type value in Table 49 for srcName. + JSHandle srcName(thread, JSTypedArray::Cast(*thisObj)->GetTypedArrayName()); + DataViewType srcType = TypedArrayHelper::GetTypeFromName(thread, srcName); + // 19. Let targetName be the String value of A’s [[TypedArrayName]] internal slot. + // 20. Let targetType be the String value of the Element Type value in Table 49 for targetName. + JSHandle targetName(thread, JSTypedArray::Cast(*newArrObj)->GetTypedArrayName()); + DataViewType targetType = TypedArrayHelper::GetTypeFromName(thread, targetName); + // 21. If SameValue(srcType, targetType) is false, then + // a. Let n be 0. + // b. Repeat, while k < final + // i. Let Pk be ToString(k). + // ii. Let kValue be Get(O, Pk). + // iii. ReturnIfAbrupt(kValue). + // iv. Let status be Set(A, ToString(n), kValue, true ). + // v. ReturnIfAbrupt(status). + // vi. Increase k by 1. + // vii. Increase n by 1. + JSMutableHandle tKey(thread, JSTaggedValue::Undefined()); + JSMutableHandle kValue(thread, JSTaggedValue::Undefined()); + JSMutableHandle ntKey(thread, JSTaggedValue::Undefined()); + if (srcType != targetType) { + double n = 0; + while (k < final) { + tKey.Update(JSTaggedValue(k)); + JSHandle kKey(JSTaggedValue::ToString(thread, tKey)); + kValue.Update(JSTaggedValue::GetProperty(thread, thisHandle, kKey).GetValue().GetTaggedValue()); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + ntKey.Update(JSTaggedValue(n)); + JSHandle nKey(JSTaggedValue::ToString(thread, ntKey)); + JSTaggedValue::SetProperty(thread, JSHandle(newArrObj), nKey, kValue, true); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + n++; + k++; + } + } else if (count > 0) { + // 22. Else if count > 0, + // a. Let srcBuffer be the value of O’s [[ViewedArrayBuffer]] internal slot. + // b. If IsDetachedBuffer(srcBuffer) is true, throw a TypeError exception. + JSHandle srcBuffer(thread, JSTypedArray::Cast(*thisObj)->GetViewedArrayBuffer()); + if (BuiltinsArrayBuffer::IsDetachedBuffer(srcBuffer.GetTaggedValue())) { + THROW_TYPE_ERROR_AND_RETURN(thread, "The ArrayBuffer of this value is detached buffer.", + JSTaggedValue::Exception()); + } + // c. Let targetBuffer be the value of A’s [[ViewedArrayBuffer]] internal slot. + JSHandle targetBuffer(thread, JSTypedArray::Cast(*newArrObj)->GetViewedArrayBuffer()); + // d. Let elementSize be the Number value of the Element Size value specified in Table 49 for srcType. + int32_t elementSize = TypedArrayHelper::GetSizeFromName(thread, srcName); + // e. NOTE: If srcType and targetType are the same the transfer must be performed in a manner that + // preserves the bit-level encoding of the source data. f. Let srcByteOffset be the value of O’s + // [[ByteOffset]] internal slot. + int32_t srcByteOffset = TypedArrayHelper::GetByteOffset(thread, thisObj); + // g. Let targetByteIndex be 0. + // h. Let srcByteIndex be (k × elementSize) + srcByteOffset. + + int32_t srcByteIndex = k * elementSize + srcByteOffset; + // i. Repeat, while targetByteIndex < count × elementSize + // i. Let value be GetValueFromBuffer(srcBuffer, srcByteIndex, "Uint8"). + // ii. Perform SetValueInBuffer (targetBuffer, targetByteIndex, "Uint8", value). + // iii. Increase srcByteIndex by 1. + // iv. Increase targetByteIndex by 1. + JSMutableHandle value(thread, JSTaggedValue::Undefined()); + for (int32_t targetByteIndex = 0; targetByteIndex < count * elementSize; srcByteIndex++, targetByteIndex++) { + JSTaggedValue taggedData = BuiltinsArrayBuffer::GetValueFromBuffer(srcBuffer.GetTaggedValue(), srcByteIndex, + DataViewType::UINT8, true); + value.Update(taggedData); + JSTaggedNumber kNumber = JSTaggedValue::ToNumber(thread, value); + BuiltinsArrayBuffer::SetValueInBuffer(targetBuffer.GetTaggedValue(), targetByteIndex, DataViewType::UINT8, + kNumber, true); + } + } + // 23. Return A. + return newArrObj.GetTaggedValue(); +} + +// 22.2.3.24 +JSTaggedValue BuiltinsTypedArray::Some(EcmaRuntimeCallInfo *argv) +{ + ASSERT(argv); + if (!GetThis(argv)->IsTypedArray()) { + THROW_TYPE_ERROR_AND_RETURN(argv->GetThread(), "This is not a TypedArray.", JSTaggedValue::Exception()); + } + return BuiltinsArray::Some(argv); +} + +// 22.2.3.25 +JSTaggedValue BuiltinsTypedArray::Sort(EcmaRuntimeCallInfo *argv) +{ + ASSERT(argv); + BUILTINS_API_TRACE(argv->GetThread(), TypedArray, Sort); + JSThread *thread = argv->GetThread(); + [[maybe_unused]] EcmaHandleScope handleScope(thread); + + // 1. Let obj be ToObject(this value). + JSHandle thisHandle = GetThis(argv); + if (!thisHandle->IsTypedArray()) { + THROW_TYPE_ERROR_AND_RETURN(argv->GetThread(), "This is not a TypedArray.", JSTaggedValue::Exception()); + } + + JSHandle thisObjHandle = JSTaggedValue::ToObject(thread, thisHandle); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + JSHandle thisObjVal(thisObjHandle); + + JSHandle buffer; + buffer = JSHandle(thread, TypedArrayHelper::ValidateTypedArray(thread, thisHandle)); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + double len = TypedArrayHelper::GetArrayLength(thread, thisObjHandle); + + JSHandle callbackFnHandle = GetCallArg(argv, 0); + + uint32_t i = 0; + while (i < len - 1) { + uint32_t j = len - 1; + while (j > i) { + JSHandle xValue = JSTaggedValue::GetProperty(thread, thisObjVal, j - 1).GetValue(); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + JSHandle yValue = JSTaggedValue::GetProperty(thread, thisObjVal, j).GetValue(); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + int32_t compareResult; + compareResult = TypedArrayHelper::SortCompare(thread, callbackFnHandle, buffer, xValue, yValue); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + if (compareResult > 0) { + JSTaggedValue::SetProperty(thread, thisObjVal, j - 1, yValue, true); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + JSTaggedValue::SetProperty(thread, thisObjVal, j, xValue, true); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + } + j--; + } + i++; + } + return JSTaggedValue::ToObject(thread, thisHandle).GetTaggedValue(); +} + +// 22.2.3.26 %TypedArray%.prototype.subarray( [ begin [ , end ] ] ) +JSTaggedValue BuiltinsTypedArray::Subarray(EcmaRuntimeCallInfo *argv) +{ + ASSERT(argv); + BUILTINS_API_TRACE(argv->GetThread(), TypedArray, Subarray); + JSThread *thread = argv->GetThread(); + [[maybe_unused]] EcmaHandleScope handleScope(thread); + // 1. Let O be the this value. + JSHandle thisHandle = GetThis(argv); + // 2. If Type(O) is not Object, throw a TypeError exception. + if (!thisHandle->IsECMAObject()) { + THROW_TYPE_ERROR_AND_RETURN(thread, "This value is not an object.", JSTaggedValue::Exception()); + } + JSHandle thisObj(thisHandle); + // 3. If O does not have a [[TypedArrayName]] internal slot, throw a TypeError exception. + if (!thisHandle->IsTypedArray()) { + THROW_TYPE_ERROR_AND_RETURN(thread, "This value does not have a [[TypedArrayName]] internal slot.", + JSTaggedValue::Exception()); + } + // 4. Assert: O has a [[ViewedArrayBuffer]] internal slot. + // 6. Let srcLength be the value of O’s [[ArrayLength]] internal slot. + int32_t srcLength = TypedArrayHelper::GetArrayLength(thread, thisObj); + // 7. Let relativeBegin be ToInteger(begin). + JSTaggedNumber tRelativeBegin = JSTaggedValue::ToInteger(thread, GetCallArg(argv, 0)); + // 8. ReturnIfAbrupt(relativeBegin). + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + double relativeBegin = tRelativeBegin.GetNumber(); + + double beginIndex; + // 9. If relativeBegin < 0, let beginIndex be max((srcLength + relativeBegin), 0); else let beginIndex be + // min(relativeBegin, srcLength). + if (relativeBegin < 0) { + beginIndex = relativeBegin + srcLength > 0 ? relativeBegin + srcLength : 0; + } else { + beginIndex = relativeBegin < srcLength ? relativeBegin : srcLength; + } + + // 10. If end is undefined, let relativeEnd be srcLength; else, let relativeEnd be ToInteger(end). + double relativeEnd = srcLength; + JSHandle end = GetCallArg(argv, 1); + if (!end->IsUndefined()) { + JSTaggedNumber tRelativeEnd = JSTaggedValue::ToInteger(thread, end); + // 11. ReturnIfAbrupt(relativeEnd). + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + relativeEnd = tRelativeEnd.GetNumber(); + } + // 12. If relativeEnd < 0, let endIndex be max((srcLength + relativeEnd), 0); else let endIndex be + // min(relativeEnd, srcLength). + double endIndex; + if (relativeEnd < 0) { + endIndex = relativeEnd + srcLength > 0 ? relativeEnd + srcLength : 0; + } else { + endIndex = relativeEnd < srcLength ? relativeEnd : srcLength; + } + // 13. Let newLength be max(endIndex – beginIndex, 0). + double newLength = (endIndex - beginIndex) > 0 ? (endIndex - beginIndex) : 0; + // 14. Let constructorName be the String value of O’s [[TypedArrayName]] internal slot. + // 15. Let elementSize be the Number value of the Element Size value specified in Table 49 for constructorName. + // 16. Let srcByteOffset be the value of O’s [[ByteOffset]] internal slot. + // 17. Let beginByteOffset be srcByteOffset + beginIndex × elementSize. + JSHandle constructorName(thread, JSTypedArray::Cast(*thisObj)->GetTypedArrayName()); + int32_t elementSize = TypedArrayHelper::GetSizeFromName(thread, constructorName); + int32_t srcByteOffset = TypedArrayHelper::GetByteOffset(thread, thisObj); + int32_t beginByteOffset = srcByteOffset + beginIndex * elementSize; + // 21. Let argumentsList be «buffer, beginByteOffset, newLength». + // 5. Let buffer be the value of O’s [[ViewedArrayBuffer]] internal slot. + JSTaggedValue buffer = JSTypedArray::Cast(*thisObj)->GetViewedArrayBuffer(); + // 22. Return Construct(constructor, argumentsList). + InternalCallParams *arguments = thread->GetInternalCallParams(); + arguments->MakeArgv(buffer, JSTaggedValue(beginByteOffset), JSTaggedValue(newLength)); + JSHandle newArr = + TypedArrayHelper::TypedArraySpeciesCreate(thread, thisObj, 3, arguments->GetArgv()); // 3: three args + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + return newArr.GetTaggedValue(); +} + +// 22.2.3.27 +JSTaggedValue BuiltinsTypedArray::ToLocaleString(EcmaRuntimeCallInfo *argv) +{ + ASSERT(argv); + if (!GetThis(argv)->IsTypedArray()) { + THROW_TYPE_ERROR_AND_RETURN(argv->GetThread(), "This is not a TypedArray.", JSTaggedValue::Exception()); + } + return BuiltinsArray::ToLocaleString(argv); +} + +// 22.2.3.28 +JSTaggedValue BuiltinsTypedArray::ToString(EcmaRuntimeCallInfo *argv) +{ + ASSERT(argv); + if (!GetThis(argv)->IsTypedArray()) { + THROW_TYPE_ERROR_AND_RETURN(argv->GetThread(), "This is not a TypedArray.", JSTaggedValue::Exception()); + } + return BuiltinsArray::ToString(argv); +} + +// 22.2.3.29 +JSTaggedValue BuiltinsTypedArray::Values(EcmaRuntimeCallInfo *argv) +{ + ASSERT(argv); + BUILTINS_API_TRACE(argv->GetThread(), TypedArray, Values); + JSThread *thread = argv->GetThread(); + [[maybe_unused]] EcmaHandleScope handleScope(thread); + // 1. Let O be the this value. + JSHandle thisHandle = GetThis(argv); + // 2. Let valid be ValidateTypedArray(O). + TypedArrayHelper::ValidateTypedArray(thread, thisHandle); + // 3. ReturnIfAbrupt(valid). + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(argv->GetThread()); + JSHandle self(thisHandle); + ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); + // 4. Return CreateArrayIterator(O, "value"). + JSHandle iter(factory->NewJSArrayIterator(self, IterationKind::VALUE)); + return iter.GetTaggedValue(); +} + +// 22.2.3.31 +JSTaggedValue BuiltinsTypedArray::ToStringTag(EcmaRuntimeCallInfo *argv) +{ + ASSERT(argv); + BUILTINS_API_TRACE(argv->GetThread(), TypedArray, ToStringTag); + JSThread *thread = argv->GetThread(); + [[maybe_unused]] EcmaHandleScope handleScope(thread); + // 1. Let O be the this value. + JSHandle thisHandle = GetThis(argv); + // 2. If Type(O) is not Object, return undefined. + if (!thisHandle->IsECMAObject()) { + return JSTaggedValue::Undefined(); + } + // 3. If O does not have a [[TypedArrayName]] internal slot, return undefined. + if (!thisHandle->IsTypedArray()) { + return JSTaggedValue::Undefined(); + } + // 4. Let name be the value of O’s [[TypedArrayName]] internal slot. + JSTaggedValue name = JSHandle::Cast(thisHandle)->GetTypedArrayName(); + // 5. Assert: name is a String value. + ASSERT(name.IsString()); + // 6. Return name. + return name; +} +} // namespace panda::ecmascript::builtins diff --git a/runtime/builtins/builtins_typedarray.h b/runtime/builtins/builtins_typedarray.h new file mode 100644 index 000000000..e0542e294 --- /dev/null +++ b/runtime/builtins/builtins_typedarray.h @@ -0,0 +1,110 @@ +/* + * Copyright (c) 2021-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_BUILTINS_BUILTINS_TYPEDARRAY_H +#define ECMASCRIPT_BUILTINS_BUILTINS_TYPEDARRAY_H + +#include "plugins/ecmascript/runtime/base/builtins_base.h" + +namespace panda::ecmascript::builtins { +class BuiltinsTypedArray : public ecmascript::base::BuiltinsBase { +public: + // 22.2.1 + static JSTaggedValue TypedArrayBaseConstructor(EcmaRuntimeCallInfo *argv); + static JSTaggedValue Int8ArrayConstructor(EcmaRuntimeCallInfo *argv); + static JSTaggedValue Uint8ArrayConstructor(EcmaRuntimeCallInfo *argv); + static JSTaggedValue Uint8ClampedArrayConstructor(EcmaRuntimeCallInfo *argv); + static JSTaggedValue Int16ArrayConstructor(EcmaRuntimeCallInfo *argv); + static JSTaggedValue Uint16ArrayConstructor(EcmaRuntimeCallInfo *argv); + static JSTaggedValue Int32ArrayConstructor(EcmaRuntimeCallInfo *argv); + static JSTaggedValue Uint32ArrayConstructor(EcmaRuntimeCallInfo *argv); + static JSTaggedValue Float32ArrayConstructor(EcmaRuntimeCallInfo *argv); + static JSTaggedValue Float64ArrayConstructor(EcmaRuntimeCallInfo *argv); + + // 22.2.1.2.1 + static JSTaggedValue AllocateTypedArray(EcmaRuntimeCallInfo *argv); + + // 22.2.2.1 + static JSTaggedValue From(EcmaRuntimeCallInfo *argv); + // 22.2.2.2 + static JSTaggedValue Of(EcmaRuntimeCallInfo *argv); + // 22.2.2.4 + static JSTaggedValue Species(EcmaRuntimeCallInfo *argv); + + // prototype + // 22.2.3.1 + static JSTaggedValue GetBuffer(EcmaRuntimeCallInfo *argv); + // 22.2.3.2 + static JSTaggedValue GetByteLength(EcmaRuntimeCallInfo *argv); + // 22.2.3.3 + static JSTaggedValue GetByteOffset(EcmaRuntimeCallInfo *argv); + // 22.2.3.5 + static JSTaggedValue CopyWithin(EcmaRuntimeCallInfo *argv); + // 22.2.3.6 + static JSTaggedValue Entries(EcmaRuntimeCallInfo *argv); + // 22.2.3.7 + static JSTaggedValue Every(EcmaRuntimeCallInfo *argv); + // 22.2.3.8 + static JSTaggedValue Fill(EcmaRuntimeCallInfo *argv); + // 22.2.3.9 + static JSTaggedValue Filter(EcmaRuntimeCallInfo *argv); + // 22.2.3.10 + static JSTaggedValue Find(EcmaRuntimeCallInfo *argv); + // 22.2.3.11 + static JSTaggedValue FindIndex(EcmaRuntimeCallInfo *argv); + // 22.2.3.12 + static JSTaggedValue ForEach(EcmaRuntimeCallInfo *argv); + // ES2021 23.2.3.13 + static JSTaggedValue Includes(EcmaRuntimeCallInfo *argv); + // 22.2.3.13 + static JSTaggedValue IndexOf(EcmaRuntimeCallInfo *argv); + // 22.2.3.14 + static JSTaggedValue Join(EcmaRuntimeCallInfo *argv); + // 22.2.3.15 + static JSTaggedValue Keys(EcmaRuntimeCallInfo *argv); + // 22.2.3.16 + static JSTaggedValue LastIndexOf(EcmaRuntimeCallInfo *argv); + // 22.2.3.17 + static JSTaggedValue GetLength(EcmaRuntimeCallInfo *argv); + // 22.2.3.18 + static JSTaggedValue Map(EcmaRuntimeCallInfo *argv); + // 22.2.3.19 + static JSTaggedValue Reduce(EcmaRuntimeCallInfo *argv); + // 22.2.3.20 + static JSTaggedValue ReduceRight(EcmaRuntimeCallInfo *argv); + // 22.2.3.21 + static JSTaggedValue Reverse(EcmaRuntimeCallInfo *argv); + // 22.2.3.22 + static JSTaggedValue Set(EcmaRuntimeCallInfo *argv); + // 22.2.3.23 + static JSTaggedValue Slice(EcmaRuntimeCallInfo *argv); + // 22.2.3.24 + static JSTaggedValue Some(EcmaRuntimeCallInfo *argv); + // 22.2.3.25 + static JSTaggedValue Sort(EcmaRuntimeCallInfo *argv); + // 22.2.3.26 + static JSTaggedValue Subarray(EcmaRuntimeCallInfo *argv); + // 22.2.3.27 + static JSTaggedValue ToLocaleString(EcmaRuntimeCallInfo *argv); + // 22.2.3.28 + static JSTaggedValue ToString(EcmaRuntimeCallInfo *argv); + // 22.2.3.29 + static JSTaggedValue Values(EcmaRuntimeCallInfo *argv); + // 22.2.3.31 + static JSTaggedValue ToStringTag(EcmaRuntimeCallInfo *argv); +}; +} // namespace panda::ecmascript::builtins + +#endif // ECMASCRIPT_BUILTINS_BUILTINS_TYPEDARRAY_H diff --git a/runtime/builtins/builtins_weak_map.cpp b/runtime/builtins/builtins_weak_map.cpp new file mode 100644 index 000000000..7ce6649e4 --- /dev/null +++ b/runtime/builtins/builtins_weak_map.cpp @@ -0,0 +1,220 @@ +/* + * Copyright (c) 2021-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 "plugins/ecmascript/runtime/builtins/builtins_weak_map.h" +#include "plugins/ecmascript/runtime/ecma_vm.h" +#include "plugins/ecmascript/runtime/global_env.h" +#include "plugins/ecmascript/runtime/internal_call_params.h" +#include "plugins/ecmascript/runtime/js_invoker.h" +#include "plugins/ecmascript/runtime/js_map_iterator.h" +#include "plugins/ecmascript/runtime/js_weak_container.h" +#include "plugins/ecmascript/runtime/linked_hash_table.h" +#include "plugins/ecmascript/runtime/object_factory.h" + +namespace panda::ecmascript::builtins { +JSTaggedValue BuiltinsWeakMap::WeakMapConstructor(EcmaRuntimeCallInfo *argv) +{ + ASSERT(argv); + BUILTINS_API_TRACE(argv->GetThread(), WeakMap, Constructor); + JSThread *thread = argv->GetThread(); + [[maybe_unused]] EcmaHandleScope handleScope(thread); + ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); + // 1.If NewTarget is undefined, throw a TypeError exception + JSHandle newTarget = GetNewTarget(argv); + if (newTarget->IsUndefined()) { + // throw type error + THROW_TYPE_ERROR_AND_RETURN(thread, "new target can't be undefined", JSTaggedValue::Exception()); + } + // 2.Let WeakMap be OrdinaryCreateFromConstructor(NewTarget, "%WeakMapPrototype%", «‍[[WeakMapData]]» ). + JSHandle constructor = GetConstructor(argv); + JSHandle obj = factory->NewJSObjectByConstructor(JSHandle(constructor), newTarget); + // 3.returnIfAbrupt() + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + JSHandle weakMap = JSHandle::Cast(obj); + + // 4.Set weakmap’s [[WeakMapData]] internal slot to a new empty List. + JSHandle linkedMap = LinkedHashMap::Create(thread, true); + weakMap->SetLinkedMap(thread, linkedMap); + // add data into set from iterable + // 5.If iterable is not present, let iterable be undefined. + // 6.If iterable is either undefined or null, let iter be undefined. + JSHandle iterable = GetCallArg(argv, 0); + // 8.If iter is undefined, return set + if (iterable->IsUndefined() || iterable->IsNull()) { + return weakMap.GetTaggedValue(); + } + if (!iterable->IsECMAObject()) { + THROW_TYPE_ERROR_AND_RETURN(thread, "iterable is not object", JSTaggedValue::Exception()); + } + // Let adder be Get(weakMap, "set"). + JSHandle adderKey(factory->NewFromCanBeCompressString("set")); + JSHandle adder = + JSObject::GetProperty(thread, JSHandle(weakMap), adderKey).GetValue(); + // ReturnIfAbrupt(adder). + RETURN_VALUE_IF_ABRUPT_COMPLETION(thread, adder.GetTaggedValue()); + // If IsCallable(adder) is false, throw a TypeError exception + if (!adder->IsCallable()) { + THROW_TYPE_ERROR_AND_RETURN(thread, "adder is not callable", adder.GetTaggedValue()); + } + // Let iter be GetIterator(iterable). + JSHandle iter(JSIterator::GetIterator(thread, iterable)); + // ReturnIfAbrupt(iter). + RETURN_VALUE_IF_ABRUPT_COMPLETION(thread, iter.GetTaggedValue()); + JSHandle keyIndex(thread, JSTaggedValue(0)); + JSHandle valueIndex(thread, JSTaggedValue(1)); + JSHandle next = JSIterator::IteratorStep(thread, iter); + RETURN_VALUE_IF_ABRUPT_COMPLETION(thread, next.GetTaggedValue()); + JSMutableHandle status(thread, JSTaggedValue::Undefined()); + while (!next->IsFalse()) { + // ReturnIfAbrupt(next). + RETURN_VALUE_IF_ABRUPT_COMPLETION(thread, next.GetTaggedValue()); + // Let nextValue be IteratorValue(next). + JSHandle nextValue(JSIterator::IteratorValue(thread, next)); + // ReturnIfAbrupt(nextValue). + RETURN_VALUE_IF_ABRUPT_COMPLETION(thread, next.GetTaggedValue()); + // If Type(nextItem) is not Object + if (!nextValue->IsECMAObject()) { + JSHandle typeError = factory->GetJSError(ErrorType::TYPE_ERROR, "nextItem is not Object"); + JSHandle record( + factory->NewCompletionRecord(CompletionRecord::THROW, JSHandle(typeError))); + JSTaggedValue ret = JSIterator::IteratorClose(thread, iter, record).GetTaggedValue(); + if (!thread->HasPendingException()) { + THROW_NEW_ERROR_AND_RETURN_VALUE(thread, typeError.GetTaggedValue(), ret); + }; + return ret; + } + // Let k be Get(nextItem, "0"). + JSHandle key = JSObject::GetProperty(thread, nextValue, keyIndex).GetValue(); + // If k is an abrupt completion, return IteratorClose(iter, k). + if (thread->HasPendingException()) { + return JSIterator::IteratorCloseAndReturn(thread, iter, key); + } + + // Let v be Get(nextItem, "1"). + JSHandle value = JSObject::GetProperty(thread, nextValue, valueIndex).GetValue(); + // If v is an abrupt completion, return IteratorClose(iter, v). + if (thread->HasPendingException()) { + return JSIterator::IteratorCloseAndReturn(thread, iter, value); + } + + // Let status be Call(adder, weakMap, «nextValue.[[value]]»). + InternalCallParams *arguments = thread->GetInternalCallParams(); + arguments->MakeArgv(key, value); + JSTaggedValue ret = JSFunction::Call(thread, + adder, JSHandle(weakMap), 2, arguments->GetArgv()); // 2: key and value pair + + status.Update(ret); + // If status is an abrupt completion, return IteratorClose(iter, status). + if (thread->HasPendingException()) { + return JSIterator::IteratorCloseAndReturn(thread, iter, status); + } + // Let next be IteratorStep(iter). + next = JSIterator::IteratorStep(thread, iter); + } + return weakMap.GetTaggedValue(); +} + +JSTaggedValue BuiltinsWeakMap::Delete(EcmaRuntimeCallInfo *argv) +{ + ASSERT(argv); + BUILTINS_API_TRACE(argv->GetThread(), WeakMap, Delete); + JSThread *thread = argv->GetThread(); + [[maybe_unused]] EcmaHandleScope handleScope(thread); + JSHandle self = GetThis(argv); + // 2.If Type(S) is not Object, throw a TypeError exception. + // 3.If S does not have a [[WeakMapData]] internal slot, throw a TypeError exception. + if (!self->IsJSWeakMap()) { + THROW_TYPE_ERROR_AND_RETURN(thread, "obj is not JSWeakMap.", JSTaggedValue::Exception()); + } + + JSHandle weakMap(self); + JSHandle key = GetCallArg(argv, 0); + // 5.if Type(key) is not Object, return false. + if (!key->IsHeapObject()) { + return GetTaggedBoolean(false); + } + return GetTaggedBoolean(JSWeakMap::Delete(thread, weakMap, key)); +} + +JSTaggedValue BuiltinsWeakMap::Has(EcmaRuntimeCallInfo *argv) +{ + ASSERT(argv); + BUILTINS_API_TRACE(argv->GetThread(), WeakMap, Has); + JSThread *thread = argv->GetThread(); + [[maybe_unused]] EcmaHandleScope handleScope(thread); + JSHandle self(GetThis(argv)); + // 2.If Type(S) is not Object, throw a TypeError exception. + // 3.If S does not have a [[WeakMapData]] internal slot, throw a TypeError exception. + if (!self->IsJSWeakMap()) { + THROW_TYPE_ERROR_AND_RETURN(thread, "obj is not JSWeakMap.", JSTaggedValue::Exception()); + } + JSWeakMap *jsWeakMap = JSWeakMap::Cast(*JSTaggedValue::ToObject(thread, self)); + JSHandle key = GetCallArg(argv, 0); + // 5.if Type(key) is not Object, return false. + if (!key->IsHeapObject()) { + return GetTaggedBoolean(false); + } + return GetTaggedBoolean(jsWeakMap->Has(key.GetTaggedValue())); +} + +JSTaggedValue BuiltinsWeakMap::Get(EcmaRuntimeCallInfo *argv) +{ + ASSERT(argv); + BUILTINS_API_TRACE(argv->GetThread(), WeakMap, Get); + JSThread *thread = argv->GetThread(); + [[maybe_unused]] EcmaHandleScope handleScope(thread); + JSHandle self(GetThis(argv)); + // 2.If Type(S) is not Object, throw a TypeError exception. + // 3.If S does not have a [[WeakMapData]] internal slot, throw a TypeError exception. + if (!self->IsJSWeakMap()) { + THROW_TYPE_ERROR_AND_RETURN(thread, "obj is not JSWeakMap.", JSTaggedValue::Exception()); + } + JSWeakMap *jsWeakMap = JSWeakMap::Cast(*JSTaggedValue::ToObject(thread, self)); + JSHandle key = GetCallArg(argv, 0); + if (!key->IsHeapObject()) { + return JSTaggedValue::Undefined(); + } + return jsWeakMap->Get(key.GetTaggedValue()); +} + +JSTaggedValue BuiltinsWeakMap::Set(EcmaRuntimeCallInfo *argv) +{ + ASSERT(argv); + BUILTINS_API_TRACE(argv->GetThread(), WeakMap, Set); + JSThread *thread = argv->GetThread(); + [[maybe_unused]] EcmaHandleScope handleScope(thread); + JSHandle self = GetThis(argv); + + // 2.If Type(S) is not Object, throw a TypeError exception. + // 3.If S does not have a [[WeakMapData]] internal slot, throw a TypeError exception. + if (!self->IsJSWeakMap()) { + THROW_TYPE_ERROR_AND_RETURN(thread, "obj is not JSWeakMap.", JSTaggedValue::Exception()); + } + + JSHandle key = GetCallArg(argv, 0); + if (!key->IsHeapObject()) { + THROW_TYPE_ERROR_AND_RETURN(thread, "obj is not an object.", JSTaggedValue::Exception()); + } + if (key->IsSymbol() || key->IsString()) { + THROW_TYPE_ERROR_AND_RETURN(thread, "key is Symblol or String", JSTaggedValue::Exception()); + } + + JSHandle value = GetCallArg(argv, 1); + + JSHandle map(self); + JSWeakMap::Set(thread, map, key, value); + return map.GetTaggedValue(); +} +} // namespace panda::ecmascript::builtins diff --git a/runtime/builtins/builtins_weak_map.h b/runtime/builtins/builtins_weak_map.h new file mode 100644 index 000000000..0a71ffc8b --- /dev/null +++ b/runtime/builtins/builtins_weak_map.h @@ -0,0 +1,38 @@ +/* + * Copyright (c) 2021-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_BUILTINS_BUILTINS_WEAK_MAP_H +#define ECMASCRIPT_BUILTINS_BUILTINS_WEAK_MAP_H + +#include "plugins/ecmascript/runtime/base/builtins_base.h" +#include "plugins/ecmascript/runtime/ecma_runtime_call_info.h" + +namespace panda::ecmascript::builtins { +class BuiltinsWeakMap : public ecmascript::base::BuiltinsBase { +public: + // 23.3.3.1 + static JSTaggedValue WeakMapConstructor(EcmaRuntimeCallInfo *argv); + // 23.3.3.2 + static JSTaggedValue Delete(EcmaRuntimeCallInfo *argv); + // 23.3.3.3 + static JSTaggedValue Get(EcmaRuntimeCallInfo *argv); + // 23.3.3.4 + static JSTaggedValue Has(EcmaRuntimeCallInfo *argv); + // 23.1.3.5 + static JSTaggedValue Set(EcmaRuntimeCallInfo *argv); + // 23.1.3.6 @@toStringTag +}; +} // namespace panda::ecmascript::builtins +#endif // ECMASCRIPT_BUILTINS_BUILTINS_MAP_H diff --git a/runtime/builtins/builtins_weak_set.cpp b/runtime/builtins/builtins_weak_set.cpp new file mode 100644 index 000000000..20bf0bebe --- /dev/null +++ b/runtime/builtins/builtins_weak_set.cpp @@ -0,0 +1,174 @@ +/* + * Copyright (c) 2021-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 "builtins_weak_set.h" +#include "plugins/ecmascript/runtime/ecma_vm.h" +#include "plugins/ecmascript/runtime/global_env.h" +#include "plugins/ecmascript/runtime/internal_call_params.h" +#include "plugins/ecmascript/runtime/js_invoker.h" +#include "plugins/ecmascript/runtime/js_set_iterator.h" +#include "plugins/ecmascript/runtime/js_weak_container.h" +#include "plugins/ecmascript/runtime/linked_hash_table-inl.h" +#include "plugins/ecmascript/runtime/object_factory.h" + +namespace panda::ecmascript::builtins { +JSTaggedValue BuiltinsWeakSet::WeakSetConstructor(EcmaRuntimeCallInfo *argv) +{ + ASSERT(argv); + BUILTINS_API_TRACE(argv->GetThread(), WeakSet, Constructor); + JSThread *thread = argv->GetThread(); + [[maybe_unused]] EcmaHandleScope handleScope(thread); + ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); + // 1.If NewTarget is undefined, throw a TypeError exception + JSHandle newTarget = GetNewTarget(argv); + if (newTarget->IsUndefined()) { + // throw type error + THROW_TYPE_ERROR_AND_RETURN(thread, "new target can't be undefined", JSTaggedValue::Exception()); + } + // 2.Let weakset be OrdinaryCreateFromConstructor(NewTarget, "%WeakSetPrototype%", «‍[[WeakSetData]]» ). + JSHandle constructor = GetConstructor(argv); + JSHandle obj = factory->NewJSObjectByConstructor(JSHandle(constructor), newTarget); + // 3.returnIfAbrupt() + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + JSHandle weakSet = JSHandle::Cast(obj); + // 3.ReturnIfAbrupt(weakSet). + // 4.WeakSet set’s [[WeakSetData]] internal slot to a new empty List. + JSHandle linkedSet = LinkedHashSet::Create(thread, true); + weakSet->SetLinkedSet(thread, linkedSet); + + // add data into weakset from iterable + // 5.If iterable is not present, let iterable be undefined. + // 6.If iterable is either undefined or null, let iter be undefined. + JSHandle iterable(GetCallArg(argv, 0)); + // 8.If iter is undefined, return weakset + if (iterable->IsUndefined() || iterable->IsNull()) { + return weakSet.GetTaggedValue(); + } + // Let adder be Get(weakset, "add"). + JSHandle adderKey(factory->NewFromCanBeCompressString("add")); + JSHandle weakSetHandle(weakSet); + JSHandle adder = JSObject::GetProperty(thread, weakSetHandle, adderKey).GetValue(); + // ReturnIfAbrupt(adder). + RETURN_VALUE_IF_ABRUPT_COMPLETION(thread, adder.GetTaggedValue()); + // If IsCallable(adder) is false, throw a TypeError exception + if (!adder->IsCallable()) { + THROW_TYPE_ERROR_AND_RETURN(thread, "adder is not callable", adder.GetTaggedValue()); + } + // Let iter be GetIterator(iterable). + JSHandle iter(JSIterator::GetIterator(thread, iterable)); + + // ReturnIfAbrupt(iter). + RETURN_VALUE_IF_ABRUPT_COMPLETION(thread, iter.GetTaggedValue()); + // values in iterator_result may be a JSArray, values[0] = key values[1]=value, used valueIndex to get value from + // jsarray + JSHandle valueIndex(thread, JSTaggedValue(1)); + JSHandle next = JSIterator::IteratorStep(thread, iter); + JSMutableHandle status(thread, JSTaggedValue::Undefined()); + while (!next->IsFalse()) { + // ReturnIfAbrupt(next). + RETURN_VALUE_IF_ABRUPT_COMPLETION(thread, next.GetTaggedValue()); + // Let nextValue be IteratorValue(next). + JSHandle nextValue(JSIterator::IteratorValue(thread, next)); + // ReturnIfAbrupt(nextValue). + RETURN_VALUE_IF_ABRUPT_COMPLETION(thread, nextValue.GetTaggedValue()); + InternalCallParams *arguments = thread->GetInternalCallParams(); + arguments->MakeArgv(nextValue); + if (nextValue->IsArray(thread)) { + auto prop = JSObject::GetProperty(thread, nextValue, valueIndex).GetValue(); + arguments->MakeArgv(prop); + } + JSTaggedValue ret = JSFunction::Call(thread, adder, JSHandle(weakSet), 1, arguments->GetArgv()); + + // Let status be Call(adder, weakset, «nextValue.[[value]]»). + status.Update(ret); + // If status is an abrupt completion, return IteratorClose(iter, status). + if (thread->HasPendingException()) { + return JSIterator::IteratorCloseAndReturn(thread, iter, status); + } + // Let next be IteratorStep(iter). + next = JSIterator::IteratorStep(thread, iter); + } + return weakSet.GetTaggedValue(); +} + +JSTaggedValue BuiltinsWeakSet::Add(EcmaRuntimeCallInfo *argv) +{ + ASSERT(argv); + BUILTINS_API_TRACE(argv->GetThread(), WeakSet, Add); + JSThread *thread = argv->GetThread(); + [[maybe_unused]] EcmaHandleScope handleScope(thread); + JSHandle self = GetThis(argv); + + // 2.If Type(S) is not Object, throw a TypeError exception. + // 3.If S does not have a [[WeakSetData]] internal slot, throw a TypeError exception. + if (!self->IsJSWeakSet()) { + THROW_TYPE_ERROR_AND_RETURN(thread, "obj is not JSWeakSet", JSTaggedValue::Exception()); + } + + JSHandle value(GetCallArg(argv, 0)); + if (!value->IsHeapObject()) { + THROW_TYPE_ERROR_AND_RETURN(thread, "value is not an object", JSTaggedValue::Exception()); + } + if (value->IsSymbol() || value->IsString()) { + THROW_TYPE_ERROR_AND_RETURN(thread, "value is Symblol or String", JSTaggedValue::Exception()); + } + + JSHandle weakSet(thread, JSWeakSet::Cast(*JSTaggedValue::ToObject(thread, self))); + + JSWeakSet::Add(thread, weakSet, value); + return weakSet.GetTaggedValue(); +} + +JSTaggedValue BuiltinsWeakSet::Delete(EcmaRuntimeCallInfo *argv) +{ + ASSERT(argv); + BUILTINS_API_TRACE(argv->GetThread(), WeakSet, Delete); + JSThread *thread = argv->GetThread(); + [[maybe_unused]] EcmaHandleScope handleScope(thread); + JSHandle self = GetThis(argv); + // 2.If Type(S) is not Object, throw a TypeError exception. + // 3.If S does not have a [[WeakSetData]] internal slot, throw a TypeError exception. + if (!self->IsJSWeakSet()) { + THROW_TYPE_ERROR_AND_RETURN(thread, "obj is not JSWeakSet", JSTaggedValue::Exception()); + } + + JSHandle weakSet(thread, JSWeakSet::Cast(*JSTaggedValue::ToObject(thread, self))); + JSHandle value = GetCallArg(argv, 0); + if (!value->IsHeapObject()) { + GetTaggedBoolean(false); + } + return GetTaggedBoolean(JSWeakSet::Delete(thread, weakSet, value)); +} + +JSTaggedValue BuiltinsWeakSet::Has(EcmaRuntimeCallInfo *argv) +{ + ASSERT(argv); + BUILTINS_API_TRACE(argv->GetThread(), WeakSet, Has); + JSThread *thread = argv->GetThread(); + [[maybe_unused]] EcmaHandleScope handleScope(thread); + JSHandle self = GetThis(argv); + // 2.If Type(S) is not Object, throw a TypeError exception. + // 3.If S does not have a [[SetData]] internal slot, throw a TypeError exception. + if (!self->IsJSWeakSet()) { + THROW_TYPE_ERROR_AND_RETURN(thread, "obj is not JSWeakSet", JSTaggedValue::Exception()); + } + JSWeakSet *jsWeakSet = JSWeakSet::Cast(*JSTaggedValue::ToObject(thread, self)); + JSHandle value = GetCallArg(argv, 0); + if (!value->IsHeapObject()) { + GetTaggedBoolean(false); + } + return GetTaggedBoolean(jsWeakSet->Has(value.GetTaggedValue())); +} +} // namespace panda::ecmascript::builtins diff --git a/runtime/builtins/builtins_weak_set.h b/runtime/builtins/builtins_weak_set.h new file mode 100644 index 000000000..5d5349ab3 --- /dev/null +++ b/runtime/builtins/builtins_weak_set.h @@ -0,0 +1,35 @@ +/* + * Copyright (c) 2021-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_BUILTINS_BUILTINS_WEAK_SET_H +#define ECMASCRIPT_BUILTINS_BUILTINS_WEAK_SET_H + +#include "plugins/ecmascript/runtime/base/builtins_base.h" +#include "plugins/ecmascript/runtime/ecma_runtime_call_info.h" + +namespace panda::ecmascript::builtins { +class BuiltinsWeakSet : public ecmascript::base::BuiltinsBase { +public: + // 23.4.1.1 + static JSTaggedValue WeakSetConstructor(EcmaRuntimeCallInfo *argv); + // 23.4.3.1 + static JSTaggedValue Add(EcmaRuntimeCallInfo *argv); + // 23.4.3.3 + static JSTaggedValue Delete(EcmaRuntimeCallInfo *argv); + // 23.4.3.4 + static JSTaggedValue Has(EcmaRuntimeCallInfo *argv); +}; +} // namespace panda::ecmascript::builtins +#endif // ECMASCRIPT_BUILTINS_BUILTINS_SET_H diff --git a/runtime/class_info_extractor.cpp b/runtime/class_info_extractor.cpp new file mode 100644 index 000000000..2c9c84dd4 --- /dev/null +++ b/runtime/class_info_extractor.cpp @@ -0,0 +1,419 @@ +/* + * Copyright (c) 2021-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 "plugins/ecmascript/runtime/class_info_extractor.h" + +#include "plugins/ecmascript/runtime/global_env.h" +#include "plugins/ecmascript/runtime/js_function.h" +#include "plugins/ecmascript/runtime/tagged_dictionary.h" + +namespace panda::ecmascript { +void ClassInfoExtractor::BuildClassInfoExtractorFromLiteral(JSThread *thread, JSHandle &extractor, + const JSHandle &literal) +{ + [[maybe_unused]] EcmaHandleScope handleScope(thread); + const GlobalEnvConstants *globalConst = thread->GlobalConstants(); + ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); + + uint32_t literalBufferLength = literal->GetLength(); + // non static properties number is hidden in the last index of Literal buffer + uint32_t nonStaticNum = literal->Get(thread, literalBufferLength - 1).GetInt(); + + // Reserve sufficient length to prevent frequent creation. + JSHandle nonStaticKeys = factory->NewTaggedArray(nonStaticNum + NON_STATIC_RESERVED_LENGTH); + JSHandle nonStaticProperties = factory->NewTaggedArray(nonStaticNum + NON_STATIC_RESERVED_LENGTH); + + nonStaticKeys->Set(thread, CONSTRUCTOR_INDEX, globalConst->GetConstructorString()); + + JSHandle nonStaticElements = factory->EmptyArray(); + + if (nonStaticNum != 0U) { + ExtractContentsDetail nonStaticDetail {0, nonStaticNum * 2, NON_STATIC_RESERVED_LENGTH, nullptr}; + + if (UNLIKELY(ExtractAndReturnWhetherWithElements(thread, literal, nonStaticDetail, nonStaticKeys, + nonStaticProperties, nonStaticElements))) { + extractor->SetNonStaticWithElements(); + extractor->SetNonStaticElements(thread, nonStaticElements); + } + } + + extractor->SetNonStaticKeys(thread, nonStaticKeys); + extractor->SetNonStaticProperties(thread, nonStaticProperties); + + JSHandle prototypeHClass = CreatePrototypeHClass(thread, nonStaticKeys, nonStaticProperties); + extractor->SetPrototypeHClass(thread, prototypeHClass); + + uint32_t staticNum = (literalBufferLength - 1) / 2 - nonStaticNum; + + // Reserve sufficient length to prevent frequent creation. + JSHandle staticKeys = factory->NewTaggedArray(staticNum + STATIC_RESERVED_LENGTH); + JSHandle staticProperties = factory->NewTaggedArray(staticNum + STATIC_RESERVED_LENGTH); + + staticKeys->Set(thread, LENGTH_INDEX, globalConst->GetLengthString()); + staticKeys->Set(thread, NAME_INDEX, globalConst->GetNameString()); + staticKeys->Set(thread, PROTOTYPE_INDEX, globalConst->GetPrototypeString()); + + JSHandle staticElements = factory->EmptyArray(); + + if (staticNum != 0U) { + ExtractContentsDetail staticDetail {nonStaticNum * 2, literalBufferLength - 1, STATIC_RESERVED_LENGTH, + extractor->GetConstructorMethod()}; + + if (UNLIKELY(ExtractAndReturnWhetherWithElements(thread, literal, staticDetail, staticKeys, staticProperties, + staticElements))) { + extractor->SetStaticWithElements(); + extractor->SetStaticElements(thread, staticElements); + } + } else { + // without static properties, set class name + CString clsName = extractor->GetConstructorMethod()->ParseFunctionName(); + JSHandle clsNameHandle = factory->NewFromString(clsName); + staticProperties->Set(thread, NAME_INDEX, clsNameHandle); + } + + // set prototype internal accessor + JSHandle prototypeAccessor = globalConst->GetHandledFunctionPrototypeAccessor(); + staticProperties->Set(thread, PROTOTYPE_INDEX, prototypeAccessor); + + extractor->SetStaticKeys(thread, staticKeys); + extractor->SetStaticProperties(thread, staticProperties); + + JSHandle ctorHClass = CreateConstructorHClass(thread, staticKeys, staticProperties); + extractor->SetConstructorHClass(thread, ctorHClass); +} + +bool ClassInfoExtractor::ExtractAndReturnWhetherWithElements(JSThread *thread, const JSHandle &literal, + const ExtractContentsDetail &detail, + JSHandle &keys, + JSHandle &properties, + JSHandle &elements) +{ + const GlobalEnvConstants *globalConst = thread->GlobalConstants(); + + ASSERT(keys->GetLength() == properties->GetLength() && elements->GetLength() == 0); + + uint32_t pos = detail.fillStartLoc; + bool withElemenstFlag = false; + bool isStaticFlag = detail.ctorMethod != nullptr; + bool keysHasNameFlag = false; + + JSHandle nameString = globalConst->GetHandledNameString(); + JSMutableHandle firstValue(thread, JSTaggedValue::Undefined()); + JSMutableHandle secondValue(thread, JSTaggedValue::Undefined()); + for (uint32_t index = detail.extractBegin; index < detail.extractEnd; index += 2) { // 2: key-value pair + firstValue.Update(literal->Get(index)); + secondValue.Update(literal->Get(index + 1)); + ASSERT_PRINT(JSTaggedValue::IsPropertyKey(firstValue), "Key is not a property key"); + + if (LIKELY(firstValue->IsString())) { + if (isStaticFlag && !keysHasNameFlag && JSTaggedValue::SameValue(firstValue, nameString)) { + properties->Set(thread, NAME_INDEX, secondValue); + keysHasNameFlag = true; + continue; + } + + // front-end can do better: write index in class literal directly. + uint32_t elementIndex = 0; + if (JSTaggedValue::StringToElementIndex(firstValue.GetTaggedValue(), &elementIndex)) { + ASSERT(elementIndex < JSObject::MAX_ELEMENT_INDEX); + uint32_t elementsLength = elements->GetLength(); + elements = TaggedArray::SetCapacity(thread, elements, elementsLength + 2); // 2: key-value pair + elements->Set(thread, elementsLength, firstValue); + elements->Set(thread, elementsLength + 1, secondValue); + withElemenstFlag = true; + continue; + } + } + + keys->Set(thread, pos, firstValue); + properties->Set(thread, pos, secondValue); + pos++; + } + + if (isStaticFlag) { + if (LIKELY(!keysHasNameFlag)) { + [[maybe_unused]] EcmaHandleScope handleScope(thread); + ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); + CString clsName = detail.ctorMethod->ParseFunctionName(); + JSHandle clsNameHandle = factory->NewFromString(clsName); + properties->Set(thread, NAME_INDEX, clsNameHandle); + } else { + // class has static name property, reserved length bigger 1 than actual, need trim + uint32_t trimOneLength = keys->GetLength() - 1; + keys->Trim(thread, trimOneLength); + properties->Trim(thread, trimOneLength); + } + } + + if (UNLIKELY(withElemenstFlag)) { + ASSERT(pos + elements->GetLength() / 2 == properties->GetLength()); // 2: half + keys->Trim(thread, pos); + properties->Trim(thread, pos); + } + + return withElemenstFlag; +} + +JSHandle ClassInfoExtractor::CreatePrototypeHClass(JSThread *thread, JSHandle &keys, + JSHandle &properties) +{ + ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); + + uint32_t length = keys->GetLength(); + JSHandle hclass; + if (LIKELY(length <= PropertyAttributes::MAX_CAPACITY_OF_PROPERTIES)) { + JSMutableHandle key(thread, JSTaggedValue::Undefined()); + JSHandle layout = factory->CreateLayoutInfo(length); + for (uint32_t index = 0; index < length; ++index) { + key.Update(keys->Get(index)); + ASSERT_PRINT(JSTaggedValue::IsPropertyKey(key), "Key is not a property key"); + PropertyAttributes attributes = PropertyAttributes::Default(true, false, true); // non-enumerable + + if (UNLIKELY(properties->Get(index).IsAccessor())) { + attributes.SetIsAccessor(true); + } + + attributes.SetIsInlinedProps(true); + attributes.SetRepresentation(Representation::MIXED); + attributes.SetOffset(index); + layout->AddKey(thread, index, key.GetTaggedValue(), attributes); + } + + hclass = factory->NewEcmaDynClass(JSObject::SIZE, JSType::JS_OBJECT, length); + // Not need set proto here + hclass->SetLayout(thread, layout); + hclass->SetNumberOfProps(length); + } else { + // dictionary mode + hclass = factory->NewEcmaDynClass(JSObject::SIZE, JSType::JS_OBJECT, 0); // without in-obj + hclass->SetIsDictionaryMode(true); + hclass->SetNumberOfProps(0); + } + + hclass->SetClassPrototype(true); + hclass->SetIsPrototype(true); + return hclass; +} + +JSHandle ClassInfoExtractor::CreateConstructorHClass(JSThread *thread, JSHandle &keys, + JSHandle &properties) +{ + ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); + + uint32_t length = keys->GetLength(); + JSHandle hclass; + if (LIKELY(length <= PropertyAttributes::MAX_CAPACITY_OF_PROPERTIES)) { + JSMutableHandle key(thread, JSTaggedValue::Undefined()); + JSHandle layout = factory->CreateLayoutInfo(length); + for (uint32_t index = 0; index < length; ++index) { + key.Update(keys->Get(index)); + ASSERT_PRINT(JSTaggedValue::IsPropertyKey(key), "Key is not a property key"); + PropertyAttributes attributes; + switch (index) { + case LENGTH_INDEX: + attributes = PropertyAttributes::Default(false, false, true); + break; + case NAME_INDEX: + if (LIKELY(properties->Get(NAME_INDEX).IsString())) { + attributes = PropertyAttributes::Default(false, false, true); + } else { + ASSERT(properties->Get(NAME_INDEX).IsJSFunction() || properties->Get(NAME_INDEX).IsNull()); + attributes = PropertyAttributes::Default(true, false, true); + } + break; + case PROTOTYPE_INDEX: + attributes = PropertyAttributes::DefaultAccessor(false, false, false); + break; + default: + attributes = PropertyAttributes::Default(true, false, true); + break; + } + + if (UNLIKELY(properties->Get(index).IsAccessor())) { + attributes.SetIsAccessor(true); + } + + attributes.SetIsInlinedProps(true); + attributes.SetRepresentation(Representation::MIXED); + attributes.SetOffset(index); + layout->AddKey(thread, index, key.GetTaggedValue(), attributes); + } + + hclass = factory->NewEcmaDynClass(factory->hclassClass_, JSFunction::SIZE, JSType::JS_FUNCTION, + HClass::IS_CALLABLE, length); + // Not need set proto here + hclass->SetLayout(thread, layout); + hclass->SetNumberOfProps(length); + } else { + // dictionary mode + hclass = factory->NewEcmaDynClass(JSFunction::SIZE, JSType::JS_FUNCTION, 0); // without in-obj + hclass->SetIsDictionaryMode(true); + hclass->SetNumberOfProps(0); + } + + hclass->SetClassConstructor(true); + hclass->SetConstructor(true); + hclass->GetHClass()->MarkFieldAsNative(JSFunction::METHOD_OFFSET); + + return hclass; +} + +JSHandle ClassHelper::DefineClassTemplate(JSThread *thread, JSHandle &extractor, + const JSHandle &constantpool) +{ + ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); + + JSHandle prototypeHClass(thread, extractor->GetPrototypeHClass()); + JSHandle prototype = factory->NewJSObject(prototypeHClass); + + JSHandle constructorHClass(thread, extractor->GetConstructorHClass()); + JSHandle constructor = factory->NewJSFunctionByDynClass( + extractor->GetConstructorMethod(), constructorHClass, FunctionKind::CLASS_CONSTRUCTOR); + + // non-static + JSHandle nonStaticProperties(thread, extractor->GetNonStaticProperties()); + nonStaticProperties->Set(thread, 0, constructor); + + uint32_t nonStaticLength = nonStaticProperties->GetLength(); + JSMutableHandle propValue(thread, JSTaggedValue::Undefined()); + + if (LIKELY(!prototypeHClass->IsDictionaryMode())) { + for (uint32_t index = 0; index < nonStaticLength; ++index) { + propValue.Update(nonStaticProperties->Get(index)); + if (propValue->IsJSFunction()) { + JSHandle propFunc = JSHandle::Cast(propValue); + propFunc->SetHomeObject(thread, prototype); + propFunc->SetConstantPool(thread, constantpool); + } + prototype->SetPropertyInlinedProps(thread, index, propValue.GetTaggedValue()); + } + } else { + JSHandle nonStaticKeys(thread, extractor->GetNonStaticKeys()); + JSHandle dict = BuildDictionaryPropeties(thread, prototype, nonStaticKeys, nonStaticProperties, + ClassPropertyType::NON_STATIC, constantpool); + prototype->SetProperties(thread, dict); + } + + // non-static elements + if (UNLIKELY(extractor->IsNonStaticWithElements())) { + JSHandle nonStaticElements(thread, extractor->GetNonStaticElements()); + ClassHelper::HandleElementsProperties(thread, prototype, nonStaticElements, constantpool); + } + + // static + JSHandle staticProperties(thread, extractor->GetStaticProperties()); + uint32_t staticLength = staticProperties->GetLength(); + + if (LIKELY(!constructorHClass->IsDictionaryMode())) { + for (uint32_t index = 0; index < staticLength; ++index) { + propValue.Update(staticProperties->Get(index)); + if (propValue->IsJSFunction()) { + JSHandle propFunc = JSHandle::Cast(propValue); + propFunc->SetHomeObject(thread, constructor); + propFunc->SetConstantPool(thread, constantpool); + } + JSHandle::Cast(constructor)->SetPropertyInlinedProps(thread, index, propValue.GetTaggedValue()); + } + } else { + JSHandle staticKeys(thread, extractor->GetStaticKeys()); + JSHandle dict = + BuildDictionaryPropeties(thread, JSHandle(constructor), staticKeys, staticProperties, + ClassPropertyType::STATIC, constantpool); + constructor->SetProperties(thread, dict); + } + + // static elements + if (UNLIKELY(extractor->IsStaticWithElements())) { + JSHandle staticElements(thread, extractor->GetStaticElements()); + ClassHelper::HandleElementsProperties(thread, JSHandle(constructor), staticElements, constantpool); + } + + constructor->SetProtoOrDynClass(thread, prototype); + + return constructor; +} + +JSHandle ClassHelper::BuildDictionaryPropeties(JSThread *thread, const JSHandle &object, + JSHandle &keys, + JSHandle &properties, + ClassPropertyType type, + const JSHandle &constantpool) +{ + uint32_t length = keys->GetLength(); + ASSERT(length > PropertyAttributes::MAX_CAPACITY_OF_PROPERTIES); + ASSERT(keys->GetLength() == properties->GetLength()); + + JSMutableHandle dict(thread, + NameDictionary::Create(thread, NameDictionary::ComputeHashTableSize(length))); + JSMutableHandle propKey(thread, JSTaggedValue::Undefined()); + JSMutableHandle propValue(thread, JSTaggedValue::Undefined()); + for (uint32_t index = 0; index < length; index++) { + PropertyAttributes attributes; + if (type == ClassPropertyType::STATIC) { + switch (index) { + case ClassInfoExtractor::LENGTH_INDEX: + attributes = PropertyAttributes::Default(false, false, true); + break; + case ClassInfoExtractor::NAME_INDEX: + if (LIKELY(properties->Get(ClassInfoExtractor::NAME_INDEX).IsString())) { + attributes = PropertyAttributes::Default(false, false, true); + } else { + ASSERT(properties->Get(ClassInfoExtractor::NAME_INDEX).IsJSFunction()); + attributes = PropertyAttributes::Default(true, false, true); + } + break; + case ClassInfoExtractor::PROTOTYPE_INDEX: + attributes = PropertyAttributes::DefaultAccessor(false, false, false); + break; + default: + attributes = PropertyAttributes::Default(true, false, true); + break; + } + } else { + attributes = PropertyAttributes::Default(true, false, true); // non-enumerable + } + propKey.Update(keys->Get(index)); + propValue.Update(properties->Get(index)); + if (propValue->IsJSFunction()) { + JSHandle propFunc = JSHandle::Cast(propValue); + propFunc->SetHomeObject(thread, object); + propFunc->SetConstantPool(thread, constantpool); + } + JSHandle newDict = NameDictionary::PutIfAbsent(thread, dict, propKey, propValue, attributes); + dict.Update(newDict); + } + + return dict; +} + +void ClassHelper::HandleElementsProperties(JSThread *thread, const JSHandle &object, + JSHandle &elements, const JSHandle &constantpool) +{ + JSMutableHandle elementsKey(thread, JSTaggedValue::Undefined()); + JSMutableHandle elementsValue(thread, JSTaggedValue::Undefined()); + for (uint32_t index = 0; index < elements->GetLength(); index += 2) { // 2: key-value pair + elementsKey.Update(elements->Get(index)); + elementsValue.Update(elements->Get(index + 1)); + // class property attribute is not default, will transition to dictionary directly. + JSObject::DefinePropertyByLiteral(thread, object, elementsKey, elementsValue, true); + + if (elementsValue->IsJSFunction()) { + JSHandle elementsFunc = JSHandle::Cast(elementsValue); + elementsFunc->SetHomeObject(thread, object); + elementsFunc->SetConstantPool(thread, constantpool); + } + } +} +} // namespace panda::ecmascript diff --git a/runtime/class_info_extractor.h b/runtime/class_info_extractor.h new file mode 100644 index 000000000..723212798 --- /dev/null +++ b/runtime/class_info_extractor.h @@ -0,0 +1,125 @@ +/* + * Copyright (c) 2021-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_CLASS_INFO_EXTRACTOR_H +#define ECMASCRIPT_CLASS_INFO_EXTRACTOR_H + +#include "js_tagged_value-inl.h" + +namespace panda::ecmascript { +// ClassInfoExtractor will analyze and extract the contents from class literal to keys, properties and elements(both +// non-static and static), later generate the complete hclass (both prototype and constructor) based on keys. +// Attention: keys accessor stores the property key and properties accessor stores the property value, but elements +// accessor stores the key-value pair abuttally. +class ClassInfoExtractor : public TaggedObject { +public: + static constexpr uint8_t NON_STATIC_RESERVED_LENGTH = 1; + static constexpr uint8_t STATIC_RESERVED_LENGTH = 3; + + static constexpr uint8_t CONSTRUCTOR_INDEX = 0; + static constexpr uint8_t LENGTH_INDEX = 0; + static constexpr uint8_t NAME_INDEX = 1; + static constexpr uint8_t PROTOTYPE_INDEX = 2; + + struct ExtractContentsDetail { + uint32_t extractBegin; + uint32_t extractEnd; + uint8_t fillStartLoc; + JSMethod *ctorMethod; + }; + + CAST_CHECK(ClassInfoExtractor, IsClassInfoExtractor); + + using NonStaticWithElementsBit = BitField; + using StaticWithElementsBit = NonStaticWithElementsBit::NextFlag; + + static void BuildClassInfoExtractorFromLiteral(JSThread *thread, JSHandle &extractor, + const JSHandle &literal); + + inline void InitializeBitField() + { + SetBitField(0UL); + } + + inline void SetNonStaticWithElements() + { + uint8_t bits = GetBitField(); + uint8_t newVal = NonStaticWithElementsBit::Update(bits, true); + SetBitField(newVal); + } + + inline void SetStaticWithElements() + { + uint8_t bits = GetBitField(); + uint8_t newVal = StaticWithElementsBit::Update(bits, true); + SetBitField(newVal); + } + + inline bool IsNonStaticWithElements() const + { + return NonStaticWithElementsBit::Decode(GetBitField()); + } + + inline bool IsStaticWithElements() const + { + return StaticWithElementsBit::Decode(GetBitField()); + } + + static constexpr size_t BIT_FIELD_OFFSET = TaggedObjectSize(); + SET_GET_PRIMITIVE_FIELD(BitField, uint8_t, BIT_FIELD_OFFSET, CONSTRUCTOR_METHOD_OFFSET) + SET_GET_NATIVE_FIELD(ConstructorMethod, JSMethod, CONSTRUCTOR_METHOD_OFFSET, PROTOTYPE_HCLASS_OFFSET) + ACCESSORS(PrototypeHClass, PROTOTYPE_HCLASS_OFFSET, NON_STATIC_KEYS_OFFSET) + ACCESSORS(NonStaticKeys, NON_STATIC_KEYS_OFFSET, NON_STATIC_PROPERTIES_OFFSET) + ACCESSORS(NonStaticProperties, NON_STATIC_PROPERTIES_OFFSET, NON_STATIC_ELEMENTS_OFFSET) + ACCESSORS(NonStaticElements, NON_STATIC_ELEMENTS_OFFSET, CONSTRUCTOR_HCLASS_OFFSET) + ACCESSORS(ConstructorHClass, CONSTRUCTOR_HCLASS_OFFSET, STATIC_KEYS_OFFSET) + ACCESSORS(StaticKeys, STATIC_KEYS_OFFSET, STATIC_PROPERTIES_OFFSET) + ACCESSORS(StaticProperties, STATIC_PROPERTIES_OFFSET, STATIC_ELEMENTS_OFFSET) + ACCESSORS(StaticElements, STATIC_ELEMENTS_OFFSET, SIZE) + + DECL_VISIT_OBJECT(PROTOTYPE_HCLASS_OFFSET, SIZE) + DECL_DUMP() + +private: + static bool ExtractAndReturnWhetherWithElements(JSThread *thread, const JSHandle &literal, + const ExtractContentsDetail &detail, + JSHandle &keys, JSHandle &properties, + JSHandle &elements); + + static JSHandle CreatePrototypeHClass(JSThread *thread, JSHandle &keys, + JSHandle &properties); + + static JSHandle CreateConstructorHClass(JSThread *thread, JSHandle &keys, + JSHandle &properties); +}; + +enum class ClassPropertyType : uint8_t { NON_STATIC = 0, STATIC }; + +class ClassHelper { +public: + static JSHandle DefineClassTemplate(JSThread *thread, JSHandle &extractor, + const JSHandle &constantpool); + +private: + static JSHandle BuildDictionaryPropeties(JSThread *thread, const JSHandle &object, + JSHandle &keys, + JSHandle &properties, ClassPropertyType type, + const JSHandle &constantpool); + + static void HandleElementsProperties(JSThread *thread, const JSHandle &object, + JSHandle &elements, const JSHandle &constantpool); +}; +} // namespace panda::ecmascript +#endif // ECMASCRIPT_CLASS_INFO_EXTRACTOR_H diff --git a/runtime/class_linker/panda_file_translator.cpp b/runtime/class_linker/panda_file_translator.cpp new file mode 100644 index 000000000..def03eb75 --- /dev/null +++ b/runtime/class_linker/panda_file_translator.cpp @@ -0,0 +1,587 @@ +/* + * Copyright (c) 2021-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 "panda_file_translator.h" + +#include +#include +#include + +#include "plugins/ecmascript/runtime/class_info_extractor.h" +#include "plugins/ecmascript/runtime/class_linker/program_object-inl.h" +#include "plugins/ecmascript/runtime/global_env.h" +#include "plugins/ecmascript/runtime/interpreter/interpreter.h" +#include "plugins/ecmascript/runtime/js_array.h" +#include "plugins/ecmascript/runtime/js_function.h" +#include "plugins/ecmascript/runtime/js_thread.h" +#include "plugins/ecmascript/runtime/literal_data_extractor.h" +#include "plugins/ecmascript/runtime/object_factory.h" +#include "plugins/ecmascript/runtime/tagged_array.h" +#include "libpandabase/mem/mem.h" +#include "libpandabase/utils/logger.h" +#include "libpandabase/utils/utf.h" +#include "libpandafile/bytecode_instruction-inl.h" +#include "libpandafile/class_data_accessor-inl.h" + +#include "libpandabase/os/filesystem.h" + +namespace panda::ecmascript { +PandaFileTranslator::PandaFileTranslator(EcmaVM *vm) + : ecmaVm_(vm), factory_(vm->GetFactory()), thread_(vm->GetJSThread()) +{ +} + +JSHandle PandaFileTranslator::TranslatePandaFile(EcmaVM *vm, const panda_file::File &pf, + const CString &methodName) +{ + PandaFileTranslator translator(vm); + translator.TranslateClasses(pf, methodName); + auto result = translator.GenerateProgram(pf); + return JSHandle(translator.thread_, result); +} + +void PandaFileTranslator::QuickPandaFile(EcmaVM *vm, const panda_file::File &pf) +{ + PandaFileTranslator translator(vm); + translator.TranslateClasses(pf, ""); +} + +template +static T *InitializeMemory(T *mem, Args... args) +{ + return new (mem) T(std::forward(args)...); +} + +const JSMethod *PandaFileTranslator::FindMethods(uint32_t offset) const +{ + Span methods = GetMethods(); + auto pred = [offset](const JSMethod &method) { return method.GetFileId().GetOffset() == offset; }; + auto it = std::find_if(methods.begin(), methods.end(), pred); + if (it != methods.end()) { + return &*it; + } + return nullptr; +} + +void PandaFileTranslator::TranslateClasses(const panda_file::File &pf, const CString &methodName) +{ + RegionFactory *factory = ecmaVm_->GetRegionFactory(); + Span classIndexes = pf.GetClasses(); + uint32_t numMethods = 0; + + for (const uint32_t index : classIndexes) { + panda_file::File::EntityId classId(index); + if (pf.IsExternal(classId)) { + continue; + } + panda_file::ClassDataAccessor cda(pf, classId); + numMethods += cda.GetMethodsNumber(); + } + + auto methodsData = factory->AllocateBuffer(sizeof(JSMethod) * numMethods); + Span methods {static_cast(methodsData), numMethods}; + size_t methodIdx = 0; + + panda_file::File::StringData sd = {static_cast(methodName.size()), + reinterpret_cast(methodName.c_str())}; + + auto aot_manager = Runtime::GetCurrent()->GetClassLinker()->GetAotManager(); + auto filename = os::GetAbsolutePath(pf.GetFilename()); + auto aot_pfile = aot_manager->FindPandaFile(filename); + + for (const uint32_t index : classIndexes) { + panda_file::File::EntityId classId(index); + if (pf.IsExternal(classId)) { + continue; + } + panda_file::ClassDataAccessor cda(pf, classId); + auto descriptor = cda.GetDescriptor(); + + compiler::AotClass aot_class = + (aot_pfile != nullptr) ? aot_pfile->GetClass(classId.GetOffset()) : compiler::AotClass::Invalid(); + + cda.EnumerateMethods( + [this, &aot_class, &sd, &methods, &methodIdx, &pf, &descriptor](panda_file::MethodDataAccessor &mda) { + TranslateMethod(aot_class, sd, methods, &methodIdx, pf, descriptor, mda); + }); + } + + SetMethods(methods, numMethods); +} + +void PandaFileTranslator::TranslateMethod(const compiler::AotClass &aot_class, const panda_file::File::StringData &sd, + Span &methods, size_t *methodIdx, const panda_file::File &pf, + const uint8_t *descriptor, panda_file::MethodDataAccessor &mda) +{ + auto codeId = mda.GetCodeId(); + ASSERT(codeId.has_value()); + + JSMethod *method = &methods[(*methodIdx)++]; + panda_file::CodeDataAccessor codeDataAccessor(pf, codeId.value()); + uint32_t codeSize = codeDataAccessor.GetCodeSize(); + + if (mainMethodIndex_ == 0 && pf.GetStringData(mda.GetNameId()) == sd) { + mainMethodIndex_ = mda.GetMethodId().GetOffset(); + } + + panda_file::ProtoDataAccessor pda(pf, mda.GetProtoId()); + + auto *class_linker = Runtime::GetCurrent()->GetClassLinker(); + auto *extension = class_linker->GetExtension(panda::panda_file::SourceLang::ECMASCRIPT); + auto klass = extension->GetClass(descriptor); + if (klass == nullptr) { + // Case for ark_aot. This is workaround. + klass = class_linker->LoadClass(&pf, descriptor, panda::panda_file::SourceLang::ECMASCRIPT); + } + + ASSERT(klass != nullptr); // Very important assert. + + InitializeMemory(method, klass, &pf, mda.GetMethodId(), codeDataAccessor.GetCodeId(), mda.GetAccessFlags(), + codeDataAccessor.GetNumArgs(), reinterpret_cast(pda.GetShorty().Data())); + + if (aot_class.IsValid()) { + auto entry = aot_class.FindMethodCodeEntry(*methodIdx - 1); + if (entry != nullptr) { + method->SetCompiledEntryPoint(entry); + } else { + method->SetInterpreterEntryPoint(); + } + } else { + method->SetInterpreterEntryPoint(); + } + + method->SetCallTypeFromAnnotation(); + const uint8_t *insns = codeDataAccessor.GetInstructions(); + if (translated_code_.find(insns) == translated_code_.end()) { + translated_code_.insert(insns); + TranslateBytecode(codeSize, insns, pf, method); + } +} + +Program *PandaFileTranslator::GenerateProgram(const panda_file::File &pf) +{ + EcmaHandleScope handleScope(thread_); + + JSHandle program = factory_->NewProgram(); + JSHandle location = factory_->NewFromStdStringUnCheck(pf.GetFilename(), true); + + // +1 for program + JSHandle constpool = factory_->NewConstantPool(constpoolIndex_ + 1); + program->SetConstantPool(thread_, constpool.GetTaggedValue()); + program->SetLocation(thread_, location.GetTaggedValue()); + + JSHandle env = ecmaVm_->GetGlobalEnv(); + JSHandle dynclass = JSHandle::Cast(env->GetFunctionClassWithProto()); + JSHandle normalDynclass = JSHandle::Cast(env->GetFunctionClassWithoutProto()); + JSHandle asyncDynclass = JSHandle::Cast(env->GetAsyncFunctionClass()); + JSHandle generatorDynclass = JSHandle::Cast(env->GetGeneratorFunctionClass()); + JSHandle asyncgeneratorDynclass = JSHandle::Cast(env->GetAsyncGeneratorFunctionClass()); + + for (const auto &it : constpoolMap_) { + ConstPoolValue value(it.second); + if (value.GetConstpoolType() == ConstPoolType::STRING) { + panda_file::File::EntityId id(it.first); + auto foundStr = pf.GetStringData(id); + auto string = + factory_->GetRawStringFromStringTable(foundStr.data, foundStr.utf16_length, foundStr.is_ascii); + if (string == nullptr) { + LOG(FATAL, ECMASCRIPT) << "Not enough memory"; + } + constpool->Set(thread_, value.GetConstpoolIndex(), JSTaggedValue(string)); + } else if (value.GetConstpoolType() == ConstPoolType::BASE_FUNCTION) { + ASSERT(mainMethodIndex_ != it.first); + panda_file::File::EntityId id(it.first); + auto method = const_cast(FindMethods(it.first)); + ASSERT(method != nullptr); + + JSHandle jsFunc = + factory_->NewJSFunctionByDynClass(method, dynclass, FunctionKind::BASE_CONSTRUCTOR); + constpool->Set(thread_, value.GetConstpoolIndex(), jsFunc.GetTaggedValue()); + jsFunc->SetConstantPool(thread_, constpool.GetTaggedValue()); + } else if (value.GetConstpoolType() == ConstPoolType::NC_FUNCTION) { + ASSERT(mainMethodIndex_ != it.first); + panda_file::File::EntityId id(it.first); + auto method = const_cast(FindMethods(it.first)); + ASSERT(method != nullptr); + + JSHandle jsFunc = + factory_->NewJSFunctionByDynClass(method, normalDynclass, FunctionKind::NORMAL_FUNCTION); + constpool->Set(thread_, value.GetConstpoolIndex(), jsFunc.GetTaggedValue()); + jsFunc->SetConstantPool(thread_, constpool.GetTaggedValue()); + } else if (value.GetConstpoolType() == ConstPoolType::GENERATOR_FUNCTION) { + ASSERT(mainMethodIndex_ != it.first); + panda_file::File::EntityId id(it.first); + auto method = const_cast(FindMethods(it.first)); + ASSERT(method != nullptr); + + JSHandle jsFunc = + factory_->NewJSFunctionByDynClass(method, generatorDynclass, FunctionKind::GENERATOR_FUNCTION); + // 26.3.4.3 prototype + // Whenever a GeneratorFunction instance is created another ordinary object is also created and + // is the initial value of the generator function's "prototype" property. + JSHandle objFun = env->GetObjectFunction(); + JSHandle initialGeneratorFuncPrototype = + factory_->NewJSObjectByConstructor(JSHandle(objFun), objFun); + JSObject::SetPrototype(thread_, initialGeneratorFuncPrototype, env->GetGeneratorPrototype()); + jsFunc->SetProtoOrDynClass(thread_, initialGeneratorFuncPrototype); + + constpool->Set(thread_, value.GetConstpoolIndex(), jsFunc.GetTaggedValue()); + jsFunc->SetConstantPool(thread_, constpool.GetTaggedValue()); + } else if (value.GetConstpoolType() == ConstPoolType::ASYNC_FUNCTION) { + ASSERT(mainMethodIndex_ != it.first); + panda_file::File::EntityId id(it.first); + auto method = const_cast(FindMethods(it.first)); + ASSERT(method != nullptr); + + JSHandle jsFunc = + factory_->NewJSFunctionByDynClass(method, asyncDynclass, FunctionKind::ASYNC_FUNCTION); + constpool->Set(thread_, value.GetConstpoolIndex(), jsFunc.GetTaggedValue()); + jsFunc->SetConstantPool(thread_, constpool.GetTaggedValue()); + } else if (value.GetConstpoolType() == ConstPoolType::ASYNC_GENERATOR_FUNCTION) { + ASSERT(mainMethodIndex_ != it.first); + panda_file::File::EntityId id(it.first); + auto method = const_cast(FindMethods(it.first)); + ASSERT(method != nullptr); + + JSHandle jsFunc = factory_->NewJSFunctionByDynClass(method, asyncgeneratorDynclass, + FunctionKind::ASYNC_GENERATOR_FUNCTION); + constpool->Set(thread_, value.GetConstpoolIndex(), jsFunc.GetTaggedValue()); + jsFunc->SetConstantPool(thread_, constpool.GetTaggedValue()); + } else if (value.GetConstpoolType() == ConstPoolType::CLASS_FUNCTION) { + ASSERT(mainMethodIndex_ != it.first); + panda_file::File::EntityId id(it.first); + auto method = const_cast(FindMethods(it.first)); + ASSERT(method != nullptr); + JSHandle classInfoExtractor = factory_->NewClassInfoExtractor(method); + constpool->Set(thread_, value.GetConstpoolIndex(), classInfoExtractor.GetTaggedValue()); + } else if (value.GetConstpoolType() == ConstPoolType::METHOD) { + ASSERT(mainMethodIndex_ != it.first); + panda_file::File::EntityId id(it.first); + auto method = const_cast(FindMethods(it.first)); + ASSERT(method != nullptr); + + JSHandle jsFunc = + factory_->NewJSFunctionByDynClass(method, normalDynclass, FunctionKind::NORMAL_FUNCTION); + constpool->Set(thread_, value.GetConstpoolIndex(), jsFunc.GetTaggedValue()); + jsFunc->SetConstantPool(thread_, constpool.GetTaggedValue()); + } else if (value.GetConstpoolType() == ConstPoolType::OBJECT_LITERAL) { + size_t index = it.first; + JSMutableHandle elements(thread_, JSTaggedValue::Undefined()); + JSMutableHandle properties(thread_, JSTaggedValue::Undefined()); + LiteralDataExtractor::ExtractObjectDatas(thread_, &pf, index, elements, properties, this); + JSHandle obj = JSObject::CreateObjectFromProperties(thread_, properties); + + JSMutableHandle key(thread_, JSTaggedValue::Undefined()); + JSMutableHandle valueHandle(thread_, JSTaggedValue::Undefined()); + size_t elementsLen = elements->GetLength(); + for (size_t i = 0; i < elementsLen; i += 2) { // 2: Each literal buffer contains a pair of key-value. + key.Update(elements->Get(i)); + if (key->IsHole()) { + break; + } + valueHandle.Update(elements->Get(i + 1)); + JSObject::DefinePropertyByLiteral(thread_, obj, key, valueHandle); + } + constpool->Set(thread_, value.GetConstpoolIndex(), obj.GetTaggedValue()); + } else if (value.GetConstpoolType() == ConstPoolType::ARRAY_LITERAL) { + size_t index = it.first; + JSHandle literal = + LiteralDataExtractor::GetDatasIgnoreType(thread_, &pf, static_cast(index)); + uint32_t length = literal->GetLength(); + + JSHandle arr(JSArray::ArrayCreate(thread_, JSTaggedNumber(length))); + arr->SetElements(thread_, literal); + constpool->Set(thread_, value.GetConstpoolIndex(), arr.GetTaggedValue()); + } else if (value.GetConstpoolType() == ConstPoolType::CLASS_LITERAL) { + size_t index = it.first; + JSHandle literal = + LiteralDataExtractor::GetDatasIgnoreType(thread_, &pf, static_cast(index), this); + constpool->Set(thread_, value.GetConstpoolIndex(), literal.GetTaggedValue()); + } + } + { + auto method = const_cast(FindMethods(mainMethodIndex_)); + ASSERT(method != nullptr); + JSHandle mainFunc = + factory_->NewJSFunctionByDynClass(method, dynclass, FunctionKind::BASE_CONSTRUCTOR); + mainFunc->SetConstantPool(thread_, constpool.GetTaggedValue()); + program->SetMainFunction(thread_, mainFunc.GetTaggedValue()); + program->SetMethodsData(methods_); + methods_ = nullptr; + program->SetNumberMethods(numMethods_); + // link program + constpool->Set(thread_, constpoolIndex_, program.GetTaggedValue()); + } + + DefineClassInConstPool(constpool); + return *program; +} + +void PandaFileTranslator::FixInstructionId32(const BytecodeInstruction &inst, [[maybe_unused]] uint32_t index, + uint32_t fixOrder) const +{ + // NOLINTNEXTLINE(hicpp-use-auto) + auto pc = const_cast(inst.GetAddress()); + switch (inst.GetFormat()) { + case BytecodeInstruction::Format::ID32: { + uint8_t size = sizeof(uint32_t); + // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-pointer-arithmetic) + if (memcpy_s(pc + FixInstructionIndex::FIX_ONE, size, &index, size) != EOK) { + LOG_ECMA(FATAL) << "memcpy_s failed"; + UNREACHABLE(); + } + break; + } + case BytecodeInstruction::Format::PREF_ID16_V8: { + uint16_t u16Index = index; + uint8_t size = sizeof(uint16_t); + // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-pointer-arithmetic) + if (memcpy_s(pc + FixInstructionIndex::FIX_TWO, size, &u16Index, size) != EOK) { + LOG_ECMA(FATAL) << "memcpy_s failed"; + UNREACHABLE(); + } + break; + } + case BytecodeInstruction::Format::PREF_ID32: + case BytecodeInstruction::Format::PREF_ID32_V8: { + uint8_t size = sizeof(uint32_t); + // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-pointer-arithmetic) + if (memcpy_s(pc + FixInstructionIndex::FIX_TWO, size, &index, size) != EOK) { + LOG_ECMA(FATAL) << "memcpy_s failed"; + UNREACHABLE(); + } + break; + } + case BytecodeInstruction::Format::PREF_IMM16: { + ASSERT(static_cast(index) == index); + uint16_t u16Index = index; + uint8_t size = sizeof(uint16_t); + // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-pointer-arithmetic) + if (memcpy_s(pc + FixInstructionIndex::FIX_TWO, size, &u16Index, size) != EOK) { + LOG_ECMA(FATAL) << "memcpy_s failed"; + UNREACHABLE(); + } + break; + } + case BytecodeInstruction::Format::PREF_ID16_IMM16_V8_V8: { + // Usually, we fix one part of instruction one time. But as for instruction DefineClassWithBuffer, + // which use both method id and literal buffer id.Using fixOrder indicates fix Location. + if (fixOrder == 0) { + uint8_t size = sizeof(uint16_t); + ASSERT(static_cast(index) == index); + // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-pointer-arithmetic) + if (memcpy_s(pc + FixInstructionIndex::FIX_TWO, size, &index, size) != EOK) { + LOG_ECMA(FATAL) << "memcpy_s failed"; + UNREACHABLE(); + } + break; + } + if (fixOrder == 1) { + ASSERT(static_cast(index) == index); + uint16_t u16Index = index; + uint8_t size = sizeof(uint16_t); + // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-pointer-arithmetic) + if (memcpy_s(pc + FixInstructionIndex::FIX_FOUR, size, &u16Index, size) != EOK) { + LOG_ECMA(FATAL) << "memcpy_s failed"; + UNREACHABLE(); + } + break; + } + break; + } + default: + UNREACHABLE(); + } +} + +void PandaFileTranslator::TranslateBytecode(uint32_t insSz, const uint8_t *insArr, const panda_file::File &pf, + const JSMethod *method) +{ + auto bcIns = BytecodeInstruction(insArr); + auto bcInsLast = bcIns.JumpTo(insSz); + + while (bcIns.GetAddress() != bcInsLast.GetAddress()) { + if (bcIns.HasFlag(BytecodeInstruction::Flags::STRING_ID) && BytecodeInstruction::HasId(bcIns.GetFormat(), 0)) { + auto index = GetOrInsertConstantPool(ConstPoolType::STRING, bcIns.GetId().AsFileId().GetOffset()); + FixInstructionId32(bcIns, index); + } else { + auto opcode = static_cast(bcIns.GetOpcode()); + switch (opcode) { + uint32_t index; + uint32_t methodId; + case BytecodeInstruction::Opcode::ECMA_DEFINEFUNCDYN_PREF_ID16_V8: + methodId = pf.ResolveMethodIndex(method->GetFileId(), bcIns.GetId().AsIndex()).GetOffset(); + index = GetOrInsertConstantPool(ConstPoolType::BASE_FUNCTION, methodId); + FixInstructionId32(bcIns, index); + break; + case BytecodeInstruction::Opcode::ECMA_DEFINENCFUNCDYN_PREF_ID16_V8: + methodId = pf.ResolveMethodIndex(method->GetFileId(), bcIns.GetId().AsIndex()).GetOffset(); + index = GetOrInsertConstantPool(ConstPoolType::NC_FUNCTION, methodId); + FixInstructionId32(bcIns, index); + break; + case BytecodeInstruction::Opcode::ECMA_DEFINEGENERATORFUNC_PREF_ID16_V8: + methodId = pf.ResolveMethodIndex(method->GetFileId(), bcIns.GetId().AsIndex()).GetOffset(); + index = GetOrInsertConstantPool(ConstPoolType::GENERATOR_FUNCTION, methodId); + FixInstructionId32(bcIns, index); + break; + case BytecodeInstruction::Opcode::ECMA_DEFINEASYNCFUNC_PREF_ID16_V8: + methodId = pf.ResolveMethodIndex(method->GetFileId(), bcIns.GetId().AsIndex()).GetOffset(); + index = GetOrInsertConstantPool(ConstPoolType::ASYNC_FUNCTION, methodId); + FixInstructionId32(bcIns, index); + break; + case BytecodeInstruction::Opcode::ECMA_DEFINEASYNCGENERATORFUNC_PREF_ID16_V8: + methodId = pf.ResolveMethodIndex(method->GetFileId(), bcIns.GetId().AsIndex()).GetOffset(); + index = GetOrInsertConstantPool(ConstPoolType::ASYNC_GENERATOR_FUNCTION, methodId); + FixInstructionId32(bcIns, index); + break; + case BytecodeInstruction::Opcode::ECMA_DEFINEMETHOD_PREF_ID16_V8: + methodId = pf.ResolveMethodIndex(method->GetFileId(), bcIns.GetId().AsIndex()).GetOffset(); + index = GetOrInsertConstantPool(ConstPoolType::METHOD, methodId); + FixInstructionId32(bcIns, index); + break; + case BytecodeInstruction::Opcode::ECMA_CREATEOBJECTWITHBUFFER_PREF_IMM16: + case BytecodeInstruction::Opcode::ECMA_CREATEOBJECTHAVINGMETHOD_PREF_IMM16: + index = GetOrInsertConstantPool(ConstPoolType::OBJECT_LITERAL, + bcIns.GetImm()); + FixInstructionId32(bcIns, index); + break; + case BytecodeInstruction::Opcode::ECMA_CREATEARRAYWITHBUFFER_PREF_IMM16: + index = GetOrInsertConstantPool(ConstPoolType::ARRAY_LITERAL, + bcIns.GetImm()); + FixInstructionId32(bcIns, index); + break; + case BytecodeInstruction::Opcode::ECMA_DEFINECLASSWITHBUFFER_PREF_ID16_IMM16_V8_V8: + methodId = pf.ResolveMethodIndex(method->GetFileId(), bcIns.GetId().AsIndex()).GetOffset(); + index = GetOrInsertConstantPool(ConstPoolType::CLASS_FUNCTION, methodId); + FixInstructionId32(bcIns, index); + index = GetOrInsertConstantPool(ConstPoolType::CLASS_LITERAL, + bcIns.GetImm()); + FixInstructionId32(bcIns, index, 1); + break; + default: + break; + } + } + bcIns = bcIns.GetNext(); + UpdateICOffset(const_cast(method), bcIns); + } +} + +void PandaFileTranslator::UpdateICOffset(JSMethod *method, const BytecodeInstruction &inst) const +{ + uint32_t offset = method->GetSlotSize(); + if (UNLIKELY(offset == JSMethod::MAX_SLOT_SIZE)) { + return; + } + + auto opcode = inst.GetOpcode(); + uint32_t bc_offset = inst.GetAddress() - method->GetInstructions(); + + switch (opcode) { + case BytecodeInstruction::Opcode::ECMA_TRYLDGLOBALBYNAME_PREF_ID32: + case BytecodeInstruction::Opcode::ECMA_TRYSTGLOBALBYNAME_PREF_ID32: + case BytecodeInstruction::Opcode::ECMA_LDGLOBALVAR_PREF_ID32: + case BytecodeInstruction::Opcode::ECMA_STGLOBALVAR_PREF_ID32: + method->SetSlotSize(bc_offset + 1); + break; + case BytecodeInstruction::Opcode::ECMA_LDOBJBYVALUE_PREF_V8_V8: + case BytecodeInstruction::Opcode::ECMA_STOBJBYVALUE_PREF_V8_V8: + case BytecodeInstruction::Opcode::ECMA_STOWNBYVALUE_PREF_V8_V8: + case BytecodeInstruction::Opcode::ECMA_LDOBJBYNAME_PREF_ID32_V8: + case BytecodeInstruction::Opcode::ECMA_STOBJBYNAME_PREF_ID32_V8: + case BytecodeInstruction::Opcode::ECMA_STOWNBYNAME_PREF_ID32_V8: + case BytecodeInstruction::Opcode::ECMA_LDSUPERBYVALUE_PREF_V8_V8: + case BytecodeInstruction::Opcode::ECMA_STSUPERBYVALUE_PREF_V8_V8: + case BytecodeInstruction::Opcode::ECMA_LDSUPERBYNAME_PREF_ID32_V8: + case BytecodeInstruction::Opcode::ECMA_STSUPERBYNAME_PREF_ID32_V8: + case BytecodeInstruction::Opcode::ECMA_LDMODVARBYNAME_PREF_ID32_V8: + case BytecodeInstruction::Opcode::ECMA_STMODULEVAR_PREF_ID32: + method->SetSlotSize(bc_offset + 2); + break; + default: + return; + } +} + +uint32_t PandaFileTranslator::GetOrInsertConstantPool(ConstPoolType type, uint32_t offset) +{ + auto it = constpoolMap_.find(offset); + if (it != constpoolMap_.cend()) { + ConstPoolValue value(it->second); + return value.GetConstpoolIndex(); + } + ASSERT(constpoolIndex_ != UINT32_MAX); + uint32_t index = constpoolIndex_++; + ConstPoolValue value(type, index); + constpoolMap_.insert({offset, value.GetValue()}); + return index; +} + +JSHandle PandaFileTranslator::DefineMethodInLiteral(uint32_t methodId, FunctionKind kind) const +{ + auto method = const_cast(FindMethods(methodId)); + ASSERT(method != nullptr); + + JSHandle env = thread_->GetEcmaVM()->GetGlobalEnv(); + JSHandle functionClass; + if (kind == FunctionKind::NORMAL_FUNCTION) { + functionClass = JSHandle::Cast(env->GetFunctionClassWithoutProto()); + } else { + functionClass = JSHandle::Cast(env->GetGeneratorFunctionClass()); + } + JSHandle jsFunc = factory_->NewJSFunctionByDynClass(method, functionClass, kind); + + if (kind == FunctionKind::GENERATOR_FUNCTION) { + JSHandle objFun = env->GetObjectFunction(); + JSHandle initialGeneratorFuncPrototype = + factory_->NewJSObjectByConstructor(JSHandle(objFun), objFun); + JSObject::SetPrototype(thread_, initialGeneratorFuncPrototype, env->GetGeneratorPrototype()); + jsFunc->SetProtoOrDynClass(thread_, initialGeneratorFuncPrototype); + } else if (kind == FunctionKind::ASYNC_GENERATOR_FUNCTION) { + JSHandle objFun = env->GetObjectFunction(); + JSHandle initialAsyncGeneratorFuncPrototype = + factory_->NewJSObjectByConstructor(JSHandle(objFun), objFun); + JSObject::SetPrototype(thread_, initialAsyncGeneratorFuncPrototype, env->GetAsyncGeneratorPrototype()); + jsFunc->SetProtoOrDynClass(thread_, initialAsyncGeneratorFuncPrototype); + } + jsFunc->SetupFunctionLength(thread_); + return jsFunc; +} + +void PandaFileTranslator::DefineClassInConstPool(const JSHandle &constpool) const +{ + uint32_t length = constpool->GetLength(); + uint32_t index = 0; + while (index < length - 1) { + JSTaggedValue value = constpool->Get(index); + if (!value.IsClassInfoExtractor()) { + index++; + continue; + } + + // Here, using a law: when inserting ctor in index of constantpool, the index + 1 location will be inserted by + // corresponding class literal. Because translator fixes ECMA_DEFINECLASSWITHBUFFER two consecutive times. + JSTaggedValue nextValue = constpool->Get(index + 1); + ASSERT(nextValue.IsTaggedArray()); + + JSHandle extractor(thread_, value); + JSHandle literal(thread_, nextValue); + ClassInfoExtractor::BuildClassInfoExtractorFromLiteral(thread_, extractor, literal); + JSHandle cls = ClassHelper::DefineClassTemplate(thread_, extractor, constpool); + constpool->Set(thread_, index, cls); + index += 2; // 2: pair of extractor and literal + } +} +} // namespace panda::ecmascript diff --git a/runtime/class_linker/panda_file_translator.h b/runtime/class_linker/panda_file_translator.h new file mode 100644 index 000000000..1c77bb5a7 --- /dev/null +++ b/runtime/class_linker/panda_file_translator.h @@ -0,0 +1,130 @@ +/* + * Copyright (c) 2021-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_CLASS_LINKER_PANDA_FILE_TRANSLATOR_H +#define ECMASCRIPT_CLASS_LINKER_PANDA_FILE_TRANSLATOR_H + +#include "plugins/ecmascript/runtime/ecma_vm.h" +#include "plugins/ecmascript/runtime/js_function.h" +#include "libpandafile/bytecode_instruction.h" +#include "libpandafile/code_data_accessor-inl.h" +#include "libpandafile/file-inl.h" +#include "utils/bit_field.h" + +namespace panda::ecmascript { +class JSThread; +class Program; + +class PandaFileTranslator { +public: + enum FixInstructionIndex : uint8_t { FIX_ONE = 1, FIX_TWO = 2, FIX_FOUR = 4 }; + + explicit PandaFileTranslator(EcmaVM *vm); + ~PandaFileTranslator() + { + ecmaVm_->GetRegionFactory()->FreeBuffer(methods_); + } + NO_COPY_SEMANTIC(PandaFileTranslator); + NO_MOVE_SEMANTIC(PandaFileTranslator); + static JSHandle TranslatePandaFile(EcmaVM *vm, const panda_file::File &pf, const CString &methodName); + JSHandle DefineMethodInLiteral(uint32_t methodId, FunctionKind kind) const; + static void QuickPandaFile(EcmaVM *vm, const panda_file::File &pf); + +private: + enum class ConstPoolType : uint8_t { + STRING, + BASE_FUNCTION, + NC_FUNCTION, + GENERATOR_FUNCTION, + ASYNC_FUNCTION, + ASYNC_GENERATOR_FUNCTION, + CLASS_FUNCTION, + METHOD, + ARRAY_LITERAL, + OBJECT_LITERAL, + CLASS_LITERAL, + }; + + class ConstPoolValue { + public: + ConstPoolValue(ConstPoolType type, uint32_t index) + : value_(ConstPoolIndexField::Encode(index) | ConstPoolTypeField::Encode(type)) + { + } + + explicit ConstPoolValue(uint64_t v) : value_(v) {} + ~ConstPoolValue() = default; + NO_COPY_SEMANTIC(ConstPoolValue); + NO_MOVE_SEMANTIC(ConstPoolValue); + + inline uint64_t GetValue() const + { + return value_; + } + + inline uint32_t GetConstpoolIndex() const + { + return ConstPoolIndexField::Get(value_); + } + + inline ConstPoolType GetConstpoolType() const + { + return ConstPoolTypeField::Get(value_); + } + + private: + // NOLINTNEXTLINE(readability-magic-numbers) + using ConstPoolIndexField = BitField; // 32: 32 bit + // NOLINTNEXTLINE(readability-magic-numbers) + using ConstPoolTypeField = BitField; // 32: offset, 4: 4bit + + uint64_t value_ {0}; + }; + uint32_t GetOrInsertConstantPool(ConstPoolType type, uint32_t offset); + const JSMethod *FindMethods(uint32_t offset) const; + Program *GenerateProgram(const panda_file::File &pf); + void TranslateClasses(const panda_file::File &pf, const CString &methodName); + void TranslateMethod(const compiler::AotClass &aot_class, const panda_file::File::StringData &sd, + Span &methods, size_t *methodIdx, const panda_file::File &pf, + const uint8_t *descriptor, panda_file::MethodDataAccessor &mda); + void TranslateBytecode(uint32_t insSz, const uint8_t *insArr, const panda_file::File &pf, const JSMethod *method); + void FixInstructionId32(const BytecodeInstruction &inst, uint32_t index, uint32_t fixOrder = 0) const; + void UpdateICOffset(JSMethod *method, const BytecodeInstruction &inst) const; + void DefineClassInConstPool(const JSHandle &constpool) const; + + void SetMethods(Span methods, const uint32_t numMethods) + { + methods_ = methods.data(); + numMethods_ = numMethods; + } + + Span GetMethods() const + { + return {methods_, numMethods_}; + } + + EcmaVM *ecmaVm_; + ObjectFactory *factory_; + JSThread *thread_; + uint32_t constpoolIndex_ {0}; + uint32_t numMethods_ {0}; + uint32_t mainMethodIndex_ {0}; + JSMethod *methods_ {nullptr}; + + std::unordered_map constpoolMap_; + std::set translated_code_; +}; +} // namespace panda::ecmascript +#endif // ECMASCRIPT_CLASS_LINKER_PANDA_FILE_TRANSLATOR_H diff --git a/runtime/class_linker/program_object-inl.h b/runtime/class_linker/program_object-inl.h new file mode 100644 index 000000000..b4928850d --- /dev/null +++ b/runtime/class_linker/program_object-inl.h @@ -0,0 +1,42 @@ +/* + * Copyright (c) 2021-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_CLASS_LINKER_PROGRAM_INL_H +#define ECMASCRIPT_CLASS_LINKER_PROGRAM_INL_H + +#include "program_object.h" +#include "plugins/ecmascript/runtime/mem/region_factory.h" + +namespace panda { +namespace ecmascript { +const uint8_t *LexicalFunction::GetInstructions() const +{ + JSTaggedValue inst = GetBytecode(); + void *buffer = JSNativePointer::Cast(inst.GetTaggedObject())->GetExternalPointer(); + return static_cast(buffer); +} + +JSTaggedValue ConstantPool::GetObjectFromCache(uint32_t index) const +{ + return Get(index); +} + +void Program::FreeMethodData(RegionFactory *factory) +{ + factory->FreeBuffer(GetMethodsData()); +} +} // namespace ecmascript +} // namespace panda +#endif // ECMASCRIPT_CLASS_LINKER_PROGRAM_INL_H diff --git a/runtime/class_linker/program_object.h b/runtime/class_linker/program_object.h new file mode 100644 index 000000000..ca1b58efb --- /dev/null +++ b/runtime/class_linker/program_object.h @@ -0,0 +1,84 @@ +/* + * Copyright (c) 2021-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_CLASS_LINKER_PROGRAM_H +#define ECMASCRIPT_CLASS_LINKER_PROGRAM_H + +#include "plugins/ecmascript/runtime/ecma_macros.h" +#include "plugins/ecmascript/runtime/js_tagged_value-inl.h" + +namespace panda { +namespace ecmascript { +class JSThread; +class RegionFactory; + +class Program : public ECMAObject { +public: + DECL_CAST(Program) + + static constexpr size_t LOCATION_OFFSET = ECMAObject::SIZE; + ACCESSORS(Location, LOCATION_OFFSET, CONSTANT_POOL_OFFSET) + ACCESSORS(ConstantPool, CONSTANT_POOL_OFFSET, MAIN_FUNCTION_OFFSET) + ACCESSORS(MainFunction, MAIN_FUNCTION_OFFSET, METHODS_DATA_OFFSET) + SET_GET_NATIVE_FIELD(MethodsData, JSMethod, METHODS_DATA_OFFSET, NUMBER_METHODS_OFFSET) + SET_GET_PRIMITIVE_FIELD(NumberMethods, uint32_t, NUMBER_METHODS_OFFSET, SIZE); + + inline void FreeMethodData(RegionFactory *factory); + + DECL_VISIT_OBJECT(LOCATION_OFFSET, METHODS_DATA_OFFSET) + DECL_DUMP() +}; + +class LexicalFunction : public ECMAObject { +public: + DECL_CAST(LexicalFunction) + + uint32_t GetNumVregs() const + { + return static_cast(GetNumberVRegs().GetInt()); + } + + uint32_t GetNumICSlots() const + { + return static_cast(GetNumberICSlots().GetInt()); + } + + inline const uint8_t *GetInstructions() const; + + static constexpr size_t NAME_OFFSET = ECMAObject::SIZE; + ACCESSORS(Name, NAME_OFFSET, NUMBER_VREGS_OFFSET) + ACCESSORS(NumberVRegs, NUMBER_VREGS_OFFSET, NUMBER_IC_SLOTS_OFFSET) + ACCESSORS(NumberICSlots, NUMBER_IC_SLOTS_OFFSET, BYTECODE_OFFSET) + ACCESSORS(Bytecode, BYTECODE_OFFSET, PROGRAM_OFFSET) + ACCESSORS(Program, PROGRAM_OFFSET, SIZE) + + DECL_VISIT_OBJECT(NAME_OFFSET, SIZE) + DECL_DUMP() +}; + +class ConstantPool : public TaggedArray { +public: + static ConstantPool *Cast(ObjectHeader *object) + { + ASSERT(JSTaggedValue(object).IsTaggedArray()); + return static_cast(object); + } + + inline JSTaggedValue GetObjectFromCache(uint32_t index) const; + DECL_DUMP() +}; +} // namespace ecmascript +} // namespace panda +#endif // ECMASCRIPT_CLASS_LINKER_PROGRAM_H diff --git a/runtime/common.h b/runtime/common.h new file mode 100644 index 000000000..e663f65e1 --- /dev/null +++ b/runtime/common.h @@ -0,0 +1,40 @@ +/* + * Copyright (c) 2021-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_COMMON_H +#define ECMASCRIPT_COMMON_H + +#include "libpandabase/macros.h" + +namespace panda { +namespace ecmascript { +enum BarrierMode { SKIP_BARRIER, WRITE_BARRIER, READ_BARRIER }; + +constexpr size_t NUM_MANDATORY_JSFUNC_ARGS = 3; + +// NOLINTNEXTLINE(cppcoreguidelines-macro-usage) +#define PUBLIC_API PANDA_PUBLIC_API + +#ifdef NDEBUG +// NOLINTNEXTLINE(cppcoreguidelines-macro-usage) +#define DUMP_API_ATTR __attribute__((unused)) +#else +// NOLINTNEXTLINE(cppcoreguidelines-macro-usage) +#define DUMP_API_ATTR __attribute__((visibility ("default"), used)) +#endif +} // namespace ecmascript +} // namespace panda + +#endif // ECMASCRIPT_COMMON_H diff --git a/runtime/compiler/ecmascript_runtime_interface.cpp b/runtime/compiler/ecmascript_runtime_interface.cpp new file mode 100644 index 000000000..1107515dd --- /dev/null +++ b/runtime/compiler/ecmascript_runtime_interface.cpp @@ -0,0 +1,46 @@ +/* + * Copyright (c) 2021-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 "plugins/ecmascript/runtime/compiler/ecmascript_runtime_interface.h" +#include "plugins/ecmascript/runtime/js_function.h" +#include "plugins/ecmascript/compiler/ecmascript_extensions/ecmascript_environment.h" + +namespace panda { + +size_t EcmaRuntimeInterface::GetConstantPoolOffset() +{ + return ecmascript::JSFunction::GetOffsetConstantPool(); +} + +size_t EcmaRuntimeInterface::GetLexicalEnvOffset() +{ + return ecmascript::JSFunction::GetOffsetLexicalEnv(); +} + +bool EcmaRuntimeInterface::IsEcmascriptRuntime() +{ + auto lang = Thread::GetCurrent()->GetVM()->GetLanguageContext().GetLanguage(); + return lang == panda_file::SourceLang::ECMASCRIPT; +} + +size_t EcmaRuntimeInterface::GetEcmascriptLanguageExtensionSize() +{ + if (IsEcmascriptRuntime()) { + return panda::ecmascript::EcmascriptEnvironment::GetSize(); + } + return 0; +} + +} // namespace panda diff --git a/runtime/compiler/ecmascript_runtime_interface.h b/runtime/compiler/ecmascript_runtime_interface.h new file mode 100644 index 000000000..decfe58be --- /dev/null +++ b/runtime/compiler/ecmascript_runtime_interface.h @@ -0,0 +1,47 @@ +/* + * 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 PANDA_RUNTIME_ECMASCRIPT_RUNTIME_INTERFACE_H +#define PANDA_RUNTIME_ECMASCRIPT_RUNTIME_INTERFACE_H + +#include "runtime/compiler.h" +#include "plugins/ecmascript/runtime/js_method.h" + +namespace panda { +class EcmaRuntimeInterface : public PandaRuntimeInterface { + size_t GetConstantPoolOffset() override; + + size_t GetLexicalEnvOffset() override; + + bool IsEcmascriptRuntime() override; + + size_t GetEcmascriptLanguageExtensionSize(); + + size_t GetLanguageExtensionSize() override + { + size_t ext_size = 0; + ext_size = GetEcmascriptLanguageExtensionSize(); + + return ext_size; + } + + std::string GetMethodFullName(MethodPtr method, [[maybe_unused]] bool with_signature) const override + { + ASSERT(panda::panda_file::IsDynamicLanguage(MethodCast(method)->GetClass()->GetSourceLang())); + return std::string(static_cast(MethodCast(method))->GetFullName()); + } +}; +} // namespace panda + +#endif // PANDA_RUNTIME_ECMASCRIPT_RUNTIME_INTERFACE_H diff --git a/runtime/containers/containers_arraylist.cpp b/runtime/containers/containers_arraylist.cpp new file mode 100644 index 000000000..a43e155d3 --- /dev/null +++ b/runtime/containers/containers_arraylist.cpp @@ -0,0 +1,69 @@ +/* + * Copyright (c) 2021-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 "containers_arraylist.h" +#include "plugins/ecmascript/runtime/ecma_vm.h" +#include "plugins/ecmascript/runtime/js_arraylist.h" +#include "plugins/ecmascript/runtime/object_factory.h" +#include "plugins/ecmascript/runtime/tagged_array-inl.h" + +namespace panda::ecmascript::containers { +JSTaggedValue ContainersArrayList::ArrayListConstructor(EcmaRuntimeCallInfo *argv) +{ + ASSERT(argv); + BUILTINS_API_TRACE(argv->GetThread(), ArrayList, Constructor); + JSThread *thread = argv->GetThread(); + [[maybe_unused]] EcmaHandleScope handleScope(thread); + ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); + JSHandle newTarget = GetNewTarget(argv); + if (newTarget->IsUndefined()) { + THROW_TYPE_ERROR_AND_RETURN(thread, "new target can't be undefined", JSTaggedValue::Exception()); + } + JSHandle constructor = GetConstructor(argv); + JSHandle obj = factory->NewJSObjectByConstructor(JSHandle(constructor), newTarget); + + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + + return obj.GetTaggedValue(); +} + +JSTaggedValue ContainersArrayList::Add(EcmaRuntimeCallInfo *argv) +{ + ASSERT(argv); + BUILTINS_API_TRACE(argv->GetThread(), ArrayList, Add); + JSThread *thread = argv->GetThread(); + [[maybe_unused]] EcmaHandleScope handleScope(thread); + JSHandle self = GetThis(argv); + + if (!self->IsJSArrayList()) { + THROW_TYPE_ERROR_AND_RETURN(thread, "obj is not JSArrayList", JSTaggedValue::Exception()); + } + + JSHandle value(GetCallArg(argv, 0)); + JSArrayList::Add(thread, JSHandle::Cast(self), value); + + RETURN_VALUE_IF_ABRUPT_COMPLETION(thread, JSTaggedValue::False()); + return JSTaggedValue::True(); +} + +JSTaggedValue ContainersArrayList::Iterator(EcmaRuntimeCallInfo *argv) +{ + ASSERT(argv); + BUILTINS_API_TRACE(argv->GetThread(), ArrayList, Iterator); + JSThread *thread = argv->GetThread(); + [[maybe_unused]] EcmaHandleScope handleScope(thread); + return JSTaggedValue::Undefined(); +} +} // namespace panda::ecmascript::containers diff --git a/runtime/containers/containers_arraylist.h b/runtime/containers/containers_arraylist.h new file mode 100644 index 000000000..acf34ab73 --- /dev/null +++ b/runtime/containers/containers_arraylist.h @@ -0,0 +1,31 @@ +/* + * Copyright (c) 2021-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_CONTAINERS_CONTAINERS_ARRAYLIST_H +#define ECMASCRIPT_CONTAINERS_CONTAINERS_ARRAYLIST_H + +#include "plugins/ecmascript/runtime/base/builtins_base.h" +#include "plugins/ecmascript/runtime/ecma_runtime_call_info.h" + +namespace panda::ecmascript::containers { +class ContainersArrayList : public base::BuiltinsBase { +public: + static JSTaggedValue ArrayListConstructor(EcmaRuntimeCallInfo *argv); + + static JSTaggedValue Add(EcmaRuntimeCallInfo *argv); + static JSTaggedValue Iterator(EcmaRuntimeCallInfo *argv); +}; +} // namespace panda::ecmascript::containers +#endif // ECMASCRIPT_CONTAINERS_CONTAINERS_ARRAYLIST_H diff --git a/runtime/containers/containers_private.cpp b/runtime/containers/containers_private.cpp new file mode 100644 index 000000000..4cc3e7b34 --- /dev/null +++ b/runtime/containers/containers_private.cpp @@ -0,0 +1,191 @@ +/* + * Copyright (c) 2021-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 "plugins/ecmascript/runtime/containers/containers_private.h" + +#include "containers_arraylist.h" +#include "plugins/ecmascript/runtime/global_env.h" +#include "plugins/ecmascript/runtime/interpreter/fast_runtime_stub-inl.h" +#include "plugins/ecmascript/runtime/js_arraylist.h" +#include "plugins/ecmascript/runtime/js_function.h" + +namespace panda::ecmascript::containers { +JSTaggedValue ContainersPrivate::Load(EcmaRuntimeCallInfo *msg) +{ + ASSERT(msg); + JSThread *thread = msg->GetThread(); + [[maybe_unused]] EcmaHandleScope handleScope(thread); + JSHandle argv = GetCallArg(msg, 0); + JSHandle thisValue(GetThis(msg)); + + uint32_t tag = 0; + if (!JSTaggedValue::ToElementIndex(argv.GetTaggedValue(), &tag) || tag >= ContainerTag::END) { + THROW_TYPE_ERROR_AND_RETURN(thread, "Incorrect input parameters", JSTaggedValue::Exception()); + } + + ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); + JSTaggedValue res = JSTaggedValue::Undefined(); + switch (tag) { + case ContainerTag::ArrayList: { + JSHandle key(factory->NewFromCanBeCompressString("ArrayListConstructor")); + JSTaggedValue value = + FastRuntimeStub::GetPropertyByName(thread, thisValue.GetTaggedValue(), key.GetTaggedValue()); + if (value != JSTaggedValue::Undefined()) { + res = value; + } else { + JSHandle arrayList = InitializeArrayList(thread); + SetFrozenConstructor(thread, thisValue, "ArrayListConstructor", arrayList); + res = arrayList.GetTaggedValue(); + } + break; + } + case ContainerTag::Queue: + case ContainerTag::END: + break; + default: + UNREACHABLE(); + } + + return res; +} + +JSHandle ContainersPrivate::NewContainerConstructor(JSThread *thread, const JSHandle &prototype, + EcmaEntrypoint ctorFunc, const char *name, int length) +{ + JSHandle env = thread->GetEcmaVM()->GetGlobalEnv(); + ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); + JSHandle ctor = + factory->NewJSFunction(env, reinterpret_cast(ctorFunc), FunctionKind::BUILTIN_CONSTRUCTOR); + + const GlobalEnvConstants *globalConst = thread->GlobalConstants(); + JSFunction::SetFunctionLength(thread, ctor, JSTaggedValue(length)); + JSHandle nameString(factory->NewFromCanBeCompressString(name)); + JSFunction::SetFunctionName(thread, JSHandle(ctor), nameString, + JSHandle(thread, JSTaggedValue::Undefined())); + JSHandle constructorKey = globalConst->GetHandledConstructorString(); + PropertyDescriptor descriptor1(thread, JSHandle::Cast(ctor), true, false, true); + JSObject::DefineOwnProperty(thread, prototype, constructorKey, descriptor1); + + /* set "prototype" in constructor */ + ctor->SetFunctionPrototype(thread, prototype.GetTaggedValue()); + + return ctor; +} + +void ContainersPrivate::SetFrozenFunction(JSThread *thread, const JSHandle &obj, const char *key, + EcmaEntrypoint func, int length) +{ + ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); + JSHandle keyString(factory->NewFromCanBeCompressString(key)); + JSHandle function = NewFunction(thread, keyString, func, length); + PropertyDescriptor descriptor(thread, JSHandle(function), false, false, false); + JSObject::DefineOwnProperty(thread, obj, keyString, descriptor); +} + +void ContainersPrivate::SetFrozenConstructor(JSThread *thread, const JSHandle &obj, const char *keyChar, + JSHandle &value) +{ + JSObject::PreventExtensions(thread, obj); + ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); + JSHandle key(factory->NewFromCanBeCompressString(keyChar)); + PropertyDescriptor descriptor(thread, value, false, false, false); + JSObject::DefineOwnProperty(thread, obj, key, descriptor); +} + +JSHandle ContainersPrivate::NewFunction(JSThread *thread, const JSHandle &key, + EcmaEntrypoint func, int length) +{ + ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); + JSHandle function = + factory->NewJSFunction(thread->GetEcmaVM()->GetGlobalEnv(), reinterpret_cast(func)); + JSFunction::SetFunctionLength(thread, function, JSTaggedValue(length)); + JSHandle baseFunction(function); + JSFunction::SetFunctionName(thread, baseFunction, key, thread->GlobalConstants()->GetHandledUndefined()); + return function; +} + +JSHandle ContainersPrivate::CreateGetter(JSThread *thread, EcmaEntrypoint func, const char *name, + int length) +{ + JSHandle env = thread->GetEcmaVM()->GetGlobalEnv(); + ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); + JSHandle function = factory->NewJSFunction(env, reinterpret_cast(func)); + JSFunction::SetFunctionLength(thread, function, JSTaggedValue(length)); + JSHandle funcName(factory->NewFromString(name)); + JSHandle prefix = thread->GlobalConstants()->GetHandledGetString(); + JSFunction::SetFunctionName(thread, JSHandle(function), funcName, prefix); + return JSHandle(function); +} + +void ContainersPrivate::SetGetter(JSThread *thread, const JSHandle &obj, const JSHandle &key, + const JSHandle &getter) +{ + ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); + JSHandle accessor = factory->NewAccessorData(); + accessor->SetGetter(thread, getter); + PropertyAttributes attr = PropertyAttributes::DefaultAccessor(false, false, false); + JSObject::AddAccessor(thread, JSHandle::Cast(obj), key, accessor, attr); +} + +void ContainersPrivate::SetFunctionAtSymbol(JSThread *thread, const JSHandle &env, + const JSHandle &obj, const JSHandle &symbol, + const char *name, EcmaEntrypoint func, int length) +{ + ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); + JSHandle function = factory->NewJSFunction(env, reinterpret_cast(func)); + JSFunction::SetFunctionLength(thread, function, JSTaggedValue(length)); + JSHandle nameString(factory->NewFromString(name)); + JSHandle baseFunction(function); + JSFunction::SetFunctionName(thread, baseFunction, nameString, thread->GlobalConstants()->GetHandledUndefined()); + + PropertyDescriptor descriptor(thread, JSHandle::Cast(function), false, false, false); + JSObject::DefineOwnProperty(thread, obj, symbol, descriptor); +} + +void ContainersPrivate::SetStringTagSymbol(JSThread *thread, const JSHandle &env, + const JSHandle &obj, const char *key) +{ + ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); + JSHandle tag(factory->NewFromString(key)); + JSHandle symbol = env->GetToStringTagSymbol(); + PropertyDescriptor desc(thread, tag, false, false, false); + JSObject::DefineOwnProperty(thread, obj, symbol, desc); +} + +JSHandle ContainersPrivate::InitializeArrayList(JSThread *thread) +{ + const GlobalEnvConstants *globalConst = thread->GlobalConstants(); + ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); + // ArrayList.prototype + JSHandle arrayListFuncPrototype = factory->NewEmptyJSObject(); + JSHandle arrayListFuncPrototypeValue(arrayListFuncPrototype); + // ArrayList.prototype_or_dynclass + JSHandle arrayListInstanceDynclass = + factory->NewEcmaDynClass(JSArrayList::SIZE, JSType::JS_ARRAY_LIST, arrayListFuncPrototypeValue); + // ArrayList() = new Function() + JSHandle arrayListFunction(NewContainerConstructor( + thread, arrayListFuncPrototype, ContainersArrayList::ArrayListConstructor, "ArrayList", FuncLength::ZERO)); + JSHandle(arrayListFunction)->SetFunctionPrototype(thread, arrayListInstanceDynclass.GetTaggedValue()); + + // "constructor" property on the prototype + JSHandle constructorKey = globalConst->GetHandledConstructorString(); + JSObject::SetProperty(thread, JSHandle(arrayListFuncPrototype), constructorKey, arrayListFunction); + + // ArrayList.prototype.add() + SetFrozenFunction(thread, arrayListFuncPrototype, "add", ContainersArrayList::Add, FuncLength::ONE); + + return arrayListFunction; +} +} // namespace panda::ecmascript::containers diff --git a/runtime/containers/containers_private.h b/runtime/containers/containers_private.h new file mode 100644 index 000000000..55cfdc988 --- /dev/null +++ b/runtime/containers/containers_private.h @@ -0,0 +1,69 @@ +/* + * Copyright (c) 2021-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_CONTAINERS_CONTAINERS_PRIVATE_H +#define ECMASCRIPT_CONTAINERS_CONTAINERS_PRIVATE_H + +#include "plugins/ecmascript/runtime/base/builtins_base.h" + +namespace panda::ecmascript::containers { +enum FuncLength : uint8_t { ZERO = 0, ONE, TWO, THREE, FOUR }; +enum ContainerTag : uint8_t { + ArrayList = 0, + Queue, + Deque, + Stack, + Vector, + List, + LinkedList, + TreeMap, + TreeSet, + HashMap, + HashSet, + LightWightMap, + LightWightSet, + PlainArray, + END +}; +// Using Lazy-loading container, including ArrayList, Queue, Stack, Vector, List, LinkedList, Deque, +// TreeMap, TreeSet, HashMap, HashSet, LightWightMap, LightWightSet, PlainArray. +// Use through ArkPrivate.Load([ContainerTag]) in js, ContainTag was declaerd in ArkPrivate like ArkPrivate::ArrayList. +class ContainersPrivate : public base::BuiltinsBase { +public: + static JSTaggedValue Load(EcmaRuntimeCallInfo *msg); + +private: + static JSHandle NewContainerConstructor(JSThread *thread, const JSHandle &prototype, + EcmaEntrypoint ctorFunc, const char *name, int length); + static JSHandle NewFunction(JSThread *thread, const JSHandle &key, EcmaEntrypoint func, + int length); + static void SetFrozenFunction(JSThread *thread, const JSHandle &obj, const char *key, EcmaEntrypoint func, + int length); + static void SetFrozenConstructor(JSThread *thread, const JSHandle &obj, const char *keyChar, + JSHandle &value); + static JSHandle CreateGetter(JSThread *thread, EcmaEntrypoint func, const char *name, + int length); + static void SetGetter(JSThread *thread, const JSHandle &obj, + const JSHandle &key, const JSHandle &getter); + static void SetFunctionAtSymbol(JSThread *thread, const JSHandle &env, + const JSHandle &obj, const JSHandle &symbol, + const char *name, EcmaEntrypoint func, int length); + static void SetStringTagSymbol(JSThread *thread, const JSHandle &env, + const JSHandle &obj, const char *key); + static JSHandle InitializeArrayList(JSThread *thread); +}; +} // namespace panda::ecmascript::containers + +#endif // ECMASCRIPT_CONTAINERS_CONTAINERS_PRIVATE_H diff --git a/runtime/dump.cpp b/runtime/dump.cpp new file mode 100644 index 000000000..6b8fb9ad0 --- /dev/null +++ b/runtime/dump.cpp @@ -0,0 +1,3131 @@ +/* + * Copyright (c) 2021-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 +#include +#include +#include + +#include "include/stack_walker.h" +#include "plugins/ecmascript/runtime/accessor_data.h" +#include "plugins/ecmascript/runtime/class_info_extractor.h" +#include "plugins/ecmascript/runtime/class_linker/program_object-inl.h" +#include "plugins/ecmascript/runtime/ecma_module.h" +#include "plugins/ecmascript/runtime/ecma_vm.h" +#include "plugins/ecmascript/runtime/global_dictionary-inl.h" +#include "plugins/ecmascript/runtime/global_env.h" +#include "plugins/ecmascript/runtime/ic/ic_handler.h" +#include "plugins/ecmascript/runtime/ic/property_box.h" +#include "plugins/ecmascript/runtime/ic/proto_change_details.h" +#include "plugins/ecmascript/runtime/jobs/micro_job_queue.h" +#include "plugins/ecmascript/runtime/jobs/pending_job.h" +#include "plugins/ecmascript/runtime/js_array.h" +#include "plugins/ecmascript/runtime/js_array_iterator.h" +#include "plugins/ecmascript/runtime/js_arraybuffer.h" +#include "plugins/ecmascript/runtime/js_arraylist.h" +#include "plugins/ecmascript/runtime/js_async_from_sync_iterator_object.h" +#include "plugins/ecmascript/runtime/js_async_function.h" +#include "plugins/ecmascript/runtime/js_async_generator_object.h" +#include "plugins/ecmascript/runtime/js_collator.h" +#include "plugins/ecmascript/runtime/js_dataview.h" +#include "plugins/ecmascript/runtime/js_date.h" +#include "plugins/ecmascript/runtime/js_date_time_format.h" +#include "plugins/ecmascript/runtime/js_for_in_iterator.h" +#include "plugins/ecmascript/runtime/js_function.h" +#include "plugins/ecmascript/runtime/js_function_extra_info.h" +#include "plugins/ecmascript/runtime/js_generator_object.h" +#include "plugins/ecmascript/runtime/js_global_object.h" +#include "plugins/ecmascript/runtime/js_handle.h" +#include "plugins/ecmascript/runtime/js_intl.h" +#include "plugins/ecmascript/runtime/js_locale.h" +#include "plugins/ecmascript/runtime/js_map.h" +#include "plugins/ecmascript/runtime/js_map_iterator.h" +#include "plugins/ecmascript/runtime/js_number_format.h" +#include "plugins/ecmascript/runtime/js_object-inl.h" +#include "plugins/ecmascript/runtime/js_plural_rules.h" +#include "plugins/ecmascript/runtime/js_primitive_ref.h" +#include "plugins/ecmascript/runtime/js_promise.h" +#include "plugins/ecmascript/runtime/js_realm.h" +#include "plugins/ecmascript/runtime/js_regexp.h" +#include "plugins/ecmascript/runtime/js_relative_time_format.h" +#include "plugins/ecmascript/runtime/js_set.h" +#include "plugins/ecmascript/runtime/js_set_iterator.h" +#include "plugins/ecmascript/runtime/js_string_iterator.h" +#include "plugins/ecmascript/runtime/js_tagged_number.h" +#include "plugins/ecmascript/runtime/js_tagged_value-inl.h" +#include "plugins/ecmascript/runtime/js_thread.h" +#include "plugins/ecmascript/runtime/js_typed_array.h" +#include "plugins/ecmascript/runtime/js_weak_container.h" +#include "plugins/ecmascript/runtime/layout_info-inl.h" +#include "plugins/ecmascript/runtime/lexical_env.h" +#include "plugins/ecmascript/runtime/linked_hash_table-inl.h" +#include "plugins/ecmascript/runtime/mem/assert_scope-inl.h" +#include "plugins/ecmascript/runtime/mem/c_containers.h" +#include "plugins/ecmascript/runtime/mem/machine_code.h" +#include "plugins/ecmascript/runtime/tagged_array.h" +#include "plugins/ecmascript/runtime/tagged_dictionary.h" +#include "plugins/ecmascript/runtime/template_map.h" +#include "plugins/ecmascript/runtime/transitions_dictionary.h" + +namespace panda::ecmascript { +using MicroJobQueue = panda::ecmascript::job::MicroJobQueue; +using PendingJob = panda::ecmascript::job::PendingJob; + +static constexpr uint32_t DUMP_TYPE_OFFSET = 12; +static constexpr uint32_t DUMP_PROPERTY_OFFSET = 20; + +CString JSHClass::DumpJSType(JSType type) +{ + switch (type) { + case JSType::HCLASS: + return "JSHClass"; + case JSType::TAGGED_ARRAY: + return "TaggedArray"; + case JSType::TAGGED_DICTIONARY: + return "TaggedDictionary"; + case JSType::LINKED_HASH_SET: + return "LinkedHashSet"; + case JSType::LINKED_HASH_MAP: + return "LinkedHashMap"; + case JSType::STRING: + return "BaseString"; + case JSType::JS_NATIVE_POINTER: + return "NativePointer"; + case JSType::JS_OBJECT: + return "Object"; + case JSType::JS_FUNCTION_BASE: + return "Function Base"; + case JSType::JS_FUNCTION: + return "Function"; + case JSType::JS_ERROR: + return "Error"; + case JSType::JS_EVAL_ERROR: + return "Eval Error"; + case JSType::JS_RANGE_ERROR: + return "Range Error"; + case JSType::JS_TYPE_ERROR: + return "Type Error"; + case JSType::JS_REFERENCE_ERROR: + return "Reference Error"; + case JSType::JS_URI_ERROR: + return "Uri Error"; + case JSType::JS_SYNTAX_ERROR: + return "Syntax Error"; + case JSType::JS_REG_EXP: + return "Regexp"; + case JSType::JS_SET: + return "Set"; + case JSType::JS_MAP: + return "Map"; + case JSType::JS_WEAK_SET: + return "WeakSet"; + case JSType::JS_WEAK_MAP: + return "WeakMap"; + case JSType::JS_DATE: + return "Date"; + case JSType::JS_BOUND_FUNCTION: + return "Bound Function"; + case JSType::JS_ARRAY: + return "Array"; + case JSType::JS_TYPED_ARRAY: + return "Typed Array"; + case JSType::JS_INT8_ARRAY: + return "Int8 Array"; + case JSType::JS_UINT8_ARRAY: + return "Uint8 Array"; + case JSType::JS_UINT8_CLAMPED_ARRAY: + return "Uint8 Clamped Array"; + case JSType::JS_INT16_ARRAY: + return "Int16 Array"; + case JSType::JS_UINT16_ARRAY: + return "Uint16 Array"; + case JSType::JS_INT32_ARRAY: + return "Int32 Array"; + case JSType::JS_UINT32_ARRAY: + return "Uint32 Array"; + case JSType::JS_FLOAT32_ARRAY: + return "Float32 Array"; + case JSType::JS_FLOAT64_ARRAY: + return "Float64 Array"; + case JSType::JS_ARGUMENTS: + return "Arguments"; + case JSType::JS_PROXY: + return "Proxy"; + case JSType::JS_PRIMITIVE_REF: + return "Primitive"; + case JSType::JS_DATA_VIEW: + return "DataView"; + case JSType::JS_ITERATOR: + return "Iterator"; + case JSType::JS_FORIN_ITERATOR: + return "ForinInterator"; + case JSType::JS_MAP_ITERATOR: + return "MapIterator"; + case JSType::JS_SET_ITERATOR: + return "SetIterator"; + case JSType::JS_ARRAY_ITERATOR: + return "ArrayIterator"; + case JSType::JS_STRING_ITERATOR: + return "StringIterator"; + case JSType::JS_ARRAY_BUFFER: + return "ArrayBuffer"; + case JSType::JS_PROXY_REVOC_FUNCTION: + return "ProxyRevocFunction"; + case JSType::PROMISE_REACTIONS: + return "PromiseReaction"; + case JSType::PROMISE_CAPABILITY: + return "PromiseCapability"; + case JSType::PROMISE_ITERATOR_RECORD: + return "PromiseIteratorRecord"; + case JSType::PROMISE_RECORD: + return "PromiseRecord"; + case JSType::RESOLVING_FUNCTIONS_RECORD: + return "ResolvingFunctionsRecord"; + case JSType::JS_PROMISE: + return "Promise"; + case JSType::JS_PROMISE_REACTIONS_FUNCTION: + return "PromiseReactionsFunction"; + case JSType::JS_PROMISE_EXECUTOR_FUNCTION: + return "PromiseExecutorFunction"; + case JSType::JS_PROMISE_ALL_RESOLVE_ELEMENT_FUNCTION: + return "PromiseAllResolveElementFunction"; + case JSType::MICRO_JOB_QUEUE: + return "MicroJobQueue"; + case JSType::PENDING_JOB: + return "PendingJob"; + case JSType::COMPLETION_RECORD: + return "CompletionRecord"; + case JSType::GLOBAL_ENV: + return "GlobalEnv"; + case JSType::ACCESSOR_DATA: + return "AccessorData"; + case JSType::INTERNAL_ACCESSOR: + return "InternalAccessor"; + case JSType::SYMBOL: + return "Symbol"; + case JSType::PROPERTY_BOX: + return "PropertyBox"; + case JSType::JS_ASYNC_FUNCTION: + return "AsyncFunction"; + case JSType::JS_ASYNC_GENERATOR_FUNCTION: + return "AsyncGeneratorFunction"; + case JSType::JS_ASYNC_AWAIT_STATUS_FUNCTION: + return "AsyncAwaitStatusFunction"; + case JSType::JS_ASYNC_GENERATOR_RESOLVE_NEXT_FUNCTION: + return "AsynGeneratorResolveNextFunction"; + case JSType::JS_ASYNC_FROM_SYNC_ITERATOR_VALUE_UNWRAP_FUNCTION: + return "AsynFromSyncIteratorValueUnwrapFunction"; + case JSType::JS_ASYNC_FUNC_OBJECT: + return "AsyncFunctionObject"; + case JSType::JS_REALM: + return "Realm"; + case JSType::JS_GLOBAL_OBJECT: + return "GlobalObject"; + case JSType::JS_INTL: + return "JSIntl"; + case JSType::JS_LOCALE: + return "JSLocale"; + case JSType::JS_DATE_TIME_FORMAT: + return "JSDateTimeFormat"; + case JSType::JS_RELATIVE_TIME_FORMAT: + return "JSRelativeTimeFormat"; + case JSType::JS_NUMBER_FORMAT: + return "JSNumberFormat"; + case JSType::JS_COLLATOR: + return "JSCollator"; + case JSType::JS_PLURAL_RULES: + return "JSPluralRules"; + case JSType::JS_GENERATOR_OBJECT: + return "JSGeneratorObject"; + case JSType::JS_GENERATOR_CONTEXT: + return "JSGeneratorContext"; + case JSType::PROTO_CHANGE_MARKER: + return "ProtoChangeMarker"; + case JSType::PROTOTYPE_INFO: + return "PrototypeInfo"; + case JSType::PROGRAM: + return "program"; + case JSType::LEXICAL_FUNCTION: + return "LexicalFunction"; + case JSType::FUNCTION_EXTRA_INFO: + return "FunctionExtraInfo"; + case JSType::MACHINE_CODE_OBJECT: + return "MachineCode"; + case JSType::ECMA_MODULE: + return "EcmaModule"; + case JSType::CLASS_INFO_EXTRACTOR: + return "ClassInfoExtractor"; + case JSType::JS_ARRAY_LIST: + return "ArrayList"; + default: { + CString ret = "unknown type "; + return ret + static_cast(type); + } + } +} + +static void DumpArrayClass(JSThread *thread, const TaggedArray *arr, std::ostream &os) +{ + DISALLOW_GARBAGE_COLLECTION; + uint32_t len = arr->GetLength(); + os << " \n"; + for (uint32_t i = 0; i < len; i++) { + JSTaggedValue val(arr->Get(i)); + if (!val.IsHole()) { + os << std::right << std::setw(DUMP_PROPERTY_OFFSET) << i << ": "; + val.DumpTaggedValue(thread, os); + os << "\n"; + } + } +} + +static void DumpStringClass(const EcmaString *str, std::ostream &os) +{ + DISALLOW_GARBAGE_COLLECTION; + CString string = ConvertToString(str); + os << string; +} + +static void DumpPropertyKey(JSTaggedValue key, std::ostream &os) +{ + if (key.IsString()) { + DumpStringClass(EcmaString::Cast(key.GetTaggedObject()), os); + } else if (key.IsSymbol()) { + JSSymbol *sym = JSSymbol::Cast(key.GetTaggedObject()); + DumpStringClass(EcmaString::Cast(sym->GetDescription().GetTaggedObject()), os); + } else { + UNREACHABLE(); + } +} + +static void DumpHClass(JSThread *thread, const JSHClass *jshclass, std::ostream &os, bool withDetail) +{ + DISALLOW_GARBAGE_COLLECTION; + os << "JSHClass :" << std::setw(DUMP_TYPE_OFFSET); + os << "Type :" << JSHClass::DumpJSType(jshclass->GetObjectType()) << "\n"; + + os << " - Prototype :" << std::setw(DUMP_TYPE_OFFSET); + jshclass->GetPrototype().DumpTaggedValue(thread, os); + os << "\n"; + os << " - PropertyDescriptors :" << std::setw(DUMP_TYPE_OFFSET); + JSTaggedValue attrs = jshclass->GetLayout(); + attrs.DumpTaggedValue(thread, os); + os << "\n"; + if (withDetail && !attrs.IsNull()) { + LayoutInfo *layoutInfo = LayoutInfo::Cast(attrs.GetTaggedObject()); + layoutInfo->Dump(thread, os); + } + os << " - Transitions :" << std::setw(DUMP_TYPE_OFFSET); + JSTaggedValue transtions = jshclass->GetTransitions(); + transtions.DumpTaggedValue(thread, os); + os << "\n"; + if (withDetail && !transtions.IsNull()) { + transtions.Dump(thread, os); + } + os << " - Parent :" << std::setw(DUMP_TYPE_OFFSET); + jshclass->GetParent().DumpTaggedValue(thread, os); + os << "\n"; + + os << " - Flags : " << std::setw(DUMP_TYPE_OFFSET); + os << "Ctor :" << jshclass->IsConstructor(); + os << "| Callable :" << jshclass->IsCallable(); + os << "| Extensible :" << jshclass->IsExtensible(); + os << "| ElementRepresentation :" << static_cast(jshclass->GetElementRepresentation()); + os << "| NumberOfProps :" << std::dec << jshclass->NumberOfProps(); + os << "| InlinedProperties :" << std::dec << jshclass->GetInlinedProperties(); + os << "\n"; +} + +static void DumpDynClass(JSThread *thread, TaggedObject *obj, std::ostream &os) +{ + JSHClass *hclass = obj->GetClass(); + os << "JSHClass :" << std::setw(DUMP_TYPE_OFFSET) << " klass_(" << std::hex << hclass << ")\n"; + DumpHClass(thread, hclass, os, true); +} + +static void DumpAttr(const PropertyAttributes &attr, bool fastMode, std::ostream &os) +{ + if (attr.IsAccessor()) { + os << "(Accessor) "; + } + + os << "Attr("; + if (attr.IsNoneAttributes()) { + os << "NONE"; + } + if (attr.IsWritable()) { + os << "W"; + } + if (attr.IsEnumerable()) { + os << "E"; + } + if (attr.IsConfigurable()) { + os << "C"; + } + os << ")"; + + os << " InlinedProps: " << attr.IsInlinedProps(); + + if (fastMode) { + os << " Order: " << std::dec << attr.GetOffset(); + os << " SortedIndex: " << std::dec << attr.GetSortedIndex(); + } else { + os << " Order: " << std::dec << attr.GetDictionaryOrder(); + } +} + +// NOLINTNEXTLINE(readability-function-size) +static void DumpObject(JSThread *thread, TaggedObject *obj, std::ostream &os) +{ + DISALLOW_GARBAGE_COLLECTION; + auto jsHclass = obj->GetClass(); + JSType type = jsHclass->GetObjectType(); + + switch (type) { + case JSType::HCLASS: + return DumpDynClass(thread, obj, os); + case JSType::TAGGED_ARRAY: + case JSType::LINKED_HASH_SET: + case JSType::LINKED_HASH_MAP: + case JSType::TAGGED_DICTIONARY: + case JSType::TEMPLATE_MAP: + DumpArrayClass(thread, TaggedArray::Cast(obj), os); + break; + case JSType::STRING: + DumpStringClass(EcmaString::Cast(obj), os); + os << "\n"; + break; + case JSType::JS_NATIVE_POINTER: + break; + case JSType::JS_OBJECT: + case JSType::JS_GLOBAL_OBJECT: + case JSType::JS_ERROR: + case JSType::JS_EVAL_ERROR: + case JSType::JS_RANGE_ERROR: + case JSType::JS_TYPE_ERROR: + case JSType::JS_REFERENCE_ERROR: + case JSType::JS_URI_ERROR: + case JSType::JS_SYNTAX_ERROR: + case JSType::JS_ARGUMENTS: + case JSType::JS_FUNCTION_BASE: + JSObject::Cast(obj)->Dump(thread, os); + break; + case JSType::GLOBAL_ENV: + GlobalEnv::Cast(obj)->Dump(thread, os); + break; + case JSType::ACCESSOR_DATA: + break; + case JSType::JS_FUNCTION: + JSFunction::Cast(obj)->Dump(thread, os); + break; + case JSType::JS_BOUND_FUNCTION: + JSBoundFunction::Cast(obj)->Dump(thread, os); + break; + case JSType::JS_SET: + JSSet::Cast(obj)->Dump(thread, os); + break; + case JSType::JS_MAP: + JSMap::Cast(obj)->Dump(thread, os); + break; + case JSType::JS_WEAK_SET: + JSWeakSet::Cast(obj)->Dump(thread, os); + break; + case JSType::JS_WEAK_MAP: + JSWeakMap::Cast(obj)->Dump(thread, os); + break; + case JSType::JS_REG_EXP: + JSRegExp::Cast(obj)->Dump(thread, os); + break; + case JSType::JS_DATE: + JSDate::Cast(obj)->Dump(thread, os); + break; + case JSType::JS_ARRAY: + JSArray::Cast(obj)->Dump(thread, os); + break; + case JSType::JS_TYPED_ARRAY: + case JSType::JS_INT8_ARRAY: + case JSType::JS_UINT8_ARRAY: + case JSType::JS_UINT8_CLAMPED_ARRAY: + case JSType::JS_INT16_ARRAY: + case JSType::JS_UINT16_ARRAY: + case JSType::JS_INT32_ARRAY: + case JSType::JS_UINT32_ARRAY: + case JSType::JS_FLOAT32_ARRAY: + case JSType::JS_FLOAT64_ARRAY: + JSTypedArray::Cast(obj)->Dump(thread, os); + break; + case JSType::JS_PROXY: + JSProxy::Cast(obj)->Dump(thread, os); + break; + case JSType::JS_PRIMITIVE_REF: + JSPrimitiveRef::Cast(obj)->Dump(thread, os); + break; + case JSType::SYMBOL: + JSSymbol::Cast(obj)->Dump(thread, os); + break; + case JSType::JS_DATA_VIEW: + JSDataView::Cast(obj)->Dump(thread, os); + break; + case JSType::JS_ARRAY_BUFFER: + JSArrayBuffer::Cast(obj)->Dump(thread, os); + break; + case JSType::PROMISE_REACTIONS: + PromiseReaction::Cast(obj)->Dump(thread, os); + break; + case JSType::PROMISE_CAPABILITY: + PromiseCapability::Cast(obj)->Dump(thread, os); + break; + case JSType::PROMISE_ITERATOR_RECORD: + PromiseIteratorRecord::Cast(obj)->Dump(thread, os); + break; + case JSType::PROMISE_RECORD: + PromiseRecord::Cast(obj)->Dump(thread, os); + break; + case JSType::RESOLVING_FUNCTIONS_RECORD: + ResolvingFunctionsRecord::Cast(obj)->Dump(thread, os); + break; + case JSType::JS_PROMISE: + JSPromise::Cast(obj)->Dump(thread, os); + break; + case JSType::JS_PROMISE_REACTIONS_FUNCTION: + JSPromiseReactionsFunction::Cast(obj)->Dump(thread, os); + break; + case JSType::JS_PROMISE_EXECUTOR_FUNCTION: + JSPromiseExecutorFunction::Cast(obj)->Dump(thread, os); + break; + case JSType::JS_PROMISE_ALL_RESOLVE_ELEMENT_FUNCTION: + JSPromiseAllResolveElementFunction::Cast(obj)->Dump(thread, os); + break; + case JSType::MICRO_JOB_QUEUE: + MicroJobQueue::Cast(obj)->Dump(thread, os); + break; + case JSType::PENDING_JOB: + PendingJob::Cast(obj)->Dump(thread, os); + break; + case JSType::COMPLETION_RECORD: + CompletionRecord::Cast(obj)->Dump(thread, os); + break; + case JSType::JS_PROXY_REVOC_FUNCTION: + JSProxyRevocFunction::Cast(obj)->Dump(thread, os); + break; + case JSType::JS_ASYNC_FUNCTION: + JSAsyncFunction::Cast(obj)->Dump(thread, os); + break; + case JSType::JS_ASYNC_GENERATOR_FUNCTION: + JSAsyncGeneratorFunction::Cast(obj)->Dump(thread, os); + break; + case JSType::JS_ASYNC_AWAIT_STATUS_FUNCTION: + JSAsyncAwaitStatusFunction::Cast(obj)->Dump(thread, os); + break; + case JSType::JS_ASYNC_GENERATOR_RESOLVE_NEXT_FUNCTION: + JSAsyncGeneratorResolveNextFunction::Cast(obj)->Dump(thread, os); + break; + case JSType::JS_ASYNC_FROM_SYNC_ITERATOR_VALUE_UNWRAP_FUNCTION: + JSAsyncFromSyncIteratorValueUnwrapFunction::Cast(obj)->Dump(thread, os); + break; + case JSType::JS_GENERATOR_FUNCTION: + JSGeneratorFunction::Cast(obj)->Dump(thread, os); + break; + case JSType::JS_INTL_BOUND_FUNCTION: + JSIntlBoundFunction::Cast(obj)->Dump(thread, os); + break; + case JSType::JS_ITERATOR: + break; + case JSType::JS_FORIN_ITERATOR: + JSForInIterator::Cast(obj)->Dump(thread, os); + break; + case JSType::JS_MAP_ITERATOR: + JSMapIterator::Cast(obj)->Dump(thread, os); + break; + case JSType::JS_SET_ITERATOR: + JSSetIterator::Cast(obj)->Dump(thread, os); + break; + case JSType::JS_ARRAY_ITERATOR: + JSArrayIterator::Cast(obj)->Dump(thread, os); + break; + case JSType::JS_STRING_ITERATOR: + JSStringIterator::Cast(obj)->Dump(thread, os); + break; + case JSType::PROTOTYPE_HANDLER: + PrototypeHandler::Cast(obj)->Dump(thread, os); + break; + case JSType::TRANSITION_HANDLER: + TransitionHandler::Cast(obj)->Dump(thread, os); + break; + case JSType::PROPERTY_BOX: + PropertyBox::Cast(obj)->Dump(thread, os); + break; + case JSType::JS_REALM: + JSRealm::Cast(obj)->Dump(thread, os); + break; + case JSType::JS_INTL: + JSIntl::Cast(obj)->Dump(thread, os); + break; + case JSType::JS_LOCALE: + JSLocale::Cast(obj)->Dump(thread, os); + break; + case JSType::JS_DATE_TIME_FORMAT: + JSDateTimeFormat::Cast(obj)->Dump(thread, os); + break; + case JSType::JS_RELATIVE_TIME_FORMAT: + JSRelativeTimeFormat::Cast(obj)->Dump(thread, os); + break; + case JSType::JS_NUMBER_FORMAT: + JSNumberFormat::Cast(obj)->Dump(thread, os); + break; + case JSType::JS_COLLATOR: + JSCollator::Cast(obj)->Dump(thread, os); + break; + case JSType::JS_PLURAL_RULES: + JSPluralRules::Cast(obj)->Dump(thread, os); + break; + case JSType::JS_GENERATOR_OBJECT: + JSGeneratorObject::Cast(obj)->Dump(thread, os); + break; + case JSType::JS_ASYNC_FUNC_OBJECT: + JSAsyncFuncObject::Cast(obj)->Dump(thread, os); + break; + case JSType::JS_GENERATOR_CONTEXT: + GeneratorContext::Cast(obj)->Dump(thread, os); + break; + case JSType::PROTOTYPE_INFO: + ProtoChangeDetails::Cast(obj)->Dump(thread, os); + break; + case JSType::PROTO_CHANGE_MARKER: + ProtoChangeMarker::Cast(obj)->Dump(thread, os); + break; + case JSType::PROGRAM: + Program::Cast(obj)->Dump(thread, os); + break; + case JSType::LEXICAL_FUNCTION: + LexicalFunction::Cast(obj)->Dump(thread, os); + break; + case JSType::FUNCTION_EXTRA_INFO: + JSFunctionExtraInfo::Cast(obj)->Dump(thread, os); + break; + case JSType::MACHINE_CODE_OBJECT: + MachineCode::Cast(obj)->Dump(thread, os); + break; + case JSType::ECMA_MODULE: + EcmaModule::Cast(obj)->Dump(thread, os); + break; + case JSType::CLASS_INFO_EXTRACTOR: + ClassInfoExtractor::Cast(obj)->Dump(thread, os); + break; + case JSType::JS_ARRAY_LIST: + JSArrayList::Cast(obj)->Dump(thread, os); + break; + default: + UNREACHABLE(); + break; + } + + DumpHClass(thread, jsHclass, os, false); +} + +void JSTaggedValue::DumpSpecialValue([[maybe_unused]] JSThread *thread, std::ostream &os) const +{ + ASSERT(IsSpecial()); + os << "[Special Value] : "; + switch (GetRawData()) { + case VALUE_HOLE: + os << "Hole"; + break; + case VALUE_NULL: + os << "Null"; + break; + case VALUE_FALSE: + os << "False"; + break; + case VALUE_TRUE: + os << "True"; + break; + case VALUE_UNDEFINED: + os << "Undefined"; + break; + case VALUE_EXCEPTION: + os << "Exception"; + break; + default: + UNREACHABLE(); + break; + } +} + +void JSTaggedValue::DumpHeapObjectType([[maybe_unused]] JSThread *thread, std::ostream &os) const +{ + ASSERT(IsWeak() || IsHeapObject()); + bool isWeak = IsWeak(); + TaggedObject *obj = isWeak ? GetTaggedWeakRef() : GetTaggedObject(); + if (isWeak) { + os << "----------Dump Weak Referent----------" + << "\n"; + } + + JSType type = GetTaggedObject()->GetClass()->GetObjectType(); + if (type == JSType::STRING) { + CString string = ConvertToString(EcmaString::Cast(obj)); + os << std::left << std::setw(DUMP_TYPE_OFFSET) << "[" + string + "]"; + } else { + std::ostringstream address; + address << obj; + CString addrStr = CString(address.str()); + + os << std::left << std::setw(DUMP_TYPE_OFFSET) << "[" + JSHClass::DumpJSType(type) + "(" + addrStr + ")]"; + } +} + +void JSTaggedValue::DumpTaggedValue(JSThread *thread, std::ostream &os) const +{ + if (IsInt()) { + os << std::left << std::setw(DUMP_TYPE_OFFSET) << "[Int] : " << std::hex << "0x" << GetInt() << std::dec << " (" + << GetInt() << ")"; + } else if (IsDouble()) { + os << std::left << std::setw(DUMP_TYPE_OFFSET) << "[Double] : " << GetDouble(); + } else if (IsSpecial()) { + DumpSpecialValue(thread, os); + } else { + DumpHeapObjectType(thread, os); + } +} + +void JSTaggedValue::Dump(JSThread *thread, std::ostream &os) const +{ + DumpTaggedValue(thread, os); + os << "\n"; + + if (IsHeapObject()) { + TaggedObject *obj = GetTaggedObject(); + if (thread == nullptr) { + thread = obj->GetJSThread(); + } + DumpObject(thread, obj, os); + } +} + +void JSTaggedValue::D() const +{ + Dump(nullptr, std::cout); +} + +void JSTaggedValue::DV(JSTaggedType val) +{ + JSTaggedValue(val).D(); +} + +void JSThread::DumpStack() +{ + StackWalker::Create(this).Dump(std::cout); +} + +void NumberDictionary::Dump(JSThread *thread, std::ostream &os) const +{ + DISALLOW_GARBAGE_COLLECTION; + int size = Size(); + for (int hashIndex = 0; hashIndex < size; hashIndex++) { + JSTaggedValue key(GetKey(hashIndex)); + if (!key.IsUndefined() && !key.IsHole()) { + JSTaggedValue val(GetValue(hashIndex)); + os << std::right << std::setw(DUMP_PROPERTY_OFFSET) + << static_cast(JSTaggedNumber(key).GetNumber()) << ": "; + val.DumpTaggedValue(thread, os); + os << " "; + DumpAttr(GetAttributes(hashIndex), false, os); + os << "\n"; + } + } +} + +void NameDictionary::Dump(JSThread *thread, std::ostream &os) const +{ + DISALLOW_GARBAGE_COLLECTION; + int size = Size(); + for (int hashIndex = 0; hashIndex < size; hashIndex++) { + JSTaggedValue key(GetKey(hashIndex)); + if (!key.IsUndefined() && !key.IsHole()) { + JSTaggedValue val(GetValue(hashIndex)); + os << std::right << std::setw(DUMP_PROPERTY_OFFSET); + DumpPropertyKey(key, os); + os << ": "; + val.DumpTaggedValue(thread, os); + os << " "; + DumpAttr(GetAttributes(hashIndex), false, os); + os << "\n"; + } + } +} + +void GlobalDictionary::Dump(JSThread *thread, std::ostream &os) const +{ + DISALLOW_GARBAGE_COLLECTION; + int size = Size(); + for (int hashIndex = 0; hashIndex < size; hashIndex++) { + JSTaggedValue key(GetKey(hashIndex)); + if (!key.IsUndefined() && !key.IsHole()) { + JSTaggedValue val(GetValue(hashIndex)); + os << std::right << std::setw(DUMP_PROPERTY_OFFSET); + DumpPropertyKey(key, os); + os << " : "; + val.DumpTaggedValue(thread, os); + os << " "; + DumpAttr(GetAttributes(hashIndex), false, os); + os << "\n"; + } + } +} + +void LayoutInfo::Dump([[maybe_unused]] JSThread *thread, std::ostream &os) const +{ + DISALLOW_GARBAGE_COLLECTION; + int num = NumberOfElements(); + for (int i = 0; i < num; i++) { + JSTaggedValue key = GetKey(i); + PropertyAttributes attr = GetAttr(i); + os << std::right << std::setw(DUMP_PROPERTY_OFFSET); + os << "[" << i << "]: "; + DumpPropertyKey(key, os); + os << " : "; + DumpAttr(attr, true, os); + os << "\n"; + } +} + +void TransitionsDictionary::Dump(JSThread *thread, std::ostream &os) const +{ + DISALLOW_GARBAGE_COLLECTION; + int size = Size(); + for (int hashIndex = 0; hashIndex < size; hashIndex++) { + JSTaggedValue key(GetKey(hashIndex)); + if (!key.IsUndefined() && !key.IsHole()) { + os << std::right << std::setw(DUMP_PROPERTY_OFFSET); + DumpPropertyKey(key, os); + os << " : "; + GetValue(hashIndex).DumpTaggedValue(thread, os); + os << " : "; + GetAttributes(hashIndex).DumpTaggedValue(thread, os); + os << "\n"; + } + } +} + +void LinkedHashSet::Dump(JSThread *thread, std::ostream &os) const +{ + DISALLOW_GARBAGE_COLLECTION; + int capacity = NumberOfElements() + NumberOfDeletedElements(); + for (int hashIndex = 0; hashIndex < capacity; hashIndex++) { + JSTaggedValue key(GetKey(hashIndex)); + if (!key.IsUndefined() && !key.IsHole()) { + os << std::right << std::setw(DUMP_PROPERTY_OFFSET); + key.DumpTaggedValue(thread, os); + os << "\n"; + } + } +} + +void LinkedHashMap::Dump(JSThread *thread, std::ostream &os) const +{ + DISALLOW_GARBAGE_COLLECTION; + int capacity = NumberOfElements() + NumberOfDeletedElements(); + for (int hashIndex = 0; hashIndex < capacity; hashIndex++) { + JSTaggedValue key(GetKey(hashIndex)); + if (!key.IsUndefined() && !key.IsHole()) { + JSTaggedValue val(GetValue(hashIndex)); + os << std::right << std::setw(DUMP_PROPERTY_OFFSET); + key.DumpTaggedValue(thread, os); + os << ": "; + val.DumpTaggedValue(thread, os); + os << "\n"; + } + } +} + +void JSObject::Dump(JSThread *thread, std::ostream &os) const +{ + DISALLOW_GARBAGE_COLLECTION; + JSHClass *jshclass = GetJSHClass(); + os << " - hclass: " << std::hex << jshclass << "\n"; + os << " - prototype: "; + jshclass->GetPrototype().DumpTaggedValue(thread, os); + os << "\n"; + + TaggedArray *elements = TaggedArray::Cast(GetElements().GetTaggedObject()); + os << " - elements: " << std::hex << elements; + if (elements->GetLength() == 0) { + os << " NONE\n"; + } else if (!elements->IsDictionaryMode()) { + DumpArrayClass(thread, elements, os); + } else { + NumberDictionary *dict = NumberDictionary::Cast(elements); + os << " EntriesCount() << "]>\n"; + dict->Dump(thread, os); + } + + TaggedArray *properties = TaggedArray::Cast(GetProperties().GetTaggedObject()); + os << " - properties: " << std::hex << properties; + if (IsJSGlobalObject()) { + GlobalDictionary *dict = GlobalDictionary::Cast(properties); + os << " EntriesCount() << "]>\n"; + dict->Dump(thread, os); + return; + } + + if (!properties->IsDictionaryMode()) { + JSTaggedValue attrs = jshclass->GetLayout(); + if (attrs.IsNull()) { + return; + } + + LayoutInfo *layoutInfo = LayoutInfo::Cast(attrs.GetTaggedObject()); + int propNumber = jshclass->NumberOfProps(); + os << " \n"; + for (int i = 0; i < propNumber; i++) { + JSTaggedValue key = layoutInfo->GetKey(i); + PropertyAttributes attr = layoutInfo->GetAttr(i); + ASSERT(i == static_cast(attr.GetOffset())); + os << " " << std::right << std::setw(DUMP_PROPERTY_OFFSET); + DumpPropertyKey(key, os); + os << ": ("; + JSTaggedValue val; + if (attr.IsInlinedProps()) { + val = GetPropertyInlinedProps(i); + } else { + val = properties->Get(i - jshclass->GetInlinedProperties()); + } + val.DumpTaggedValue(thread, os); + os << ") "; + DumpAttr(attr, true, os); + os << "\n"; + } + } else { + NameDictionary *dict = NameDictionary::Cast(properties); + os << " EntriesCount() << "]>\n"; + dict->Dump(thread, os); + } +} + +void AccessorData::Dump(JSThread *thread, std::ostream &os) const +{ + auto *hclass = GetClass(); + if (hclass->GetObjectType() == JSType::INTERNAL_ACCESSOR) { + os << " - Getter: " << reinterpret_cast(GetGetter().GetTaggedObject()) << "\n"; + os << " - Setter: " << reinterpret_cast(GetSetter().GetTaggedObject()) << "\n"; + return; + } + + os << " - Getter: "; + GetGetter().DumpTaggedValue(thread, os); + os << "\n"; + + os << " - Setter: "; + GetSetter().DumpTaggedValue(thread, os); + os << "\n"; +} + +void Program::Dump([[maybe_unused]] JSThread *thread, std::ostream &os) const +{ + os << " - Location: "; + GetLocation().D(); + os << "\n"; + os << " - ConstantPool: "; + GetConstantPool().D(); + os << "\n"; + os << " - MainFunction: "; + GetMainFunction().D(); + os << "\n"; + os << " - MethodsData: " << GetMethodsData() << "\n"; + os << " - NumberMethods: " << GetNumberMethods() << "\n"; +} + +void ConstantPool::Dump(JSThread *thread, std::ostream &os) const +{ + DumpArrayClass(thread, this, os); +} + +void JSFunction::Dump(JSThread *thread, std::ostream &os) const +{ + os << " - ProtoOrDynClass: "; + GetProtoOrDynClass().D(); + os << "\n"; + os << " - LexicalEnv: "; + GetLexicalEnv().D(); + os << "\n"; + os << " - HomeObject: "; + GetHomeObject().D(); + os << "\n"; + os << " - FunctionInfoFlag: "; + GetFunctionInfoFlag().D(); + os << "\n"; + os << " - FunctionExtraInfo: "; + GetFunctionExtraInfo().D(); + os << "\n"; + os << " - ConstantPool: "; + GetConstantPool().D(); + os << "\n"; + os << " - ProfileTypeInfo: "; + GetProfileTypeInfo().D(); + os << "\n"; + JSObject::Dump(thread, os); +} + +void JSHClass::Dump(JSThread *thread, std::ostream &os) const +{ + DumpHClass(thread, this, os, true); +} + +void JSBoundFunction::Dump(JSThread *thread, std::ostream &os) const +{ + os << " - BoundTarget: "; + GetBoundTarget().DumpTaggedValue(thread, os); + os << "\n"; + + os << " - BoundThis: "; + GetBoundThis().DumpTaggedValue(thread, os); + os << "\n"; + + os << " - BoundArguments: "; + GetBoundArguments().DumpTaggedValue(thread, os); + os << "\n"; + + JSObject::Dump(thread, os); +} + +void JSPrimitiveRef::Dump(JSThread *thread, std::ostream &os) const +{ + os << " - SubValue : "; + GetValue().DumpTaggedValue(thread, os); + os << "\n"; + JSObject::Dump(thread, os); +} + +void JSDate::Dump(JSThread *thread, std::ostream &os) const +{ + os << " - time: " << GetTime().GetDouble() << "\n"; + os << " - localOffset: " << GetLocalOffset().GetDouble() << "\n"; + JSObject::Dump(thread, os); +} + +void JSMap::Dump(JSThread *thread, std::ostream &os) const +{ + LinkedHashMap *map = LinkedHashMap::Cast(GetLinkedMap().GetTaggedObject()); + os << " - elements: " << std::dec << map->NumberOfElements() << "\n"; + os << " - deleted-elements: " << std::dec << map->NumberOfDeletedElements() << "\n"; + os << " - capacity: " << std::dec << map->Capacity() << "\n"; + JSObject::Dump(thread, os); + + os << " NumberOfElements() << "]>\n"; + map->Dump(thread, os); +} + +void JSForInIterator::Dump(JSThread *thread, std::ostream &os) const +{ + os << " - Object : "; + GetObject().DumpTaggedValue(thread, os); + os << "\n"; + os << " - WasVisited : "; + GetWasVisited().DumpTaggedValue(thread, os); + os << "\n"; + os << " - VisitedKeys : "; + GetVisitedKeys().DumpTaggedValue(thread, os); + os << "\n"; + os << " - RemainingKeys : "; + GetRemainingKeys().DumpTaggedValue(thread, os); + os << "\n"; + JSObject::Dump(thread, os); +} + +void JSMapIterator::Dump(JSThread *thread, std::ostream &os) const +{ + LinkedHashMap *map = LinkedHashMap::Cast(GetIteratedMap().GetTaggedObject()); + os << " - elements: " << std::dec << map->NumberOfElements() << "\n"; + os << " - deleted-elements: " << std::dec << map->NumberOfDeletedElements() << "\n"; + os << " - capacity: " << std::dec << map->Capacity() << "\n"; + os << " - nextIndex: " << std::dec << GetNextIndex().GetInt() << "\n"; + os << " - IterationKind: " << std::dec << GetIterationKind().GetInt() << "\n"; + JSObject::Dump(thread, os); + + os << " NumberOfElements() << "]>\n"; + map->Dump(thread, os); +} + +void JSSet::Dump(JSThread *thread, std::ostream &os) const +{ + LinkedHashSet *set = LinkedHashSet::Cast(GetLinkedSet().GetTaggedObject()); + os << " - elements: " << std::dec << set->NumberOfElements() << "\n"; + os << " - deleted-elements: " << std::dec << set->NumberOfDeletedElements() << "\n"; + os << " - capacity: " << std::dec << set->Capacity() << "\n"; + JSObject::Dump(thread, os); + + os << " NumberOfElements() << "]>\n"; + set->Dump(thread, os); +} + +void JSWeakMap::Dump(JSThread *thread, std::ostream &os) const +{ + LinkedHashMap *map = LinkedHashMap::Cast(GetLinkedMap().GetTaggedObject()); + os << " - length: " << std::dec << GetSize() << "\n"; + os << " - elements: " << std::dec << map->NumberOfElements() << "\n"; + os << " - deleted-elements: " << std::dec << map->NumberOfDeletedElements() << "\n"; + os << " - capacity: " << std::dec << map->Capacity() << "\n"; + JSObject::Dump(thread, os); + + os << " NumberOfElements() << "]>\n"; + map->Dump(thread, os); +} + +void JSWeakSet::Dump(JSThread *thread, std::ostream &os) const +{ + LinkedHashSet *set = LinkedHashSet::Cast(GetLinkedSet().GetTaggedObject()); + os << " - size: " << std::dec << GetSize() << "\n"; + os << " - elements: " << std::dec << set->NumberOfElements() << "\n"; + os << " - deleted-elements: " << std::dec << set->NumberOfDeletedElements() << "\n"; + os << " - capacity: " << std::dec << set->Capacity() << "\n"; + JSObject::Dump(thread, os); + + os << " NumberOfElements() << "]>\n"; + set->Dump(thread, os); +} + +void JSSetIterator::Dump(JSThread *thread, std::ostream &os) const +{ + LinkedHashSet *set = LinkedHashSet::Cast(GetIteratedSet().GetTaggedObject()); + os << " - elements: " << std::dec << set->NumberOfElements() << "\n"; + os << " - deleted-elements: " << std::dec << set->NumberOfDeletedElements() << "\n"; + os << " - capacity: " << std::dec << set->Capacity() << "\n"; + os << " - nextIndex: " << std::dec << GetNextIndex().GetInt() << "\n"; + os << " - IterationKind: " << std::dec << GetIterationKind().GetInt() << "\n"; + JSObject::Dump(thread, os); + + os << " NumberOfElements() << "]>\n"; + set->Dump(thread, os); +} + +void JSArray::Dump(JSThread *thread, std::ostream &os) const +{ + os << " - length: " << std::dec << GetArrayLength() << "\n"; + JSObject::Dump(thread, os); +} + +void JSArrayList::Dump(JSThread *thread, std::ostream &os) const +{ + os << " - length: " << std::dec << GetLength().GetArrayLength() << "\n"; + JSObject::Dump(thread, os); +} + +void JSArrayIterator::Dump(JSThread *thread, std::ostream &os) const +{ + JSArray *array = JSArray::Cast(GetIteratedArray().GetTaggedObject()); + os << " - length: " << std::dec << array->GetArrayLength() << "\n"; + os << " - nextIndex: " << std::dec << GetNextIndex().GetInt() << "\n"; + os << " - IterationKind: " << std::dec << GetIterationKind().GetInt() << "\n"; + JSObject::Dump(thread, os); +} + +void JSStringIterator::Dump(JSThread *thread, std::ostream &os) const +{ + EcmaString *str = EcmaString::Cast(GetIteratedString().GetTaggedObject()); + os << " - IteratedString: " << str->GetCString().get() << "\n"; + os << " - StringIteratorNextIndex: " << std::dec << GetStringIteratorNextIndex().GetInt() << "\n"; + JSObject::Dump(thread, os); +} +void JSTypedArray::Dump(JSThread *thread, std::ostream &os) const +{ + os << " - viewed-array-buffer: "; + GetViewedArrayBuffer().D(); + os << " - typed-array-name: "; + GetTypedArrayName().D(); + os << " - byte-length: "; + GetByteLength().D(); + os << " - byte-offset: "; + GetByteOffset().D(); + os << " - array-length: "; + GetArrayLength().D(); + JSObject::Dump(thread, os); +} + +void JSRegExp::Dump(JSThread *thread, std::ostream &os) const +{ + os << " - LastIndex: "; + GetLastIndex().D(); + os << "\n"; + os << " - ByteCodeBuffer: "; + GetByteCodeBuffer().D(); + os << "\n"; + os << " - OriginalSource: "; + GetOriginalSource().D(); + os << "\n"; + os << " - OriginalFlags: "; + GetOriginalFlags().D(); + os << "\n"; + os << " - Length: "; + GetLength().D(); + os << "\n"; + JSObject::Dump(thread, os); +} + +void JSProxy::Dump(JSThread *thread, std::ostream &os) const +{ + os << " - Target: "; + os << "\n"; + JSObject::Cast(GetTarget().GetTaggedObject())->Dump(thread, os); + os << " - Handler: "; + os << "\n"; + JSObject::Cast(GetHandler().GetTaggedObject())->Dump(thread, os); + os << "\n"; +} + +void JSSymbol::Dump([[maybe_unused]] JSThread *thread, std::ostream &os) const +{ + os << " - hash-field: "; + JSTaggedValue hashField = GetHashField(); + hashField.D(); + os << " - flags: "; + JSTaggedValue flags = GetFlags(); + flags.D(); + os << " - description: "; + JSTaggedValue description = GetDescription(); + description.D(); +} + +void LexicalEnv::Dump(JSThread *thread, std::ostream &os) const +{ + DumpArrayClass(thread, this, os); +} + +// NOLINTNEXTLINE(readability-function-size) +void GlobalEnv::Dump(JSThread *thread, std::ostream &os) const +{ + auto globalConst = thread->GlobalConstants(); + os << " - ObjectFunction: "; + GetObjectFunction().GetTaggedValue().Dump(thread, os); + os << " - FunctionFunction: "; + GetFunctionFunction().GetTaggedValue().Dump(thread, os); + os << " - NumberFunction: "; + GetNumberFunction().GetTaggedValue().Dump(thread, os); + os << " - DateFunction: "; + GetDateFunction().GetTaggedValue().Dump(thread, os); + os << " - BooleanFunction: "; + GetBooleanFunction().GetTaggedValue().Dump(thread, os); + os << " - ErrorFunction: "; + GetErrorFunction().GetTaggedValue().Dump(thread, os); + os << " - ArrayFunction: "; + GetArrayFunction().GetTaggedValue().Dump(thread, os); + os << " - TypedArrayFunction: "; + GetTypedArrayFunction().GetTaggedValue().Dump(thread, os); + os << " - Int8ArrayFunction: "; + GetInt8ArrayFunction().GetTaggedValue().Dump(thread, os); + os << " - Uint8ArrayFunction: "; + GetUint8ArrayFunction().GetTaggedValue().Dump(thread, os); + os << " - Uint8ClampedArrayFunction: "; + GetUint8ClampedArrayFunction().GetTaggedValue().Dump(thread, os); + os << " - Int16ArrayFunction: "; + GetInt16ArrayFunction().GetTaggedValue().Dump(thread, os); + os << " - ArrayBufferFunction: "; + GetArrayBufferFunction().GetTaggedValue().Dump(thread, os); + os << " - SymbolFunction: "; + GetSymbolFunction().GetTaggedValue().Dump(thread, os); + os << " - RangeErrorFunction: "; + GetRangeErrorFunction().GetTaggedValue().Dump(thread, os); + os << " - ReferenceErrorFunction: "; + GetReferenceErrorFunction().GetTaggedValue().Dump(thread, os); + os << " - TypeErrorFunction: "; + GetTypeErrorFunction().GetTaggedValue().Dump(thread, os); + os << " - URIErrorFunction: "; + GetURIErrorFunction().GetTaggedValue().Dump(thread, os); + os << " - SyntaxErrorFunction: "; + GetSyntaxErrorFunction().GetTaggedValue().Dump(thread, os); + os << " - EvalErrorFunction: "; + GetEvalErrorFunction().GetTaggedValue().Dump(thread, os); + os << " - RegExpFunction: "; + GetRegExpFunction().GetTaggedValue().Dump(thread, os); + os << " - BuiltinsSetFunction: "; + GetBuiltinsSetFunction().GetTaggedValue().Dump(thread, os); + os << " - BuiltinsMapFunction: "; + GetBuiltinsMapFunction().GetTaggedValue().Dump(thread, os); + os << " - BuiltinsWeakSetFunction: "; + GetBuiltinsWeakSetFunction().GetTaggedValue().Dump(thread, os); + os << " - BuiltinsWeakMapFunction: "; + GetBuiltinsWeakMapFunction().GetTaggedValue().Dump(thread, os); + os << " - MathFunction: "; + GetMathFunction().GetTaggedValue().Dump(thread, os); + os << " - JsonFunction: "; + GetJsonFunction().GetTaggedValue().Dump(thread, os); + os << " - StringFunction: "; + GetStringFunction().GetTaggedValue().Dump(thread, os); + os << " - ProxyFunction: "; + GetProxyFunction().GetTaggedValue().Dump(thread, os); + os << " - ReflectFunction: "; + GetReflectFunction().GetTaggedValue().Dump(thread, os); + os << " - AsyncFunction: "; + GetAsyncFunction().GetTaggedValue().Dump(thread, os); + os << " - AsyncFunctionPrototype: "; + GetAsyncFunctionPrototype().GetTaggedValue().Dump(thread, os); + os << " - JSGlobalObject: "; + GetJSGlobalObject().GetTaggedValue().Dump(thread, os); + os << " - EmptyArray: "; + GetEmptyArray().GetTaggedValue().Dump(thread, os); + os << " - EmptyString "; + globalConst->GetEmptyString().Dump(thread, os); + os << " - EmptyTaggedQueue: "; + GetEmptyTaggedQueue().GetTaggedValue().Dump(thread, os); + os << " - PrototypeString: "; + globalConst->GetPrototypeString().Dump(thread, os); + os << " - HasInstanceSymbol: "; + GetHasInstanceSymbol().GetTaggedValue().Dump(thread, os); + os << " - IsConcatSpreadableSymbol: "; + GetIsConcatSpreadableSymbol().GetTaggedValue().Dump(thread, os); + os << " - ToStringTagSymbol: "; + GetToStringTagSymbol().GetTaggedValue().Dump(thread, os); + os << " - IteratorSymbol: "; + GetIteratorSymbol().GetTaggedValue().Dump(thread, os); + os << " - MatchSymbol: "; + GetMatchSymbol().GetTaggedValue().Dump(thread, os); + os << " - ReplaceSymbol: "; + GetReplaceSymbol().GetTaggedValue().Dump(thread, os); + os << " - SearchSymbol: "; + GetSearchSymbol().GetTaggedValue().Dump(thread, os); + os << " - SpeciesSymbol: "; + GetSpeciesSymbol().GetTaggedValue().Dump(thread, os); + os << " - SplitSymbol: "; + GetSplitSymbol().GetTaggedValue().Dump(thread, os); + os << " - ToPrimitiveSymbol: "; + GetToPrimitiveSymbol().GetTaggedValue().Dump(thread, os); + os << " - UnscopablesSymbol: "; + GetUnscopablesSymbol().GetTaggedValue().Dump(thread, os); + os << " - HoleySymbol: "; + GetHoleySymbol().GetTaggedValue().Dump(thread, os); + os << " - ConstructorString: "; + globalConst->GetConstructorString().Dump(thread, os); + os << " - IteratorPrototype: "; + GetIteratorPrototype().GetTaggedValue().Dump(thread, os); + os << " - ForinIteratorPrototype: "; + GetForinIteratorPrototype().GetTaggedValue().Dump(thread, os); + os << " - StringIterator: "; + GetStringIterator().GetTaggedValue().Dump(thread, os); + os << " - MapIteratorPrototype: "; + GetMapIteratorPrototype().GetTaggedValue().Dump(thread, os); + os << " - SetIteratorPrototype: "; + GetSetIteratorPrototype().GetTaggedValue().Dump(thread, os); + os << " - ArrayIteratorPrototype: "; + GetArrayIteratorPrototype().GetTaggedValue().Dump(thread, os); + os << " - StringIteratorPrototype: "; + GetStringIteratorPrototype().GetTaggedValue().Dump(thread, os); + os << " - LengthString: "; + globalConst->GetLengthString().Dump(thread, os); + os << " - ValueString: "; + globalConst->GetValueString().Dump(thread, os); + os << " - WritableString: "; + globalConst->GetWritableString().Dump(thread, os); + os << " - GetString: "; + globalConst->GetGetString().Dump(thread, os); + os << " - SetString: "; + globalConst->GetSetString().Dump(thread, os); + os << " - EnumerableString: "; + globalConst->GetEnumerableString().Dump(thread, os); + os << " - ConfigurableString: "; + globalConst->GetConfigurableString().Dump(thread, os); + os << " - NameString: "; + globalConst->GetNameString().Dump(thread, os); + os << " - ValueOfString: "; + globalConst->GetValueOfString().Dump(thread, os); + os << " - ToStringString: "; + globalConst->GetToStringString().Dump(thread, os); + os << " - ToLocaleStringString: "; + globalConst->GetToLocaleStringString().Dump(thread, os); + os << " - UndefinedString: "; + globalConst->GetUndefinedString().Dump(thread, os); + os << " - NullString: "; + globalConst->GetNullString().Dump(thread, os); + os << " - TrueString: "; + globalConst->GetTrueString().Dump(thread, os); + os << " - FalseString: "; + globalConst->GetFalseString().Dump(thread, os); + os << " - RegisterSymbols: "; + GetRegisterSymbols().GetTaggedValue().Dump(thread, os); + os << " - ThrowTypeError: "; + GetThrowTypeError().GetTaggedValue().Dump(thread, os); + os << " - GetPrototypeOfString: "; + globalConst->GetGetPrototypeOfString().Dump(thread, os); + os << " - SetPrototypeOfString: "; + globalConst->GetSetPrototypeOfString().Dump(thread, os); + os << " - IsExtensibleString: "; + globalConst->GetIsExtensibleString().Dump(thread, os); + os << " - PreventExtensionsString: "; + globalConst->GetPreventExtensionsString().Dump(thread, os); + os << " - GetOwnPropertyDescriptorString: "; + globalConst->GetGetOwnPropertyDescriptorString().Dump(thread, os); + os << " - DefinePropertyString: "; + globalConst->GetDefinePropertyString().Dump(thread, os); + os << " - HasString: "; + globalConst->GetHasString().Dump(thread, os); + os << " - DeletePropertyString: "; + globalConst->GetDeletePropertyString().Dump(thread, os); + os << " - EnumerateString: "; + globalConst->GetEnumerateString().Dump(thread, os); + os << " - OwnKeysString: "; + globalConst->GetOwnKeysString().Dump(thread, os); + os << " - ApplyString: "; + globalConst->GetApplyString().Dump(thread, os); + os << " - ProxyString: "; + globalConst->GetProxyString().Dump(thread, os); + os << " - RevokeString: "; + globalConst->GetRevokeString().Dump(thread, os); + os << " - ProxyConstructString: "; + globalConst->GetProxyConstructString().Dump(thread, os); + os << " - ProxyCallString: "; + globalConst->GetProxyCallString().Dump(thread, os); + os << " - DoneString: "; + globalConst->GetDoneString().Dump(thread, os); + os << " - NegativeZeroString: "; + globalConst->GetNegativeZeroString().Dump(thread, os); + os << " - NextString: "; + globalConst->GetNextString().Dump(thread, os); + os << " - PromiseThenString: "; + globalConst->GetPromiseThenString().Dump(thread, os); + os << " - PromiseFunction: "; + GetPromiseFunction().GetTaggedValue().Dump(thread, os); + os << " - PromiseReactionJob: "; + GetPromiseReactionJob().GetTaggedValue().Dump(thread, os); + os << " - PromiseResolveThenableJob: "; + GetPromiseResolveThenableJob().GetTaggedValue().Dump(thread, os); + os << " - ScriptJobString: "; + globalConst->GetScriptJobString().Dump(thread, os); + os << " - PromiseString: "; + globalConst->GetPromiseString().Dump(thread, os); + os << " - IdentityString: "; + globalConst->GetIdentityString().Dump(thread, os); + os << " - AsyncFunctionString: "; + globalConst->GetAsyncFunctionString().Dump(thread, os); + os << " - ThrowerString: "; + globalConst->GetThrowerString().Dump(thread, os); + os << " - Undefined: "; + globalConst->GetUndefined().Dump(thread, os); +} + +void JSDataView::Dump([[maybe_unused]] JSThread *thread, std::ostream &os) const +{ + os << " - data-view: "; + GetDataView().D(); + os << " - buffer: "; + GetViewedArrayBuffer().D(); + os << " - byte-length: "; + GetByteLength().D(); + os << " - byte-offset: "; + GetByteOffset().D(); +} + +void JSArrayBuffer::Dump([[maybe_unused]] JSThread *thread, std::ostream &os) const +{ + os << " - byte-length: "; + GetArrayBufferByteLength().D(); + os << " - buffer-data: "; + GetArrayBufferData().D(); + os << " - Shared: "; + GetShared().D(); +} + +void PromiseReaction::Dump([[maybe_unused]] JSThread *thread, std::ostream &os) const +{ + os << " - promise-capability: "; + GetPromiseCapability().D(); + os << " - type: "; + GetType().D(); + os << " - handler: "; + GetHandler().D(); +} + +void PromiseCapability::Dump([[maybe_unused]] JSThread *thread, std::ostream &os) const +{ + os << " - promise: "; + GetPromise().D(); + os << " - resolve: "; + GetResolve().D(); + os << " - reject: "; + GetReject().D(); +} + +void PromiseIteratorRecord::Dump([[maybe_unused]] JSThread *thread, std::ostream &os) const +{ + os << " - iterator: "; + GetIterator().D(); + os << " - done: "; + GetDone().D(); +} + +void PromiseRecord::Dump([[maybe_unused]] JSThread *thread, std::ostream &os) const +{ + os << " - value: "; + GetValue().D(); +} + +void ResolvingFunctionsRecord::Dump([[maybe_unused]] JSThread *thread, std::ostream &os) const +{ + os << " - resolve-function: "; + GetResolveFunction().D(); + os << " - reject-function: "; + GetRejectFunction().D(); +} + +void JSPromise::Dump(JSThread *thread, std::ostream &os) const +{ + os << " - promise-state: "; + GetPromiseState().D(); + os << " - promise-result: "; + GetPromiseResult().D(); + os << " - promise-fulfill-reactions: "; + GetPromiseFulfillReactions().D(); + os << " - promise-reject-reactions: "; + GetPromiseRejectReactions().D(); + os << " - promise-is-handled: "; + GetPromiseIsHandled().D(); + JSObject::Dump(thread, os); +} + +void JSPromiseReactionsFunction::Dump(JSThread *thread, std::ostream &os) const +{ + os << " - promise: "; + GetPromise().D(); + os << " - already-resolved: "; + GetAlreadyResolved().D(); + JSObject::Dump(thread, os); +} + +void JSPromiseExecutorFunction::Dump(JSThread *thread, std::ostream &os) const +{ + os << " - capability: "; + GetCapability().D(); + JSObject::Dump(thread, os); +} + +void JSPromiseAllResolveElementFunction::Dump(JSThread *thread, std::ostream &os) const +{ + os << " - index: "; + GetIndex().D(); + os << " - values: "; + GetValues().D(); + os << " - capability: "; + GetCapabilities().D(); + os << " - remaining-elements: "; + GetRemainingElements().D(); + os << " - already-called: "; + GetAlreadyCalled().D(); + JSObject::Dump(thread, os); +} + +void MicroJobQueue::Dump([[maybe_unused]] JSThread *thread, std::ostream &os) const +{ + os << " - promise-job-queue: "; + GetPromiseJobQueue().D(); + os << " - script-job-queue: "; + GetScriptJobQueue().D(); +} + +void PendingJob::Dump([[maybe_unused]] JSThread *thread, std::ostream &os) const +{ + os << " - job: "; + GetJob().D(); + os << " - arguments: "; + GetArguments().D(); +} + +void CompletionRecord::Dump([[maybe_unused]] JSThread *thread, std::ostream &os) const +{ + os << " - type: "; + GetType().D(); + os << " - value: "; + GetValue().D(); +} + +void JSProxyRevocFunction::Dump([[maybe_unused]] JSThread *thread, std::ostream &os) const +{ + os << " - RevocableProxy: "; + os << "\n"; + GetRevocableProxy().D(); + os << "\n"; +} + +void JSAsyncFunction::Dump([[maybe_unused]] JSThread *thread, std::ostream &os) const +{ + JSFunction::Dump(thread, os); +} + +void JSAsyncGeneratorFunction::Dump([[maybe_unused]] JSThread *thread, std::ostream &os) const +{ + JSFunction::Dump(thread, os); +} + +void JSAsyncAwaitStatusFunction::Dump([[maybe_unused]] JSThread *thread, std::ostream &os) const +{ + os << " - AsyncContext: "; + os << "\n"; + GetAsyncContext().D(); + os << "\n"; +} + +void JSAsyncGeneratorResolveNextFunction::Dump([[maybe_unused]] JSThread *thread, std::ostream &os) const +{ + os << " - AsyncGenerator: "; + os << "\n"; + JSObject::Cast(GetAsyncGenerator().GetHeapObject())->Dump(thread, os); + os << "\n"; +} + +void JSAsyncFromSyncIteratorValueUnwrapFunction::Dump([[maybe_unused]] JSThread *thread, std::ostream &os) const +{ + os << " - Done: "; + os << "\n"; + GetDone().D(); + os << "\n"; +} + +void JSGeneratorFunction::Dump(JSThread *thread, std::ostream &os) const +{ + JSFunction::Dump(thread, os); +} + +void JSIntlBoundFunction::Dump(JSThread *thread, std::ostream &os) const +{ + os << " - NumberFormat: "; + GetNumberFormat().D(); + os << "\n"; + os << " - DateTimeFormat: "; + GetDateTimeFormat().D(); + os << "\n"; + os << " - Collator: "; + GetCollator().D(); + os << "\n"; + JSObject::Dump(thread, os); +} + +void PropertyBox::Dump([[maybe_unused]] JSThread *thread, std::ostream &os) const +{ + os << " - Value: "; + GetValue().D(); + os << "\n"; +} + +void PrototypeHandler::Dump([[maybe_unused]] JSThread *thread, std::ostream &os) const +{ + os << " - HandlerInfo: "; + GetHandlerInfo().D(); + os << "\n"; + os << " - ProtoCell: "; + GetHandlerInfo().D(); + os << "\n"; + os << " - Holder: "; + GetHandlerInfo().D(); + os << "\n"; +} + +void TransitionHandler::Dump([[maybe_unused]] JSThread *thread, std::ostream &os) const +{ + os << " - HandlerInfo: "; + GetHandlerInfo().D(); + os << "\n"; + os << " - TransitionHClass: "; + GetTransitionHClass().D(); + os << "\n"; +} + +void JSRealm::Dump(JSThread *thread, std::ostream &os) const +{ + os << " - Value: "; + GetValue().D(); + os << "\n"; + os << " - GlobalEnv: "; + GetGlobalEnv().D(); + os << "\n"; + JSObject::Dump(thread, os); +} + +void JSIntl::Dump(JSThread *thread, std::ostream &os) const +{ + os << " - FallbackSymbol: "; + GetFallbackSymbol().D(); + os << "\n"; + JSObject::Dump(thread, os); +} + +void JSLocale::Dump(JSThread *thread, std::ostream &os) const +{ + os << " - IcuField: "; + GetIcuField().D(); + os << "\n"; + JSObject::Dump(thread, os); +} + +void JSDateTimeFormat::Dump(JSThread *thread, std::ostream &os) const +{ + os << " - Locale: "; + GetLocale().D(); + os << "\n"; + os << " - Calendar: "; + GetCalendar().D(); + os << "\n"; + os << " - NumberingSystem: "; + GetNumberingSystem().D(); + os << "\n"; + os << " - TimeZone: "; + GetTimeZone().D(); + os << "\n"; + os << " - HourCycle: "; + GetHourCycle().D(); + os << "\n"; + os << " - LocaleIcu: "; + GetLocaleIcu().D(); + os << "\n"; + os << " - SimpleDateTimeFormatIcu: "; + GetSimpleDateTimeFormatIcu().D(); + os << "\n"; + os << " - Iso8601: "; + GetIso8601().D(); + os << "\n"; + os << " - DateStyle: "; + GetDateStyle().D(); + os << "\n"; + os << " - TimeStyle: "; + GetTimeStyle().D(); + os << "\n"; + os << " - BoundFormat: "; + GetBoundFormat().D(); + os << "\n"; + JSObject::Dump(thread, os); +} + +void JSRelativeTimeFormat::Dump(JSThread *thread, std::ostream &os) const +{ + os << " - Locale: "; + GetLocale().D(); + os << "\n"; + os << " - InitializedRelativeTimeFormat: "; + GetInitializedRelativeTimeFormat().D(); + os << "\n"; + os << " - NumberingSystem: "; + GetNumberingSystem().D(); + os << "\n"; + os << " - Style: "; + GetStyle().D(); + os << "\n"; + os << " - Numeric: "; + GetNumeric().D(); + os << "\n"; + os << " - AvailableLocales: "; + GetAvailableLocales().D(); + os << "\n"; + os << " - IcuField: "; + GetIcuField().D(); + os << "\n"; + JSObject::Dump(thread, os); +} + +void JSNumberFormat::Dump(JSThread *thread, std::ostream &os) const +{ + os << " - Locale: "; + GetLocale().D(); + os << "\n" + << " - NumberingSystem: "; + GetNumberingSystem().D(); + os << "\n" + << " - Style: "; + GetStyle().D(); + os << "\n" + << " - Currency: "; + GetCurrency().D(); + os << "\n" + << " - CurrencyDisplay: "; + GetCurrencyDisplay().D(); + os << "\n" + << " - CurrencySign: "; + GetCurrencySign().D(); + os << "\n" + << " - Unit: "; + GetUnit().D(); + os << "\n" + << " - UnitDisplay: "; + GetUnitDisplay().D(); + os << "\n" + << " - MinimumIntegerDigits: "; + GetMinimumIntegerDigits().D(); + os << "\n" + << " - MinimumFractionDigits: "; + GetMinimumFractionDigits().D(); + os << "\n" + << " - MaximumFractionDigits: "; + GetMaximumFractionDigits().D(); + os << "\n" + << " - MinimumSignificantDigits: "; + GetMinimumSignificantDigits().D(); + os << "\n" + << " - MaximumSignificantDigits: "; + GetMaximumSignificantDigits().D(); + os << "\n" + << " - UseGrouping: "; + GetUseGrouping().D(); + os << "\n" + << " - RoundingType: "; + GetUseGrouping().D(); + os << "\n" + << " - Notation: "; + GetMinimumIntegerDigits().D(); + os << "\n" + << " - CompactDisplay: "; + GetMaximumSignificantDigits().D(); + os << "\n" + << " - SignDisplay: "; + GetMinimumFractionDigits().D(); + os << "\n" + << " - BoundFormat: "; + GetMaximumFractionDigits().D(); + os << "\n" + << " - IcuField: "; + GetMinimumSignificantDigits().D(); + os << "\n"; + JSObject::Dump(thread, os); +} + +void JSCollator::Dump(JSThread *thread, std::ostream &os) const +{ + os << " - IcuField: "; + GetIcuField().D(); + os << "\n"; + os << " - Locale: "; + GetLocale().D(); + os << "\n"; + os << " - Usage: "; + GetUsage().D(); + os << "\n"; + os << " - Sensitivity: "; + GetSensitivity().D(); + os << "\n"; + os << " - IgnorePunctuation: "; + GetIgnorePunctuation().D(); + os << "\n"; + os << " - Collation: "; + GetCollation().D(); + os << "\n"; + os << " - Numeric: "; + GetNumeric().D(); + os << "\n"; + os << " - CaseFirst: "; + GetCaseFirst().D(); + os << "\n"; + os << " - BoundCompare: "; + GetBoundCompare().D(); + os << "\n"; + JSObject::Dump(thread, os); +} + +void JSPluralRules::Dump(JSThread *thread, std::ostream &os) const +{ + os << " - Locale: "; + GetLocale().D(); + os << "\n"; + os << " - InitializedPluralRules: "; + GetInitializedPluralRules().D(); + os << "\n"; + os << " - Type: "; + GetType().D(); + os << "\n"; + os << " - MinimumIntegerDigits: "; + GetMinimumIntegerDigits().D(); + os << "\n"; + os << " - MinimumFractionDigits: "; + GetMinimumFractionDigits().D(); + os << "\n"; + os << " - MaximumFractionDigits: "; + GetMaximumFractionDigits().D(); + os << "\n"; + os << " - MinimumSignificantDigits: "; + GetMinimumSignificantDigits().D(); + os << "\n"; + os << " - MaximumSignificantDigits: "; + GetMaximumSignificantDigits().D(); + os << "\n"; + os << " - RoundingType: "; + GetRoundingType().D(); + os << "\n"; + os << " - IcuPR: "; + GetIcuPR().D(); + os << "\n"; + os << " - IcuNF: "; + GetIcuNF().D(); + os << "\n"; + JSObject::Dump(thread, os); +} + +void JSGeneratorObject::Dump(JSThread *thread, std::ostream &os) const +{ + os << " - GeneratorState: "; + GetGeneratorState().D(); + os << "\n"; + os << " - GeneratorContext: "; + GetGeneratorContext().D(); + os << "\n"; + os << " - ResumeResult: "; + GetResumeResult().D(); + os << "\n"; + os << " - ResumeMode: "; + GetResumeMode().D(); + os << "\n"; + JSObject::Dump(thread, os); +} + +void JSAsyncFuncObject::Dump([[maybe_unused]] JSThread *thread, std::ostream &os) const +{ + os << " - Promise: "; + GetPromise().D(); + os << "\n"; +} + +void GeneratorContext::Dump([[maybe_unused]] JSThread *thread, std::ostream &os) const +{ + os << " - RegsArray: "; + GetRegsArray().D(); + os << "\n"; + os << " - Method: "; + GetMethod().D(); + os << "\n"; + os << " - Acc: "; + GetAcc().D(); + os << "\n"; + os << " - NRegs: "; + GetNRegs().D(); + os << "\n"; + os << " - BCOffset: "; + GetBCOffset().D(); + os << "\n"; + os << " - GeneratorObject: "; + GetGeneratorObject().D(); + os << "\n"; + os << " - LexicalEnv: "; + GetLexicalEnv().D(); + os << "\n"; +} + +void ProtoChangeMarker::Dump([[maybe_unused]] JSThread *thread, std::ostream &os) const +{ + os << " - HasChanged: " << GetHasChanged() << "\n"; +} + +void ProtoChangeDetails::Dump([[maybe_unused]] JSThread *thread, std::ostream &os) const +{ + os << " - ChangeListener: "; + GetChangeListener().D(); + os << "\n"; + os << " - RegisterIndex: "; + GetRegisterIndex().D(); + os << "\n"; +} + +void LexicalFunction::Dump([[maybe_unused]] JSThread *thread, std::ostream &os) const +{ + os << " - Name: "; + GetName().D(); + os << "\n"; + os << " - NumberVRegs: "; + GetNumberVRegs().D(); + os << "\n"; + os << " - NumberICSlots: "; + GetNumberICSlots().D(); + os << "\n"; + os << " - Bytecode: "; + GetBytecode().D(); + os << "\n"; + os << " - Program: "; + GetProgram().D(); + os << "\n"; +} + +void JSFunctionExtraInfo::Dump([[maybe_unused]] JSThread *thread, std::ostream &os) const +{ + os << " - Callback: "; + GetCallback().D(); + os << "\n"; + os << " - Data: "; + GetData().D(); + os << "\n"; +} + +void MachineCode::Dump([[maybe_unused]] JSThread *thread, std::ostream &os) const +{ + os << " - InstructionSizeInBytes: "; + GetInstructionSizeInBytes().D(); + os << "\n"; +} + +void EcmaModule::Dump([[maybe_unused]] JSThread *thread, std::ostream &os) const +{ + os << " - NameDictionary: "; + GetNameDictionary().D(); + os << "\n"; +} + +void ClassInfoExtractor::Dump([[maybe_unused]] JSThread *thread, std::ostream &os) const +{ + os << " - PrototypeHClass: "; + GetPrototypeHClass().D(); + os << "\n"; + os << " - NonStaticKeys: "; + GetNonStaticKeys().D(); + os << "\n"; + os << " - NonStaticProperties: "; + GetNonStaticProperties().D(); + os << "\n"; + os << " - NonStaticElements: "; + GetNonStaticElements().D(); + os << "\n"; + os << " - ConstructorHClass: "; + GetConstructorHClass().D(); + os << "\n"; + os << " - StaticKeys: "; + GetStaticKeys().D(); + os << "\n"; + os << " - StaticProperties: "; + GetStaticProperties().D(); + os << "\n"; + os << " - StaticElements: "; + GetStaticElements().D(); + os << "\n"; +} + +// ######################################################################################## +// Dump for Snapshot +// ######################################################################################## +static void DumpArrayClass([[maybe_unused]] JSThread *thread, const TaggedArray *arr, + std::vector> &vec) +{ + DISALLOW_GARBAGE_COLLECTION; + uint32_t len = arr->GetLength(); + for (uint32_t i = 0; i < len; i++) { + JSTaggedValue val(arr->Get(i)); + CString str = ToCString(i); + vec.emplace_back(std::make_pair(str, val)); + } +} + +static void DumpStringClass([[maybe_unused]] JSThread *thread, const EcmaString *str, + std::vector> &vec) +{ + vec.emplace_back(std::make_pair("string", JSTaggedValue(str))); +} + +static void DumpDynClass([[maybe_unused]] JSThread *thread, TaggedObject *obj, + std::vector> &vec) +{ + JSHClass *jshclass = obj->GetClass(); + vec.emplace_back(std::make_pair("__proto__", jshclass->GetPrototype())); +} + +// NOLINTNEXTLINE(readability-function-size) +static void DumpObject(JSThread *thread, TaggedObject *obj, std::vector> &vec, + bool isVmMode) +{ + DISALLOW_GARBAGE_COLLECTION; + auto jsHclass = obj->GetClass(); + JSType type = jsHclass->GetObjectType(); + + switch (type) { + case JSType::HCLASS: + DumpDynClass(thread, obj, vec); + return; + case JSType::TAGGED_ARRAY: + case JSType::TAGGED_DICTIONARY: + DumpArrayClass(thread, TaggedArray::Cast(obj), vec); + return; + case JSType::STRING: + DumpStringClass(thread, EcmaString::Cast(obj), vec); + return; + case JSType::JS_NATIVE_POINTER: + return; + case JSType::JS_OBJECT: + case JSType::JS_ERROR: + case JSType::JS_EVAL_ERROR: + case JSType::JS_RANGE_ERROR: + case JSType::JS_TYPE_ERROR: + case JSType::JS_REFERENCE_ERROR: + case JSType::JS_URI_ERROR: + case JSType::JS_SYNTAX_ERROR: + case JSType::JS_ARGUMENTS: + case JSType::JS_GLOBAL_OBJECT: + JSObject::Cast(obj)->DumpForSnapshot(thread, vec); + return; + case JSType::JS_FUNCTION_BASE: + case JSType::JS_FUNCTION: + JSFunction::Cast(obj)->DumpForSnapshot(thread, vec); + return; + case JSType::JS_BOUND_FUNCTION: + JSBoundFunction::Cast(obj)->DumpForSnapshot(thread, vec); + return; + case JSType::JS_SET: + JSSet::Cast(obj)->DumpForSnapshot(thread, vec); + return; + case JSType::JS_MAP: + JSMap::Cast(obj)->DumpForSnapshot(thread, vec); + return; + case JSType::JS_WEAK_SET: + JSWeakSet::Cast(obj)->DumpForSnapshot(thread, vec); + return; + case JSType::JS_WEAK_MAP: + JSWeakMap::Cast(obj)->DumpForSnapshot(thread, vec); + return; + case JSType::JS_REG_EXP: + JSRegExp::Cast(obj)->DumpForSnapshot(thread, vec); + return; + case JSType::JS_DATE: + JSDate::Cast(obj)->DumpForSnapshot(thread, vec); + return; + case JSType::JS_ARRAY: + JSArray::Cast(obj)->DumpForSnapshot(thread, vec); + return; + case JSType::JS_TYPED_ARRAY: + case JSType::JS_INT8_ARRAY: + case JSType::JS_UINT8_ARRAY: + case JSType::JS_UINT8_CLAMPED_ARRAY: + case JSType::JS_INT16_ARRAY: + case JSType::JS_UINT16_ARRAY: + case JSType::JS_INT32_ARRAY: + case JSType::JS_UINT32_ARRAY: + case JSType::JS_FLOAT32_ARRAY: + case JSType::JS_FLOAT64_ARRAY: + JSTypedArray::Cast(obj)->DumpForSnapshot(thread, vec); + return; + case JSType::JS_PROXY: + JSProxy::Cast(obj)->DumpForSnapshot(thread, vec); + return; + case JSType::JS_PRIMITIVE_REF: + JSPrimitiveRef::Cast(obj)->DumpForSnapshot(thread, vec); + return; + case JSType::SYMBOL: + JSSymbol::Cast(obj)->DumpForSnapshot(thread, vec); + return; + case JSType::ACCESSOR_DATA: + case JSType::INTERNAL_ACCESSOR: + AccessorData::Cast(obj)->DumpForSnapshot(thread, vec); + return; + case JSType::JS_DATA_VIEW: + JSDataView::Cast(obj)->DumpForSnapshot(thread, vec); + return; + case JSType::PROMISE_REACTIONS: + PromiseReaction::Cast(obj)->DumpForSnapshot(thread, vec); + return; + case JSType::PROMISE_CAPABILITY: + PromiseCapability::Cast(obj)->DumpForSnapshot(thread, vec); + return; + case JSType::PROMISE_ITERATOR_RECORD: + PromiseIteratorRecord::Cast(obj)->DumpForSnapshot(thread, vec); + return; + case JSType::PROMISE_RECORD: + PromiseRecord::Cast(obj)->DumpForSnapshot(thread, vec); + return; + case JSType::RESOLVING_FUNCTIONS_RECORD: + ResolvingFunctionsRecord::Cast(obj)->DumpForSnapshot(thread, vec); + return; + case JSType::JS_PROMISE: + JSPromise::Cast(obj)->DumpForSnapshot(thread, vec); + return; + case JSType::JS_PROMISE_REACTIONS_FUNCTION: + JSPromiseReactionsFunction::Cast(obj)->DumpForSnapshot(thread, vec); + return; + case JSType::JS_PROMISE_EXECUTOR_FUNCTION: + JSPromiseExecutorFunction::Cast(obj)->DumpForSnapshot(thread, vec); + return; + case JSType::JS_PROMISE_ALL_RESOLVE_ELEMENT_FUNCTION: + JSPromiseAllResolveElementFunction::Cast(obj)->DumpForSnapshot(thread, vec); + return; + case JSType::MICRO_JOB_QUEUE: + MicroJobQueue::Cast(obj)->DumpForSnapshot(thread, vec); + return; + case JSType::PENDING_JOB: + PendingJob::Cast(obj)->DumpForSnapshot(thread, vec); + return; + case JSType::COMPLETION_RECORD: + CompletionRecord::Cast(obj)->DumpForSnapshot(thread, vec); + return; + case JSType::JS_ITERATOR: + case JSType::JS_FORIN_ITERATOR: + case JSType::JS_MAP_ITERATOR: + case JSType::JS_SET_ITERATOR: + case JSType::JS_ARRAY_ITERATOR: + case JSType::JS_STRING_ITERATOR: + case JSType::JS_ARRAY_BUFFER: + JSArrayBuffer::Cast(obj)->DumpForSnapshot(thread, vec); + return; + case JSType::JS_PROXY_REVOC_FUNCTION: + JSProxyRevocFunction::Cast(obj)->DumpForSnapshot(thread, vec); + return; + case JSType::JS_ASYNC_FUNCTION: + JSAsyncFunction::Cast(obj)->DumpForSnapshot(thread, vec); + return; + case JSType::JS_ASYNC_AWAIT_STATUS_FUNCTION: + JSAsyncAwaitStatusFunction::Cast(obj)->DumpForSnapshot(thread, vec); + return; + case JSType::JS_ASYNC_GENERATOR_RESOLVE_NEXT_FUNCTION: + JSAsyncGeneratorResolveNextFunction::Cast(obj)->DumpForSnapshot(thread, vec); + return; + case JSType::JS_ASYNC_FROM_SYNC_ITERATOR_VALUE_UNWRAP_FUNCTION: + JSAsyncFromSyncIteratorValueUnwrapFunction::Cast(obj)->DumpForSnapshot(thread, vec); + return; + case JSType::JS_ASYNC_GENERATOR_FUNCTION: + JSAsyncGeneratorFunction::Cast(obj)->DumpForSnapshot(thread, vec); + return; + case JSType::JS_GENERATOR_FUNCTION: + JSGeneratorFunction::Cast(obj)->DumpForSnapshot(thread, vec); + return; + case JSType::JS_INTL_BOUND_FUNCTION: + JSIntlBoundFunction::Cast(obj)->DumpForSnapshot(thread, vec); + return; + case JSType::JS_REALM: + JSRealm::Cast(obj)->DumpForSnapshot(thread, vec); + return; + case JSType::JS_INTL: + JSIntl::Cast(obj)->DumpForSnapshot(thread, vec); + return; + case JSType::JS_LOCALE: + JSLocale::Cast(obj)->DumpForSnapshot(thread, vec); + return; + case JSType::JS_DATE_TIME_FORMAT: + JSDateTimeFormat::Cast(obj)->DumpForSnapshot(thread, vec); + return; + case JSType::JS_RELATIVE_TIME_FORMAT: + JSRelativeTimeFormat::Cast(obj)->DumpForSnapshot(thread, vec); + return; + case JSType::JS_NUMBER_FORMAT: + JSNumberFormat::Cast(obj)->DumpForSnapshot(thread, vec); + return; + case JSType::JS_COLLATOR: + JSCollator::Cast(obj)->DumpForSnapshot(thread, vec); + return; + case JSType::JS_PLURAL_RULES: + JSPluralRules::Cast(obj)->DumpForSnapshot(thread, vec); + return; + case JSType::JS_GENERATOR_OBJECT: + JSGeneratorObject::Cast(obj)->DumpForSnapshot(thread, vec); + return; + case JSType::JS_ASYNC_FUNC_OBJECT: + JSAsyncFuncObject::Cast(obj)->DumpForSnapshot(thread, vec); + return; + case JSType::JS_GENERATOR_CONTEXT: + GeneratorContext::Cast(obj)->DumpForSnapshot(thread, vec); + return; + case JSType::FUNCTION_EXTRA_INFO: + JSFunctionExtraInfo::Cast(obj)->DumpForSnapshot(thread, vec); + return; + case JSType::ECMA_MODULE: + EcmaModule::Cast(obj)->DumpForSnapshot(thread, vec); + return; + case JSType::JS_ARRAY_LIST: + JSArrayList::Cast(obj)->DumpForSnapshot(thread, vec); + return; + default: + break; + } + if (isVmMode) { + switch (type) { + case JSType::PROPERTY_BOX: + PropertyBox::Cast(obj)->DumpForSnapshot(thread, vec); + return; + case JSType::TEMPLATE_MAP: + DumpArrayClass(thread, TaggedArray::Cast(obj), vec); + return; + case JSType::LEXICAL_FUNCTION: + LexicalFunction::Cast(obj)->DumpForSnapshot(thread, vec); + return; + case JSType::GLOBAL_ENV: + GlobalEnv::Cast(obj)->DumpForSnapshot(thread, vec); + return; + case JSType::PROTO_CHANGE_MARKER: + ProtoChangeMarker::Cast(obj)->DumpForSnapshot(thread, vec); + return; + case JSType::PROTOTYPE_INFO: + ProtoChangeDetails::Cast(obj)->DumpForSnapshot(thread, vec); + return; + case JSType::PROGRAM: + Program::Cast(obj)->DumpForSnapshot(thread, vec); + return; + case JSType::MACHINE_CODE_OBJECT: + MachineCode::Cast(obj)->DumpForSnapshot(thread, vec); + return; + case JSType::TRANSITION_HANDLER: + TransitionHandler::Cast(obj)->DumpForSnapshot(thread, vec); + return; + case JSType::PROTOTYPE_HANDLER: + PrototypeHandler::Cast(obj)->DumpForSnapshot(thread, vec); + return; + case JSType::CLASS_INFO_EXTRACTOR: + ClassInfoExtractor::Cast(obj)->DumpForSnapshot(thread, vec); + return; + default: + UNREACHABLE(); + break; + } + } +} + +static inline void EcmaStringToStd(CString &res, EcmaString *str) +{ + if (str->GetLength() == 0) { + CString emptyStr = "EmptyString"; + res.append(emptyStr); + } + + CString string = ConvertToString(str); + res.append(string); +} + +static void KeyToStd(CString &res, JSTaggedValue key) +{ + if (key.IsInt()) { + res = std::to_string(key.GetInt()); + } else if (key.IsDouble()) { + res = std::to_string(key.GetDouble()); + } else if (key.IsBoolean()) { + res = key.IsTrue() ? "true" : "false"; + } else if (key.IsHeapObject()) { + if (key.IsWeak()) { + key.RemoveWeakTag(); + } + if (key.IsString()) { + EcmaStringToStd(res, EcmaString::Cast(key.GetTaggedObject())); + } else if (key.IsSymbol()) { + JSSymbol *sym = JSSymbol::Cast(key.GetTaggedObject()); + EcmaStringToStd(res, EcmaString::Cast(sym->GetDescription().GetTaggedObject())); + } + } +} + +void JSTaggedValue::DumpForSnapshot(JSThread *thread, std::vector> &vec, + bool isVmMode) const +{ + if (IsHeapObject()) { + return DumpObject(thread, GetTaggedObject(), vec, isVmMode); + } + + UNREACHABLE(); +} + +void NumberDictionary::DumpForSnapshot([[maybe_unused]] JSThread *thread, + std::vector> &vec) const +{ + DISALLOW_GARBAGE_COLLECTION; + int size = Size(); + for (int hashIndex = 0; hashIndex < size; hashIndex++) { + JSTaggedValue key(GetKey(hashIndex)); + if (!key.IsUndefined() && !key.IsHole() && !key.IsNull()) { + JSTaggedValue val(GetValue(hashIndex)); + CString str = ToCString(static_cast(JSTaggedNumber(key).GetNumber())); + vec.emplace_back(std::make_pair(str, val)); + } + } +} + +void NameDictionary::DumpForSnapshot([[maybe_unused]] JSThread *thread, + std::vector> &vec) const +{ + DISALLOW_GARBAGE_COLLECTION; + int size = Size(); + for (int hashIndex = 0; hashIndex < size; hashIndex++) { + JSTaggedValue key(GetKey(hashIndex)); + if (!key.IsUndefined() && !key.IsHole() && !key.IsNull()) { + JSTaggedValue val(GetValue(hashIndex)); + CString str; + KeyToStd(str, key); + vec.emplace_back(std::make_pair(str, val)); + } + } +} + +void GlobalDictionary::DumpForSnapshot([[maybe_unused]] JSThread *thread, + std::vector> &vec) const +{ + DISALLOW_GARBAGE_COLLECTION; + int size = Size(); + for (int hashIndex = 0; hashIndex < size; hashIndex++) { + JSTaggedValue key(GetKey(hashIndex)); + if (!key.IsUndefined() && !key.IsHole() && !key.IsNull()) { + CString str; + KeyToStd(str, key); + JSTaggedValue val = GetValue(hashIndex); + vec.emplace_back(std::make_pair(str, val)); + } + } +} + +void LinkedHashSet::DumpForSnapshot([[maybe_unused]] JSThread *thread, + std::vector> &vec) const +{ + DISALLOW_GARBAGE_COLLECTION; + int capacity = NumberOfElements() + NumberOfDeletedElements(); + for (int hashIndex = 0; hashIndex < capacity; hashIndex++) { + JSTaggedValue key(GetKey(hashIndex)); + if (!key.IsUndefined() && !key.IsHole() && !key.IsNull()) { + CString str; + KeyToStd(str, key); + vec.emplace_back(std::make_pair(str, JSTaggedValue::Hole())); + } + } +} + +void LinkedHashMap::DumpForSnapshot([[maybe_unused]] JSThread *thread, + std::vector> &vec) const +{ + DISALLOW_GARBAGE_COLLECTION; + int capacity = NumberOfElements() + NumberOfDeletedElements(); + for (int hashIndex = 0; hashIndex < capacity; hashIndex++) { + JSTaggedValue key(GetKey(hashIndex)); + if (!key.IsUndefined() && !key.IsHole() && !key.IsNull()) { + JSTaggedValue val = GetValue(hashIndex); + CString str; + KeyToStd(str, key); + vec.emplace_back(std::make_pair(str, val)); + } + } +} + +void JSObject::DumpForSnapshot(JSThread *thread, std::vector> &vec) const +{ + DISALLOW_GARBAGE_COLLECTION; + JSHClass *jshclass = GetJSHClass(); + vec.emplace_back(std::make_pair("__proto__", jshclass->GetPrototype())); + + TaggedArray *elements = TaggedArray::Cast(GetElements().GetTaggedObject()); + if (elements->GetLength() == 0) { + } else if (!elements->IsDictionaryMode()) { + DumpArrayClass(thread, elements, vec); + } else { + NumberDictionary *dict = NumberDictionary::Cast(elements); + dict->DumpForSnapshot(thread, vec); + } + + TaggedArray *properties = TaggedArray::Cast(GetProperties().GetTaggedObject()); + if (IsJSGlobalObject()) { + GlobalDictionary *dict = GlobalDictionary::Cast(properties); + dict->DumpForSnapshot(thread, vec); + return; + } + + if (!properties->IsDictionaryMode()) { + JSTaggedValue attrs = jshclass->GetLayout(); + if (attrs.IsNull()) { + return; + } + + LayoutInfo *layoutInfo = LayoutInfo::Cast(attrs.GetTaggedObject()); + int propNumber = jshclass->NumberOfProps(); + for (int i = 0; i < propNumber; i++) { + JSTaggedValue key = layoutInfo->GetKey(i); + PropertyAttributes attr = layoutInfo->GetAttr(i); + ASSERT(i == static_cast(attr.GetOffset())); + JSTaggedValue val; + if (attr.IsInlinedProps()) { + val = GetPropertyInlinedProps(i); + } else { + val = properties->Get(i - jshclass->GetInlinedProperties()); + } + + CString str; + KeyToStd(str, key); + vec.emplace_back(std::make_pair(str, val)); + } + } else { + NameDictionary *dict = NameDictionary::Cast(properties); + dict->DumpForSnapshot(thread, vec); + } +} + +void JSHClass::DumpForSnapshot([[maybe_unused]] JSThread *thread, + [[maybe_unused]] std::vector> &vec) const +{ +} + +void JSFunction::DumpForSnapshot([[maybe_unused]] JSThread *thread, + std::vector> &vec) const +{ + vec.emplace_back(std::make_pair(CString("ProtoOrDynClass"), GetProtoOrDynClass())); + vec.emplace_back(std::make_pair(CString("LexicalEnv"), GetLexicalEnv())); + vec.emplace_back(std::make_pair(CString("HomeObject"), GetHomeObject())); + vec.emplace_back(std::make_pair(CString("FunctionInfoFlag"), GetFunctionInfoFlag())); + vec.emplace_back(std::make_pair(CString("FunctionExtraInfo"), GetFunctionExtraInfo())); + vec.emplace_back(std::make_pair(CString("ConstantPool"), GetConstantPool())); + vec.emplace_back(std::make_pair(CString("ProfileTypeInfo"), GetProfileTypeInfo())); + JSObject::DumpForSnapshot(thread, vec); +} + +void Program::DumpForSnapshot([[maybe_unused]] JSThread *thread, + std::vector> &vec) const +{ + vec.emplace_back(std::make_pair(CString("Location"), GetLocation())); + vec.emplace_back(std::make_pair(CString("ConstantPool"), GetConstantPool())); + vec.emplace_back(std::make_pair(CString("MainFunction"), GetMainFunction())); + // MethodsData is another native field, and we don't dump it for JS heap. + vec.emplace_back(std::make_pair(CString("NumberMethods"), JSTaggedValue(GetNumberMethods()))); +} + +void ConstantPool::DumpForSnapshot([[maybe_unused]] JSThread *thread, + std::vector> &vec) const +{ + DumpArrayClass(thread, this, vec); +} + +void JSBoundFunction::DumpForSnapshot([[maybe_unused]] JSThread *thread, + std::vector> &vec) const +{ + JSObject::DumpForSnapshot(thread, vec); + + vec.emplace_back(std::make_pair(CString("BoundTarget"), GetBoundTarget())); + vec.emplace_back(std::make_pair(CString("BoundThis"), GetBoundThis())); + vec.emplace_back(std::make_pair(CString("BoundArguments"), GetBoundArguments())); +} + +void JSPrimitiveRef::DumpForSnapshot([[maybe_unused]] JSThread *thread, + std::vector> &vec) const +{ + vec.emplace_back(std::make_pair(CString("subValue"), GetValue())); + JSObject::DumpForSnapshot(thread, vec); +} + +void JSDate::DumpForSnapshot([[maybe_unused]] JSThread *thread, + std::vector> &vec) const +{ + vec.emplace_back(std::make_pair(CString("time"), GetTime())); + vec.emplace_back(std::make_pair(CString("localOffset"), GetLocalOffset())); + + JSObject::DumpForSnapshot(thread, vec); +} + +void JSMap::DumpForSnapshot([[maybe_unused]] JSThread *thread, + std::vector> &vec) const +{ + LinkedHashMap *map = LinkedHashMap::Cast(GetLinkedMap().GetTaggedObject()); + map->DumpForSnapshot(thread, vec); + + JSObject::DumpForSnapshot(thread, vec); +} + +void JSForInIterator::DumpForSnapshot([[maybe_unused]] JSThread *thread, + std::vector> &vec) const +{ + vec.emplace_back(std::make_pair(CString("Object"), GetObject())); + vec.emplace_back(std::make_pair(CString("WasVisited"), GetWasVisited())); + vec.emplace_back(std::make_pair(CString("VisitedKeys"), GetVisitedKeys())); + vec.emplace_back(std::make_pair(CString("RemainingKeys"), GetRemainingKeys())); + JSObject::DumpForSnapshot(thread, vec); +} + +void JSMapIterator::DumpForSnapshot([[maybe_unused]] JSThread *thread, + std::vector> &vec) const +{ + LinkedHashMap *map = LinkedHashMap::Cast(GetIteratedMap().GetTaggedObject()); + map->DumpForSnapshot(thread, vec); + vec.emplace_back(std::make_pair(CString("NextIndex"), GetNextIndex())); + vec.emplace_back(std::make_pair(CString("IterationKind"), GetIterationKind())); + JSObject::DumpForSnapshot(thread, vec); +} + +void JSSet::DumpForSnapshot([[maybe_unused]] JSThread *thread, + std::vector> &vec) const +{ + LinkedHashSet *set = LinkedHashSet::Cast(GetLinkedSet().GetTaggedObject()); + set->DumpForSnapshot(thread, vec); + + JSObject::DumpForSnapshot(thread, vec); +} + +void JSWeakMap::DumpForSnapshot([[maybe_unused]] JSThread *thread, + std::vector> &vec) const +{ + LinkedHashMap *map = LinkedHashMap::Cast(GetLinkedMap().GetTaggedObject()); + map->DumpForSnapshot(thread, vec); + + JSObject::DumpForSnapshot(thread, vec); +} + +void JSWeakSet::DumpForSnapshot([[maybe_unused]] JSThread *thread, + std::vector> &vec) const +{ + LinkedHashSet *set = LinkedHashSet::Cast(GetLinkedSet().GetTaggedObject()); + set->DumpForSnapshot(thread, vec); + + JSObject::DumpForSnapshot(thread, vec); +} +void JSSetIterator::DumpForSnapshot([[maybe_unused]] JSThread *thread, + std::vector> &vec) const +{ + LinkedHashSet *set = LinkedHashSet::Cast(GetIteratedSet().GetTaggedObject()); + set->DumpForSnapshot(thread, vec); + vec.emplace_back(std::make_pair(CString("NextIndex"), GetNextIndex())); + vec.emplace_back(std::make_pair(CString("IterationKind"), GetIterationKind())); + JSObject::DumpForSnapshot(thread, vec); +} + +void JSArray::DumpForSnapshot([[maybe_unused]] JSThread *thread, + std::vector> &vec) const +{ + JSObject::DumpForSnapshot(thread, vec); +} + +void JSArrayList::DumpForSnapshot([[maybe_unused]] JSThread *thread, + std::vector> &vec) const +{ + JSObject::DumpForSnapshot(thread, vec); +} + +void JSArrayIterator::DumpForSnapshot([[maybe_unused]] JSThread *thread, + std::vector> &vec) const +{ + JSArray *array = JSArray::Cast(GetIteratedArray().GetTaggedObject()); + array->DumpForSnapshot(thread, vec); + vec.emplace_back(std::make_pair(CString("NextIndex"), GetNextIndex())); + vec.emplace_back(std::make_pair(CString("IterationKind"), GetIterationKind())); + JSObject::DumpForSnapshot(thread, vec); +} + +void JSStringIterator::DumpForSnapshot([[maybe_unused]] JSThread *thread, + std::vector> &vec) const +{ + vec.emplace_back(std::make_pair(CString("IteratedString"), GetIteratedString())); + vec.emplace_back(std::make_pair(CString("StringIteratorNextIndex"), GetStringIteratorNextIndex())); + JSObject::DumpForSnapshot(thread, vec); +} + +void JSTypedArray::DumpForSnapshot([[maybe_unused]] JSThread *thread, + std::vector> &vec) const +{ + vec.emplace_back(std::make_pair(CString("viewed-array-buffer"), GetViewedArrayBuffer())); + vec.emplace_back(std::make_pair(CString("typed-array-name"), GetTypedArrayName())); + vec.emplace_back(std::make_pair(CString("byte-length"), GetByteLength())); + vec.emplace_back(std::make_pair(CString("byte-offset"), GetByteOffset())); + vec.emplace_back(std::make_pair(CString("array-length"), GetArrayLength())); +} + +void JSRegExp::DumpForSnapshot([[maybe_unused]] JSThread *thread, + std::vector> &vec) const +{ + vec.emplace_back(std::make_pair(CString("originalSource"), GetOriginalSource())); + vec.emplace_back(std::make_pair(CString("originalFlags"), GetOriginalFlags())); + + JSObject::DumpForSnapshot(thread, vec); +} + +void JSProxy::DumpForSnapshot([[maybe_unused]] JSThread *thread, + std::vector> &vec) const +{ + vec.emplace_back(std::make_pair(CString("target"), GetTarget())); + vec.emplace_back(std::make_pair(CString("handler"), GetHandler())); +} + +void JSSymbol::DumpForSnapshot([[maybe_unused]] JSThread *thread, + std::vector> &vec) const +{ + vec.emplace_back(std::make_pair(CString("hash-field"), GetHashField())); + vec.emplace_back(std::make_pair(CString("flags"), GetFlags())); + vec.emplace_back(std::make_pair(CString("description"), GetDescription())); +} + +void AccessorData::DumpForSnapshot([[maybe_unused]] JSThread *thread, + std::vector> &vec) const +{ + vec.emplace_back(std::make_pair(CString("getter"), GetGetter())); + vec.emplace_back(std::make_pair(CString("setter"), GetSetter())); +} + +void LexicalEnv::DumpForSnapshot([[maybe_unused]] JSThread *thread, + std::vector> &vec) const +{ + DumpArrayClass(thread, this, vec); +} + +void GlobalEnv::DumpForSnapshot([[maybe_unused]] JSThread *thread, + std::vector> &vec) const +{ + auto globalConst = thread->GlobalConstants(); + vec.emplace_back(std::make_pair(CString("ObjectFunction"), GetObjectFunction().GetTaggedValue())); + vec.emplace_back(std::make_pair(CString("FunctionFunction"), GetFunctionFunction().GetTaggedValue())); + vec.emplace_back(std::make_pair(CString("NumberFunction"), GetNumberFunction().GetTaggedValue())); + vec.emplace_back(std::make_pair(CString("DateFunction"), GetDateFunction().GetTaggedValue())); + vec.emplace_back(std::make_pair(CString("BooleanFunction"), GetBooleanFunction().GetTaggedValue())); + vec.emplace_back(std::make_pair(CString("ErrorFunction"), GetErrorFunction().GetTaggedValue())); + vec.emplace_back(std::make_pair(CString("ArrayFunction"), GetArrayFunction().GetTaggedValue())); + vec.emplace_back(std::make_pair(CString("TypedArrayFunction"), GetTypedArrayFunction().GetTaggedValue())); + vec.emplace_back(std::make_pair(CString("Int8ArrayFunction"), GetInt8ArrayFunction().GetTaggedValue())); + vec.emplace_back(std::make_pair(CString("Uint8ArrayFunction"), GetUint8ArrayFunction().GetTaggedValue())); + vec.emplace_back( + std::make_pair(CString("Uint8ClampedArrayFunction"), GetUint8ClampedArrayFunction().GetTaggedValue())); + vec.emplace_back(std::make_pair(CString("Int16ArrayFunction"), GetInt16ArrayFunction().GetTaggedValue())); + vec.emplace_back(std::make_pair(CString("Uint16ArrayFunction"), GetUint16ArrayFunction().GetTaggedValue())); + vec.emplace_back(std::make_pair(CString("Int32ArrayFunction"), GetInt32ArrayFunction().GetTaggedValue())); + vec.emplace_back(std::make_pair(CString("Uint32ArrayFunction"), GetUint32ArrayFunction().GetTaggedValue())); + vec.emplace_back(std::make_pair(CString("Float32ArrayFunction"), GetFloat32ArrayFunction().GetTaggedValue())); + vec.emplace_back(std::make_pair(CString("Float64ArrayFunction"), GetFloat64ArrayFunction().GetTaggedValue())); + vec.emplace_back(std::make_pair(CString("ArrayBufferFunction"), GetArrayBufferFunction().GetTaggedValue())); + vec.emplace_back(std::make_pair(CString("SymbolFunction"), GetSymbolFunction().GetTaggedValue())); + vec.emplace_back(std::make_pair(CString("RangeErrorFunction"), GetRangeErrorFunction().GetTaggedValue())); + vec.emplace_back(std::make_pair(CString("ReferenceErrorFunction"), GetReferenceErrorFunction().GetTaggedValue())); + vec.emplace_back(std::make_pair(CString("TypeErrorFunction"), GetTypeErrorFunction().GetTaggedValue())); + vec.emplace_back(std::make_pair(CString("URIErrorFunction"), GetURIErrorFunction().GetTaggedValue())); + vec.emplace_back(std::make_pair(CString("SyntaxErrorFunction"), GetSyntaxErrorFunction().GetTaggedValue())); + vec.emplace_back(std::make_pair(CString("EvalErrorFunction"), GetEvalErrorFunction().GetTaggedValue())); + vec.emplace_back(std::make_pair(CString("RegExpFunction"), GetRegExpFunction().GetTaggedValue())); + vec.emplace_back(std::make_pair(CString("BuiltinsSetFunction"), GetBuiltinsSetFunction().GetTaggedValue())); + vec.emplace_back(std::make_pair(CString("BuiltinsMapFunction"), GetBuiltinsMapFunction().GetTaggedValue())); + vec.emplace_back(std::make_pair(CString("BuiltinsWeakSetFunction"), GetBuiltinsWeakSetFunction().GetTaggedValue())); + vec.emplace_back(std::make_pair(CString("BuiltinsWeakMapFunction"), GetBuiltinsWeakMapFunction().GetTaggedValue())); + vec.emplace_back(std::make_pair(CString("MathFunction"), GetMathFunction().GetTaggedValue())); + vec.emplace_back(std::make_pair(CString("JsonFunction"), GetJsonFunction().GetTaggedValue())); + vec.emplace_back(std::make_pair(CString("StringFunction"), GetStringFunction().GetTaggedValue())); + vec.emplace_back(std::make_pair(CString("ProxyFunction"), GetProxyFunction().GetTaggedValue())); + vec.emplace_back(std::make_pair(CString("ReflectFunction"), GetReflectFunction().GetTaggedValue())); + vec.emplace_back(std::make_pair(CString("AsyncFunction"), GetAsyncFunction().GetTaggedValue())); + vec.emplace_back(std::make_pair(CString("AsyncFunctionPrototype"), GetAsyncFunctionPrototype().GetTaggedValue())); + vec.emplace_back(std::make_pair(CString("JSGlobalObject"), GetJSGlobalObject().GetTaggedValue())); + vec.emplace_back(std::make_pair(CString("EmptyArray"), GetEmptyArray().GetTaggedValue())); + vec.emplace_back(std::make_pair(CString("EmptyString"), globalConst->GetEmptyString())); + vec.emplace_back(std::make_pair(CString("EmptyTaggedQueue"), GetEmptyTaggedQueue().GetTaggedValue())); + vec.emplace_back(std::make_pair(CString("PrototypeString"), globalConst->GetPrototypeString())); + vec.emplace_back(std::make_pair(CString("HasInstanceSymbol"), GetHasInstanceSymbol().GetTaggedValue())); + vec.emplace_back( + std::make_pair(CString("IsConcatSpreadableSymbol"), GetIsConcatSpreadableSymbol().GetTaggedValue())); + vec.emplace_back(std::make_pair(CString("ToStringTagSymbol"), GetToStringTagSymbol().GetTaggedValue())); + vec.emplace_back(std::make_pair(CString("IteratorSymbol"), GetIteratorSymbol().GetTaggedValue())); + vec.emplace_back(std::make_pair(CString("MatchSymbol"), GetMatchSymbol().GetTaggedValue())); + vec.emplace_back(std::make_pair(CString("ReplaceSymbol"), GetReplaceSymbol().GetTaggedValue())); + vec.emplace_back(std::make_pair(CString("SearchSymbol"), GetSearchSymbol().GetTaggedValue())); + vec.emplace_back(std::make_pair(CString("SpeciesSymbol"), GetSpeciesSymbol().GetTaggedValue())); + vec.emplace_back(std::make_pair(CString("SplitSymbol"), GetSplitSymbol().GetTaggedValue())); + vec.emplace_back(std::make_pair(CString("ToPrimitiveSymbol"), GetToPrimitiveSymbol().GetTaggedValue())); + vec.emplace_back(std::make_pair(CString("UnscopablesSymbol"), GetUnscopablesSymbol().GetTaggedValue())); + vec.emplace_back(std::make_pair(CString("HoleySymbol"), GetHoleySymbol().GetTaggedValue())); + vec.emplace_back(std::make_pair(CString("ConstructorString"), globalConst->GetConstructorString())); + vec.emplace_back(std::make_pair(CString("IteratorPrototype"), GetIteratorPrototype().GetTaggedValue())); + vec.emplace_back(std::make_pair(CString("ForinIteratorPrototype"), GetForinIteratorPrototype().GetTaggedValue())); + vec.emplace_back(std::make_pair(CString("StringIterator"), GetStringIterator().GetTaggedValue())); + vec.emplace_back(std::make_pair(CString("MapIteratorPrototype"), GetMapIteratorPrototype().GetTaggedValue())); + vec.emplace_back(std::make_pair(CString("SetIteratorPrototype"), GetSetIteratorPrototype().GetTaggedValue())); + vec.emplace_back(std::make_pair(CString("ArrayIteratorPrototype"), GetArrayIteratorPrototype().GetTaggedValue())); + vec.emplace_back(std::make_pair(CString("StringIteratorPrototype"), GetStringIteratorPrototype().GetTaggedValue())); + vec.emplace_back(std::make_pair(CString("LengthString"), globalConst->GetLengthString())); + vec.emplace_back(std::make_pair(CString("ValueString"), globalConst->GetValueString())); + vec.emplace_back(std::make_pair(CString("WritableString"), globalConst->GetWritableString())); + vec.emplace_back(std::make_pair(CString("GetString"), globalConst->GetGetString())); + vec.emplace_back(std::make_pair(CString("SetString"), globalConst->GetSetString())); + vec.emplace_back(std::make_pair(CString("EnumerableString"), globalConst->GetEnumerableString())); + vec.emplace_back(std::make_pair(CString("ConfigurableString"), globalConst->GetConfigurableString())); + vec.emplace_back(std::make_pair(CString("NameString"), globalConst->GetNameString())); + vec.emplace_back(std::make_pair(CString("ValueOfString"), globalConst->GetValueOfString())); + vec.emplace_back(std::make_pair(CString("ToStringString"), globalConst->GetToStringString())); + vec.emplace_back(std::make_pair(CString("ToLocaleStringString"), globalConst->GetToLocaleStringString())); + vec.emplace_back(std::make_pair(CString("UndefinedString"), globalConst->GetUndefinedString())); + vec.emplace_back(std::make_pair(CString("NullString"), globalConst->GetNullString())); + vec.emplace_back(std::make_pair(CString("TrueString"), globalConst->GetTrueString())); + vec.emplace_back(std::make_pair(CString("FalseString"), globalConst->GetFalseString())); + vec.emplace_back(std::make_pair(CString("RegisterSymbols"), GetRegisterSymbols().GetTaggedValue())); + vec.emplace_back(std::make_pair(CString("ThrowTypeError"), GetThrowTypeError().GetTaggedValue())); + vec.emplace_back(std::make_pair(CString("GetPrototypeOfString"), globalConst->GetGetPrototypeOfString())); + vec.emplace_back(std::make_pair(CString("SetPrototypeOfString"), globalConst->GetSetPrototypeOfString())); + vec.emplace_back(std::make_pair(CString("IsExtensibleString"), globalConst->GetIsExtensibleString())); + vec.emplace_back(std::make_pair(CString("PreventExtensionsString"), globalConst->GetPreventExtensionsString())); + vec.emplace_back( + std::make_pair(CString("GetOwnPropertyDescriptorString"), globalConst->GetGetOwnPropertyDescriptorString())); + vec.emplace_back(std::make_pair(CString("DefinePropertyString"), globalConst->GetDefinePropertyString())); + vec.emplace_back(std::make_pair(CString("HasString"), globalConst->GetHasString())); + vec.emplace_back(std::make_pair(CString("DeletePropertyString"), globalConst->GetDeletePropertyString())); + vec.emplace_back(std::make_pair(CString("EnumerateString"), globalConst->GetEnumerateString())); + vec.emplace_back(std::make_pair(CString("OwnKeysString"), globalConst->GetOwnKeysString())); + vec.emplace_back(std::make_pair(CString("ApplyString"), globalConst->GetApplyString())); + vec.emplace_back(std::make_pair(CString("ProxyString"), globalConst->GetProxyString())); + vec.emplace_back(std::make_pair(CString("RevokeString"), globalConst->GetRevokeString())); + vec.emplace_back(std::make_pair(CString("ProxyConstructString"), globalConst->GetProxyConstructString())); + vec.emplace_back(std::make_pair(CString("ProxyCallString"), globalConst->GetProxyCallString())); + vec.emplace_back(std::make_pair(CString("DoneString"), globalConst->GetDoneString())); + vec.emplace_back(std::make_pair(CString("NegativeZeroString"), globalConst->GetNegativeZeroString())); + vec.emplace_back(std::make_pair(CString("NextString"), globalConst->GetNextString())); + vec.emplace_back(std::make_pair(CString("PromiseThenString"), globalConst->GetPromiseThenString())); + vec.emplace_back(std::make_pair(CString("PromiseFunction"), GetPromiseFunction().GetTaggedValue())); + vec.emplace_back(std::make_pair(CString("PromiseReactionJob"), GetPromiseReactionJob().GetTaggedValue())); + vec.emplace_back( + std::make_pair(CString("PromiseResolveThenableJob"), GetPromiseResolveThenableJob().GetTaggedValue())); + vec.emplace_back(std::make_pair(CString("ScriptJobString"), globalConst->GetScriptJobString())); + vec.emplace_back(std::make_pair(CString("PromiseString"), globalConst->GetPromiseString())); + vec.emplace_back(std::make_pair(CString("IdentityString"), globalConst->GetIdentityString())); + vec.emplace_back(std::make_pair(CString("AsyncFunctionString"), globalConst->GetAsyncFunctionString())); + vec.emplace_back(std::make_pair(CString("ThrowerString"), globalConst->GetThrowerString())); + vec.emplace_back(std::make_pair(CString("Undefined"), globalConst->GetUndefined())); +} + +void JSDataView::DumpForSnapshot([[maybe_unused]] JSThread *thread, + std::vector> &vec) const +{ + vec.emplace_back(std::make_pair(CString("data-view"), GetDataView())); + vec.emplace_back(std::make_pair(CString("buffer"), GetViewedArrayBuffer())); + vec.emplace_back(std::make_pair(CString("byte-length"), GetByteLength())); + vec.emplace_back(std::make_pair(CString("byte-offset"), GetByteOffset())); +} + +void JSArrayBuffer::DumpForSnapshot([[maybe_unused]] JSThread *thread, + std::vector> &vec) const +{ + vec.emplace_back(std::make_pair(CString("byte-length"), GetArrayBufferByteLength())); + vec.emplace_back(std::make_pair(CString("buffer-data"), GetArrayBufferData())); + vec.emplace_back(std::make_pair(CString("shared"), GetShared())); +} + +void PromiseReaction::DumpForSnapshot([[maybe_unused]] JSThread *thread, + std::vector> &vec) const +{ + vec.emplace_back(std::make_pair(CString("promise-capability"), GetPromiseCapability())); + vec.emplace_back(std::make_pair(CString("type"), GetType())); + vec.emplace_back(std::make_pair(CString("handler"), GetHandler())); +} + +void PromiseCapability::DumpForSnapshot([[maybe_unused]] JSThread *thread, + std::vector> &vec) const +{ + vec.emplace_back(std::make_pair(CString("promise"), GetPromise())); + vec.emplace_back(std::make_pair(CString("resolve"), GetResolve())); + vec.emplace_back(std::make_pair(CString("reject"), GetReject())); +} + +void PromiseIteratorRecord::DumpForSnapshot([[maybe_unused]] JSThread *thread, + std::vector> &vec) const +{ + vec.emplace_back(std::make_pair(CString("iterator"), GetIterator())); + vec.emplace_back(std::make_pair(CString("done"), GetDone())); +} + +void PromiseRecord::DumpForSnapshot([[maybe_unused]] JSThread *thread, + std::vector> &vec) const +{ + vec.emplace_back(std::make_pair(CString("value"), GetValue())); +} + +void ResolvingFunctionsRecord::DumpForSnapshot([[maybe_unused]] JSThread *thread, + std::vector> &vec) const +{ + vec.emplace_back(std::make_pair(CString("resolve-function"), GetResolveFunction())); + vec.emplace_back(std::make_pair(CString("reject-function"), GetRejectFunction())); +} + +void JSPromise::DumpForSnapshot([[maybe_unused]] JSThread *thread, + std::vector> &vec) const +{ + vec.emplace_back(std::make_pair(CString("promise-state"), GetPromiseState())); + vec.emplace_back(std::make_pair(CString("promise-result"), GetPromiseResult())); + vec.emplace_back(std::make_pair(CString("promise-fulfill-reactions"), GetPromiseFulfillReactions())); + vec.emplace_back(std::make_pair(CString("promise-reject-reactions"), GetPromiseRejectReactions())); + vec.emplace_back(std::make_pair(CString("promise-is-handled"), GetPromiseIsHandled())); + JSObject::DumpForSnapshot(thread, vec); +} + +void JSPromiseReactionsFunction::DumpForSnapshot([[maybe_unused]] JSThread *thread, + std::vector> &vec) const +{ + vec.emplace_back(std::make_pair(CString("promise"), GetPromise())); + vec.emplace_back(std::make_pair(CString("already-resolved"), GetAlreadyResolved())); + JSObject::DumpForSnapshot(thread, vec); +} + +void JSPromiseExecutorFunction::DumpForSnapshot([[maybe_unused]] JSThread *thread, + std::vector> &vec) const +{ + vec.emplace_back(std::make_pair(CString("capability"), GetCapability())); + JSObject::DumpForSnapshot(thread, vec); +} + +void JSPromiseAllResolveElementFunction::DumpForSnapshot([[maybe_unused]] JSThread *thread, + std::vector> &vec) const +{ + vec.emplace_back(std::make_pair(CString("index"), GetIndex())); + vec.emplace_back(std::make_pair(CString("values"), GetValues())); + vec.emplace_back(std::make_pair(CString("capabilities"), GetCapabilities())); + vec.emplace_back(std::make_pair(CString("remaining-elements"), GetRemainingElements())); + vec.emplace_back(std::make_pair(CString("already-called"), GetAlreadyCalled())); + JSObject::DumpForSnapshot(thread, vec); +} + +void MicroJobQueue::DumpForSnapshot([[maybe_unused]] JSThread *thread, + std::vector> &vec) const +{ + vec.emplace_back(std::make_pair(CString("promise-job-queue"), GetPromiseJobQueue())); + vec.emplace_back(std::make_pair(CString("script-job-queue"), GetScriptJobQueue())); +} + +void PendingJob::DumpForSnapshot([[maybe_unused]] JSThread *thread, + std::vector> &vec) const +{ + vec.emplace_back(std::make_pair(CString("job"), GetJob())); + vec.emplace_back(std::make_pair(CString("arguments"), GetArguments())); +} + +void CompletionRecord::DumpForSnapshot([[maybe_unused]] JSThread *thread, + std::vector> &vec) const +{ + vec.emplace_back(std::make_pair(CString("type"), GetType())); + vec.emplace_back(std::make_pair(CString("value"), GetValue())); +} + +void JSProxyRevocFunction::DumpForSnapshot([[maybe_unused]] JSThread *thread, + std::vector> &vec) const +{ + vec.emplace_back(std::make_pair(CString("RevocableProxy"), GetRevocableProxy())); +} + +void JSAsyncFunction::DumpForSnapshot([[maybe_unused]] JSThread *thread, + std::vector> &vec) const +{ + JSFunction::DumpForSnapshot(thread, vec); +} + +void JSAsyncGeneratorResolveNextFunction::DumpForSnapshot([[maybe_unused]] JSThread *thread, + std::vector> &vec) const +{ + vec.emplace_back(std::make_pair(CString("AsyncGenerator"), GetAsyncGenerator())); +} + +void JSAsyncFromSyncIteratorValueUnwrapFunction::DumpForSnapshot( + [[maybe_unused]] JSThread *thread, std::vector> &vec) const +{ + vec.emplace_back(std::make_pair(CString("done"), GetDone())); +} + +void JSAsyncAwaitStatusFunction::DumpForSnapshot([[maybe_unused]] JSThread *thread, + std::vector> &vec) const +{ + vec.emplace_back(std::make_pair(CString("AsyncContext"), GetAsyncContext())); +} + +void JSGeneratorFunction::DumpForSnapshot([[maybe_unused]] JSThread *thread, + std::vector> &vec) const +{ + JSFunction::DumpForSnapshot(thread, vec); +} + +void JSIntlBoundFunction::DumpForSnapshot(JSThread *thread, std::vector> &vec) const +{ + vec.emplace_back(std::make_pair(CString("NumberFormat"), GetNumberFormat())); + vec.emplace_back(std::make_pair(CString("DateTimeFormat"), GetDateTimeFormat())); + vec.emplace_back(std::make_pair(CString("Collator"), GetCollator())); + JSObject::DumpForSnapshot(thread, vec); +} + +void PropertyBox::DumpForSnapshot([[maybe_unused]] JSThread *thread, + std::vector> &vec) const +{ + vec.emplace_back(std::make_pair(CString("Value"), GetValue())); +} + +void PrototypeHandler::DumpForSnapshot([[maybe_unused]] JSThread *thread, + std::vector> &vec) const +{ + vec.emplace_back(std::make_pair(CString("HandlerInfo"), GetHandlerInfo())); + vec.emplace_back(std::make_pair(CString("ProtoCell"), GetProtoCell())); + vec.emplace_back(std::make_pair(CString("Holder"), GetHolder())); +} + +void TransitionHandler::DumpForSnapshot([[maybe_unused]] JSThread *thread, + std::vector> &vec) const +{ + vec.emplace_back(std::make_pair(CString("HandlerInfo"), GetHandlerInfo())); + vec.emplace_back(std::make_pair(CString("TransitionHClass"), GetTransitionHClass())); +} + +void JSRealm::DumpForSnapshot([[maybe_unused]] JSThread *thread, + std::vector> &vec) const +{ + vec.emplace_back(std::make_pair(CString("Value"), GetValue())); + vec.emplace_back(std::make_pair(CString("GLobalEnv"), GetGlobalEnv())); + JSObject::DumpForSnapshot(thread, vec); +} + +void JSIntl::DumpForSnapshot(JSThread *thread, std::vector> &vec) const +{ + vec.emplace_back(std::make_pair(CString("FallbackSymbol"), GetFallbackSymbol())); + JSObject::DumpForSnapshot(thread, vec); +} + +void JSLocale::DumpForSnapshot(JSThread *thread, std::vector> &vec) const +{ + vec.emplace_back(std::make_pair(CString("IcuField"), GetIcuField())); + JSObject::DumpForSnapshot(thread, vec); +} + +void JSDateTimeFormat::DumpForSnapshot(JSThread *thread, std::vector> &vec) const +{ + vec.emplace_back(std::make_pair(CString("Locale"), GetLocale())); + vec.emplace_back(std::make_pair(CString("Calendar"), GetCalendar())); + vec.emplace_back(std::make_pair(CString("NumberingSystem"), GetNumberingSystem())); + vec.emplace_back(std::make_pair(CString("TimeZone"), GetTimeZone())); + vec.emplace_back(std::make_pair(CString("HourCycle"), GetHourCycle())); + vec.emplace_back(std::make_pair(CString("LocaleIcu"), GetLocaleIcu())); + vec.emplace_back(std::make_pair(CString("SimpleDateTimeFormatIcu"), GetSimpleDateTimeFormatIcu())); + vec.emplace_back(std::make_pair(CString("Iso8601"), GetIso8601())); + vec.emplace_back(std::make_pair(CString("DateStyle"), GetDateStyle())); + vec.emplace_back(std::make_pair(CString("TimeStyle"), GetTimeStyle())); + vec.emplace_back(std::make_pair(CString("BoundFormat"), GetBoundFormat())); + JSObject::DumpForSnapshot(thread, vec); +} + +void JSRelativeTimeFormat::DumpForSnapshot(JSThread *thread, std::vector> &vec) const +{ + vec.emplace_back(std::make_pair(CString("Locale"), GetLocale())); + vec.emplace_back(std::make_pair(CString("InitializedRelativeTimeFormat"), GetInitializedRelativeTimeFormat())); + vec.emplace_back(std::make_pair(CString("NumberingSystem"), GetNumberingSystem())); + vec.emplace_back(std::make_pair(CString("Style"), GetStyle())); + vec.emplace_back(std::make_pair(CString("Numeric"), GetNumeric())); + vec.emplace_back(std::make_pair(CString("AvailableLocales"), GetAvailableLocales())); + vec.emplace_back(std::make_pair(CString("IcuField"), GetIcuField())); + JSObject::DumpForSnapshot(thread, vec); +} + +void JSNumberFormat::DumpForSnapshot(JSThread *thread, std::vector> &vec) const +{ + vec.emplace_back(std::make_pair(CString("Locale"), GetLocale())); + vec.emplace_back(std::make_pair(CString("NumberingSystem"), GetNumberingSystem())); + vec.emplace_back(std::make_pair(CString("Style"), GetStyle())); + vec.emplace_back(std::make_pair(CString("Currency"), GetCurrency())); + vec.emplace_back(std::make_pair(CString("CurrencyDisplay"), GetCurrencyDisplay())); + vec.emplace_back(std::make_pair(CString("CurrencySign"), GetCurrencySign())); + vec.emplace_back(std::make_pair(CString("Unit"), GetUnit())); + vec.emplace_back(std::make_pair(CString("UnitDisplay"), GetUnitDisplay())); + vec.emplace_back(std::make_pair(CString("MinimumIntegerDigits"), GetMinimumIntegerDigits())); + vec.emplace_back(std::make_pair(CString("MinimumFractionDigits"), GetMinimumFractionDigits())); + vec.emplace_back(std::make_pair(CString("MaximumFractionDigits"), GetMaximumFractionDigits())); + vec.emplace_back(std::make_pair(CString("MinimumSignificantDigits"), GetMinimumSignificantDigits())); + vec.emplace_back(std::make_pair(CString("MaximumSignificantDigits"), GetMaximumSignificantDigits())); + vec.emplace_back(std::make_pair(CString("UseGrouping"), GetUseGrouping())); + vec.emplace_back(std::make_pair(CString("RoundingType"), GetRoundingType())); + vec.emplace_back(std::make_pair(CString("Notation"), GetNotation())); + vec.emplace_back(std::make_pair(CString("CompactDisplay"), GetCompactDisplay())); + vec.emplace_back(std::make_pair(CString("SignDisplay"), GetSignDisplay())); + vec.emplace_back(std::make_pair(CString("BoundFormat"), GetBoundFormat())); + vec.emplace_back(std::make_pair(CString("IcuField"), GetIcuField())); + JSObject::DumpForSnapshot(thread, vec); +} + +void JSCollator::DumpForSnapshot(JSThread *thread, std::vector> &vec) const +{ + vec.emplace_back(std::make_pair(CString("IcuField"), GetIcuField())); + vec.emplace_back(std::make_pair(CString("Locale"), GetLocale())); + vec.emplace_back(std::make_pair(CString("Usage"), GetUsage())); + vec.emplace_back(std::make_pair(CString("Sensitivity"), GetSensitivity())); + vec.emplace_back(std::make_pair(CString("IgnorePunctuation"), GetIgnorePunctuation())); + vec.emplace_back(std::make_pair(CString("Collation"), GetCollation())); + vec.emplace_back(std::make_pair(CString("Numeric"), GetNumeric())); + vec.emplace_back(std::make_pair(CString("CaseFirst"), GetCaseFirst())); + vec.emplace_back(std::make_pair(CString("BoundCompare"), GetBoundCompare())); + JSObject::DumpForSnapshot(thread, vec); +} + +void JSPluralRules::DumpForSnapshot(JSThread *thread, std::vector> &vec) const +{ + vec.emplace_back(std::make_pair(CString("Locale"), GetLocale())); + vec.emplace_back(std::make_pair(CString("InitializedPluralRules"), GetInitializedPluralRules())); + vec.emplace_back(std::make_pair(CString("Type"), GetType())); + vec.emplace_back(std::make_pair(CString("MinimumIntegerDigits"), GetMinimumIntegerDigits())); + vec.emplace_back(std::make_pair(CString("MinimumFractionDigits"), GetMinimumFractionDigits())); + vec.emplace_back(std::make_pair(CString("MaximumFractionDigits"), GetMaximumFractionDigits())); + vec.emplace_back(std::make_pair(CString("MinimumSignificantDigits"), GetMinimumSignificantDigits())); + vec.emplace_back(std::make_pair(CString("MaximumSignificantDigits"), GetMaximumSignificantDigits())); + vec.emplace_back(std::make_pair(CString("RoundingType"), GetRoundingType())); + vec.emplace_back(std::make_pair(CString("IcuPR"), GetIcuPR())); + vec.emplace_back(std::make_pair(CString("IcuNF"), GetIcuNF())); + JSObject::DumpForSnapshot(thread, vec); +} + +void JSGeneratorObject::DumpForSnapshot(JSThread *thread, std::vector> &vec) const +{ + vec.emplace_back(std::make_pair(CString("GeneratorState"), GetGeneratorState())); + vec.emplace_back(std::make_pair(CString("GeneratorContext"), GetGeneratorContext())); + vec.emplace_back(std::make_pair(CString("ResumeResult"), GetResumeResult())); + vec.emplace_back(std::make_pair(CString("ResumeMode"), GetResumeMode())); + JSObject::DumpForSnapshot(thread, vec); +} + +void JSAsyncGeneratorFunction::DumpForSnapshot([[maybe_unused]] JSThread *thread, + std::vector> &vec) const +{ + JSFunction::DumpForSnapshot(thread, vec); +} + +void JSAsyncFuncObject::DumpForSnapshot([[maybe_unused]] JSThread *thread, + std::vector> &vec) const +{ + vec.emplace_back(std::make_pair(CString("Promise"), GetPromise())); +} + +void GeneratorContext::DumpForSnapshot([[maybe_unused]] JSThread *thread, + std::vector> &vec) const +{ + vec.emplace_back(std::make_pair(CString("RegsArray"), GetRegsArray())); + vec.emplace_back(std::make_pair(CString("Method"), GetMethod())); + vec.emplace_back(std::make_pair(CString("Acc"), GetAcc())); + vec.emplace_back(std::make_pair(CString("NRegs"), GetNRegs())); + vec.emplace_back(std::make_pair(CString("BCOffset"), GetBCOffset())); + vec.emplace_back(std::make_pair(CString("GeneratorObject"), GetGeneratorObject())); + vec.emplace_back(std::make_pair(CString("LexicalEnv"), GetLexicalEnv())); +} + +void ProtoChangeMarker::DumpForSnapshot([[maybe_unused]] JSThread *thread, + std::vector> &vec) const +{ + vec.emplace_back(std::make_pair(CString("Promise"), JSTaggedValue(GetHasChanged()))); +} + +void ProtoChangeDetails::DumpForSnapshot([[maybe_unused]] JSThread *thread, + std::vector> &vec) const +{ + vec.emplace_back(std::make_pair(CString("ChangeListener"), GetChangeListener())); + vec.emplace_back(std::make_pair(CString("RegisterIndex"), GetRegisterIndex())); +} + +void LexicalFunction::DumpForSnapshot([[maybe_unused]] JSThread *thread, + std::vector> &vec) const +{ + vec.emplace_back(std::make_pair(CString("Name"), GetName())); + vec.emplace_back(std::make_pair(CString("NumberVRegs"), GetNumberVRegs())); + vec.emplace_back(std::make_pair(CString("NumberICSlots"), GetNumberICSlots())); + vec.emplace_back(std::make_pair(CString("Bytecode"), GetBytecode())); + vec.emplace_back(std::make_pair(CString("Program"), GetProgram())); +} + +void JSFunctionExtraInfo::DumpForSnapshot([[maybe_unused]] JSThread *thread, + std::vector> &vec) const +{ + vec.emplace_back(std::make_pair(CString("Callback"), GetCallback())); + vec.emplace_back(std::make_pair(CString("Data"), GetData())); +} + +void MachineCode::DumpForSnapshot([[maybe_unused]] JSThread *thread, + std::vector> &vec) const +{ + vec.emplace_back(std::make_pair(CString("InstructionSizeInBytes"), GetInstructionSizeInBytes())); +} + +void EcmaModule::DumpForSnapshot([[maybe_unused]] JSThread *thread, + std::vector> &vec) const +{ + vec.emplace_back(std::make_pair(CString("NameDictionary"), GetNameDictionary())); +} + +void ClassInfoExtractor::DumpForSnapshot([[maybe_unused]] JSThread *thread, + std::vector> &vec) const +{ + vec.emplace_back(std::make_pair(CString("PrototypeHClass"), GetPrototypeHClass())); + vec.emplace_back(std::make_pair(CString("NonStaticKeys"), GetNonStaticKeys())); + vec.emplace_back(std::make_pair(CString("NonStaticProperties"), GetNonStaticProperties())); + vec.emplace_back(std::make_pair(CString("NonStaticElements"), GetNonStaticElements())); + vec.emplace_back(std::make_pair(CString("ConstructorHClass"), GetConstructorHClass())); + vec.emplace_back(std::make_pair(CString("StaticKeys"), GetStaticKeys())); + vec.emplace_back(std::make_pair(CString("StaticProperties"), GetStaticProperties())); + vec.emplace_back(std::make_pair(CString("StaticElements"), GetStaticElements())); +} +} // namespace panda::ecmascript diff --git a/runtime/ecma_class_linker_extension.cpp b/runtime/ecma_class_linker_extension.cpp new file mode 100644 index 000000000..68ea4536b --- /dev/null +++ b/runtime/ecma_class_linker_extension.cpp @@ -0,0 +1,149 @@ +/* + * Copyright (c) 2021-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 "plugins/ecmascript/runtime/ecma_class_linker_extension.h" +#include "plugins/ecmascript/runtime/mem/mem_manager-inl.h" +#include "plugins/ecmascript/runtime/ecma_string.h" +#include "plugins/ecmascript/runtime/object_factory.h" +#include "runtime/include/class_linker-inl.h" +#include "runtime/include/coretypes/class.h" + +namespace panda::ecmascript { +using SourceLang = panda_file::SourceLang; + +bool EcmaClassLinkerExtension::InitializeImpl([[maybe_unused]] bool cmpStrEnabled) +{ + return true; +} + +EcmaClassLinkerExtension::~EcmaClassLinkerExtension() +{ + if (!IsInitialized()) { + return; + } + FreeLoadedClasses(); +} + +void EcmaClassLinkerExtension::InitClasses(EcmaVM *vm) +{ + ASSERT(IsInitialized()); + vm_ = vm; + LanguageContext ctx = Runtime::GetCurrent()->GetLanguageContext(GetLanguage()); + [[maybe_unused]] EcmaHandleScope scope(vm->GetJSThread()); + + auto *classClass = NewClass(ctx.GetClassClassDescriptor(), 0, 0, GetClassSize(ClassRoot::CLASS)); + if (classClass == nullptr) { + return; + } + classClass->SetObjectSize(ObjectHeader::ObjectHeaderSize()); + classClass->SetState(Class::State::LOADED); + Runtime::GetCurrent()->GetClassLinker()->AddClassRoot(ClassRoot::CLASS, classClass); + + auto *objClass = NewClass(ctx.GetObjectClassDescriptor(), 0, 0, GetClassSize(ClassRoot::OBJECT)); + if (objClass == nullptr) { + return; + } + objClass->SetObjectSize(TaggedObject::TaggedObjectSize()); + objClass->SetState(Class::State::LOADED); + Runtime::GetCurrent()->GetClassLinker()->AddClassRoot(ClassRoot::OBJECT, objClass); +} + +ClassLinkerContext *EcmaClassLinkerExtension::CreateApplicationClassLinkerContext(const PandaVector &path) +{ + PandaVector app_files; + app_files.reserve(path.size()); + for (auto &p : path) { + auto pf = panda_file::OpenPandaFileOrZip(p, panda_file::File::READ_WRITE); + if (pf == nullptr) { + return nullptr; + } + app_files.push_back(std::move(pf)); + } + return ClassLinkerExtension::CreateApplicationClassLinkerContext(std::move(app_files)); +} + +Class *EcmaClassLinkerExtension::NewClass(const uint8_t *descriptor, size_t vtableSize, size_t imtSize, + size_t class_size) +{ + ASSERT(IsInitialized()); + if (vm_ == nullptr) { + return nullptr; + } + // Create an instance of panda::Class. Since all objects in JS runtime must have a dynamic class + // wrap the instance of panda::Class into an object with a dynamic class and mark panda::Class's + // data as native data. + // + // +----------+ + // | JSHClass | + // +----------+ + // | HClass | <----+ + // +----------+ | + // | + // Dynamic object | + // +--------------+ | classWord_ + // +-> | ObjetcHeader | -+ + // managed_object_ | +--------------+ + // +- | panda::Class | <- native data + // +--------------+ + [[maybe_unused]] ecmascript::EcmaHandleScope scope(vm_->GetJSThread()); + ObjectFactory *factory = vm_->GetFactory(); + JSHandle hclass = factory->NewEcmaDynClass(nullptr, JSHClass::SIZE, JSType::HCLASS, 0, 0); + if (hclass.IsEmpty()) { + return nullptr; + } + // We must cover the whole panda::Class by the native_field_mask + ASSERT(class_size < std::numeric_limits::digits * sizeof(JSTaggedType)); + JSHClass *hclass_ptr = reinterpret_cast(hclass.GetTaggedValue().GetTaggedObject()); + hclass_ptr->SetObjectSize(ObjectHeader::ObjectHeaderSize() + class_size); + hclass_ptr->GetHClass()->SetNativeFieldMask(std::numeric_limits::max()); + // All fields must be set before call SetClass to make TSAN happy. + // The intstance of this HClass may be read by update remset thread. + // The thread skips the object if tits class is null. + hclass_ptr->SetClass(hclass_ptr); + + JSHandle klass_obj(vm_->GetJSThread(), factory->NewNonMovableDynObject(hclass)); + auto *klass = reinterpret_cast( + ToUintPtr(klass_obj.GetObject()) + ObjectHeader::ObjectHeaderSize()); + new (klass) + Class(descriptor, panda_file::SourceLang::ECMASCRIPT, vtableSize, imtSize, class_size); + klass->SetManagedObject(klass_obj.GetObject()); + AddCreatedClass(klass); + return klass; +} + +size_t EcmaClassLinkerExtension::GetClassSize([[maybe_unused]] ClassRoot root) +{ + ASSERT(IsInitialized()); + // Used only in test scenarios. + return sizeof(Class); +} + +size_t EcmaClassLinkerExtension::GetArrayClassSize() +{ + ASSERT(IsInitialized()); + + return GetClassSize(ClassRoot::OBJECT); +} + +void EcmaClassLinkerExtension::FreeClass([[maybe_unused]] Class *klass) +{ + ASSERT(IsInitialized()); + if (vm_ == nullptr) { + return; + } + + RemoveCreatedClass(klass); +} +} // namespace panda::ecmascript diff --git a/runtime/ecma_class_linker_extension.h b/runtime/ecma_class_linker_extension.h new file mode 100644 index 000000000..aee9dcac8 --- /dev/null +++ b/runtime/ecma_class_linker_extension.h @@ -0,0 +1,109 @@ +/* + * Copyright (c) 2021-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_ECMA_CLASS_LINKER_EXTENSION_H +#define ECMASCRIPT_ECMA_CLASS_LINKER_EXTENSION_H + +#include "libpandafile/file_items.h" +#include "include/class_linker.h" +#include "include/class_linker_extension.h" + +namespace panda::coretypes { +class Class; // NOLINT(bugprone-forward-declaration-namespace) +} // namespace panda::coretypes + +namespace panda { +namespace ecmascript { +class EcmaVM; +class EcmaClassLinkerExtension : public ClassLinkerExtension { +public: + static EcmaClassLinkerExtension *Cast(ClassLinkerExtension *object) + { + return reinterpret_cast(object); + } + + EcmaClassLinkerExtension() : ClassLinkerExtension(panda_file::SourceLang::ECMASCRIPT) {} + + ~EcmaClassLinkerExtension() override; + + NO_COPY_SEMANTIC(EcmaClassLinkerExtension); + NO_MOVE_SEMANTIC(EcmaClassLinkerExtension); + + void InitClasses(EcmaVM *vm); + + ClassLinkerContext *CreateApplicationClassLinkerContext(const PandaVector &path) override; + + bool CanThrowException([[maybe_unused]] const Method *method) const override + { + return true; + } + + void InitializeArrayClass([[maybe_unused]] Class *array_class, [[maybe_unused]] Class *componentClass) override {} + + void InitializePrimitiveClass([[maybe_unused]] Class *primitiveClass) override {} + + size_t GetClassVTableSize([[maybe_unused]] ClassRoot root) override + { + return 0; + } + + size_t GetClassIMTSize([[maybe_unused]] ClassRoot root) override + { + return 0; + } + + size_t GetClassSize([[maybe_unused]] ClassRoot root) override; + + size_t GetArrayClassVTableSize() override + { + return 0; + } + + size_t GetArrayClassIMTSize() override + { + return 0; + } + + size_t GetArrayClassSize() override; + Class *CreateClass([[maybe_unused]] const uint8_t *descriptor, [[maybe_unused]] size_t vtableSize, size_t imtSize, + [[maybe_unused]] size_t size) override + { + return NewClass(descriptor, vtableSize, imtSize, size); + } + + void InitializeClass([[maybe_unused]] Class *klass) override {} + + const void *GetNativeEntryPointFor([[maybe_unused]] Method *method) const override + { + return nullptr; + } + + ClassLinkerErrorHandler *GetErrorHandler() override + { + return nullptr; + } + + void FreeClass(Class *klass) override; + +private: + bool InitializeImpl(bool cmpStrEnabled) override; + Class *NewClass(const uint8_t *descriptor, size_t vtableSize, size_t imtSize, size_t size); + + EcmaVM *vm_{nullptr}; +}; +} // namespace ecmascript +} // namespace panda + +#endif // ECMASCRIPT_ECMA_CLASS_LINKER_EXTENSION_H diff --git a/runtime/ecma_exceptions.cpp b/runtime/ecma_exceptions.cpp new file mode 100644 index 000000000..a1235f48b --- /dev/null +++ b/runtime/ecma_exceptions.cpp @@ -0,0 +1,45 @@ +/* + * Copyright (c) 2021-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 "plugins/ecmascript/runtime/ecma_exceptions.h" +#include "plugins/ecmascript/runtime/base/error_helper.h" +#include "plugins/ecmascript/runtime/js_function.h" +#include "plugins/ecmascript/runtime/js_object.h" +#include "plugins/ecmascript/runtime/js_thread.h" +#include "plugins/ecmascript/runtime/object_factory.h" + +namespace panda::ecmascript { +void SetException(JSThread *thread, JSObject *error) +{ + if (!thread->HasPendingException()) { + thread->SetException(JSTaggedValue(error)); + } +} + +void ThrowException(JSThread *thread, const char *name, const char *msg) +{ + auto jsThread = static_cast(thread); + ObjectFactory *factory = jsThread->GetEcmaVM()->GetFactory(); + if (std::strcmp(name, REFERENCE_ERROR_STRING) == 0) { + SetException(jsThread, *factory->GetJSError(base::ErrorType::REFERENCE_ERROR, msg)); + return; + } + + if (std::strcmp(name, TYPE_ERROR_STRING) == 0) { + SetException(jsThread, *factory->GetJSError(base::ErrorType::TYPE_ERROR, msg)); + return; + } +} +} // namespace panda::ecmascript diff --git a/runtime/ecma_exceptions.h b/runtime/ecma_exceptions.h new file mode 100644 index 000000000..64435a49a --- /dev/null +++ b/runtime/ecma_exceptions.h @@ -0,0 +1,29 @@ +/* + * Copyright (c) 2021-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_ECMA_EXCEPTIONS_H +#define ECMASCRIPT_ECMA_EXCEPTIONS_H + +/* Do not add ecma special header's here */ +#include "plugins/ecmascript/runtime/js_thread.h" + +namespace panda::ecmascript { +static constexpr const char *TYPE_ERROR_STRING = "Lecma/TypedError;"; +static constexpr const char *REFERENCE_ERROR_STRING = "Lecma/ReferenceError;"; + +void ThrowException(JSThread *thread, const char *name, const char *msg); +} // namespace panda::ecmascript + +#endif // ECMASCRIPT_ECMA_EXCEPTIONS_H diff --git a/runtime/ecma_global_storage-inl.h b/runtime/ecma_global_storage-inl.h new file mode 100644 index 000000000..258aa3b6b --- /dev/null +++ b/runtime/ecma_global_storage-inl.h @@ -0,0 +1,211 @@ +/* + * Copyright (c) 2021-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_ECMA_GLOABL_STORAGE_INL_H +#define ECMASCRIPT_ECMA_GLOABL_STORAGE_INL_H + +#include "plugins/ecmascript/runtime/ecma_global_storage.h" +#include "plugins/ecmascript/runtime/mem/chunk.h" + +namespace panda::ecmascript { +inline EcmaGlobalStorage::NodeList *EcmaGlobalStorage::NodeList::NodeToNodeList(EcmaGlobalStorage::Node *node) +{ + uintptr_t ptr = ToUintPtr(node) - node->GetIndex() * sizeof(EcmaGlobalStorage::Node); + return reinterpret_cast(ptr); +} + +EcmaGlobalStorage::Node *EcmaGlobalStorage::NodeList::NewNode(JSTaggedType value) +{ + if (IsFull()) { + return nullptr; + } + Node *node = &nodeList_[index_++]; + node->SetPrev(nullptr); + node->SetNext(usedList_); + node->SetObject(value); + node->SetFree(false); + + if (usedList_ != nullptr) { + usedList_->SetPrev(node); + } + usedList_ = node; + return node; +} + +void EcmaGlobalStorage::NodeList::FreeNode(EcmaGlobalStorage::Node *node) +{ + if (node->GetPrev() != nullptr) { + node->GetPrev()->SetNext(node->GetNext()); + } + if (node->GetNext() != nullptr) { + node->GetNext()->SetPrev(node->GetPrev()); + } + if (node == usedList_) { + usedList_ = node->GetNext(); + } + node->SetPrev(nullptr); + node->SetNext(freeList_); + node->SetObject(JSTaggedValue::Undefined().GetRawData()); + node->SetFree(true); + + if (freeList_ != nullptr) { + freeList_->SetPrev(node); + } + freeList_ = node; +} + +EcmaGlobalStorage::Node *EcmaGlobalStorage::NodeList::GetFreeNode(JSTaggedType value) +{ + Node *node = freeList_; + if (node != nullptr) { + freeList_ = node->GetNext(); + + node->SetPrev(nullptr); + node->SetNext(usedList_); + node->SetObject(value); + node->SetFree(false); + + if (usedList_ != nullptr) { + usedList_->SetPrev(node); + } + usedList_ = node; + } + return node; +} + +void EcmaGlobalStorage::NodeList::LinkTo(NodeList *prev) +{ + next_ = nullptr; + prev_ = prev; + prev_->next_ = this; +} + +void EcmaGlobalStorage::NodeList::RemoveList() +{ + if (next_ != nullptr) { + next_->SetPrev(prev_); + } + if (prev_ != nullptr) { + prev_->SetNext(next_); + } + if (freeNext_ != nullptr) { + freeNext_->SetFreePrev(freePrev_); + } + if (freePrev_ != nullptr) { + freePrev_->SetFreeNext(freeNext_); + } +} + +uintptr_t EcmaGlobalStorage::NewGlobalHandleImplement(NodeList **storage, NodeList **freeList, bool isWeak, + JSTaggedType value) +{ + if (*freeList == nullptr) { + if ((*storage)->IsFull()) { + // alloc new block + auto block = chunk_->New(isWeak); + block->LinkTo(*storage); + *storage = block; + } + Node *node = (*storage)->NewNode(value); + ASSERT(node != nullptr); + return node->GetObjectAddress(); + } + + // use node in block first + Node *node = (*storage)->NewNode(value); + if (node != nullptr) { + return node->GetObjectAddress(); + } + + // use free_list node + node = (*freeList)->GetFreeNode(value); + ASSERT(node != nullptr); + if (!(*freeList)->HasFreeNode()) { + auto next = (*freeList)->GetFreeNext(); + (*freeList)->SetFreeNext(nullptr); + (*freeList)->SetFreePrev(nullptr); + if (next != nullptr) { + next->SetFreePrev(nullptr); + } + *freeList = next; + } + return node->GetObjectAddress(); +} + +inline uintptr_t EcmaGlobalStorage::NewGlobalHandle(JSTaggedType value) +{ + return NewGlobalHandleImplement(&lastGlobalNodes_, &freeListNodes_, false, value); +} + +inline void EcmaGlobalStorage::DisposeGlobalHandle(uintptr_t nodeAddr) +{ + Node *node = reinterpret_cast(nodeAddr); + if (node->IsFree()) { + return; + } + NodeList *list = NodeList::NodeToNodeList(node); + list->FreeNode(node); + + // If NodeList has no usage node, then delete NodeList + NodeList **freeList = nullptr; + NodeList **top = nullptr; + NodeList **last = nullptr; + if (list->IsWeak()) { + freeList = &weakFreeListNodes_; + top = &topWeakGlobalNodes_; + last = &lastWeakGlobalNodes_; + } else { + freeList = &freeListNodes_; + top = &topGlobalNodes_; + last = &lastGlobalNodes_; + } + if (!list->HasUsagedNode() && (*top != *last)) { + list->RemoveList(); + if (*freeList == list) { + *freeList = list->GetNext(); + } + if (*top == list) { + *top = list->GetNext(); + } + if (*last == list) { + *last = list->GetPrev(); + } + chunk_->Delete(list); + } else { + // Add to freeList + if (list != *freeList && list->GetFreeNext() == nullptr && list->GetFreePrev() == nullptr) { + list->SetFreeNext(*freeList); + if (*freeList != nullptr) { + (*freeList)->SetFreePrev(list); + } + *freeList = list; + } + } +} + +inline uintptr_t EcmaGlobalStorage::SetWeak(uintptr_t nodeAddr) +{ + auto value = reinterpret_cast(nodeAddr)->GetObject(); + DisposeGlobalHandle(nodeAddr); + return NewGlobalHandleImplement(&lastWeakGlobalNodes_, &weakFreeListNodes_, true, value); +} + +inline bool EcmaGlobalStorage::IsWeak(uintptr_t addr) const +{ + Node *node = reinterpret_cast(addr); + NodeList *list = NodeList::NodeToNodeList(node); + return list->IsWeak(); +} +} // namespace panda::ecmascript +#endif // ECMASCRIPT_ECMA_GLOABL_STORAGE_INL_H diff --git a/runtime/ecma_global_storage.h b/runtime/ecma_global_storage.h new file mode 100644 index 000000000..3a5bd39b7 --- /dev/null +++ b/runtime/ecma_global_storage.h @@ -0,0 +1,269 @@ +/* + * Copyright (c) 2021-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_ECMA_GLOABL_STORAGE_H +#define ECMASCRIPT_ECMA_GLOABL_STORAGE_H + +#include "plugins/ecmascript/runtime/js_tagged_value.h" + +#include "plugins/ecmascript/runtime/mem/c_containers.h" +#include "plugins/ecmascript/runtime/mem/chunk.h" + +namespace panda::ecmascript { +class EcmaGlobalStorage { +public: + static const int32_t GLOBAL_BLOCK_SIZE = 256; + + explicit EcmaGlobalStorage(Chunk *chunk) : chunk_(chunk) + { + ASSERT(chunk != nullptr); + topGlobalNodes_ = lastGlobalNodes_ = chunk_->New(false); + topWeakGlobalNodes_ = lastWeakGlobalNodes_ = chunk_->New(true); + } + + ~EcmaGlobalStorage() + { + NodeList *next = topGlobalNodes_; + NodeList *current = nullptr; + while (next != nullptr) { + current = next; + next = current->GetNext(); + chunk_->Delete(current); + } + + next = topWeakGlobalNodes_; + while (next != nullptr) { + current = next; + next = current->GetNext(); + chunk_->Delete(current); + } + } + + class Node { + public: + JSTaggedType GetObject() const + { + return obj_; + } + + void SetObject(JSTaggedType obj) + { + obj_ = obj; + } + + Node *GetNext() const + { + return next_; + } + + void SetNext(Node *node) + { + next_ = node; + } + + Node *GetPrev() const + { + return prev_; + } + + void SetPrev(Node *node) + { + prev_ = node; + } + + int32_t GetIndex() + { + return index_; + } + + void SetIndex(int32_t index) + { + index_ = index; + } + + void SetFree(bool free) + { + isFree_ = free; + } + + bool IsFree() const + { + return isFree_; + } + + uintptr_t GetObjectAddress() const + { + return reinterpret_cast(&obj_); + } + + private: + JSTaggedType obj_ {}; + Node *next_ {nullptr}; + Node *prev_ {nullptr}; + int32_t index_ {-1}; + bool isFree_ {false}; + }; + + class NodeList { + public: + explicit NodeList(bool isWeak) : isWeak_(isWeak) + { + for (int i = 0; i < GLOBAL_BLOCK_SIZE; i++) { + nodeList_[i].SetIndex(i); + } + } + ~NodeList() = default; + + inline static NodeList *NodeToNodeList(Node *node); + + inline Node *NewNode(JSTaggedType value); + inline Node *GetFreeNode(JSTaggedType value); + inline void FreeNode(Node *node); + + inline void LinkTo(NodeList *prev); + inline void RemoveList(); + + inline bool IsFull() + { + return index_ >= GLOBAL_BLOCK_SIZE; + } + + inline bool IsWeak() + { + return isWeak_; + } + + inline bool HasFreeNode() + { + return freeList_ != nullptr; + } + + inline bool HasUsagedNode() + { + return !IsFull() || usedList_ != nullptr; + } + + inline void SetNext(NodeList *next) + { + next_ = next; + } + inline NodeList *GetNext() const + { + return next_; + } + + inline void SetPrev(NodeList *prev) + { + prev_ = prev; + } + inline NodeList *GetPrev() const + { + return prev_; + } + + inline void SetFreeNext(NodeList *next) + { + freeNext_ = next; + } + inline NodeList *GetFreeNext() const + { + return freeNext_; + } + + inline void SetFreePrev(NodeList *prev) + { + freePrev_ = prev; + } + inline NodeList *GetFreePrev() const + { + return freePrev_; + } + + template + inline void IterateUsageGlobal(Callback callback) + { + Node *next = usedList_; + Node *current = nullptr; + while (next != nullptr) { + current = next; + next = current->GetNext(); + ASSERT(current != next); + callback(current); + } + } + + DEFAULT_MOVE_SEMANTIC(NodeList); + DEFAULT_COPY_SEMANTIC(NodeList); + + private: + std::array nodeList_; // all + Node *freeList_ {nullptr}; // dispose node + Node *usedList_ {nullptr}; // usage node + int32_t index_ {0}; + bool isWeak_ {false}; + NodeList *next_ {nullptr}; + NodeList *prev_ {nullptr}; + NodeList *freeNext_ {nullptr}; + NodeList *freePrev_ {nullptr}; + }; + + inline uintptr_t NewGlobalHandle(JSTaggedType value); + inline void DisposeGlobalHandle(uintptr_t addr); + inline uintptr_t SetWeak(uintptr_t addr); + inline bool IsWeak(uintptr_t addr) const; + + template + void IterateUsageGlobal(Callback callback) + { + NodeList *next = topGlobalNodes_; + NodeList *current = nullptr; + while (next != nullptr) { + current = next; + next = current->GetNext(); + ASSERT(current != next); + current->IterateUsageGlobal(callback); + } + } + + template + void IterateWeakUsageGlobal(Callback callback) + { + NodeList *next = topWeakGlobalNodes_; + NodeList *current = nullptr; + while (next != nullptr) { + current = next; + next = current->GetNext(); + ASSERT(current != next); + current->IterateUsageGlobal(callback); + } + } + +private: + NO_COPY_SEMANTIC(EcmaGlobalStorage); + NO_MOVE_SEMANTIC(EcmaGlobalStorage); + + inline uintptr_t NewGlobalHandleImplement(NodeList **storage, NodeList **freeList, bool isWeak, JSTaggedType value); + + Chunk *chunk_ {nullptr}; + NodeList *topGlobalNodes_ {nullptr}; + NodeList *lastGlobalNodes_ {nullptr}; + NodeList *freeListNodes_ {nullptr}; + + NodeList *topWeakGlobalNodes_ {nullptr}; + NodeList *lastWeakGlobalNodes_ {nullptr}; + NodeList *weakFreeListNodes_ {nullptr}; +}; +} // namespace panda::ecmascript +#endif // ECMASCRIPT_ECMA_GLOABL_STORAGE_H diff --git a/runtime/ecma_handle_scope-inl.h b/runtime/ecma_handle_scope-inl.h new file mode 100644 index 000000000..a1c3c3620 --- /dev/null +++ b/runtime/ecma_handle_scope-inl.h @@ -0,0 +1,54 @@ +/* + * Copyright (c) 2021-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_HANDLE_SCOPE_INL_H +#define ECMASCRIPT_HANDLE_SCOPE_INL_H + +#include "plugins/ecmascript/runtime/ecma_handle_scope.h" +#include "plugins/ecmascript/runtime/js_thread.h" + +namespace panda::ecmascript { +inline EcmaHandleScope::EcmaHandleScope(JSThread *thread) + : thread_(thread), prevNext_(thread->handleScopeStorageNext_), prevEnd_(thread->handleScopeStorageEnd_), + prevHandleStorageIndex_(thread->currentHandleStorageIndex_) +{ + thread->HandleScopeCountAdd(); +} + +inline EcmaHandleScope::~EcmaHandleScope() +{ + thread_->HandleScopeCountDec(); + thread_->handleScopeStorageNext_ = prevNext_; + if (thread_->handleScopeStorageEnd_ != prevEnd_) { + thread_->handleScopeStorageEnd_ = prevEnd_; + thread_->ShrinkHandleStorage(prevHandleStorageIndex_); + } +} + +uintptr_t EcmaHandleScope::NewHandle(JSThread *thread, JSTaggedType value) +{ + // Each Handle must be managed by HandleScope, otherwise it may cause Handle leakage. + ASSERT(thread->handleScopeCount_ > 0); + auto result = thread->handleScopeStorageNext_; + if (result == thread->handleScopeStorageEnd_) { + result = reinterpret_cast(thread->ExpandHandleStorage()); + } + // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-pointer-arithmetic) + thread->handleScopeStorageNext_ = result + 1; + *result = value; + return reinterpret_cast(result); +} +} // namespace panda::ecmascript +#endif // ECMASCRIPT_HANDLE_SCOPE_INL_H diff --git a/runtime/ecma_handle_scope.h b/runtime/ecma_handle_scope.h new file mode 100644 index 000000000..a7e934c13 --- /dev/null +++ b/runtime/ecma_handle_scope.h @@ -0,0 +1,51 @@ +/* + * Copyright (c) 2021-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_HANDLE_SCOPE_H +#define ECMASCRIPT_HANDLE_SCOPE_H + +#include "plugins/ecmascript/runtime/js_tagged_value.h" + +namespace panda::ecmascript { +class JSThread; + +/* + * Handles are only valid within a HandleScope. When a handle is created for an object a cell is allocated in the + * current HandleScope. + */ +class EcmaHandleScope { +public: + inline explicit EcmaHandleScope(JSThread *thread); + + inline ~EcmaHandleScope(); + + static inline uintptr_t NewHandle(JSThread *thread, JSTaggedType value); + + JSThread *GetThread() const + { + return thread_; + } + +private: + JSThread *thread_; + JSTaggedType *prevNext_; + JSTaggedType *prevEnd_; + int prevHandleStorageIndex_{-1}; + + NO_COPY_SEMANTIC(EcmaHandleScope); + NO_MOVE_SEMANTIC(EcmaHandleScope); +}; +} // namespace panda::ecmascript +#endif // ECMASCRIPT_HANDLE_SCOPE_H diff --git a/runtime/ecma_language_context-inl.h b/runtime/ecma_language_context-inl.h new file mode 100644 index 000000000..954ea14de --- /dev/null +++ b/runtime/ecma_language_context-inl.h @@ -0,0 +1,30 @@ +/* + * Copyright (c) Huawei Technologies Co., Ltd. 2019-2021. All rights reserved. + * Description: ecmascript language context + */ + +#ifndef PANDA_RUNTIME_ECMASCRIPT_LANGUAGE_CONTEXT_INL_H +#define PANDA_RUNTIME_ECMASCRIPT_LANGUAGE_CONTEXT_INL_H + +#include "plugins/ecmascript/runtime/ecma_language_context.h" +#include "plugins/ecmascript/runtime/js_tagged_value-inl.h" +#include "plugins/ecmascript/runtime/interpreter/js_frame.h" + +namespace panda { +inline void EcmaLanguageContext::SetExceptionToVReg([[maybe_unused]] interpreter::AccVRegister &vreg, + [[maybe_unused]] ObjectHeader *obj) const +{ + ecmascript::JSTaggedValue value(obj); + if (value.IsObjectWrapper()) { + value = ecmascript::ObjectWrapper::Cast(value.GetTaggedObject())->GetValue(); + } + vreg.SetValue(value.GetRawData()); +} + +inline uint32_t EcmaLanguageContext::GetFrameExtSize() const +{ + return ecmascript::JSExtFrame::GetExtSize(); +} +} // namespace panda + +#endif // PANDA_RUNTIME_ECMASCRIPT_LANGUAGE_CONTEXT_INL_H diff --git a/runtime/ecma_language_context.cpp b/runtime/ecma_language_context.cpp new file mode 100644 index 000000000..33bb66e95 --- /dev/null +++ b/runtime/ecma_language_context.cpp @@ -0,0 +1,131 @@ +/* + * Copyright (c) 2021-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 "plugins/ecmascript/runtime/ecma_language_context-inl.h" +#include "plugins/ecmascript/runtime/ecma_language_context.h" + +#include "plugins/ecmascript/compiler/ecmascript_extensions/ecmascript_environment.h" +#include "plugins/ecmascript/runtime/ecma_class_linker_extension.h" +#include "plugins/ecmascript/runtime/ecma_exceptions.h" +#include "plugins/ecmascript/runtime/js_method.h" +#include "plugins/ecmascript/runtime/js_object.h" +#include "plugins/ecmascript/runtime/js_tagged_value.h" +#include "plugins/ecmascript/runtime/js_thread.h" +#include "plugins/ecmascript/runtime/object_factory.h" +#include "include/method.h" +#include "runtime/core/core_itable_builder.h" +#include "runtime/core/core_vtable_builder.h" + +namespace panda { +using ecmascript::EcmaVM; +using ecmascript::JSThread; + +std::pair EcmaLanguageContext::GetCatchMethodAndOffset(Method *method, ManagedThread *thread) const +{ + Method *catchMethod = method; + uint32_t catchOffset = 0; + auto jsThread = static_cast(thread); + for (auto stack = StackWalker::Create(thread); stack.HasFrame(); stack.NextFrame()) { + catchMethod = stack.GetMethod(); + if (catchMethod->IsNative()) { + continue; + } + catchOffset = catchMethod->FindCatchBlock(jsThread->GetException().GetTaggedObject()->ClassAddr(), + stack.GetBytecodePc()); + if (catchOffset != panda_file::INVALID_OFFSET) { + break; + } + } + + return std::make_pair(catchMethod, catchOffset); +} + +PandaVM *EcmaLanguageContext::CreateVM(Runtime *runtime, const RuntimeOptions &options) const +{ + auto ret = EcmaVM::Create(runtime, ecmascript::JSRuntimeOptions::Cast(options)); + if (ret) { + return ret.Value(); + } + return nullptr; +} + +std::unique_ptr EcmaLanguageContext::CreateClassLinkerExtension() const +{ + return std::make_unique(); +} + +PandaUniquePtr EcmaLanguageContext::CreatePtLangExt() const +{ + return PandaUniquePtr(); +} + +void EcmaLanguageContext::ThrowException(ManagedThread *thread, const uint8_t *mutf8_name, + const uint8_t *mutf8_msg) const +{ + ecmascript::ThrowException(JSThread::Cast(thread), reinterpret_cast(mutf8_name), + reinterpret_cast(mutf8_msg)); +} + +PandaUniquePtr EcmaLanguageContext::CreateITableBuilder() const +{ + return MakePandaUnique(); +} + +PandaUniquePtr EcmaLanguageContext::CreateVTableBuilder() const +{ + return MakePandaUnique(); +} + +size_t EcmaLanguageContext::GetStringSize(const ObjectHeader *string_object) const +{ + auto string = static_cast(string_object); + return string->ObjectSize(); +} + +void EcmaLanguageContext::DeoptimizeBegin(Frame *iframe, int frame_depth) const +{ + JSThread *js_thread = JSThread::GetCurrentRaw(); + + ecmascript::EcmascriptEnvironment *upper_env = nullptr; + ecmascript::EcmascriptEnvironment *cframe_env = js_thread->GetEcmascriptEnv(); + for (int i = 0; i < (-1 - frame_depth); ++i) { + upper_env = cframe_env; + cframe_env = cframe_env->GetPrevEnvironment(); + } + + ecmascript::EcmascriptEnvironment *iframe_env = ecmascript::JSExtFrame::FromFrame(iframe)->GetExtData(); + iframe_env->SetConstantPool(cframe_env->GetConstantPool()); + iframe_env->SetLexicalEnv(cframe_env->GetLexicalEnv()); + iframe_env->SetThisFunc(cframe_env->GetThisFunc()); + iframe_env->SetPrevEnvironment(cframe_env->GetPrevEnvironment()); + + if (upper_env == nullptr) { + js_thread->SetEcmascriptEnv(iframe_env); + } else { + upper_env->SetPrevEnvironment(iframe_env); + } +} + +void EcmaLanguageContext::DeoptimizeEnd() const +{ + JSThread *js_thread = JSThread::GetCurrentRaw(); + ecmascript::EcmascriptEnvironment *current_env = js_thread->GetEcmascriptEnv(); + ecmascript::EcmascriptEnvironment *prev_env = current_env->GetPrevEnvironment(); + + // Update EcmascriptEnv + js_thread->SetEcmascriptEnv(prev_env); +} + +} // namespace panda diff --git a/runtime/ecma_language_context.h b/runtime/ecma_language_context.h new file mode 100644 index 000000000..fd698bc87 --- /dev/null +++ b/runtime/ecma_language_context.h @@ -0,0 +1,308 @@ +/* + * Copyright (c) 2021-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_LANGUAGE_CONTEXT_H +#define ECMASCRIPT_LANGUAGE_CONTEXT_H + +#include "plugins/ecmascript/runtime/common.h" +#include "include/language_context.h" +#include "plugins/ecmascript/runtime/js_hclass.h" + +namespace panda { +class PUBLIC_API EcmaLanguageContext : public LanguageContextBase { +public: + EcmaLanguageContext() = default; + + DEFAULT_COPY_SEMANTIC(EcmaLanguageContext); + DEFAULT_MOVE_SEMANTIC(EcmaLanguageContext); + + ~EcmaLanguageContext() override = default; + + panda_file::SourceLang GetLanguage() const override + { + return panda_file::SourceLang::ECMASCRIPT; + } + + std::pair GetCatchMethodAndOffset(Method *method, ManagedThread *thread) const override; + + PandaVM *CreateVM(Runtime *runtime, const RuntimeOptions &options) const override; + + std::unique_ptr CreateClassLinkerExtension() const override; + + PandaUniquePtr CreatePtLangExt() const override; + + void ThrowException(ManagedThread *thread, const uint8_t *mutf8_name, const uint8_t *mutf8_msg) const override; + + coretypes::TaggedValue GetInitialTaggedValue() const override + { + UNREACHABLE(); + } + + uint64_t GetTypeTag(interpreter::TypeTag tag) const override + { + return tag; + } + + DecodedTaggedValue GetInitialDecodedValue() const override + { + return DecodedTaggedValue(coretypes::TaggedValue::VALUE_UNDEFINED, 0); + } + + DecodedTaggedValue GetDecodedTaggedValue([[maybe_unused]] const coretypes::TaggedValue &value) const override + { + UNREACHABLE(); + } + + coretypes::TaggedValue GetEncodedTaggedValue([[maybe_unused]] int64_t value, + [[maybe_unused]] int64_t tag) const override + { + UNREACHABLE(); + } + + mem::GC *CreateGC([[maybe_unused]] mem::GCType gc_type, [[maybe_unused]] mem::ObjectAllocatorBase *object_allocator, + [[maybe_unused]] const mem::GCSettings &settings) const override + { + return mem::CreateGC(gc_type, object_allocator, settings); + } + + void SetExceptionToVReg([[maybe_unused]] interpreter::AccVRegister &vreg, + [[maybe_unused]] ObjectHeader *obj) const override; + + bool IsCallableObject([[maybe_unused]] ObjectHeader *obj) const override + { + UNREACHABLE(); + } + + Method *GetCallTarget([[maybe_unused]] ObjectHeader *obj) const override + { + UNREACHABLE(); + } + + uint32_t GetFrameExtSize() const override; + + const uint8_t *GetStringClassDescriptor() const override + { + return utf::CStringAsMutf8("Lpanda/JSString;"); + } + + const uint8_t *GetObjectClassDescriptor() const override + { + return utf::CStringAsMutf8("Lpanda/JSObject;"); + } + + const uint8_t *GetClassClassDescriptor() const override + { + return utf::CStringAsMutf8("Lpanda/HClass;"); + } + + const uint8_t *GetClassArrayClassDescriptor() const override + { + return utf::CStringAsMutf8("[Lpanda/JSObject;"); + } + + const uint8_t *GetStringArrayClassDescriptor() const override + { + return utf::CStringAsMutf8("[Lpanda/JSString;"); + } + + const uint8_t *GetCtorName() const override + { + return utf::CStringAsMutf8(".ctor"); + } + + const uint8_t *GetCctorName() const override + { + return utf::CStringAsMutf8(".cctor"); + } + + const uint8_t *GetNullPointerExceptionClassDescriptor() const override + { + return utf::CStringAsMutf8("Lpanda/NullPointerException;"); + } + + const uint8_t *GetArrayIndexOutOfBoundsExceptionClassDescriptor() const override + { + return utf::CStringAsMutf8("Lpanda/ArrayIndexOutOfBoundsException;"); + } + + const uint8_t *GetIndexOutOfBoundsExceptionClassDescriptor() const override + { + return utf::CStringAsMutf8("Lpanda/IndexOutOfBoundsException;"); + } + + const uint8_t *GetIllegalStateExceptionClassDescriptor() const override + { + return utf::CStringAsMutf8("Lpanda/IllegalStateException;"); + } + + const uint8_t *GetNegativeArraySizeExceptionClassDescriptor() const override + { + return utf::CStringAsMutf8("Lpanda/NegativeArraySizeException;"); + } + + const uint8_t *GetStringIndexOutOfBoundsExceptionClassDescriptor() const override + { + return utf::CStringAsMutf8("Lpanda/StringIndexOutOfBoundsException;"); + } + + const uint8_t *GetArithmeticExceptionClassDescriptor() const override + { + return utf::CStringAsMutf8("Lpanda/ArithmeticException;"); + } + + const uint8_t *GetClassCastExceptionClassDescriptor() const override + { + return utf::CStringAsMutf8("Lpanda/ClassCastException;"); + } + + const uint8_t *GetAbstractMethodErrorClassDescriptor() const override + { + return utf::CStringAsMutf8("Lpanda/AbstractMethodError;"); + } + + const uint8_t *GetArrayStoreExceptionClassDescriptor() const override + { + return utf::CStringAsMutf8("Lpanda/ArrayStoreException;"); + } + + const uint8_t *GetRuntimeExceptionClassDescriptor() const override + { + return utf::CStringAsMutf8("Lpanda/RuntimeException;"); + } + + const uint8_t *GetFileNotFoundExceptionClassDescriptor() const override + { + return utf::CStringAsMutf8("Lpanda/FileNotFoundException;"); + } + + const uint8_t *GetIOExceptionClassDescriptor() const override + { + return utf::CStringAsMutf8("Lpanda/IOException;"); + } + + const uint8_t *GetIllegalArgumentExceptionClassDescriptor() const override + { + return utf::CStringAsMutf8("Lpanda/IllegalArgumentException;"); + } + + const uint8_t *GetIllegalAccessExceptionClassDescriptor() const override + { + return utf::CStringAsMutf8("Lpanda/IllegalAccessException;"); + } + + const uint8_t *GetOutOfMemoryErrorClassDescriptor() const override + { + return utf::CStringAsMutf8("Lpanda/OutOfMemoryError;"); + } + + const uint8_t *GetNoClassDefFoundErrorDescriptor() const override + { + return utf::CStringAsMutf8("Lpanda/NoClassDefFoundError;"); + } + + const uint8_t *GetClassCircularityErrorDescriptor() const override + { + return utf::CStringAsMutf8("Lpanda/ClassCircularityError;"); + } + + const uint8_t *GetNoSuchFieldErrorDescriptor() const override + { + return utf::CStringAsMutf8("Lpanda/NoSuchFieldError;"); + } + + const uint8_t *GetNoSuchMethodErrorDescriptor() const override + { + return utf::CStringAsMutf8("Lpanda/NoSuchMethodError;"); + } + + const uint8_t *GetExceptionInInitializerErrorDescriptor() const override + { + return utf::CStringAsMutf8("Lpanda/ExceptionInInitializerError;"); + } + + const uint8_t *GetClassNotFoundExceptionDescriptor() const override + { + return utf::CStringAsMutf8("Lpanda/ClassNotFoundException;"); + } + + const uint8_t *GetInstantiationErrorDescriptor() const override + { + return utf::CStringAsMutf8("Lpanda/InstantiationError;"); + } + + const uint8_t *GetUnsupportedOperationExceptionClassDescriptor() const override + { + return utf::CStringAsMutf8("Lpanda/UnsupportedOperationException;"); + } + + const uint8_t *GetVerifyErrorClassDescriptor() const override + { + return utf::CStringAsMutf8("Lpanda/VerifyError;"); + } + + const uint8_t *GetIllegalMonitorStateExceptionDescriptor() const override + { + return utf::CStringAsMutf8("Lpanda/IllegalMonitorStateException;"); + } + + const uint8_t *GetReferenceErrorDescriptor() const override + { + return utf::CStringAsMutf8("Lecma/ReferenceError;"); + } + + const uint8_t *GetTypedErrorDescriptor() const override + { + return utf::CStringAsMutf8("Lecma/TypedError;"); + } + + const uint8_t *GetErrorClassDescriptor() const override + { + return utf::CStringAsMutf8("Lpanda/Error;"); + } + + const uint8_t *GetIncompatibleClassChangeErrorDescriptor() const override + { + return utf::CStringAsMutf8("Lpanda/IncompatibleClassChangeError;"); + } + + panda::panda_file::File::OpenMode GetBootPandaFilesOpenMode() const override + { + // In case of JS vm open a panda file for reading / writing because EcmaVM patches bytecode in-place + return panda_file::File::READ_WRITE; + } + + PandaUniquePtr CreateITableBuilder() const override; + + PandaUniquePtr CreateVTableBuilder() const override; + + bool InitializeClass(ClassLinker *class_linker, ManagedThread *thread, Class *klass) const override + { + return ClassInitializer::Initialize(class_linker, thread, klass); + } + + size_t GetStringSize(const ObjectHeader *string_object) const override; + + void DeoptimizeBegin(Frame *iframe, int frame_depth) const override; + + void DeoptimizeEnd() const override; + + bool IsEnabledCHA() const override + { + return false; + } +}; +} // namespace panda + +#endif // ECMASCRIPT_LANGUAGE_CONTEXT_H diff --git a/runtime/ecma_macros.h b/runtime/ecma_macros.h new file mode 100644 index 000000000..340bfd9c9 --- /dev/null +++ b/runtime/ecma_macros.h @@ -0,0 +1,514 @@ +/* + * Copyright (c) 2021-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_ECMA_MACROS_H +#define ECMASCRIPT_ECMA_MACROS_H + +#include "plugins/ecmascript/runtime/common.h" +#include "plugins/ecmascript/runtime/ecma_vm.h" +#include "plugins/ecmascript/runtime/js_tagged_value.h" +#include "plugins/ecmascript/runtime/js_thread.h" +#include "plugins/ecmascript/runtime/mem/barriers-inl.h" +#include "plugins/ecmascript/runtime/mem/slots.h" +#include "utils/logger.h" + +#if defined(__cplusplus) + +template +static inline T UnalignedLoad(T const *p) +{ + struct Wrapper { + T x; + } __attribute__((packed, aligned(1))); + return reinterpret_cast(p)->x; +} + +template +static inline void UnalignedStore(T *p, T v) +{ + struct Wrapper { + T x; + } __attribute__((packed, aligned(1))); + reinterpret_cast(p)->x = v; +} + +// NOLINTNEXTLINE(cppcoreguidelines-macro-usage) +#define LOG_ECMA(type) \ + LOG(type, ECMASCRIPT) << __func__ << " Line:" << __LINE__ << " " // NOLINT(bugprone-lambda-function-name) + +// NOLINTNEXTLINE(cppcoreguidelines-macro-usage) +#define ECMA_GC_LOG() LOG(DEBUG, ECMASCRIPT) << " ecmascript gc log: " + +// NOLINTNEXTLINE(cppcoreguidelines-macro-usage) +#define OPTIONAL_LOG(ecmaVM, level, component) LOG_IF(ecmaVM->IsOptionalLogEnabled(), level, component) + +// NOLINTNEXTLINE(cppcoreguidelines-macro-usage) +#define ECMA_BYTRACE_NAME(tag, name) trace::ScopedTrace scopedTrace(name) + +/* Note: We can't statically decide the element type is a primitive or heap object, especially for */ +/* dynamically-typed languages like JavaScript. So we simply skip the read-barrier. */ +// NOLINTNEXTLINE(cppcoreguidelines-macro-usage) +#define GET_VALUE(addr, offset) Barriers::GetDynValue((addr), (offset)) + +// NOLINTNEXTLINE(cppcoreguidelines-macro-usage) +#define SET_VALUE_WITH_BARRIER(thread, addr, offset, value) \ + ObjectAccessor::SetDynValue(thread, addr, offset, value.GetRawData()) + +// NOLINTNEXTLINE(cppcoreguidelines-macro-usage) +#define ACCESSORS(name, offset, lastOffset) \ + static constexpr size_t lastOffset = offset + JSTaggedValue::TaggedTypeSize(); \ + JSTaggedValue Get##name() const \ + { \ + /* Note: We can't statically decide the element type is a primitive or heap object, especially for */ \ + /* dynamically-typed languages like JavaScript. So we simply skip the read-barrier. */ \ + return JSTaggedValue(Barriers::GetDynValue(this, offset)); \ + } \ + static constexpr size_t GetOffset##name() \ + { \ + return offset; \ + } \ + template \ + void Set##name(const JSThread *thread, JSHandle value, BarrierMode mode = WRITE_BARRIER) \ + { \ + if (mode == WRITE_BARRIER) { \ + ObjectAccessor::SetDynValue(thread, this, offset, value.GetTaggedValue().GetRawData()); \ + } else { \ + ObjectAccessor::SetDynValueWithoutBarrier(this, offset, value.GetTaggedValue().GetRawData()); \ + } \ + } \ + void Set##name(const JSThread *thread, JSTaggedValue value, BarrierMode mode = WRITE_BARRIER) \ + { \ + if (mode == WRITE_BARRIER) { \ + ObjectAccessor::SetDynValue(thread, this, offset, value.GetRawData()); \ + } else { \ + ObjectAccessor::SetDynValueWithoutBarrier(this, offset, value.GetRawData()); \ + } \ + } \ + void Set##name(JSTaggedValue value) \ + { \ + ObjectAccessor::SetPrimitive(this, offset, value.GetRawData()); \ + } + +// NOLINTNEXTLINE(cppcoreguidelines-macro-usage) +#define SET_GET_VOID_FIELD(name, offset, lastOffset) \ + static constexpr size_t lastOffset = offset + JSTaggedValue::TaggedTypeSize(); \ + void Set##name(void *fun) \ + { \ + ObjectAccessor::SetPrimitive(this, offset, fun); \ + } \ + void *Get##name() const \ + { \ + return ObjectAccessor::GetPrimitive(this, offset); \ + } + +// NOLINTNEXTLINE(cppcoreguidelines-macro-usage) +#define SET_GET_NATIVE_FIELD(name, type, offset, lastOffset) \ + static constexpr size_t lastOffset = offset + JSTaggedValue::TaggedTypeSize(); \ + void Set##name(type *mem) \ + { \ + ObjectAccessor::SetPrimitive(this, offset, mem); \ + } \ + type *Get##name() const \ + { \ + return ObjectAccessor::GetPrimitive(this, offset); \ + } \ + size_t Offset##name() const \ + { \ + return offset; \ + } + +// NOLINTNEXTLINE(cppcoreguidelines-macro-usage) +#define SET_GET_PRIMITIVE_FIELD(name, type, offset, lastOffset) \ + static constexpr size_t lastOffset = offset + JSTaggedValue::TaggedTypeSize(); \ + void Set##name(type value) \ + { \ + ObjectAccessor::SetPrimitive(this, offset, value); \ + } \ + type Get##name() const \ + { \ + return ObjectAccessor::GetPrimitive(this, offset); \ + } + +#if !defined(NDEBUG) +// NOLINTNEXTLINE(cppcoreguidelines-macro-usage) +#define DASSERT(cond) assert(cond) +// NOLINTNEXTLINE(cppcoreguidelines-macro-usage) +#define DASSERT_PRINT(cond, message) \ + if (auto cond_val = cond; UNLIKELY(!(cond_val))) { \ + std::cerr << message << std::endl; \ + ASSERT(#cond &&cond_val); \ + } +#else // NDEBUG +#define DASSERT(cond) static_cast(0) // NOLINT(cppcoreguidelines-macro-usage) +#define DASSERT_PRINT(cond, message) static_cast(0) // NOLINT(cppcoreguidelines-macro-usage) +#endif // !NDEBUG + +// NOLINTNEXTLINE(cppcoreguidelines-macro-usage) +#define RASSERT(cond) assert(cond) +// NOLINTNEXTLINE(cppcoreguidelines-macro-usage) +#define RASSERT_PRINT(cond, message) \ + if (auto cond_val = cond; UNLIKELY(!(cond_val))) { \ + std::cerr << message << std::endl; \ + RASSERT(#cond &&cond_val); \ + } + +// NOLINTNEXTLINE(cppcoreguidelines-macro-usage) +#define RETURN_IF_ABRUPT_COMPLETION(thread) \ + do { \ + if (thread->HasPendingException()) { \ + return; \ + } \ + } while (false) + +// NOLINTNEXTLINE(cppcoreguidelines-macro-usage) +#define RETURN_VALUE_IF_ABRUPT_COMPLETION(thread, value) \ + do { \ + if (thread->HasPendingException()) { \ + return (value); \ + } \ + } while (false) + +// NOLINTNEXTLINE(cppcoreguidelines-macro-usage) +#define RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread) \ + do { \ + if (thread->HasPendingException()) { \ + return JSTaggedValue::Exception(); \ + } \ + } while (false) + +// NOLINTNEXTLINE(cppcoreguidelines-macro-usage) +#define RETURN_HANDLE_IF_ABRUPT_COMPLETION(type, thread) \ + do { \ + if (thread->HasPendingException()) { \ + return JSHandle(thread, JSTaggedValue::Exception()); \ + } \ + } while (false) + +// NOLINTNEXTLINE(cppcoreguidelines-macro-usage) +#define ASSERT_NO_ABRUPT_COMPLETION(thread) ASSERT(!(thread)->HasPendingException()); + +// NOLINTNEXTLINE(cppcoreguidelines-macro-usage) +#define THROW_NEW_ERROR_AND_RETURN_VALUE(thread, error, value) \ + do { \ + if (!thread->HasPendingException()) { \ + thread->SetException(error); \ + } \ + return (value); \ + } while (false) + +// NOLINTNEXTLINE(cppcoreguidelines-macro-usage) +#define THROW_NEW_ERROR_AND_RETURN_EXCEPTION(thread, error) \ + THROW_NEW_ERROR_AND_RETURN_VALUE(thread, error, JSTaggedValue::Exception()); + +// NOLINTNEXTLINE(cppcoreguidelines-macro-usage) +#define SET_DATE_VALUE(name, code, isLocal) \ + static JSTaggedValue name(EcmaRuntimeCallInfo *argv) \ + { \ + ASSERT(argv); \ + JSThread *thread = argv->GetThread(); \ + JSHandle msg = GetThis(argv); \ + if (!msg->IsDate()) { \ + THROW_TYPE_ERROR_AND_RETURN(thread, "Not a Date Object", JSTaggedValue::Exception()); \ + } \ + JSHandle jsDate(thread, JSDate::Cast(msg->GetTaggedObject())); \ + JSTaggedValue result = jsDate->SetDateValue(argv, code, isLocal); \ + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); \ + jsDate->SetTimeValue(thread, result); \ + return result; \ + } + +// NOLINTNEXTLINE(cppcoreguidelines-macro-usage) +#define DATE_TO_STRING(name) \ + static JSTaggedValue name(EcmaRuntimeCallInfo *argv) \ + { \ + ASSERT(argv); \ + JSThread *thread = argv->GetThread(); \ + JSHandle msg = GetThis(argv); \ + if (!msg->IsDate()) { \ + THROW_TYPE_ERROR_AND_RETURN(thread, "Not a Date Object", JSTaggedValue::Exception()); \ + } \ + if (std::isnan(JSDate::Cast(msg->GetTaggedObject())->GetTimeValue().GetDouble())) { \ + THROW_RANGE_ERROR_AND_RETURN(thread, "range error", JSTaggedValue::Exception()); \ + } \ + return JSDate::Cast(msg->GetTaggedObject())->name(thread); \ + } + +// NOLINTNEXTLINE(cppcoreguidelines-macro-usage) +#define DATE_STRING(name) \ + static JSTaggedValue name(EcmaRuntimeCallInfo *argv) \ + { \ + ASSERT(argv); \ + JSThread *thread = argv->GetThread(); \ + JSHandle msg = GetThis(argv); \ + if (!msg->IsDate()) { \ + THROW_TYPE_ERROR_AND_RETURN(thread, "Not a Date Object", JSTaggedValue::Exception()); \ + } \ + if (std::isnan(JSDate::Cast(msg->GetTaggedObject())->GetTimeValue().GetDouble())) { \ + return thread->GetEcmaVM()->GetFactory()->NewFromCanBeCompressString("Invalid Date").GetTaggedValue(); \ + } \ + return JSDate::Cast(msg->GetTaggedObject())->name(thread); \ + } + +// NOLINTNEXTLINE(cppcoreguidelines-macro-usage) +#define GET_DATE_VALUE(name, code, isLocal) \ + static JSTaggedValue name(EcmaRuntimeCallInfo *argv) \ + { \ + ASSERT(argv); \ + JSThread *thread = argv->GetThread(); \ + JSHandle msg = GetThis(argv); \ + if (!msg->IsDate()) { \ + THROW_TYPE_ERROR_AND_RETURN(thread, "Not a Date Object", JSTaggedValue::Exception()); \ + } \ + JSHandle jsDate(thread, JSDate::Cast(msg->GetTaggedObject())); \ + double result = jsDate->GetDateValue(jsDate->GetTimeValue().GetDouble(), code, isLocal); \ + return GetTaggedDouble(result); \ + } + +// NOLINTNEXTLINE(cppcoreguidelines-macro-usage) +#define THROW_NEW_ERROR_AND_RETURN(thread, error) \ + do { \ + if (!thread->HasPendingException()) { \ + thread->SetException(error); \ + } \ + return; \ + } while (false) + +// NOLINTNEXTLINE(cppcoreguidelines-macro-usage) +#define THROW_TYPE_ERROR_AND_RETURN(thread, message, exception) \ + do { \ + ObjectFactory *objectFactory = thread->GetEcmaVM()->GetFactory(); \ + JSHandle error = objectFactory->GetJSError(ErrorType::TYPE_ERROR, message); \ + THROW_NEW_ERROR_AND_RETURN_VALUE(thread, error.GetTaggedValue(), exception); \ + } while (false) + +// NOLINTNEXTLINE(cppcoreguidelines-macro-usage) +#define THROW_TYPE_ERROR(thread, message) \ + do { \ + ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); \ + JSHandle error = factory->GetJSError(ErrorType::TYPE_ERROR, message); \ + THROW_NEW_ERROR_AND_RETURN(thread, error.GetTaggedValue()); \ + } while (false) + +// NOLINTNEXTLINE(cppcoreguidelines-macro-usage) +#define THROW_RANGE_ERROR_AND_RETURN(thread, message, exception) \ + do { \ + ObjectFactory *objectFactory = thread->GetEcmaVM()->GetFactory(); \ + JSHandle error = objectFactory->GetJSError(ErrorType::RANGE_ERROR, message); \ + THROW_NEW_ERROR_AND_RETURN_VALUE(thread, error.GetTaggedValue(), exception); \ + } while (false) + +// NOLINTNEXTLINE(cppcoreguidelines-macro-usage) +#define THROW_RANGE_ERROR(thread, message) \ + do { \ + ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); \ + JSHandle error = factory->GetJSError(ErrorType::RANGE_ERROR, message); \ + THROW_NEW_ERROR_AND_RETURN(thread, error.GetTaggedValue()); \ + } while (false) + +// NOLINTNEXTLINE(cppcoreguidelines-macro-usage) +#define THROW_URI_ERROR_AND_RETURN(thread, message, exception) \ + do { \ + ObjectFactory *objectFactory = thread->GetEcmaVM()->GetFactory(); \ + JSHandle error = objectFactory->GetJSError(ErrorType::URI_ERROR, message); \ + THROW_NEW_ERROR_AND_RETURN_VALUE(thread, error.GetTaggedValue(), exception); \ + } while (false) + +// NOLINTNEXTLINE(cppcoreguidelines-macro-usage) +#define THROW_SYNTAX_ERROR_AND_RETURN(thread, message, exception) \ + do { \ + ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); \ + JSHandle error = factory->GetJSError(ErrorType::SYNTAX_ERROR, message); \ + THROW_NEW_ERROR_AND_RETURN_VALUE(thread, error.GetTaggedValue(), exception); \ + } while (false) + +// NOLINTNEXTLINE(cppcoreguidelines-macro-usage) +#define RETURN_REJECT_PROMISE_IF_ABRUPT(thread, value, capability) \ + do { \ + const GlobalEnvConstants *globalConst = thread->GlobalConstants(); \ + if (value.GetTaggedValue().IsCompletionRecord()) { \ + JSHandle record = JSHandle::Cast(value); \ + if (record->IsThrow()) { \ + JSHandle reject(thread, capability->GetReject()); \ + JSHandle undefine = globalConst->GetHandledUndefined(); \ + InternalCallParams *arg = thread->GetInternalCallParams(); \ + arg->MakeArgv(record->GetValue()); \ + JSTaggedValue res = JSFunction::Call(thread, reject, undefine, 1, arg->GetArgv()); \ + RETURN_VALUE_IF_ABRUPT_COMPLETION(thread, res); \ + return capability->GetPromise(); \ + } \ + } \ + if (thread->HasPendingException()) { \ + thread->ClearException(); \ + JSHandle reject(thread, capability->GetReject()); \ + JSHandle undefined = globalConst->GetHandledUndefined(); \ + InternalCallParams *arg = thread->GetInternalCallParams(); \ + arg->MakeArgv(value); \ + JSTaggedValue res = JSFunction::Call(thread, reject, undefined, 1, arg->GetArgv()); \ + RETURN_VALUE_IF_ABRUPT_COMPLETION(thread, res); \ + return capability->GetPromise(); \ + } \ + } while (false) + +// NOLINTNEXTLINE(cppcoreguidelines-macro-usage) +#define RETURN_REJECT_PROMISE_IF_ABRUPT_THROWN_VALUE(thread, value, capability) \ + if (thread->HasPendingException()) { \ + value = JSPromise::IfThrowGetThrowValue(thread); \ + } \ + RETURN_REJECT_PROMISE_IF_ABRUPT(thread, value, capability) + +// NOLINTNEXTLINE(cppcoreguidelines-macro-usage) +#define RETURN_COMPLETION_IF_ABRUPT(thread, value) \ + do { \ + if (thread->HasPendingException()) { \ + JSHandle completionRecord = \ + factory->NewCompletionRecord(CompletionRecord::THROW, value); \ + return (completionRecord); \ + } \ + } while (false) + +// NOLINTNEXTLINE(cppcoreguidelines-macro-usage) +#define DECL_DUMP() \ + void Dump(JSThread *thread, std::ostream &os) const DUMP_API_ATTR; \ + void Dump(JSThread *thread) const DUMP_API_ATTR \ + { \ + Dump(thread, std::cout); \ + } \ + void DumpForSnapshot(JSThread *thread, std::vector> &vec) const; + +#endif // defined(__cplusplus) + +// NOLINTNEXTLINE(cppcoreguidelines-macro-usage) +#define DECL_CAST(TYPE) \ + static TYPE *Cast(ObjectHeader *object) \ + { \ + ASSERT(JSTaggedValue(object).Is##TYPE()); \ + return reinterpret_cast(object); \ + } + +// NOLINTNEXTLINE(cppcoreguidelines-macro-usage) +#define DECL_VISIT_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)); \ + } + +// 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))); \ + } + +// 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 > (END_OFFSET)) { \ + visitor(this, ObjectSlot(ToUintPtr(this) + (END_OFFSET)), ObjectSlot(ToUintPtr(this) + objSize)); \ + } \ + } \ + 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))); \ + } + +#if ECMASCRIPT_ENABLE_CAST_CHECK +// NOLINTNEXTLINE(cppcoreguidelines-macro-usage) +#define CAST_CHECK(CAST_TYPE, CHECK_METHOD) \ + static inline CAST_TYPE *Cast(ObjectHeader *object) \ + { \ + if (!JSTaggedValue(object).CHECK_METHOD()) { \ + std::abort(); \ + } \ + return static_cast(object); \ + } \ + static inline const CAST_TYPE *ConstCast(const ObjectHeader *object) \ + { \ + if (!JSTaggedValue(object).CHECK_METHOD()) { \ + std::abort(); \ + } \ + return static_cast(object); \ + } +#else +// NOLINTNEXTLINE(cppcoreguidelines-macro-usage) +#define CAST_CHECK(CAST_TYPE, CHECK_METHOD) \ + static inline CAST_TYPE *Cast(ObjectHeader *object) \ + { \ + ASSERT(JSTaggedValue(object).CHECK_METHOD()); \ + return static_cast(object); \ + } \ + static const inline CAST_TYPE *ConstCast(const ObjectHeader *object) \ + { \ + ASSERT(JSTaggedValue(object).CHECK_METHOD()); \ + return static_cast(object); \ + } +// NOLINTNEXTLINE(cppcoreguidelines-macro-usage) +#define CAST_NO_CHECK(CAST_TYPE) \ + static inline CAST_TYPE *Cast(ObjectHeader *object) \ + { \ + return static_cast(object); \ + } \ + static const inline CAST_TYPE *ConstCast(const ObjectHeader *object) \ + { \ + return static_cast(object); \ + } +#endif + +#if ECMASCRIPT_ENABLE_CAST_CHECK +// NOLINTNEXTLINE(cppcoreguidelines-macro-usage) +#define CAST_CHECK_TAGGEDVALUE(CAST_TYPE, CHECK_METHOD) \ + static inline CAST_TYPE *Cast(JSTaggedValue value) \ + { \ + if (value.IsHeapObject() && value.GetTaggedObject()->GetClass()->CHECK_METHOD()) { \ + return static_cast(value.GetTaggedObject()); \ + } \ + std::abort(); \ + } +#else +// NOLINTNEXTLINE(cppcoreguidelines-macro-usage) +#define CAST_CHECK_TAGGEDVALUE(CAST_TYPE, CHECK_METHOD) \ + static inline CAST_TYPE *Cast(JSTaggedValue value) \ + { \ + ASSERT(value.IsHeapObject() && value.GetTaggedObject()->GetClass()->CHECK_METHOD()); \ + return static_cast(value.GetTaggedObject()); \ + } +#endif + +// NOLINTNEXTLINE(cppcoreguidelines-macro-usage) +#define CHECK_DUMP_FILEDS(begin, end, num) \ + LOG_IF(num != (end - begin) / JSTaggedValue::TaggedTypeSize(), FATAL, RUNTIME) \ + << "Fileds in obj are not in dump list. "; + +// NOLINTNEXTLINE(cppcoreguidelines-macro-usage) +#define CHECK_OBJECT_SIZE(size) \ + if (size == 0) { \ + LOG(FATAL, ECMASCRIPT) << __func__ << " Line: " << __LINE__ << " objectSize is " << size; \ + } + +// NOLINTNEXTLINE(cppcoreguidelines-macro-usage) +#define CHECK_REGION_END(begin, end) \ + if (begin > end) { \ + LOG(FATAL, ECMASCRIPT) << __func__ << " Line: " << __LINE__ << " begin: " << begin << " end: " << end; \ + } + +#endif // ECMASCRIPT_ECMA_MACROS_H diff --git a/runtime/ecma_module.cpp b/runtime/ecma_module.cpp new file mode 100644 index 000000000..b206ae56f --- /dev/null +++ b/runtime/ecma_module.cpp @@ -0,0 +1,251 @@ +/* + * Copyright (c) 2021-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 "plugins/ecmascript/runtime/ecma_module.h" +#include "plugins/ecmascript/runtime/ecma_vm.h" +#include "plugins/ecmascript/runtime/global_env.h" +#include "plugins/ecmascript/runtime/js_thread.h" +#include "plugins/ecmascript/runtime/object_factory.h" +#include "plugins/ecmascript/runtime/tagged_array.h" +#include "plugins/ecmascript/runtime/tagged_dictionary.h" + +namespace panda::ecmascript { +JSHandle EcmaModule::GetItem(const JSThread *thread, JSHandle itemName) +{ + JSHandle moduleItems(thread, NameDictionary::Cast(GetNameDictionary().GetTaggedObject())); + int entry = moduleItems->FindEntry(itemName.GetTaggedValue()); + if (entry != -1) { + return JSHandle(thread, moduleItems->GetValue(entry)); + } + + return JSHandle(thread, JSTaggedValue::Undefined()); +} + +void EcmaModule::AddItem(const JSThread *thread, JSHandle module, JSHandle itemName, + JSHandle itemValue) +{ + JSMutableHandle data(thread, module->GetNameDictionary()); + if (data->IsUndefined()) { + data.Update(NameDictionary::Create(thread, DEAULT_DICTIONART_CAPACITY)); + } + JSHandle dataDict = JSHandle::Cast(data); + JSHandle newDict = + NameDictionary::Put(thread, dataDict, itemName, itemValue, PropertyAttributes::Default()); + module->SetNameDictionary(thread, newDict); +} + +void EcmaModule::RemoveItem(const JSThread *thread, JSHandle module, JSHandle itemName) +{ + JSHandle data(thread, module->GetNameDictionary()); + if (data->IsUndefined()) { + return; + } + JSHandle moduleItems(data); + int entry = moduleItems->FindEntry(itemName.GetTaggedValue()); + if (entry != -1) { + JSHandle newDict = NameDictionary::Remove(thread, moduleItems, entry); + module->SetNameDictionary(thread, newDict); + } +} + +void EcmaModule::CopyModuleInternal(const JSThread *thread, JSHandle dstModule, + JSHandle srcModule) +{ + JSHandle moduleItems(thread, + NameDictionary::Cast(srcModule->GetNameDictionary().GetTaggedObject())); + uint32_t numAllKeys = moduleItems->EntriesCount(); + ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); + JSHandle allKeys = factory->NewTaggedArray(numAllKeys); + moduleItems->GetAllKeys(thread, 0, allKeys.GetObject()); + + JSMutableHandle itemName(thread, JSTaggedValue::Undefined()); + JSMutableHandle itemValue(thread, JSTaggedValue::Undefined()); + unsigned int capcity = allKeys->GetLength(); + for (unsigned int i = 0; i < capcity; i++) { + int entry = moduleItems->FindEntry(allKeys->Get(i)); + if (entry != -1) { + itemName.Update(allKeys->Get(i)); + itemValue.Update(moduleItems->GetValue(entry)); + EcmaModule::AddItem(thread, dstModule, itemName, itemValue); + } + } +} + +void EcmaModule::DebugPrint(const JSThread *thread, const CString &caller) +{ + JSHandle moduleItems(thread, NameDictionary::Cast(GetNameDictionary().GetTaggedObject())); + uint32_t numAllKeys = moduleItems->EntriesCount(); + ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); + JSHandle allKeys = factory->NewTaggedArray(numAllKeys); + moduleItems->GetAllKeys(thread, 0, allKeys.GetObject()); + + unsigned int capcity = allKeys->GetLength(); + for (unsigned int i = 0; i < capcity; i++) { + int entry = moduleItems->FindEntry(allKeys->Get(i)); + if (entry != -1) { + std::cout << "[" << caller << "]" << std::endl + << "--itemName: " << ConvertToString(EcmaString::Cast(allKeys->Get(i).GetTaggedObject())) + << std::endl + << "--itemValue(ObjRef): 0x" << std::hex << moduleItems->GetValue(entry).GetRawData() + << std::endl; + } + } +} + +ModuleManager::ModuleManager(EcmaVM *vm) : vm_(vm) +{ + ecmaModules_ = NameDictionary::Create(vm_->GetJSThread(), DEAULT_DICTIONART_CAPACITY).GetTaggedValue(); +} + +// class ModuleManager +void ModuleManager::AddModule(JSHandle moduleName, JSHandle module) +{ + JSThread *thread = vm_->GetJSThread(); + [[maybe_unused]] EcmaHandleScope scope(thread); + JSHandle dict(thread, ecmaModules_); + ecmaModules_ = + NameDictionary::Put(thread, dict, moduleName, module, PropertyAttributes::Default()).GetTaggedValue(); +} + +void ModuleManager::RemoveModule(JSHandle moduleName) +{ + JSThread *thread = vm_->GetJSThread(); + [[maybe_unused]] EcmaHandleScope scope(thread); + JSHandle moduleItems(thread, ecmaModules_); + int entry = moduleItems->FindEntry(moduleName.GetTaggedValue()); + if (entry != -1) { + ecmaModules_ = NameDictionary::Remove(thread, moduleItems, entry).GetTaggedValue(); + } +} + +JSHandle ModuleManager::GetModule(const JSThread *thread, + [[maybe_unused]] JSHandle moduleName) +{ + int entry = NameDictionary::Cast(ecmaModules_.GetTaggedObject())->FindEntry(moduleName.GetTaggedValue()); + if (entry != -1) { + return JSHandle(thread, NameDictionary::Cast(ecmaModules_.GetTaggedObject())->GetValue(entry)); + } + return thread->GlobalConstants()->GetHandledUndefined(); +} + +CString ModuleManager::GenerateModuleFullPath(const std::string ¤tPathFile, const CString &relativeFile) +{ + // NOLINTNEXTLINE(abseil-string-find-startswith) + if (relativeFile.find("./") != 0 && relativeFile.find("../") != 0) { // not start with "./" or "../" + return relativeFile; // not relative + } + + auto slashPos = currentPathFile.find_last_of('/'); + if (slashPos == std::string::npos) { + return relativeFile; // no need to process + } + + auto dotPos = relativeFile.find_last_of('.'); + if (dotPos == std::string::npos) { + dotPos = 0; + } + + CString fullPath; + fullPath.append(currentPathFile.substr(0, slashPos + 1)); // 1: with "/" + fullPath.append(relativeFile.substr(0, dotPos)); + fullPath.append(".abc"); // ".js" -> ".abc" + return fullPath; +} + +const CString &ModuleManager::GetCurrentExportModuleName() +{ + return moduleNames_.back(); +} + +void ModuleManager::SetCurrentExportModuleName(const std::string_view &moduleFile) +{ + moduleNames_.emplace_back(CString(moduleFile)); // xx/xx/x.abc +} + +void ModuleManager::RestoreCurrentExportModuleName() +{ + auto s = moduleNames_.size(); + if (s > 0) { + moduleNames_.resize(s - 1); + return; + } + UNREACHABLE(); +} + +const CString &ModuleManager::GetPrevExportModuleName() +{ + static const int MINIMUM_COUNT = 2; + auto count = moduleNames_.size(); + ASSERT(count >= MINIMUM_COUNT); + return moduleNames_[count - MINIMUM_COUNT]; +} + +void ModuleManager::AddModuleItem(const JSThread *thread, JSHandle itemName, + JSHandle value) +{ + CString name_str = GetCurrentExportModuleName(); + ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); + + JSHandle moduleName = factory->NewFromString(name_str); + + JSHandle module = GetModule(thread, JSHandle::Cast(moduleName)); + if (module->IsUndefined()) { + JSHandle emptyModule = factory->NewEmptyEcmaModule(); + EcmaModule::AddItem(thread, emptyModule, itemName, value); + + AddModule(JSHandle::Cast(moduleName), JSHandle::Cast(emptyModule)); + } else { + EcmaModule::AddItem(thread, JSHandle(module), itemName, value); + } +} + +JSHandle ModuleManager::GetModuleItem(const JSThread *thread, JSHandle module, + JSHandle itemName) +{ + return EcmaModule::Cast(module->GetTaggedObject())->GetItem(thread, itemName); // Assume the module is exist +} + +void ModuleManager::CopyModule(const JSThread *thread, JSHandle src) +{ + ASSERT(src->IsHeapObject()); + JSHandle srcModule = JSHandle::Cast(src); + CString name_str = GetCurrentExportModuleName(); // Assume the srcModule exist when dstModule Execute + + ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); + JSHandle moduleName = factory->NewFromString(name_str); + + JSHandle dstModule = GetModule(thread, JSHandle::Cast(moduleName)); + + if (dstModule->IsUndefined()) { + JSHandle emptyModule = factory->NewEmptyEcmaModule(); + EcmaModule::CopyModuleInternal(thread, emptyModule, srcModule); + + AddModule(JSHandle::Cast(moduleName), JSHandle::Cast(emptyModule)); + } else { + EcmaModule::CopyModuleInternal(thread, JSHandle(dstModule), srcModule); + } +} + +void ModuleManager::DebugPrint([[maybe_unused]] const JSThread *thread, [[maybe_unused]] const CString &caller) +{ + std::cout << "ModuleStack:"; + for_each(moduleNames_.cbegin(), + moduleNames_.cend(), + [](const CString& s)->void { + std::cout << s << " "; + }); + std::cout << "\n"; +} +} // namespace panda::ecmascript diff --git a/runtime/ecma_module.h b/runtime/ecma_module.h new file mode 100644 index 000000000..78051b1b5 --- /dev/null +++ b/runtime/ecma_module.h @@ -0,0 +1,102 @@ +/* + * Copyright (c) 2021-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_ECMA_MODULE_H +#define ECMASCRIPT_ECMA_MODULE_H + +#include "plugins/ecmascript/runtime/js_object.h" +#include "plugins/ecmascript/runtime/js_tagged_value-inl.h" +#include "plugins/ecmascript/runtime/mem/c_string.h" + +namespace panda::ecmascript { +class EcmaVm; + +// Forward declaration +class EcmaModule : public ECMAObject { +public: + static EcmaModule *Cast(ObjectHeader *object) + { + return static_cast(object); + } + + JSHandle GetItem(const JSThread *thread, JSHandle itemName); + + static void AddItem(const JSThread *thread, JSHandle module, JSHandle itemName, + JSHandle itemValue); + + static void RemoveItem(const JSThread *thread, JSHandle module, JSHandle itemName); + + void DebugPrint(const JSThread *thread, const CString &caller); + + static constexpr uint32_t DEAULT_DICTIONART_CAPACITY = 4; + + static constexpr size_t NAME_DICTIONARY_OFFSET = ECMAObject::SIZE; + ACCESSORS(NameDictionary, NAME_DICTIONARY_OFFSET, SIZE) + + DECL_VISIT_OBJECT_FOR_JS_OBJECT(ECMAObject, NAME_DICTIONARY_OFFSET, SIZE) + DECL_DUMP() + +protected: + static void CopyModuleInternal(const JSThread *thread, JSHandle dstModule, + JSHandle srcModule); + + friend class ModuleManager; +}; + +class ModuleManager { +public: + explicit ModuleManager(EcmaVM *vm); + ~ModuleManager() = default; + + void AddModule(JSHandle moduleName, JSHandle module); + + void RemoveModule(JSHandle moduleName); + + JSHandle GetModule(const JSThread *thread, JSHandle moduleName); + + CString GenerateModuleFullPath(const std::string ¤tPathFile, const CString &relativeFile); + + const CString &GetCurrentExportModuleName(); + + const CString &GetPrevExportModuleName(); + + void SetCurrentExportModuleName(const std::string_view &moduleFile); + + void RestoreCurrentExportModuleName(); + + void AddModuleItem(const JSThread *thread, JSHandle itemName, JSHandle value); + + JSHandle GetModuleItem(const JSThread *thread, JSHandle module, + JSHandle itemName); + + void CopyModule(const JSThread *thread, JSHandle src); + + void DebugPrint(const JSThread *thread, const CString &caller); + +private: + static constexpr uint32_t DEAULT_DICTIONART_CAPACITY = 4; + + NO_COPY_SEMANTIC(ModuleManager); + NO_MOVE_SEMANTIC(ModuleManager); + + EcmaVM *vm_{nullptr}; + JSTaggedValue ecmaModules_ {JSTaggedValue::Hole()}; + std::vector moduleNames_ {DEAULT_DICTIONART_CAPACITY}; + + friend class EcmaVM; +}; +} // namespace panda::ecmascript + +#endif diff --git a/runtime/ecma_runtime.yaml b/runtime/ecma_runtime.yaml new file mode 100644 index 000000000..51c45fb92 --- /dev/null +++ b/runtime/ecma_runtime.yaml @@ -0,0 +1,1851 @@ +# Copyright (c) 2021-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. + +intrinsics_namespace: panda::ecmascript::intrinsics + +intrinsics: +- name: Ldnan + space: ecmascript + class_name: Ecmascript.Intrinsics + method_name: ldnan + static: true + signature: + ret: any + args: [] + impl: panda::ecmascript::intrinsics::Ldnan + clear_flags: [no_dce] + use_thread: false + +- name: Ldinfinity + space: ecmascript + class_name: Ecmascript.Intrinsics + method_name: ldinfinity + static: true + signature: + ret: any + args: [] + impl: panda::ecmascript::intrinsics::Ldinfinity + clear_flags: [no_dce] + use_thread: false + +- name: Ldglobalthis + space: ecmascript + class_name: Ecmascript.Intrinsics + method_name: ldglobalthis + static: true + signature: + ret: any + args: [] + impl: panda::ecmascript::intrinsics::Ldglobalthis + clear_flags: [no_dce] + +- name: Ldundefined + space: ecmascript + class_name: Ecmascript.Intrinsics + method_name: ldundefined + static: true + signature: + ret: any + args: [] + impl: panda::ecmascript::intrinsics::Ldundefined + clear_flags: [no_dce] + use_thread: false + +- name: Ldboolean + space: ecmascript + class_name: Ecmascript.Intrinsics + method_name: ldboolean + static: true + signature: + ret: any + args: [any] + impl: panda::ecmascript::intrinsics::Ldboolean + +- name: Ldnumber + space: ecmascript + class_name: Ecmascript.Intrinsics + method_name: ldnumber + static: true + signature: + ret: any + args: [any] + impl: panda::ecmascript::intrinsics::Ldnumber + +- name: Ldstring + space: ecmascript + class_name: Ecmascript.Intrinsics + method_name: ldstring + static: true + signature: + ret: any + args: [any] + impl: panda::ecmascript::intrinsics::Ldstring + +- name: Ldbigint + space: ecmascript + class_name: Ecmascript.Intrinsics + method_name: ldbigint + static: true + signature: + ret: any + args: [any] + impl: panda::ecmascript::intrinsics::Ldbigint + +- name: Ldnull + space: ecmascript + class_name: Ecmascript.Intrinsics + method_name: ldnull + static: true + signature: + ret: any + args: [] + impl: panda::ecmascript::intrinsics::Ldnull + clear_flags: [no_dce] + use_thread: false + +- name: Ldsymbol + space: ecmascript + class_name: Ecmascript.Intrinsics + method_name: ldsymbol + static: true + signature: + ret: any + args: [] + impl: panda::ecmascript::intrinsics::Ldsymbol + clear_flags: [no_dce] + +- name: Ldobject + space: ecmascript + class_name: Ecmascript.Intrinsics + method_name: ldobject + static: true + signature: + ret: any + args: [any, any] + impl: panda::ecmascript::intrinsics::Ldobject + +- name: Ldfunction + space: ecmascript + class_name: Ecmascript.Intrinsics + method_name: ldfunction + static: true + signature: + ret: any + args: [any, any] + impl: panda::ecmascript::intrinsics::Ldfunction + +- name: Ldglobal + space: ecmascript + class_name: Ecmascript.Intrinsics + method_name: ldglobal + static: true + signature: + ret: any + args: [] + impl: panda::ecmascript::intrinsics::Ldglobal + clear_flags: [no_dce] + +- name: Ldtrue + space: ecmascript + class_name: Ecmascript.Intrinsics + method_name: ldtrue + static: true + signature: + ret: any + args: [] + impl: panda::ecmascript::intrinsics::Ldtrue + clear_flags: [no_dce] + use_thread: false + +- name: Ldfalse + space: ecmascript + class_name: Ecmascript.Intrinsics + method_name: ldfalse + static: true + signature: + ret: any + args: [] + impl: panda::ecmascript::intrinsics::Ldfalse + clear_flags: [no_dce] + use_thread: false + +- name: Add2Dyn + space: ecmascript + class_name: Ecmascript.Intrinsics + method_name: add2Dyn + static: true + exception: true + signature: + ret: any + args: [any, acc] + impl: panda::ecmascript::intrinsics::Add2Dyn + fast_path: FastPathEcmaAdd + +- name: Sub2Dyn + space: ecmascript + class_name: Ecmascript.Intrinsics + method_name: sub2Dyn + static: true + exception: true + signature: + ret: any + args: [any, acc] + impl: panda::ecmascript::intrinsics::Sub2Dyn + fast_path: FastPathEcmaSub + +- name: Mul2Dyn + space: ecmascript + class_name: Ecmascript.Intrinsics + method_name: mul2Dyn + static: true + exception: true + signature: + ret: any + args: [any, acc] + impl: panda::ecmascript::intrinsics::Mul2Dyn + fast_path: FastPathEcmaMul + +- name: Div2Dyn + space: ecmascript + class_name: Ecmascript.Intrinsics + method_name: div2Dyn + static: true + exception: true + signature: + ret: any + args: [any, acc] + impl: panda::ecmascript::intrinsics::Div2Dyn + fast_path: FastPathEcmaDiv + +- name: Mod2Dyn + space: ecmascript + class_name: Ecmascript.Intrinsics + method_name: mod2Dyn + static: true + exception: true + signature: + ret: any + args: [any, acc] + impl: panda::ecmascript::intrinsics::Mod2Dyn + +- name: EqDyn + space: ecmascript + class_name: Ecmascript.Intrinsics + method_name: eqDyn + static: true + exception: true + signature: + ret: any + args: [any, acc] + impl: panda::ecmascript::intrinsics::EqDyn + +- name: NotEqDyn + space: ecmascript + class_name: Ecmascript.Intrinsics + method_name: noteqDyn + static: true + exception: true + signature: + ret: any + args: [any, acc] + impl: panda::ecmascript::intrinsics::NotEqDyn + +- name: LessDyn + space: ecmascript + class_name: Ecmascript.Intrinsics + method_name: lessDyn + static: true + exception: true + signature: + ret: any + args: [any, acc] + impl: panda::ecmascript::intrinsics::LessDyn + +- name: LessEqDyn + space: ecmascript + class_name: Ecmascript.Intrinsics + method_name: lesseqDyn + static: true + exception: true + signature: + ret: any + args: [any, acc] + impl: panda::ecmascript::intrinsics::LessEqDyn + +- name: GreaterDyn + space: ecmascript + class_name: Ecmascript.Intrinsics + method_name: greaterDyn + static: true + exception: true + signature: + ret: any + args: [any, acc] + impl: panda::ecmascript::intrinsics::GreaterDyn + +- name: GreaterEqDyn + space: ecmascript + class_name: Ecmascript.Intrinsics + method_name: greatereqDyn + static: true + exception: true + signature: + ret: any + args: [any, acc] + impl: panda::ecmascript::intrinsics::GreaterEqDyn + +- name: LdObjByValue + space: ecmascript + class_name: Ecmascript.Intrinsics + method_name: ldObjByValue + static: true + exception: true + signature: + ret: any + args: [any, any, u16] + impl: panda::ecmascript::intrinsics::LdObjByValue + +- name: TryLdGlobalByValue + space: ecmascript + class_name: Ecmascript.Intrinsics + method_name: tryLdGlobalByValue + static: true + exception: true + signature: + ret: any + args: [u16, any] + impl: panda::ecmascript::intrinsics::TryLdGlobalByValue + +- name: StObjByValue + space: ecmascript + class_name: Ecmascript.Intrinsics + method_name: stObjByValue + static: true + exception: true + signature: + ret: any + args: [any, any, acc, u16] + impl: panda::ecmascript::intrinsics::StObjByValue + +- name: Shl2Dyn + space: ecmascript + class_name: Ecmascript.Intrinsics + method_name: shl2Dyn + static: true + exception: true + signature: + ret: any + args: [any, acc] + impl: panda::ecmascript::intrinsics::Shl2Dyn + fast_path: FastPathEcmaShl + +- name: Shr2Dyn + space: ecmascript + class_name: Ecmascript.Intrinsics + method_name: shr2Dyn + static: true + exception: true + signature: + ret: any + args: [any, acc] + impl: panda::ecmascript::intrinsics::Shr2Dyn + fast_path: FastPathEcmaShr + +- name: Ashr2Dyn + space: ecmascript + class_name: Ecmascript.Intrinsics + method_name: ashr2Dyn + static: true + exception: true + signature: + ret: any + args: [any, acc] + impl: panda::ecmascript::intrinsics::Ashr2Dyn + +- name: And2Dyn + space: ecmascript + class_name: Ecmascript.Intrinsics + method_name: and2Dyn + static: true + exception: true + signature: + ret: any + args: [any, acc] + impl: panda::ecmascript::intrinsics::And2Dyn + fast_path: FastPathEcmaAnd + +- name: Or2Dyn + space: ecmascript + class_name: Ecmascript.Intrinsics + method_name: or2Dyn + static: true + exception: true + signature: + ret: any + args: [any, acc] + impl: panda::ecmascript::intrinsics::Or2Dyn + fast_path: FastPathEcmaOr + +- name: Xor2Dyn + space: ecmascript + class_name: Ecmascript.Intrinsics + method_name: xor2Dyn + static: true + exception: true + signature: + ret: any + args: [any, acc] + impl: panda::ecmascript::intrinsics::Xor2Dyn + fast_path: FastPathEcmaXor + +- name: Tonumber + space: ecmascript + class_name: Ecmascript.Intrinsics + method_name: tonumber + static: true + exception: true + signature: + ret: any + args: [any] + impl: panda::ecmascript::intrinsics::Tonumber + +- name: NegDyn + space: ecmascript + class_name: Ecmascript.Intrinsics + method_name: negDyn + static: true + exception: true + signature: + ret: any + args: [any] + impl: panda::ecmascript::intrinsics::NegDyn + fast_path: FastPathEcmaNeg + +- name: NotDyn + space: ecmascript + class_name: Ecmascript.Intrinsics + method_name: notDyn + static: true + exception: true + signature: + ret: any + args: [any] + impl: panda::ecmascript::intrinsics::NotDyn + fast_path: FastPathEcmaNot + +- name: IncDyn + space: ecmascript + class_name: Ecmascript.Intrinsics + method_name: incDyn + static: true + exception: true + signature: + ret: any + args: [any] + impl: panda::ecmascript::intrinsics::IncDyn + fast_path: FastPathEcmaInc + +- name: DecDyn + space: ecmascript + class_name: Ecmascript.Intrinsics + method_name: decDyn + static: true + exception: true + signature: + ret: any + args: [any] + impl: panda::ecmascript::intrinsics::DecDyn + fast_path: FastPathEcmaDec + +- name: RethrowDyn + space: ecmascript + class_name: Ecmascript.Intrinsics + method_name: rethrowDyn + static: true + exception: true + signature: + ret: any + args: [acc] + impl: panda::ecmascript::intrinsics::RethrowDyn + +- name: ThrowDyn + space: ecmascript + class_name: Ecmascript.Intrinsics + method_name: throwDyn + static: true + exception: true + signature: + ret: void + args: [acc] + impl: panda::ecmascript::intrinsics::ThrowDyn + +- name: Delobjprop + space: ecmascript + class_name: Ecmascript.Intrinsics + method_name: delobjprop + static: true + exception: true + signature: + ret: any + args: [any, any] + impl: panda::ecmascript::intrinsics::Delobjprop + +- name: Defineglobalvar + space: ecmascript + class_name: Ecmascript.Intrinsics + method_name: defineglobalvar + static: true + signature: + ret: any + args: [any, any] + impl: panda::ecmascript::intrinsics::Defineglobalvar + +- name: Definelocalvar + space: ecmascript + class_name: Ecmascript.Intrinsics + method_name: definelocalvar + static: true + signature: + ret: any + args: [any, any] + impl: panda::ecmascript::intrinsics::Definelocalvar + +- name: Definefuncexpr + class_name: Ecmascript.Intrinsics + space: ecmascript + method_name: definefuncexpr + static: true + signature: + ret: any + args: [any, any] + impl: panda::ecmascript::intrinsics::Definefuncexpr + +- name: DefinefuncDyn + space: ecmascript + class_name: Ecmascript.Intrinsics + method_name: definefuncDyn + static: true + exception: true + signature: + ret: any + args: [method_id, any] + impl: panda::ecmascript::intrinsics::DefinefuncDyn + +- name: DefineNCFuncDyn + space: ecmascript + class_name: Ecmascript.Intrinsics + method_name: defineNCFuncDyn + static: true + exception: true + signature: + ret: any + args: [method_id, any, acc] + impl: panda::ecmascript::intrinsics::DefineNCFuncDyn + +- name: NewobjDynrange + space: ecmascript + class_name: Ecmascript.Intrinsics + method_name: newobjDynrange + static: true + exception: true + signature: + ret: any + args: [u16, any] + impl: panda::ecmascript::intrinsics::NewobjDynrange + +- name: RefeqDyn + space: ecmascript + class_name: Ecmascript.Intrinsics + method_name: refeqDyn + static: true + signature: + ret: any + args: [any, any] + impl: panda::ecmascript::intrinsics::RefeqDyn + +- name: ExpDyn + space: ecmascript + class_name: Ecmascript.Intrinsics + method_name: expDyn + static: true + exception: true + signature: + ret: any + args: [any, acc] + impl: panda::ecmascript::intrinsics::ExpDyn + +- name: TypeofDyn + space: ecmascript + class_name: Ecmascript.Intrinsics + method_name: typeofDyn + static: true + signature: + ret: any + args: [acc] + impl: panda::ecmascript::intrinsics::TypeofDyn + clear_flags: [no_dce] + +- name: Callruntimerange + space: ecmascript + class_name: Ecmascript.Intrinsics + method_name: callruntimerange + static: true + signature: + ret: any + args: [any, any] + impl: panda::ecmascript::intrinsics::Callruntimerange + +- name: IsinDyn + space: ecmascript + class_name: Ecmascript.Intrinsics + method_name: isinDyn + static: true + exception: true + signature: + ret: any + args: [any, acc] + impl: panda::ecmascript::intrinsics::IsinDyn + +- name: InstanceofDyn + space: ecmascript + class_name: Ecmascript.Intrinsics + method_name: instanceofDyn + static: true + exception: true + signature: + ret: any + args: [any, acc] + impl: panda::ecmascript::intrinsics::InstanceofDyn + +- name: StrictNotEqDyn + space: ecmascript + class_name: Ecmascript.Intrinsics + method_name: strictNotEqDyn + static: true + signature: + ret: any + args: [any, acc] + impl: panda::ecmascript::intrinsics::StrictNotEqDyn + clear_flags: [no_dce] + use_thread: false + +- name: StrictEqDyn + space: ecmascript + class_name: Ecmascript.Intrinsics + method_name: strictEqDyn + static: true + signature: + ret: any + args: [any, acc] + impl: panda::ecmascript::intrinsics::StrictEqDyn + use_thread: false + +- name: NewobjspreadDyn + space: ecmascript + class_name: Ecmascript.Intrinsics + method_name: newobjspreadDyn + static: true + exception: true + signature: + ret: any + args: [any, any, acc] + impl: panda::ecmascript::intrinsics::NewobjspreadDyn + +- name: CallspreadDyn + space: ecmascript + class_name: Ecmascript.Intrinsics + method_name: callspreadDyn + static: true + exception: true + signature: + ret: any + args: [any, any, any] + impl: panda::ecmascript::intrinsics::CallspreadDyn + +- name: JfalseDyn + space: ecmascript + class_name: Ecmascript.Intrinsics + method_name: jfalseDyn + static: true + signature: + ret: void + args: [u16, acc] + impl: panda::ecmascript::intrinsics::JfalseDyn + +- name: JtrueDyn + space: ecmascript + class_name: Ecmascript.Intrinsics + method_name: jtrueDyn + static: true + signature: + ret: void + args: [u16, acc] + impl: panda::ecmascript::intrinsics::JtrueDyn + +- name: ReturnDyn + space: ecmascript + class_name: Ecmascript.Intrinsics + method_name: returnDyn + static: true + signature: + ret: any + args: [any] + impl: panda::ecmascript::intrinsics::ReturnDyn + +- name: NewlexenvDyn + space: ecmascript + class_name: Ecmascript.Intrinsics + method_name: newlexenvDyn + static: true + exception: true + signature: + ret: any + args: [u16] + impl: panda::ecmascript::intrinsics::NewlexenvDyn + +- name: CopylexenvDyn + space: ecmascript + class_name: Ecmascript.Intrinsics + method_name: copylexenvDyn + static: true + signature: + ret: any + args: [] + impl: panda::ecmascript::intrinsics::CopylexenvDyn + +- name: StLexVarDyn + space: ecmascript + class_name: Ecmascript.Intrinsics + method_name: StLexVarDyn + static: true + exception: true + signature: + ret: void + args: [u16, u16, acc] + impl: panda::ecmascript::intrinsics::StLexVarDyn + +- name: LdLexVarDyn + space: ecmascript + class_name: Ecmascript.Intrinsics + method_name: LdLexVarDyn + static: true + exception: true + signature: + ret: any + args: [u16, u16] + impl: panda::ecmascript::intrinsics::LdLexVarDyn + clear_flags: [no_dce] + codegen_func: LdLexVarDyn + +- name: LdlexenvDyn + space: ecmascript + class_name: Ecmascript.Intrinsics + method_name: ldlexenvDyn + static: true + exception: true + signature: + ret: any + args: [] + impl: panda::ecmascript::intrinsics::LdlexenvDyn + clear_flags: [no_dce] + codegen_func: LdlexenvDyn + +- name: PopLexenvDyn + space: ecmascript + class_name: Ecmascript.Intrinsics + method_name: popLexenvDyn + static: true + signature: + ret: void + args: [] + impl: panda::ecmascript::intrinsics::popLexenvDyn + +- name: GetUnmappedArgs + space: ecmascript + class_name: Ecmascript.Intrinsics + method_name: getUnmappedArgs + static: true + exception: true + signature: + ret: any + args: [] + impl: panda::ecmascript::intrinsics::GetUnmappedArgs + +- name: Toboolean + space: ecmascript + class_name: Ecmascript.Intrinsics + method_name: toboolean + static: true + signature: + ret: any + args: [acc] + impl: panda::ecmascript::intrinsics::Toboolean + clear_flags: [no_dce] + use_thread: false + +- name: Negate + space: ecmascript + class_name: Ecmascript.Intrinsics + method_name: negate + static: true + exception: true + signature: + ret: any + args: [acc] + impl: panda::ecmascript::intrinsics::Negate + clear_flags: [no_dce] + use_thread: false + +- name: IsUndefined + space: ecmascript + class_name: Ecmascript.Intrinsics + method_name: isUndefined + static: true + exception: true + signature: + ret: any + args: [acc] + impl: panda::ecmascript::intrinsics::IsUndefined + clear_flags: [no_dce] + use_thread: false + +- name: IsTrue + space: ecmascript + class_name: Ecmascript.Intrinsics + method_name: isTrue + static: true + exception: true + signature: + ret: any + args: [acc] + impl: panda::ecmascript::intrinsics::IsTrue + clear_flags: [no_dce] + use_thread: false + +- name: IsFalse + space: ecmascript + class_name: Ecmascript.Intrinsics + method_name: isFalse + static: true + exception: true + signature: + ret: any + args: [acc] + impl: panda::ecmascript::intrinsics::IsFalse + clear_flags: [no_dce] + use_thread: false + +- name: IsCoercible + space: ecmascript + class_name: Ecmascript.Intrinsics + method_name: isCoercible + static: true + exception: true + signature: + ret: any + args: [acc] + impl: panda::ecmascript::intrinsics::IsCoercible + clear_flags: [no_dce] + use_thread: false + +- name: GetPropIterator + space: ecmascript + class_name: Ecmascript.Intrinsics + method_name: getPropIterator + static: true + exception: true + signature: + ret: any + args: [acc] + impl: panda::ecmascript::intrinsics::GetPropIterator + +- name: DefineGeneratorFunc + space: ecmascript + class_name: Ecmascript.Intrinsics + method_name: defineGeneratorFunc + static: true + exception: true + signature: + ret: any + args: [method_id, any] + impl: panda::ecmascript::intrinsics::DefineGeneratorFunc + +- name: CreateIterResultObj + space: ecmascript + class_name: Ecmascript.Intrinsics + method_name: createIterResultObj + static: true + exception: true + signature: + ret: any + args: [acc, u8] + impl: panda::ecmascript::intrinsics::CreateIterResultObj + +- name: SuspendGenerator + space: ecmascript + class_name: Ecmascript.Intrinsics + method_name: suspendGenerator + static: true + exception: true + signature: + ret: any + args: [any, acc] + impl: panda::ecmascript::intrinsics::SuspendGenerator + +- name: SuspendAsyncGenerator + space: ecmascript + class_name: Ecmascript.Intrinsics + method_name: suspendAsyncGenerator + static: true + exception: true + signature: + ret: any + args: [any, acc] + impl: panda::ecmascript::intrinsics::SuspendAsyncGenerator + +- name: ResumeGenerator + space: ecmascript + class_name: Ecmascript.Intrinsics + method_name: resumeGenerator + static: true + exception: true + signature: + ret: any + args: [any] + impl: panda::ecmascript::intrinsics::ResumeGenerator + clear_flags: [no_dce] + use_thread: false + +- name: GetResumeMode + space: ecmascript + class_name: Ecmascript.Intrinsics + method_name: getResumeMode + static: true + exception: true + signature: + ret: any + args: [any] + impl: panda::ecmascript::intrinsics::GetResumeMode + clear_flags: [no_dce] + use_thread: false + +- name: CreateGeneratorObj + space: ecmascript + class_name: Ecmascript.Intrinsics + method_name: createGeneratorObj + static: true + exception: true + signature: + ret: any + args: [any] + impl: panda::ecmascript::intrinsics::CreateGeneratorObj + +- name: SetGeneratorState + space: ecmascript + class_name: Ecmascript.Intrinsics + method_name: setGeneratorState + static: true + exception: true + signature: + ret: any + args: [any, u8] + impl: panda::ecmascript::intrinsics::SetGeneratorState + +- name: CreateAsyncGeneratorObj + space: ecmascript + class_name: Ecmascript.Intrinsics + method_name: createAsyncGeneratorObj + static: true + exception: true + signature: + ret: any + args: [any] + impl: panda::ecmascript::intrinsics::CreateAsyncGeneratorObj + +- name: DefineAsyncGeneratorFunc + space: ecmascript + class_name: Ecmascript.Intrinsics + method_name: defineAsyncGeneratorFunc + static: true + exception: true + signature: + ret: any + args: [method_id, any] + impl: panda::ecmascript::intrinsics::DefineAsyncGeneratorFunc + +- name: DefineAsyncFunc + space: ecmascript + class_name: Ecmascript.Intrinsics + method_name: defineAsyncFunc + static: true + exception: true + signature: + ret: any + args: [method_id, any] + impl: panda::ecmascript::intrinsics::DefineAsyncFunc + +- name: AsyncFunctionEnter + space: ecmascript + class_name: Ecmascript.Intrinsics + method_name: asyncFunctionEnter + static: true + exception: true + signature: + ret: any + args: [] + impl: panda::ecmascript::intrinsics::AsyncFunctionEnter + +- name: AsyncFunctionAwait + space: ecmascript + class_name: Ecmascript.Intrinsics + method_name: asyncFunctionAwait + static: true + exception: true + signature: + ret: any + args: [any, acc] + impl: panda::ecmascript::intrinsics::AsyncFunctionAwait + +- name: AsyncFunctionResolve + space: ecmascript + class_name: Ecmascript.Intrinsics + method_name: asyncFunctionResolve + static: true + exception: true + signature: + ret: any + args: [any, acc] + impl: panda::ecmascript::intrinsics::AsyncFunctionResolve + +- name: AsyncFunctionReject + space: ecmascript + class_name: Ecmascript.Intrinsics + method_name: asyncFunctionReject + exception: true + static: true + signature: + ret: any + args: [any, acc] + impl: panda::ecmascript::intrinsics::AsyncFunctionReject + +- name: AsyncGeneratorResolve + space: ecmascript + class_name: Ecmascript.Intrinsics + method_name: asyncGeneratorResolve + static: true + exception: true + signature: + ret: any + args: [any, acc] + impl: panda::ecmascript::intrinsics::AsyncGeneratorResolve + +- name: AsyncGeneratorReject + space: ecmascript + class_name: Ecmascript.Intrinsics + method_name: asyncGeneratorReject + exception: true + static: true + signature: + ret: any + args: [any, acc] + impl: panda::ecmascript::intrinsics::AsyncGeneratorReject + +- name: ThrowUndefined + space: ecmascript + class_name: Ecmascript.Intrinsics + method_name: throwUndefined + static: true + exception: true + signature: + ret: void + args: [any] + impl: panda::ecmascript::intrinsics::ThrowUndefined + +- name: ThrowConstAssignment + space: ecmascript + class_name: Ecmascript.Intrinsics + method_name: throwConstAssignment + static: true + exception: true + signature: + ret: void + args: [string_id] + impl: panda::ecmascript::intrinsics::ThrowConstAssignment + +- name: ThrowUndefinedIfHole + space: ecmascript + class_name: Ecmascript.Intrinsics + method_name: throwUndefinedIfHole + static: true + exception: true + signature: + ret: any + args: [string_id, acc] + impl: panda::ecmascript::intrinsics::ThrowUndefinedIfHole + +- name: Copyrestargs + space: ecmascript + class_name: Ecmascript.Intrinsics + method_name: copyrestargs + static: true + exception: true + signature: + ret: any + args: [u16] + impl: panda::ecmascript::intrinsics::Copyrestargs + +- name: Ldhole + space: ecmascript + class_name: Ecmascript.Intrinsics + method_name: ldHole + static: true + signature: + ret: any + args: [] + clear_flags: [no_dce] + impl: panda::ecmascript::intrinsics::Ldhole + use_thread: false + +- name: TryStGlobalByValue + space: ecmascript + class_name: Ecmascript.Intrinsics + method_name: tryStGlobalByValue + exception: true + static: true + signature: + ret: void + args: [u16, any, acc] + impl: panda::ecmascript::intrinsics::TryStGlobalByValue + +- name: GetMethod + space: ecmascript + class_name: Ecmascript.Intrinsics + method_name: getMethod + static: true + exception: true + signature: + ret: any + args: [string_id, any] + impl: panda::ecmascript::intrinsics::GetMethod + +- name: GetTemplateObject + space: ecmascript + class_name: Ecmascript.Intrinsics + method_name: getTemplateObject + static: true + exception: true + signature: + ret: any + args: [any] + impl: panda::ecmascript::intrinsics::GetTemplateObject + +- name: TryLdGlobalByName + space: ecmascript + class_name: Ecmascript.Intrinsics + method_name: TryLdGlobalByName + static: true + exception: true + signature: + ret: any + args: [string_id, u16] + impl: panda::ecmascript::intrinsics::TryLdGlobalByName + +- name: TryStGlobalByName + space: ecmascript + class_name: Ecmascript.Intrinsics + method_name: TryStGlobalByName + static: true + exception: true + signature: + ret: any + args: [string_id, acc, u16] + impl: panda::ecmascript::intrinsics::TryStGlobalByName + +- name: LdGlobalVar + space: ecmascript + class_name: Ecmascript.Intrinsics + method_name: LdGlobalVar + static: true + exception: true + signature: + ret: any + args: [string_id, u16] + impl: panda::ecmascript::intrinsics::LdGlobalVar + +- name: StGlobalVar + space: ecmascript + class_name: Ecmascript.Intrinsics + method_name: StGlobalVar + static: true + exception: true + signature: + ret: any + args: [string_id, acc, u16] + impl: panda::ecmascript::intrinsics::StGlobalVar + +- name: StGlobalLet + space: ecmascript + class_name: Ecmascript.Intrinsics + method_name: StGlobalLet + static: true + exception: true + signature: + ret: any + args: [string_id, acc] + impl: panda::ecmascript::intrinsics::StGlobalLet + +- name: LdObjByName + space: ecmascript + class_name: Ecmascript.Intrinsics + method_name: LdObjByName + static: true + exception: true + signature: + ret: any + args: [string_id, any, u16] + impl: panda::ecmascript::intrinsics::LdObjByName + +- name: StObjByName + space: ecmascript + class_name: Ecmascript.Intrinsics + method_name: StObjByName + static: true + exception: true + signature: + ret: any + args: [string_id, any, acc, u16] + impl: panda::ecmascript::intrinsics::StObjByName + +- name: LdObjByIndex + space: ecmascript + class_name: Ecmascript.Intrinsics + method_name: LdObjByIndex + static: true + exception: true + signature: + ret: any + args: [u32, any] + impl: panda::ecmascript::intrinsics::LdObjByIndex + +- name: StObjByIndex + space: ecmascript + class_name: Ecmascript.Intrinsics + method_name: StObjByIndex + static: true + exception: true + signature: + ret: any + args: [u32, any, acc] + impl: panda::ecmascript::intrinsics::StObjByIndex + +- name: GetNextPropName + space: ecmascript + class_name: Ecmascript.Intrinsics + method_name: getnextpropname + static: true + exception: true + signature: + ret: any + args: [any] + impl: panda::ecmascript::intrinsics::GetNextPropName + +- name: ReturnUndefined + space: ecmascript + class_name: Ecmascript.Intrinsics + method_name: ReturnUndefined + static: true + signature: + ret: void + args: [] + impl: panda::ecmascript::intrinsics::ReturnUndefined + +- name: Call0Dyn + space: ecmascript + class_name: Ecmascript.Intrinsics + method_name: Call0Dyn + static: true + exception: true + signature: + ret: any + args: [any] + impl: panda::ecmascript::intrinsics::Call0Dyn + +- name: Call1Dyn + space: ecmascript + class_name: Ecmascript.Intrinsics + method_name: Call1Dyn + static: true + exception: true + signature: + ret: any + args: [any, any] + impl: panda::ecmascript::intrinsics::Call1Dyn + +- name: Call2Dyn + space: ecmascript + class_name: Ecmascript.Intrinsics + method_name: Call2Dyn + static: true + exception: true + signature: + ret: any + args: [any, any, any] + impl: panda::ecmascript::intrinsics::Call2Dyn + +- name: Call3Dyn + space: ecmascript + class_name: Ecmascript.Intrinsics + method_name: Call3Dyn + static: true + exception: true + signature: + ret: any + args: [any, any, any, any] + impl: panda::ecmascript::intrinsics::Call3Dyn + +- name: CalliRangeDyn + space: ecmascript + class_name: Ecmascript.Intrinsics + method_name: CalliRangeDyn + static: true + exception: true + signature: + ret: any + args: [u16, any] + impl: panda::ecmascript::intrinsics::CalliRangeDyn + +- name: CalliThisRangeDyn + space: ecmascript + class_name: Ecmascript.Intrinsics + method_name: CalliThisRangeDyn + static: true + exception: true + signature: + ret: any + args: [u16, any] + impl: panda::ecmascript::intrinsics::CalliThisRangeDyn + +- name: CreateEmptyObject + space: ecmascript + class_name: Ecmascript.Intrinsics + method_name: createemptyobject + static: true + exception: true + signature: + ret: any + args: [] + impl: panda::ecmascript::intrinsics::CreateEmptyObject + clear_flags: [no_dce] + +- name: CreateObjectWithBuffer + space: ecmascript + class_name: Ecmascript.Intrinsics + method_name: createobjectwithbuffer + static: true + exception: true + signature: + ret: any + args: [u16] + impl: panda::ecmascript::intrinsics::CreateObjectWithBuffer + +- name: CopyDataProperties + space: ecmascript + class_name: Ecmascript.Intrinsics + method_name: copydataproperties + static: true + exception: true + signature: + ret: any + args: [any, any] + impl: panda::ecmascript::intrinsics::CopyDataProperties + +- name: DefineGetterSetterByValue + space: ecmascript + class_name: Ecmascript.Intrinsics + method_name: definegettersetterbyvalue + static: true + exception: true + signature: + ret: any + args: [any, any, any, any, acc] + impl: panda::ecmascript::intrinsics::DefineGetterSetterByValue + +- name: CreateEmptyArray + space: ecmascript + class_name: Ecmascript.Intrinsics + method_name: createemptyarray + static: true + exception: true + signature: + ret: any + args: [] + impl: panda::ecmascript::intrinsics::CreateEmptyArray + clear_flags: [no_dce] + +- name: CreateArrayWithBuffer + space: ecmascript + class_name: Ecmascript.Intrinsics + method_name: createarraywithbuffer + static: true + exception: true + signature: + ret: any + args: [u16] + impl: panda::ecmascript::intrinsics::CreateArrayWithBuffer + +- name: StOwnByName + space: ecmascript + class_name: Ecmascript.Intrinsics + method_name: StOwnByName + static: true + exception: true + signature: + ret: any + args: [string_id, any, acc] + impl: panda::ecmascript::intrinsics::StOwnByName + +- name: StOwnByIndex + space: ecmascript + class_name: Ecmascript.Intrinsics + method_name: StOwnByIndex + static: true + exception: true + signature: + ret: any + args: [u32, any, acc] + impl: panda::ecmascript::intrinsics::StOwnByIndex + +- name: StOwnByValue + space: ecmascript + class_name: Ecmascript.Intrinsics + method_name: StOwnByValue + static: true + exception: true + signature: + ret: any + args: [any, any, acc] + impl: panda::ecmascript::intrinsics::StOwnByValue + +- name: StArraySpread + space: ecmascript + class_name: Ecmascript.Intrinsics + method_name: starrayspread + static: true + exception: true + signature: + ret: any + args: [any, any, acc] + impl: panda::ecmascript::intrinsics::StArraySpread + +- name: GetIterator + space: ecmascript + class_name: Ecmascript.Intrinsics + method_name: GetIterator + static: true + exception: true + signature: + ret: any + args: [acc] + impl: panda::ecmascript::intrinsics::GetIterator + +- name: GetAsyncIterator + space: ecmascript + class_name: Ecmascript.Intrinsics + method_name: GetAsyncIterator + static: true + exception: true + signature: + ret: any + args: [acc] + impl: panda::ecmascript::intrinsics::GetAsyncIterator + +- name: ThrowIfNotObject + space: ecmascript + class_name: Ecmascript.Intrinsics + method_name: ThrowIfNotObject + static: true + exception: true + signature: + ret: any + args: [acc] + impl: panda::ecmascript::intrinsics::ThrowIfNotObject + +- name: ThrowThrowNotExists + space: ecmascript + class_name: Ecmascript.Intrinsics + method_name: ThrowThrowNotExists + static: true + exception: true + signature: + ret: void + args: [] + impl: panda::ecmascript::intrinsics::ThrowThrowNotExists + +- name: CreateObjectWithExcludedKeys + space: ecmascript + class_name: Ecmascript.Intrinsics + method_name: CreateObjectWithExcludedKeys + static: true + exception: true + range: true + signature: + ret: any + args: [u16, any, any] + impl: panda::ecmascript::intrinsics::CreateObjectWithExcludedKeys + +- name: ThrowPatternNonCoercible + space: ecmascript + class_name: Ecmascript.Intrinsics + method_name: ThrowPatternNonCoercible + static: true + exception: true + signature: + ret: void + args: [] + impl: panda::ecmascript::intrinsics::ThrowPatternNonCoercible + +- name: CloseIterator + space: ecmascript + class_name: Ecmascript.Intrinsics + method_name: CloseIterator + static: true + exception: true + signature: + ret: any + args: [any, acc] + impl: panda::ecmascript::intrinsics::CloseIterator + +- name: ImportModule + space: ecmascript + class_name: Ecmascript.Intrinsics + method_name: ImportModule + static: true + exception: true + signature: + ret: any + args: [string_id] + impl: panda::ecmascript::intrinsics::ImportModule + clear_flags: [no_dce] + +- name: StModuleVar + space: ecmascript + class_name: Ecmascript.Intrinsics + method_name: StModuleVar + static: true + exception: true + signature: + ret: void + args: [string_id, acc] + impl: panda::ecmascript::intrinsics::StModuleVar + +- name: CopyModule + space: ecmascript + class_name: Ecmascript.Intrinsics + method_name: CopyModule + static: true + exception: true + signature: + ret: void + args: [any] + impl: panda::ecmascript::intrinsics::CopyModule + +- name: LdModvarByName + space: ecmascript + class_name: Ecmascript.Intrinsics + method_name: LdModvarByName + static: true + exception: true + signature: + ret: any + args: [string_id, any] + impl: panda::ecmascript::intrinsics::LdModvarByName + clear_flags: [no_dce] + +- name: DefineClassWithBuffer + space: ecmascript + class_name: Ecmascript.Intrinsics + method_name: DefineClassWithBuffer + static: true + exception: true + signature: + ret: any + args: [method_id, u16, any, any] + impl: panda::ecmascript::intrinsics::DefineClassWithBuffer + +- name: SuperCall + space: ecmascript + class_name: Ecmascript.Intrinsics + method_name: SuperCall + static: true + exception: true + signature: + ret: any + args: [u16, any, acc] + impl: panda::ecmascript::intrinsics::SuperCall + +- name: SuperCallSpread + space: ecmascript + class_name: Ecmascript.Intrinsics + method_name: SuperCallSpread + static: true + exception: true + signature: + ret: any + args: [any, any, acc] + impl: panda::ecmascript::intrinsics::SuperCallSpread + +- name: DefineMethod + space: ecmascript + class_name: Ecmascript.Intrinsics + method_name: defineMethod + static: true + exception: true + signature: + ret: any + args: [method_id, any, acc] + impl: panda::ecmascript::intrinsics::DefineMethod + +- name: LdSuperByName + space: ecmascript + class_name: Ecmascript.Intrinsics + method_name: LdSuperByName + static: true + exception: true + signature: + ret: any + args: [string_id, any] + impl: panda::ecmascript::intrinsics::LdSuperByName + +- name: StSuperByName + space: ecmascript + class_name: Ecmascript.Intrinsics + method_name: StSuperByName + static: true + exception: true + signature: + ret: any + args: [string_id, any, any] + impl: panda::ecmascript::intrinsics::StSuperByName + +- name: StSuperByValue + space: ecmascript + class_name: Ecmascript.Intrinsics + method_name: StSuperByValue + static: true + exception: true + signature: + ret: any + args: [any, any, acc] + impl: panda::ecmascript::intrinsics::StSuperByValue + +- name: LdSuperByValue + space: ecmascript + class_name: Ecmascript.Intrinsics + method_name: LdSuperByValue + static: true + exception: true + signature: + ret: any + args: [any, any] + impl: panda::ecmascript::intrinsics::LdSuperByValue + +- name: CreateObjectHavingMethod + space: ecmascript + class_name: Ecmascript.Intrinsics + method_name: createobjecthavingmethod + static: true + exception: true + signature: + ret: any + args: [u16, acc] + impl: panda::ecmascript::intrinsics::CreateObjectHavingMethod + +- name: ThrowIfSuperNotCorrectCall + space: ecmascript + class_name: Ecmascript.Intrinsics + method_name: ThrowIfSuperNotCorrectCall + static: true + exception: true + signature: + ret: any + args: [u16, acc] + impl: panda::ecmascript::intrinsics::ThrowIfSuperNotCorrectCall + +- name: LdHomeObject + space: ecmascript + class_name: Ecmascript.Intrinsics + method_name: LdHomeObject + static: true + signature: + ret: any + args: [] + impl: panda::ecmascript::intrinsics::LdHomeObject + clear_flags: [no_dce] + +- name: SetObjectWithProto + space: ecmascript + class_name: Ecmascript.Intrinsics + method_name: setobjectwithproto + static: true + exception: true + signature: + ret: any + args: [any, any] + impl: panda::ecmascript::intrinsics::SetObjectWithProto + +- name: ThrowDeleteSuperProperty + space: ecmascript + class_name: Ecmascript.Intrinsics + method_name: ThrowDeleteSuperProperty + static: true + exception: true + signature: + ret: void + args: [] + impl: panda::ecmascript::intrinsics::ThrowDeleteSuperProperty + +- name: Debugger + space: ecmascript + class_name: Ecmascript.Intrinsics + method_name: debugger + static: true + signature: + ret: void + args: [] + impl: panda::ecmascript::intrinsics::Debugger + use_thread: false + +- name: NativeMethodWrapper + space: ecmascript + class_name: Ecmascript.Intrinsics + method_name: NativeMethodWrapper + static: true + exception: true + signature: + ret: void + args: [any] + impl: panda::ecmascript::intrinsics::NativeMethodWrapper + use_thread: false + +- name: GetObjectClassType + space: ecmascript + class_name: Ecmascript.Intrinsics + method_name: GetObjectClassType + static: true + codegen_func: GetObjectClassTypeIntrinsic + signature: + ret: void + args: [any] + impl: panda::ecmascript::intrinsics::GetObjectClassType + use_thread: false + clear_flags: [no_dce, barrier, require_state, runtime_call] + +- name: IsNan + space: ecmascript + class_name: Ecmascript.Intrinsics + method_name: IsNan + static: true + signature: + ret: any + args: [f64] + impl: panda::ecmascript::intrinsics::IsNan + use_thread: false + +- name: GetEcmaConstantPool + space: ecmascript + class_name: Ecmascript.Intrinsics + method_name: GetEcmaConstantPool + static: true + codegen_func: GetEcmaConstantPool + signature: + ret: any + args: [] + impl: panda::ecmascript::intrinsics::GetEcmaConstantPool + clear_flags: [no_dce, barrier, require_state, runtime_call] + +- name: GetEcmaThisFunc + space: ecmascript + class_name: Ecmascript.Intrinsics + method_name: GetEcmaThisFunc + static: true + codegen_func: GetEcmaThisFunc + signature: + ret: any + args: [] + impl: panda::ecmascript::intrinsics::GetEcmaThisFunc + clear_flags: [no_dce, barrier, require_state, runtime_call] + +- name: GetWeakReferent + space: ecmascript + class_name: Ecmascript.Intrinsics + method_name: GetWeakReferent + static: true + codegen_func: GetWeakReferent + signature: + ret: any + args: [any] + impl: panda::ecmascript::intrinsics::GetWeakReferent + use_thread: false + clear_flags: [no_dce, barrier, require_state, runtime_call] + +- name: DynClassIsDictionaryElement + space: ecmascript + class_name: Ecmascript.Intrinsics + method_name: DynClassIsDictionaryElement + static: true + signature: + ret: u1 + args: [any] + impl: panda::ecmascript::intrinsics::DynClassIsDictionaryElement + codegen_func: CreateDynClassIsDictionaryElement + use_thread: false + clear_flags: [no_dce, barrier, require_state, runtime_call] + +- name: DynClassIsExtensible + space: ecmascript + class_name: Ecmascript.Intrinsics + method_name: DynClassIsExtensible + static: true + signature: + ret: u1 + args: [any] + impl: panda::ecmascript::intrinsics::DynClassIsExtensible + codegen_func: CreateDynClassIsExtensible + use_thread: false + clear_flags: [no_dce, barrier, require_state, runtime_call] + +- name: DynObjectGetClass + space: ecmascript + class_name: Ecmascript.Intrinsics + method_name: DynObjectGetClass + static: true + signature: + ret: any + args: [any] + impl: panda::ecmascript::intrinsics::DynObjectGetClass + codegen_func: CreateDynObjectGetClass + use_thread: false + clear_flags: [no_dce, barrier, require_state, runtime_call] + +- name: DynObjectSetClass + space: ecmascript + class_name: Ecmascript.Intrinsics + method_name: DynObjectSetClass + static: true + signature: + ret: void + args: [any, any] + impl: panda::ecmascript::intrinsics::DynObjectSetClass + codegen_func: CreateDynObjectSetClass + use_thread: false + clear_flags: [require_state, runtime_call] diff --git a/runtime/ecma_runtime_call_info.h b/runtime/ecma_runtime_call_info.h new file mode 100644 index 000000000..d5272093a --- /dev/null +++ b/runtime/ecma_runtime_call_info.h @@ -0,0 +1,128 @@ +/* + * Copyright (c) 2021-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_ECMA_RUNTIM_CALL_INFO_H +#define ECMASCRIPT_ECMA_RUNTIM_CALL_INFO_H + +#include + +#include "plugins/ecmascript/runtime/common.h" +#include "plugins/ecmascript/runtime/js_tagged_value.h" +#include "plugins/ecmascript/runtime/js_thread.h" +#include "js_handle.h" + +namespace panda::ecmascript { +class EcmaRuntimeCallInfo; +using EcmaEntrypoint = JSTaggedValue (*)(EcmaRuntimeCallInfo *); + +class EcmaRuntimeCallInfo { +public: + // For builtins interpreter call + EcmaRuntimeCallInfo(JSThread *thread, uint32_t numArgs, interpreter::VRegister *args) + : thread_(thread), numArgs_(numArgs), gprArgs_(args, numArgs), stackArgs_(nullptr, static_cast(0)) + { + ASSERT(numArgs_ >= NUM_MANDATORY_JSFUNC_ARGS); + } + + ~EcmaRuntimeCallInfo() = default; + + inline JSThread *GetThread() const + { + return thread_; + } + + inline void SetNewTarget(JSTaggedValue tagged, [[maybe_unused]] int64_t tag = 0) + { + SetArg(NEW_TARGET_INDEX, tagged); + } + + inline void SetFunction(JSTaggedValue tagged, [[maybe_unused]] int64_t tag = 0) + { + SetArg(FUNC_INDEX, tagged); + } + + inline void SetThis(JSTaggedValue tagged, [[maybe_unused]] int64_t tag = 0) + { + SetArg(THIS_INDEX, tagged); + } + + inline void SetCallArg(uint32_t idx, JSTaggedValue tagged, [[maybe_unused]] int64_t tag = 0) + { + ASSERT_PRINT(idx < GetArgsNumber(), "Can not set values out of index range"); + SetArg(idx + FIRST_ARGS_INDEX, tagged); + } + + inline JSHandle GetArg(uint32_t idx) const + { + return JSHandle(GetArgAddress(idx)); + } + + inline JSHandle GetFunction() const + { + return GetArg(FUNC_INDEX); + } + + inline JSHandle GetNewTarget() const + { + return GetArg(NEW_TARGET_INDEX); + } + + inline JSHandle GetThis() const + { + return GetArg(THIS_INDEX); + } + + inline JSHandle GetCallArg(uint32_t idx) const + { + return GetArg(idx + FIRST_ARGS_INDEX); + } + + /* + * The number of arguments pairs excluding the 'func', 'new.target' and 'this'. For instance: + * for code fragment: " foo(v1); ", GetArgsNumber() returns 1 + */ + inline uint32_t GetArgsNumber() const + { + return numArgs_ - NUM_MANDATORY_JSFUNC_ARGS; + } + + inline uintptr_t GetArgAddress(uint32_t idx) const + { + if (idx < gprArgs_.size()) { + return reinterpret_cast(&gprArgs_[idx]); + } + return reinterpret_cast(&stackArgs_[idx - gprArgs_.size()]); + } + +private: + DEFAULT_COPY_SEMANTIC(EcmaRuntimeCallInfo); + DEFAULT_MOVE_SEMANTIC(EcmaRuntimeCallInfo); + + enum ArgsIndex : uint8_t { FUNC_INDEX = 0, NEW_TARGET_INDEX, THIS_INDEX, FIRST_ARGS_INDEX }; + + inline void SetArg(uint32_t idx, JSTaggedValue tagged) + { + *reinterpret_cast(GetArgAddress(idx)) = tagged; + } + +private: + JSThread *thread_; + uint32_t numArgs_; + Span gprArgs_; + Span stackArgs_; +}; +} // namespace panda::ecmascript + +#endif // ECMASCRIPT_ECMA_RUNTIM_CALL_INFO_H diff --git a/runtime/ecma_string-inl.h b/runtime/ecma_string-inl.h new file mode 100644 index 000000000..af630fe8d --- /dev/null +++ b/runtime/ecma_string-inl.h @@ -0,0 +1,164 @@ +/* + * Copyright (c) 2021-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_STRING_INL_H +#define ECMASCRIPT_STRING_INL_H + +#include "plugins/ecmascript/runtime/ecma_string.h" +#include "plugins/ecmascript/runtime/ecma_vm.h" +#include "plugins/ecmascript/runtime/object_factory-inl.h" +#include "plugins/ecmascript/runtime/js_handle.h" +#include "plugins/ecmascript/runtime/js_tagged_value-inl.h" + +namespace panda::ecmascript { +/* static */ +inline EcmaString *EcmaString::Cast(ObjectHeader *object) +{ + ASSERT(JSTaggedValue(object).IsString()); + return static_cast(object); +} + +/* static */ +inline const EcmaString *EcmaString::ConstCast(const TaggedObject *object) +{ + ASSERT(JSTaggedValue(object).IsString()); + return static_cast(object); +} + +/* static */ +inline EcmaString *EcmaString::CreateEmptyString(const EcmaVM *vm) +{ + auto string = vm->GetFactory()->AllocNonMovableStringObject(EcmaString::SIZE); + string->SetLength(0, GetCompressedStringsEnabled()); + string->SetRawHashcode(0); + return string; +} + +/* static */ +inline EcmaString *EcmaString::CreateFromUtf8(const uint8_t *utf8Data, uint32_t utf8Len, const EcmaVM *vm, + bool canBeCompress) +{ + if (utf8Len == 0) { + return vm->GetFactory()->GetEmptyString().GetObject(); + } + EcmaString *string = nullptr; + if (canBeCompress) { + string = AllocStringObject(utf8Len, true, vm); + ASSERT(string != nullptr); + + if (memcpy_s(string->GetDataUtf8Writable(), utf8Len, utf8Data, utf8Len) != EOK) { + LOG_ECMA(FATAL) << "memcpy_s failed"; + UNREACHABLE(); + } + } else { + auto utf16Len = base::utf_helper::Utf8ToUtf16Size(utf8Data, utf8Len); + string = AllocStringObject(utf16Len, false, vm); + ASSERT(string != nullptr); + + [[maybe_unused]] auto len = + base::utf_helper::ConvertRegionUtf8ToUtf16(utf8Data, string->GetDataUtf16Writable(), utf8Len, utf16Len, 0); + ASSERT(len == utf16Len); + } + + return string; +} + +inline EcmaString *EcmaString::CreateFromUtf16(const uint16_t *utf16Data, uint32_t utf16Len, const EcmaVM *vm, + bool canBeCompress) +{ + if (utf16Len == 0) { + return vm->GetFactory()->GetEmptyString().GetObject(); + } + auto string = AllocStringObject(utf16Len, canBeCompress, vm); + ASSERT(string != nullptr); + + if (canBeCompress) { + CopyUtf16AsUtf8(utf16Data, string->GetDataUtf8Writable(), utf16Len); + } else { + uint32_t len = utf16Len * (sizeof(uint16_t) / sizeof(uint8_t)); + if (memcpy_s(string->GetDataUtf16Writable(), len, utf16Data, len) != EOK) { + LOG_ECMA(FATAL) << "memcpy_s failed"; + UNREACHABLE(); + } + } + + return string; +} + +template +inline uint16_t EcmaString::At(int32_t index) const +{ + int32_t length = GetLength(); + if (verify) { + if ((index < 0) || (index >= length)) { + return 0; + } + } + if (!IsUtf16()) { + Span sp(GetDataUtf8(), length); + return sp[index]; + } + Span sp(GetDataUtf16(), length); + return sp[index]; +} + +/* static */ +inline EcmaString *EcmaString::AllocStringObject(size_t length, bool compressed, const EcmaVM *vm) +{ + size_t size = compressed ? ComputeSizeUtf8(length) : ComputeSizeUtf16(length); + auto string = reinterpret_cast(vm->GetFactory()->AllocStringObject(size)); + string->SetLength(length, compressed); + string->SetRawHashcode(0); + return reinterpret_cast(string); +} + +void EcmaString::WriteData(EcmaString *src, uint32_t start, uint32_t destSize, uint32_t length) +{ + if (IsUtf8()) { + ASSERT(src->IsUtf8()); + // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-pointer-arithmetic) + if (length != 0 && memcpy_s(GetDataUtf8Writable() + start, destSize, src->GetDataUtf8(), length) != EOK) { + LOG_ECMA(FATAL) << "memcpy_s failed"; + UNREACHABLE(); + } + } else if (src->IsUtf8()) { + // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-pointer-arithmetic) + Span to(GetDataUtf16Writable() + start, length); + Span from(src->GetDataUtf8(), length); + for (uint32_t i = 0; i < length; i++) { + to[i] = from[i]; + } + } else { + // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-pointer-arithmetic) + if (length != 0 && memcpy_s(GetDataUtf16Writable() + start, destSize, src->GetDataUtf16(), length) != EOK) { + LOG_ECMA(FATAL) << "memcpy_s failed"; + UNREACHABLE(); + } + } +} + +void EcmaString::WriteData(char src, uint32_t start) +{ + if (IsUtf8()) { + // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-pointer-arithmetic) + *(GetDataUtf8Writable() + start) = src; + } else { + // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-pointer-arithmetic) + *(GetDataUtf16Writable() + start) = src; + } +} +} // namespace panda::ecmascript + +#endif diff --git a/runtime/ecma_string.cpp b/runtime/ecma_string.cpp new file mode 100644 index 000000000..0f462774f --- /dev/null +++ b/runtime/ecma_string.cpp @@ -0,0 +1,447 @@ +/* + * Copyright (c) 2021-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 "plugins/ecmascript/runtime/ecma_string-inl.h" + +#include "plugins/ecmascript/runtime/js_symbol.h" +#include "plugins/ecmascript/runtime/mem/c_containers.h" + +namespace panda::ecmascript { +bool EcmaString::compressedStringsEnabled = true; +static constexpr int SMALL_STRING_SIZE = 128; + +EcmaString *EcmaString::Concat(const JSHandle &str1Handle, const JSHandle &str2Handle, + const EcmaVM *vm) +{ + // allocator may trig gc and move src, need to hold it + EcmaString *string1 = *str1Handle; + EcmaString *string2 = *str2Handle; + + uint32_t length1 = string1->GetLength(); + + uint32_t length2 = string2->GetLength(); + uint32_t newLength = length1 + length2; + if (newLength == 0) { + return vm->GetFactory()->GetEmptyString().GetObject(); + } + bool compressed = GetCompressedStringsEnabled() && (!string1->IsUtf16() && !string2->IsUtf16()); + auto newString = AllocStringObject(newLength, compressed, vm); + + // retrieve strings after gc + string1 = *str1Handle; + string2 = *str2Handle; + if (compressed) { + Span sp(newString->GetDataUtf8Writable(), newLength); + Span src1(string1->GetDataUtf8(), length1); + EcmaString::StringCopy(sp, newLength, src1, length1); + + sp = sp.SubSpan(length1); + Span src2(string2->GetDataUtf8(), length2); + EcmaString::StringCopy(sp, newLength - length1, src2, length2); + } else { + Span sp(newString->GetDataUtf16Writable(), newLength); + if (!string1->IsUtf16()) { + for (uint32_t i = 0; i < length1; ++i) { + sp[i] = string1->At(i); + } + } else { + Span src1(string1->GetDataUtf16(), length1); + EcmaString::StringCopy(sp, newLength << 1U, src1, length1 << 1U); + } + sp = sp.SubSpan(length1); + if (!string2->IsUtf16()) { + for (uint32_t i = 0; i < length2; ++i) { + sp[i] = string2->At(i); + } + } else { + uint32_t length = length2 << 1U; + Span src2(string2->GetDataUtf16(), length2); + EcmaString::StringCopy(sp, length, src2, length); + } + } + + return newString; +} + +/* static */ +EcmaString *EcmaString::FastSubString(const JSHandle &src, uint32_t start, uint32_t utf16Len, + const EcmaVM *vm) +{ + if (utf16Len == 0) { + return vm->GetFactory()->GetEmptyString().GetObject(); + } + // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-pointer-arithmetic) + bool canBeCompressed = !src->IsUtf16(); + + // allocator may trig gc and move src, need to hold it + auto string = AllocStringObject(utf16Len, canBeCompressed, vm); + + if (src->IsUtf16()) { + if (canBeCompressed) { + // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-pointer-arithmetic) + CopyUtf16AsUtf8(src->GetDataUtf16() + start, string->GetDataUtf8Writable(), utf16Len); + } else { + uint32_t len = utf16Len * (sizeof(uint16_t) / sizeof(uint8_t)); + Span dst(string->GetDataUtf16Writable(), utf16Len); + // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-pointer-arithmetic) + Span source(src->GetDataUtf16() + start, utf16Len); + EcmaString::StringCopy(dst, len, source, len); + } + } else { + Span dst(string->GetDataUtf8Writable(), utf16Len); + // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-pointer-arithmetic) + Span source(src->GetDataUtf8() + start, utf16Len); + EcmaString::StringCopy(dst, utf16Len, source, utf16Len); + } + return string; +} + +template +int32_t CompareStringSpan(Span &lhsSp, Span &rhsSp, int32_t count) +{ + for (int32_t i = 0; i < count; ++i) { + auto left = static_cast(lhsSp[i]); + auto right = static_cast(rhsSp[i]); + if (left != right) { + return left - right; + } + } + return 0; +} + +int32_t EcmaString::Compare(const EcmaString *rhs) const +{ + const EcmaString *lhs = this; + if (lhs == rhs) { + return 0; + } + int32_t lhsCount = lhs->GetLength(); + int32_t rhsCount = rhs->GetLength(); + int32_t countDiff = lhsCount - rhsCount; + int32_t minCount = (countDiff < 0) ? lhsCount : rhsCount; + if (!lhs->IsUtf16() && !rhs->IsUtf16()) { + Span lhsSp(lhs->GetDataUtf8(), lhsCount); + Span rhsSp(rhs->GetDataUtf8(), rhsCount); + int32_t charDiff = CompareStringSpan(lhsSp, rhsSp, minCount); + if (charDiff != 0) { + return charDiff; + } + } else if (!lhs->IsUtf16()) { + Span lhsSp(lhs->GetDataUtf8(), lhsCount); + Span rhsSp(rhs->GetDataUtf16(), rhsCount); + int32_t charDiff = CompareStringSpan(lhsSp, rhsSp, minCount); + if (charDiff != 0) { + return charDiff; + } + } else if (!rhs->IsUtf16()) { + Span lhsSp(lhs->GetDataUtf16(), rhsCount); + Span rhsSp(rhs->GetDataUtf8(), lhsCount); + int32_t charDiff = CompareStringSpan(lhsSp, rhsSp, minCount); + if (charDiff != 0) { + return charDiff; + } + } else { + Span lhsSp(lhs->GetDataUtf16(), lhsCount); + Span rhsSp(rhs->GetDataUtf16(), rhsCount); + int32_t charDiff = CompareStringSpan(lhsSp, rhsSp, minCount); + if (charDiff != 0) { + return charDiff; + } + } + return countDiff; +} + +/* static */ +template +int32_t EcmaString::IndexOf(Span &lhsSp, Span &rhsSp, int32_t pos, int32_t max) +{ + ASSERT(!rhsSp.empty()); + auto first = static_cast(rhsSp[0]); + int32_t i; + for (i = pos; i <= max; i++) { + if (static_cast(lhsSp[i]) != first) { + i++; + while (i <= max && static_cast(lhsSp[i]) != first) { + i++; + } + } + /* Found first character, now look at the rest of rhsSp */ + if (i <= max) { + int j = i + 1; + int end = j + rhsSp.size() - 1; + + for (int k = 1; j < end && static_cast(lhsSp[j]) == static_cast(rhsSp[k]); j++, k++) { + } + if (j == end) { + /* Found whole string. */ + return i; + } + } + } + return -1; +} + +int32_t EcmaString::IndexOf(const EcmaString *rhs, int32_t pos) const +{ + if (rhs == nullptr) { + return -1; + } + const EcmaString *lhs = this; + int32_t lhsCount = lhs->GetLength(); + int32_t rhsCount = rhs->GetLength(); + + if (rhsCount == 0 && pos <= lhsCount) { + return pos; + } + + if (pos >= lhsCount) { + return -1; + } + + if (pos < 0) { + pos = 0; + } + + int32_t max = lhsCount - rhsCount; + if (max < 0) { + return -1; + } + if (rhs->IsUtf8() && lhs->IsUtf8()) { + Span lhsSp(lhs->GetDataUtf8(), lhsCount); + Span rhsSp(rhs->GetDataUtf8(), rhsCount); + return EcmaString::IndexOf(lhsSp, rhsSp, pos, max); + } + 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); + } + if (rhs->IsUtf16()) { + Span lhsSp(lhs->GetDataUtf8(), lhsCount); + Span rhsSp(rhs->GetDataUtf16(), rhsCount); + return EcmaString::IndexOf(lhsSp, rhsSp, pos, max); + } + Span lhsSp(lhs->GetDataUtf16(), lhsCount); + Span rhsSp(rhs->GetDataUtf8(), rhsCount); + return EcmaString::IndexOf(lhsSp, rhsSp, pos, max); + + return -1; +} + +// static +bool EcmaString::CanBeCompressed(const uint8_t *utf8Data, uint32_t utf8Len) +{ + if (!compressedStringsEnabled) { + return false; + } + bool isCompressed = true; + uint32_t index = 0; + // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-pointer-arithmetic) + while (index < utf8Len) { + // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-pointer-arithmetic) + if (!IsASCIICharacter(utf8Data[index])) { + isCompressed = false; + break; + } + ++index; + } + return isCompressed; +} + +/* static */ +bool EcmaString::CanBeCompressed(const uint16_t *utf16Data, uint32_t utf16Len) +{ + if (!compressedStringsEnabled) { + return false; + } + bool isCompressed = true; + Span data(utf16Data, utf16Len); + for (uint32_t i = 0; i < utf16Len; i++) { + if (!IsASCIICharacter(data[i])) { + isCompressed = false; + break; + } + } + return isCompressed; +} + +/* static */ +void EcmaString::CopyUtf16AsUtf8(const uint16_t *utf16From, uint8_t *utf8To, uint32_t utf16Len) +{ + Span from(utf16From, utf16Len); + Span to(utf8To, utf16Len); + for (uint32_t i = 0; i < utf16Len; i++) { + to[i] = from[i]; + } +} + +/* static */ +bool EcmaString::StringsAreEqual(EcmaString *str1, EcmaString *str2) +{ + if ((str1->IsUtf16() != str2->IsUtf16()) || (str1->GetLength() != str2->GetLength()) || + (str1->GetHashcode() != str2->GetHashcode())) { + return false; + } + + if (str1->IsUtf16()) { + Span data1(str1->GetDataUtf16(), str1->GetLength()); + Span data2(str2->GetDataUtf16(), str1->GetLength()); + return EcmaString::StringsAreEquals(data1, data2); + } + Span data1(str1->GetDataUtf8(), str1->GetLength()); + Span data2(str2->GetDataUtf8(), str1->GetLength()); + return EcmaString::StringsAreEquals(data1, data2); +} + +/* static */ +bool EcmaString::StringsAreEqualUtf8(const EcmaString *str1, const uint8_t *utf8Data, uint32_t utf8Len, + bool canBeCompress) +{ + if (canBeCompress != str1->IsUtf8()) { + return false; + } + + if (canBeCompress && str1->GetLength() != utf8Len) { + return false; + } + + if (canBeCompress) { + Span data1(str1->GetDataUtf8(), utf8Len); + Span data2(utf8Data, utf8Len); + return EcmaString::StringsAreEquals(data1, data2); + } + return IsUtf8EqualsUtf16(utf8Data, utf8Len, str1->GetDataUtf16(), str1->GetLength()); +} + +/* static */ +bool EcmaString::StringsAreEqualUtf16(const EcmaString *str1, const uint16_t *utf16Data, uint32_t utf16Len) +{ + bool result = false; + if (str1->GetLength() != utf16Len) { + result = false; + } else if (!str1->IsUtf16()) { + result = IsUtf8EqualsUtf16(str1->GetDataUtf8(), str1->GetLength(), utf16Data, utf16Len); + } else { + Span data1(str1->GetDataUtf16(), str1->GetLength()); + Span data2(utf16Data, utf16Len); + result = EcmaString::StringsAreEquals(data1, data2); + } + return result; +} + +/* static */ +template +bool EcmaString::StringsAreEquals(Span &str1, Span &str2) +{ + ASSERT(str1.Size() <= str2.Size()); + size_t size = str1.Size(); + if (size < SMALL_STRING_SIZE) { + for (size_t i = 0; i < size; i++) { + if (str1[i] != str2[i]) { + return false; + } + } + return true; + } + return !memcmp(str1.data(), str2.data(), size); +} + +template +bool EcmaString::StringCopy(Span &dst, size_t dstMax, Span &src, size_t count) +{ + ASSERT(dstMax >= count); + ASSERT(dst.Size() >= src.Size()); + if (src.Size() < SMALL_STRING_SIZE) { + for (size_t i = 0; i < src.Size(); i++) { + dst[i] = src[i]; + } + return true; + } + if (memcpy_s(dst.data(), dstMax, src.data(), count) != EOK) { + LOG_ECMA(FATAL) << "memcpy_s failed"; + UNREACHABLE(); + } + return true; +} + +static int32_t ComputeHashForUtf8(const uint8_t *utf8Data) +{ + if (utf8Data == nullptr) { + return 0; + } + uint32_t hash = 0; + while (*utf8Data != '\0') { // NOLINT(cppcoreguidelines-pro-bounds-pointer-arithmetic) + constexpr size_t SHIFT = 5; + hash = (hash << SHIFT) - hash + *utf8Data++; // NOLINT(cppcoreguidelines-pro-bounds-pointer-arithmetic) + } + return static_cast(hash); +} + +uint32_t EcmaString::ComputeHashcode() const +{ + uint32_t hash; + if (compressedStringsEnabled) { + if (!IsUtf16()) { + hash = ComputeHashForData(GetDataUtf8(), GetLength()); + } else { + hash = ComputeHashForData(GetDataUtf16(), GetLength()); + } + } else { + ASSERT(static_cast(GetLength()) < (std::numeric_limits::max() >> 1U)); + hash = ComputeHashForData(GetDataUtf16(), GetLength()); + } + return hash; +} + +/* static */ +uint32_t EcmaString::ComputeHashcodeUtf8(const uint8_t *utf8Data, size_t utf8Len, bool canBeCompress) +{ + uint32_t hash; + if (canBeCompress) { + hash = ComputeHashForUtf8(utf8Data); + } else { + auto utf16Len = base::utf_helper::Utf8ToUtf16Size(utf8Data, utf8Len); + CVector tmpBuffer(utf16Len); + [[maybe_unused]] auto len = + base::utf_helper::ConvertRegionUtf8ToUtf16(utf8Data, tmpBuffer.data(), utf8Len, utf16Len, 0); + ASSERT(len == utf16Len); + hash = ComputeHashForData(tmpBuffer.data(), utf16Len); + } + return hash; +} + +/* static */ +uint32_t EcmaString::ComputeHashcodeUtf16(const uint16_t *utf16Data, uint32_t length) +{ + return ComputeHashForData(utf16Data, length); +} + +/* static */ +bool EcmaString::IsUtf8EqualsUtf16(const uint8_t *utf8Data, 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; + } + + Span data1(tmpBuffer.data(), len); + Span data2(utf16Data, utf16Len); + return EcmaString::StringsAreEquals(data1, data2); +} +} // namespace panda::ecmascript diff --git a/runtime/ecma_string.h b/runtime/ecma_string.h new file mode 100644 index 000000000..3d092a85a --- /dev/null +++ b/runtime/ecma_string.h @@ -0,0 +1,343 @@ +/* + * Copyright (c) 2021-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_STRING_H +#define ECMASCRIPT_STRING_H + +#include +#include +#include + +#include "plugins/ecmascript/runtime/base/utf_helper.h" +#include "plugins/ecmascript/runtime/ecma_macros.h" +#include "plugins/ecmascript/runtime/js_tagged_value.h" +#include "plugins/ecmascript/runtime/mem/tagged_object.h" + +namespace panda { +namespace ecmascript { +template +class JSHandle; +class EcmaVM; + +template +static int32_t ComputeHashForData(const T *data, size_t size) +{ + uint32_t hash = 0; +#if defined(__GNUC__) +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wignored-attributes" + Span sp(data, size); +#pragma GCC diagnostic pop +#endif + for (auto c : sp) { + constexpr size_t SHIFT = 5; + hash = (hash << SHIFT) - hash + c; + } + return static_cast(hash); +} + +class EcmaString : public TaggedObject { +public: + static EcmaString *Cast(ObjectHeader *object); + static const EcmaString *ConstCast(const TaggedObject *object); + + static EcmaString *CreateEmptyString(const EcmaVM *vm); + static EcmaString *CreateFromUtf8(const uint8_t *utf8Data, uint32_t utf8Len, const EcmaVM *vm, bool canBeCompress); + static EcmaString *CreateFromUtf16(const uint16_t *utf16Data, uint32_t utf16Len, const EcmaVM *vm, + bool canBeCompress); + static EcmaString *Concat(const JSHandle &str1Handle, const JSHandle &str2Handle, + const EcmaVM *vm); + static EcmaString *FastSubString(const JSHandle &src, uint32_t start, uint32_t utf16Len, + const EcmaVM *vm); + + static constexpr uint32_t STRING_COMPRESSED_BIT = 0x1; + static constexpr uint32_t STRING_INTERN_BIT = 0x2; + enum CompressedStatus { + STRING_COMPRESSED, + STRING_UNCOMPRESSED, + }; + + template + uint16_t At(int32_t index) const; + + int32_t Compare(const EcmaString *rhs) const; + + bool IsUtf16() const + { + return compressedStringsEnabled ? ((GetMixLength() & STRING_COMPRESSED_BIT) == STRING_UNCOMPRESSED) : true; + } + + bool IsUtf8() const + { + return compressedStringsEnabled ? ((GetMixLength() & STRING_COMPRESSED_BIT) == STRING_COMPRESSED) : false; + } + + static size_t ComputeDataSizeUtf16(uint32_t length) + { + return length * sizeof(uint16_t); + } + + /** + * Methods for uncompressed strings (UTF16): + */ + static size_t ComputeSizeUtf16(uint32_t utf16Len) + { + return DATA_OFFSET + ComputeDataSizeUtf16(utf16Len); + } + + inline uint16_t *GetData() const + { + return reinterpret_cast(ToUintPtr(this) + DATA_OFFSET); + } + + const uint16_t *GetDataUtf16() const + { + LOG_IF(!IsUtf16(), FATAL, RUNTIME) << "EcmaString: Read data as utf16 for utf8 string"; + return GetData(); + } + + /** + * Methods for compresses strings (UTF8 or LATIN1): + */ + static size_t ComputeSizeUtf8(uint32_t utf8Len) + { + return DATA_OFFSET + utf8Len; + } + + /** + * It's Utf8 format, but without 0 in the end. + */ + const uint8_t *GetDataUtf8() const + { + LOG_IF(IsUtf16(), FATAL, RUNTIME) << "EcmaString: Read data as utf8 for utf16 string"; + return reinterpret_cast(GetData()); + } + + size_t GetUtf8Length() const + { + if (!IsUtf16()) { + return GetLength() + 1; // add place for zero in the end + } + return base::utf_helper::Utf16ToUtf8Size(GetData(), GetLength()); + } + + size_t GetUtf16Length() const + { + return GetLength(); + } + + inline size_t CopyDataUtf8(uint8_t *buf, size_t maxLength) const + { + ASSERT(maxLength > 0); + // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-pointer-arithmetic) + buf[maxLength - 1] = '\0'; + return CopyDataRegionUtf8(buf, 0, GetLength(), maxLength) + 1; // add place for zero in the end + } + + size_t CopyDataRegionUtf8(uint8_t *buf, size_t start, size_t length, size_t maxLength) const + { + if (length > maxLength) { + return 0; + } + uint32_t len = GetLength(); + if (start + length > len) { + return 0; + } + if (!IsUtf16()) { + if (length > std::numeric_limits::max() / 2 - 1) { // 2: half + LOG(FATAL, RUNTIME) << " length is higher than half of size_t::max"; + UNREACHABLE(); + } + // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-pointer-arithmetic) + if (memcpy_s(buf, maxLength, GetDataUtf8() + start, length) != EOK) { + LOG(FATAL, RUNTIME) << "memcpy_s failed"; + UNREACHABLE(); + } + return length; + } + return base::utf_helper::ConvertRegionUtf16ToUtf8(GetDataUtf16(), buf, length, maxLength - 1, start); + } + + inline uint32_t CopyDataUtf16(uint16_t *buf, uint32_t maxLength) const + { + return CopyDataRegionUtf16(buf, 0, GetLength(), maxLength); + } + + uint32_t CopyDataRegionUtf16(uint16_t *buf, uint32_t start, uint32_t length, uint32_t maxLength) const + { + if (length > maxLength) { + return 0; + } + uint32_t len = GetLength(); + if (start + length > len) { + return 0; + } + if (IsUtf16()) { + // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-pointer-arithmetic) + if (memcpy_s(buf, ComputeDataSizeUtf16(maxLength), GetDataUtf16() + start, ComputeDataSizeUtf16(length)) != + EOK) { + LOG(FATAL, RUNTIME) << "memcpy_s failed"; + UNREACHABLE(); + } + return length; + } + return base::utf_helper::ConvertRegionUtf8ToUtf16(GetDataUtf8(), buf, len, maxLength, start); + } + + // NOLINTNEXTLINE(modernize-avoid-c-arrays) + inline std::unique_ptr GetCString() + { + auto length = GetUtf8Length(); + char *buf = new char[length](); + CopyDataUtf8(reinterpret_cast(buf), length); + // NOLINTNEXTLINE(modernize-avoid-c-arrays) + return std::unique_ptr(buf); + } + + inline void WriteData(EcmaString *src, uint32_t start, uint32_t destSize, uint32_t length); + inline void WriteData(char src, uint32_t start); + uint32_t GetLength() const + { + return GetMixLength() >> 2U; + } + + void SetIsInternString() + { + SetMixLength(GetMixLength() | STRING_INTERN_BIT); + } + + bool IsInternString() const + { + return (GetMixLength() & STRING_INTERN_BIT) != 0; + } + + size_t ObjectSize() const + { + uint32_t length = GetLength(); + return IsUtf16() ? ComputeSizeUtf16(length) : ComputeSizeUtf8(length); + } + + uint32_t GetHashcode() + { + uint32_t hashcode = GetRawHashcode(); + if (hashcode == 0) { + hashcode = ComputeHashcode(); + SetRawHashcode(hashcode); + } + return hashcode; + } + + int32_t IndexOf(const EcmaString *rhs, int pos = 0) const; + + static constexpr uint32_t GetStringCompressionMask() + { + return STRING_COMPRESSED_BIT; + } + + /** + * Compares strings by bytes, It doesn't check canonical unicode equivalence. + */ + static bool StringsAreEqual(EcmaString *str1, EcmaString *str2); + /** + * Compares strings by bytes, It doesn't check canonical unicode equivalence. + */ + static bool StringsAreEqualUtf8(const EcmaString *str1, const uint8_t *utf8Data, uint32_t utf8Len, + bool canBeCompress); + /** + * Compares strings by bytes, It doesn't check canonical unicode equivalence. + */ + static bool StringsAreEqualUtf16(const EcmaString *str1, const uint16_t *utf16Data, uint32_t utf16Len); + static uint32_t ComputeHashcodeUtf8(const uint8_t *utf8Data, size_t utf8Len, bool canBeCompress); + static uint32_t ComputeHashcodeUtf16(const uint16_t *utf16Data, uint32_t length); + + static void SetCompressedStringsEnabled(bool val) + { + compressedStringsEnabled = val; + } + + static bool GetCompressedStringsEnabled() + { + return compressedStringsEnabled; + } + + static EcmaString *AllocStringObject(size_t length, bool compressed, const EcmaVM *vm); + + static bool CanBeCompressed(const uint8_t *utf8Data, uint32_t utf8Len); + static bool CanBeCompressed(const uint16_t *utf16Data, uint32_t utf16Len); + + static constexpr size_t MIX_LENGTH_OFFSET = TaggedObjectSize(); + // In last bit of mix_length we store if this string is compressed or not. + SET_GET_PRIMITIVE_FIELD(MixLength, uint32_t, MIX_LENGTH_OFFSET, HASHCODE_OFFSET); + SET_GET_PRIMITIVE_FIELD(RawHashcode, uint32_t, HASHCODE_OFFSET, DATA_OFFSET); + // DATA_OFFSET: the string data stored after the string header. + // Data can be stored in utf8 or utf16 form according to compressed bit. + static constexpr size_t SIZE = DATA_OFFSET; // Empty String size + +private: + void SetLength(uint32_t length, bool compressed = false) + { + ASSERT(length < 0x40000000U); + // Use 0u for compressed/utf8 expression + SetMixLength((length << 2U) | (compressed ? STRING_COMPRESSED : STRING_UNCOMPRESSED)); + } + + uint16_t *GetDataUtf16Writable() + { + LOG_IF(!IsUtf16(), FATAL, RUNTIME) << "EcmaString: Read data as utf16 for utf8 string"; + return GetData(); + } + + uint8_t *GetDataUtf8Writable() + { + LOG_IF(IsUtf16(), FATAL, RUNTIME) << "EcmaString: Read data as utf8 for utf16 string"; + return reinterpret_cast(GetData()); + } + + uint32_t ComputeHashcode() const; + static void CopyUtf16AsUtf8(const uint16_t *utf16From, uint8_t *utf8To, uint32_t utf16Len); + + static bool compressedStringsEnabled; + + static bool IsASCIICharacter(uint16_t data) + { + // \0 is not considered ASCII in Ecma-Modified-UTF8 [only modify '\u0000'] + return data - 1U < base::utf_helper::UTF8_1B_MAX; + } + + /** + * str1 should have the same length as utf16_data. + * Converts utf8Data to utf16 and compare it with given utf16_data. + */ + static bool IsUtf8EqualsUtf16(const uint8_t *utf8Data, size_t utf8Len, const uint16_t *utf16Data, + uint32_t utf16Len); + + template + /** + * Check that two spans are equal. Should have the same length. + */ + static bool StringsAreEquals(Span &str1, Span &str2); + + template + /** + * Copy String from src to dst + * */ + static bool StringCopy(Span &dst, size_t dstMax, Span &src, size_t count); + + template + static int32_t IndexOf(Span &lhsSp, Span &rhsSp, int32_t pos, int32_t max); +}; +} // namespace ecmascript +} // namespace panda +#endif // ECMASCRIPT_STRING_H diff --git a/runtime/ecma_string_table.cpp b/runtime/ecma_string_table.cpp new file mode 100644 index 000000000..c60e203d2 --- /dev/null +++ b/runtime/ecma_string_table.cpp @@ -0,0 +1,196 @@ +/* + * Copyright (c) 2021-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 "plugins/ecmascript/runtime/ecma_string_table.h" + +#include "plugins/ecmascript/runtime/ecma_string-inl.h" +#include "plugins/ecmascript/runtime/ecma_vm.h" +#include "plugins/ecmascript/runtime/js_thread.h" +#include "plugins/ecmascript/runtime/mem/c_string.h" +#include "plugins/ecmascript/runtime/object_factory.h" + +namespace panda::ecmascript { +EcmaStringTable::EcmaStringTable(const EcmaVM *vm) : vm_(vm) {} + +EcmaString *EcmaStringTable::GetString(const uint8_t *utf8Data, uint32_t utf8Len, bool canBeCompress) const +{ + uint32_t hashCode = EcmaString::ComputeHashcodeUtf8(utf8Data, utf8Len, canBeCompress); + os::memory::ReadLockHolder holder(table_lock_); + for (auto it = table_.find(hashCode); it != table_.end(); it++) { + auto foundedString = it->second; + if (EcmaString::StringsAreEqualUtf8(foundedString, utf8Data, utf8Len, canBeCompress)) { + return foundedString; + } + } + return nullptr; +} + +EcmaString *EcmaStringTable::GetString(const uint16_t *utf16Data, uint32_t utf16Len) const +{ + uint32_t hashCode = EcmaString::ComputeHashcodeUtf16(const_cast(utf16Data), utf16Len); + os::memory::ReadLockHolder holder(table_lock_); + for (auto it = table_.find(hashCode); it != table_.end(); it++) { + auto foundedString = it->second; + if (EcmaString::StringsAreEqualUtf16(foundedString, utf16Data, utf16Len)) { + return foundedString; + } + } + return nullptr; +} + +EcmaString *EcmaStringTable::GetString(EcmaString *string) const +{ + auto hash = string->GetHashcode(); + os::memory::ReadLockHolder holder(table_lock_); + for (auto it = table_.find(hash); it != table_.end(); it++) { + auto foundedString = it->second; + if (EcmaString::StringsAreEqual(foundedString, string)) { + return foundedString; + } + } + return nullptr; +} + +ObjectHeader *EcmaStringTable::ResolveString(const panda_file::File &pf, panda_file::File::EntityId id) +{ + auto foundStr = pf.GetStringData(id); + const auto factory = vm_->GetFactory(); + + if (UNLIKELY(foundStr.utf16_length == 0)) { + return *(factory->GetEmptyString()); + } + + return EcmaString::Cast(this->GetOrInternString(foundStr.data, foundStr.utf16_length, foundStr.is_ascii)); +} + +void EcmaStringTable::InternString(EcmaString *string) +{ + if (string->IsInternString()) { + return; + } + os::memory::WriteLockHolder holder(table_lock_); + table_.insert(std::pair(string->GetHashcode(), string)); + string->SetIsInternString(); +} + +void EcmaStringTable::InternEmptyString(EcmaString *emptyStr) +{ + InternString(emptyStr); +} + +EcmaString *EcmaStringTable::GetOrInternString(const uint8_t *utf8Data, uint32_t utf8Len, bool canBeCompress) +{ + EcmaString *result = GetString(utf8Data, utf8Len, canBeCompress); + if (result != nullptr) { + return result; + } + + result = EcmaString::CreateFromUtf8(utf8Data, utf8Len, vm_, canBeCompress); + InternString(result); + return result; +} + +EcmaString *EcmaStringTable::GetOrInternString(const uint16_t *utf16Data, uint32_t utf16Len, bool canBeCompress) +{ + EcmaString *result = GetString(utf16Data, utf16Len); + if (result != nullptr) { + return result; + } + + result = EcmaString::CreateFromUtf16(utf16Data, utf16Len, vm_, canBeCompress); + InternString(result); + return result; +} + +EcmaString *EcmaStringTable::GetOrInternString(EcmaString *string) +{ + if (string->IsInternString()) { + return string; + } + + EcmaString *result = GetString(string); + if (result != nullptr) { + return result; + } + InternString(string); + return string; +} + +void EcmaStringTable::VisitRoots(const StringTable::StringVisitor &visitor, + [[maybe_unused]] mem::VisitGCRootFlags flags) +{ + os::memory::ReadLockHolder holder(table_lock_); + for (const auto &v : table_) { + visitor(v.second); + } +} + +void EcmaStringTable::Sweep(const GCObjectVisitor &visitor) +{ + os::memory::WriteLockHolder holder(table_lock_); + for (auto it = table_.begin(); it != table_.end();) { + auto *object = it->second; + if (object->IsForwarded()) { + ASSERT(visitor(object) != ObjectStatus::DEAD_OBJECT); + ObjectHeader *fwd_string = panda::mem::GetForwardAddress(object); + it->second = static_cast(fwd_string); + ++it; + LOG(DEBUG, GC) << "StringTable: forward " << std::hex << object << " -> " << fwd_string; + } else if (visitor(object) == ObjectStatus::DEAD_OBJECT) { + LOG(DEBUG, GC) << "StringTable: delete string " << std::hex << object + << ", val = " << ConvertToString(object); + table_.erase(it++); + } else { + ++it; + } + } +} + +bool EcmaStringTable::UpdateMoved() { + bool updated = false; + os::memory::WriteLockHolder holder(table_lock_); + for (auto it = table_.begin(); it != table_.end();) { + ObjectHeader *object = it->second; + if (object->IsForwarded()) { + ObjectHeader *fwd_string = panda::mem::GetForwardAddress(object); + it->second = static_cast(fwd_string); + LOG(DEBUG, GC) << "StringTable: forward " << std::hex << object << " -> " << fwd_string; + updated = true; + } + ++it; + } + return updated; +} + +void EcmaStringTable::SweepWeakReference(const WeakRootVisitor &visitor) +{ + os::memory::WriteLockHolder holder(table_lock_); + for (auto it = table_.begin(); it != table_.end();) { + auto *object = it->second; + auto fwd = visitor(object); + if (fwd == nullptr) { + LOG(DEBUG, GC) << "StringTable: delete string " << std::hex << object + << ", val = " << ConvertToString(object); + table_.erase(it++); + } else if (fwd != object) { + it->second = static_cast(fwd); + ++it; + LOG(DEBUG, GC) << "StringTable: forward " << std::hex << object << " -> " << fwd; + } else { + ++it; + } + } +} +} // namespace panda::ecmascript diff --git a/runtime/ecma_string_table.h b/runtime/ecma_string_table.h new file mode 100644 index 000000000..a0d6ae212 --- /dev/null +++ b/runtime/ecma_string_table.h @@ -0,0 +1,77 @@ +/* + * Copyright (c) 2021-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_STRING_TABLE_H +#define ECMASCRIPT_STRING_TABLE_H + +#include "plugins/ecmascript/runtime/mem/c_containers.h" +#include "plugins/ecmascript/runtime/mem/object_xray.h" + +#include "runtime/string_table.h" + +namespace panda::ecmascript { +class EcmaString; +class EcmaVM; + +class EcmaStringTable { +public: + explicit EcmaStringTable(const EcmaVM *vm); + virtual ~EcmaStringTable() + { + os::memory::WriteLockHolder holder(table_lock_); + table_.clear(); + } + + void InternEmptyString(EcmaString *emptyStr); + EcmaString *GetOrInternString(const uint8_t *utf8Data, uint32_t utf8Len, bool canBeCompress); + EcmaString *GetOrInternString(const uint16_t *utf16Data, uint32_t utf16Len, bool canBeCompress); + EcmaString *GetOrInternString(EcmaString *string); + + void SweepWeakReference(const WeakRootVisitor &visitor); + + ObjectHeader *ResolveString(const panda_file::File &pf, panda_file::File::EntityId id); + + void VisitRoots(const StringTable::StringVisitor &visitor, + mem::VisitGCRootFlags flags = mem::VisitGCRootFlags::ACCESS_ROOT_ALL); + + void Sweep(const GCObjectVisitor &visitor); + bool UpdateMoved(); + +private: + NO_COPY_SEMANTIC(EcmaStringTable); + NO_MOVE_SEMANTIC(EcmaStringTable); + + EcmaString *GetString(const uint8_t *utf8Data, uint32_t utf8Len, bool canBeCompress) const; + EcmaString *GetString(const uint16_t *utf16Data, uint32_t utf16Len) const; + EcmaString *GetString(EcmaString *string) const; + + void InternString(EcmaString *string); + + void InsertStringIfNotExist(EcmaString *string) + { + EcmaString *str = GetString(string); + if (str == nullptr) { + InternString(string); + } + } + + CUnorderedMultiMap table_ GUARDED_BY(table_lock_); + mutable os::memory::RWLock table_lock_; + const EcmaVM *vm_{nullptr}; + friend class SnapShotSerialize; +}; +} // namespace panda::ecmascript + +#endif // ECMASCRIPT_STRING_TABLE_H diff --git a/runtime/ecma_vm.cpp b/runtime/ecma_vm.cpp new file mode 100644 index 000000000..8a7543882 --- /dev/null +++ b/runtime/ecma_vm.cpp @@ -0,0 +1,1049 @@ +/* + * Copyright (c) 2021-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 "plugins/ecmascript/runtime/ecma_vm.h" + +#include "js_tagged_value.h" +#include "mem/mark_word.h" +#include "plugins/ecmascript/runtime/base/string_helper.h" +#include "plugins/ecmascript/runtime/builtins.h" +#include "plugins/ecmascript/runtime/builtins/builtins_regexp.h" +#include "plugins/ecmascript/runtime/class_linker/panda_file_translator.h" +#include "plugins/ecmascript/runtime/class_linker/program_object-inl.h" +#include "plugins/ecmascript/runtime/ecma_module.h" +#include "plugins/ecmascript/runtime/ecma_string_table.h" +#include "plugins/ecmascript/runtime/global_dictionary.h" +#include "plugins/ecmascript/runtime/global_env.h" +#include "plugins/ecmascript/runtime/global_env_constants-inl.h" +#include "plugins/ecmascript/runtime/global_env_constants.h" +#include "plugins/ecmascript/runtime/internal_call_params.h" +#include "plugins/ecmascript/runtime/interpreter/js_frame-inl.h" +#include "plugins/ecmascript/runtime/jobs/micro_job_queue.h" +#include "plugins/ecmascript/runtime/js_arraybuffer.h" +#include "plugins/ecmascript/runtime/js_for_in_iterator.h" +#include "plugins/ecmascript/runtime/js_invoker.h" +#include "plugins/ecmascript/runtime/js_native_pointer.h" +#include "plugins/ecmascript/runtime/js_thread.h" +#include "plugins/ecmascript/runtime/mem/concurrent_marker.h" +#include "plugins/ecmascript/runtime/mem/heap.h" +#include "plugins/ecmascript/runtime/mem/object_xray-inl.h" +#include "plugins/ecmascript/runtime/object_factory.h" +#include "plugins/ecmascript/runtime/platform/platform.h" +#include "plugins/ecmascript/runtime/regexp/regexp_parser_cache.h" +#include "plugins/ecmascript/runtime/runtime_call_id.h" +#include "plugins/ecmascript/runtime/snapshot/mem/slot_bit.h" +#include "plugins/ecmascript/runtime/snapshot/mem/snapshot.h" +#include "plugins/ecmascript/runtime/snapshot/mem/snapshot_serialize.h" +#include "plugins/ecmascript/runtime/symbol_table.h" +#include "plugins/ecmascript/runtime/tagged_array-inl.h" +#include "plugins/ecmascript/runtime/tagged_dictionary.h" +#include "plugins/ecmascript/runtime/tagged_queue-inl.h" +#include "plugins/ecmascript/runtime/tagged_queue.h" +#include "plugins/ecmascript/runtime/template_map.h" +#include "plugins/ecmascript/runtime/vmstat/runtime_stat.h" +#include "plugins/ecmascript/compiler/ecmascript_extensions/thread_environment_api.h" +#include "plugins/ecmascript/runtime/ecma_class_linker_extension.h" +#include "plugins/ecmascript/runtime/mem/ecma_reference_processor.h" +#include "include/runtime_notification.h" +#include "libpandafile/file.h" + +#include "runtime/compiler.h" +#include "runtime/include/thread_scopes.h" +#include "runtime/mem/gc/gc_root.h" +#include "runtime/mem/gc/reference-processor/empty_reference_processor.h" +#include "runtime/mem/object_helpers.h" +#include "plugins/ecmascript/runtime/compiler/ecmascript_runtime_interface.h" +#include "runtime/compiler.h" + +#include "handle_scope-inl.h" +#include "include/coretypes/native_pointer.h" +#include "include/runtime.h" +#include "include/runtime_notification.h" +#include "include/method.h" +#include "mem/internal_allocator.h" +#include "trace/trace.h" +#include "ecmastdlib_inline_gen.h" +#include "ir-dyn-base-types.h" + +namespace panda::ecmascript { +// NOLINTNEXTLINE(fuchsia-statically-constructed-objects) +static const std::string_view ENTRY_POINTER = "_GLOBAL::func_main_0"; +JSRuntimeOptions EcmaVM::options_; // NOLINT(fuchsia-statically-constructed-objects) + +void EcmaRendezvous::SafepointBegin() +{ + ASSERT(!Locks::mutator_lock->HasLock()); + LOG(DEBUG, GC) << "Rendezvous: SafepointBegin"; + Thread *current = Thread::GetCurrent(); + ManagedThread *main_thread = current->GetVM()->GetAssociatedThread(); + + if (current != main_thread) { + main_thread->SuspendImpl(true); + } + // Acquire write MutatorLock + Locks::mutator_lock->WriteLock(); +} + +void EcmaRendezvous::SafepointEnd() +{ + ASSERT(Locks::mutator_lock->HasLock()); + LOG(DEBUG, GC) << "Rendezvous: SafepointEnd"; + // Release write MutatorLock + Locks::mutator_lock->Unlock(); + Thread *current = Thread::GetCurrent(); + ManagedThread *main_thread = current->GetVM()->GetAssociatedThread(); + if (current != main_thread) { + main_thread->ResumeImpl(true); + } + LOG(DEBUG, GC) << "Rendezvous: SafepointEnd exit"; +} + +// Create MemoryManager by RuntimeOptions +static mem::MemoryManager *CreateMM(const LanguageContext &ctx, mem::InternalAllocatorPtr internal_allocator, + const RuntimeOptions &options) +{ + mem::MemoryManager::HeapOptions heap_options { + nullptr, // is_object_finalizeble_func + nullptr, // register_finalize_reference_func + options.GetMaxGlobalRefSize(), // max_global_ref_size + options.IsGlobalReferenceSizeCheckEnabled(), // is_global_reference_size_check_enabled + true, // is_single_thread + options.IsUseTlabForAllocations(), // is_use_tlab_for_allocations + options.IsStartAsZygote(), // is_start_as_zygote + }; + + mem::GCTriggerConfig gc_trigger_config(options, panda_file::SourceLang::ECMASCRIPT); + + mem::GCSettings gc_settings(options, panda_file::SourceLang::ECMASCRIPT); + + mem::GCType gc_type = Runtime::GetGCType(options, panda_file::SourceLang::ECMASCRIPT); + + return mem::MemoryManager::Create(ctx, internal_allocator, gc_type, gc_settings, gc_trigger_config, heap_options); +} + +/* static */ +EcmaVM *EcmaVM::Create(const JSRuntimeOptions &options) +{ + auto runtime = Runtime::GetCurrent(); + auto vm = runtime->GetInternalAllocator()->New(options); + if (UNLIKELY(vm == nullptr)) { + LOG_ECMA(ERROR) << "Failed to create jsvm"; + return nullptr; + } + vm->InitializeGC(); + auto jsThread = JSThread::Create(runtime, vm); + vm->thread_ = jsThread; + if (!vm->Initialize()) { + LOG_ECMA(ERROR) << "Failed to initialize jsvm"; + runtime->GetInternalAllocator()->Delete(vm); + return nullptr; + } + return vm; +} + +// static +bool EcmaVM::Destroy(PandaVM *vm) +{ + if (vm != nullptr) { + vm->UninitializeThreads(); + vm->StopGC(); + auto runtime = Runtime::GetCurrent(); + runtime->GetInternalAllocator()->Delete(vm); + return true; + } + return false; +} + +// static +Expected EcmaVM::Create(Runtime *runtime, const JSRuntimeOptions &options) +{ + EcmaVM *vm = runtime->GetInternalAllocator()->New(options); + if (UNLIKELY(vm == nullptr)) { + LOG_ECMA(ERROR) << "Failed to create jsvm"; + return nullptr; + } + vm->InitializeGC(); + auto jsThread = ecmascript::JSThread::Create(runtime, vm); + vm->thread_ = jsThread; + return vm; +} + +EcmaVM::EcmaVM(JSRuntimeOptions options) + : stringTable_(new EcmaStringTable(this)), + regionFactory_(std::make_unique()), + chunk_(regionFactory_.get()), + nativeMethods_(&chunk_) +{ + options_ = std::move(options); + icEnable_ = options_.IsIcEnable(); + optionalLogEnabled_ = options_.IsEnableOptionalLog(); + rendezvous_ = chunk_.New(); + snapshotSerializeEnable_ = options_.IsSnapshotSerializeEnabled(); + if (!snapshotSerializeEnable_) { + snapshotDeserializeEnable_ = options_.IsSnapshotDeserializeEnabled(); + } + snapshotFileName_ = options_.GetSnapshotFile(); + frameworkAbcFileName_ = options_.GetFrameworkAbcFile(); + + auto runtime = Runtime::GetCurrent(); + notificationManager_ = chunk_.New(runtime->GetInternalAllocator()); + notificationManager_->SetRendezvous(rendezvous_); + + LanguageContext ctx = Runtime::GetCurrent()->GetLanguageContext(panda_file::SourceLang::ECMASCRIPT); + mm_ = CreateMM(ctx, Runtime::GetCurrent()->GetInternalAllocator(), options_); + if (options_.IsReferenceProcessorEnable()) { + ecma_reference_processor_ = MakePandaUnique(this); + } else { + ecma_reference_processor_ = MakePandaUnique(); + } + + auto heap_manager = mm_->GetHeapManager(); + auto internal_allocator = heap_manager->GetInternalAllocator(); + runtime_iface_ = internal_allocator->New(); + compiler_ = internal_allocator->New(heap_manager->GetCodeAllocator(), internal_allocator, options_, + heap_manager->GetMemStats(), runtime_iface_); + SetFrameExtSize(ecmascript::JSExtFrame::GetExtSize()); +} + +void EcmaVM::LoadEcmaStdLib() +{ + if (GetNativeMethodWrapper() != nullptr) { + // Ecmastdlib was loaded via parameters. + return; + } + + size_t fsize = 0; + unsigned char const *data = panda::ecmascript::ecmastdlib_inline::GetEcmastdlibData(fsize); + if (fsize == 0) { + // Ecmastdlib is missing. + LOG(FATAL, ECMASCRIPT) << "Ecmastdlib is missing!"; + return; + } + + auto pf = panda_file::OpenPandaFileFromMemory(data, fsize); + ASSERT(pf); + Runtime::GetCurrent()->GetClassLinker()->AddPandaFile(std::move(pf)); +} + +bool EcmaVM::Initialize() +{ + ECMA_BYTRACE_NAME(BYTRACE_TAG_ARK, "EcmaVM::Initialize"); + Platform::GetCurrentPlatform()->Initialize(); + + auto globalConst = const_cast(thread_->GlobalConstants()); + regExpParserCache_ = new RegExpParserCache(); + heap_ = new Heap(this); + heap_->Initialize(); + gcStats_ = chunk_.New(heap_); + factory_ = chunk_.New(thread_, heap_); + if (UNLIKELY(factory_ == nullptr)) { + LOG_ECMA(FATAL) << "alloc factory_ failed"; + UNREACHABLE(); + } + + auto runtime = Runtime::GetCurrent(); + LanguageContext ctx = runtime->GetLanguageContext(panda_file::SourceLang::ECMASCRIPT); + EcmaClassLinkerExtension::Cast(runtime->GetClassLinker()->GetExtension(ctx))->InitClasses(this); + + LoadEcmaStdLib(); + if (!intrinsics::Initialize(panda::panda_file::SourceLang::ECMASCRIPT)) { + LOG(ERROR, RUNTIME) << "Failed to initialize Ecma intrinsics"; + return false; + } + + [[maybe_unused]] EcmaHandleScope scope(thread_); + if (!snapshotDeserializeEnable_ || !VerifyFilePath(snapshotFileName_)) { + LOG_ECMA(DEBUG) << "EcmaVM::Initialize run builtins"; + + JSHandle dynClassClassHandle = + factory_->NewEcmaDynClassClass(nullptr, JSHClass::SIZE, JSType::HCLASS); + JSHClass *dynclass = reinterpret_cast(dynClassClassHandle.GetTaggedValue().GetTaggedObject()); + dynclass->SetClass(dynclass); + JSHandle globalEnvClass = + factory_->NewEcmaDynClass(*dynClassClassHandle, GlobalEnv::SIZE, JSType::GLOBAL_ENV); + + JSHandle globalEnvHandle = factory_->NewGlobalEnv(*globalEnvClass); + globalEnv_ = globalEnvHandle.GetTaggedValue(); + + // init global env + globalConst->InitRootsClass(thread_, *dynClassClassHandle); + factory_->ObtainRootClass(GetGlobalEnv()); + globalConst->InitGlobalConstant(thread_); + globalEnvHandle->SetEmptyArray(thread_, factory_->NewEmptyArray()); + globalEnvHandle->SetEmptyLayoutInfo(thread_, factory_->CreateLayoutInfo(0)); + globalEnvHandle->SetRegisterSymbols(thread_, SymbolTable::Create(thread_)); + globalEnvHandle->SetGlobalRecord(thread_, GlobalDictionary::Create(thread_)); + JSTaggedValue emptyStr = thread_->GlobalConstants()->GetEmptyString(); + stringTable_->InternEmptyString(EcmaString::Cast(emptyStr.GetTaggedObject())); + globalEnvHandle->SetEmptyTaggedQueue(thread_, factory_->NewTaggedQueue(0)); + globalEnvHandle->SetTemplateMap(thread_, TemplateMap::Create(thread_)); + globalEnvHandle->SetRegisterSymbols(GetJSThread(), SymbolTable::Create(GetJSThread())); +#ifdef ECMASCRIPT_ENABLE_STUB_AOT + std::string moduleFile = options_.GetStubModuleFile(); + thread_->LoadFastStubModule(moduleFile.c_str()); +#endif + SetupRegExpResultCache(); + microJobQueue_ = factory_->NewMicroJobQueue().GetTaggedValue(); + + { + Builtins builtins; + builtins.Initialize(globalEnvHandle, thread_); + } + + thread_->SetGlobalObject(globalEnvHandle->GetGlobalObject()); + } else { + LOG_ECMA(DEBUG) << "EcmaVM::Initialize run snapshot"; + SnapShot snapShot(this); + std::unique_ptr pf = snapShot.DeserializeGlobalEnvAndProgram(snapshotFileName_); + frameworkPandaFile_ = pf.get(); + AddPandaFile(pf.release(), false); + SetProgram(Program::Cast(frameworkProgram_.GetTaggedObject()), frameworkPandaFile_); + notificationManager_->LoadModuleEvent(pf->GetFilename()); + globalConst->InitGlobalUndefined(); + + factory_->ObtainRootClass(GetGlobalEnv()); + } + + moduleManager_ = new ModuleManager(this); + + InitializeFinish(); + notificationManager_->VmStartEvent(); + notificationManager_->VmInitializationEvent(thread_->GetThreadId()); + Platform::GetCurrentPlatform()->PostTask(std::make_unique(heap_)); + return true; +} + +bool EcmaVM::TrimNewSpaceLimitTask::Run([[maybe_unused]] uint32_t threadIndex) +{ + for (uint32_t i = 0; i < THREAD_SLEEP_COUNT; i++) { + if (IsTerminate()) { + return false; + } + usleep(THREAD_SLEEP_TIME); + } + + if (!IsTerminate() && heap_->GetMemController()->IsDelayGCMode()) { + heap_->SetFromSpaceMaximumCapacity(SEMI_SPACE_SIZE_CAPACITY); + heap_->SetNewSpaceMaximumCapacity(SEMI_SPACE_SIZE_CAPACITY); + heap_->ResetDelayGCMode(); + } + return true; +} + +void EcmaVM::InitializeEcmaScriptRunStat() +{ + // NOLINTNEXTLINE(modernize-avoid-c-arrays) + static const char *runtimeCallerNames[] = { +// NOLINTNEXTLINE(cppcoreguidelines-macro-usage) +#define INTERPRETER_CALLER_NAME(name) "InterPreter::" #name, + INTERPRETER_CALLER_LIST(INTERPRETER_CALLER_NAME) // NOLINTNEXTLINE(bugprone-suspicious-missing-comma) +#undef INTERPRETER_CALLER_NAME +// NOLINTNEXTLINE(cppcoreguidelines-macro-usage) +#define BUILTINS_API_NAME(class, name) "BuiltinsApi::" #class "_" #name, + BUITINS_API_LIST(BUILTINS_API_NAME) +#undef BUILTINS_API_NAME +// NOLINTNEXTLINE(cppcoreguidelines-macro-usage) +#define ABSTRACT_OPERATION_NAME(class, name) "AbstractOperation::" #class "_" #name, + ABSTRACT_OPERATION_LIST(ABSTRACT_OPERATION_NAME) +#undef ABSTRACT_OPERATION_NAME + }; + static_assert(sizeof(runtimeCallerNames) == sizeof(const char *) * ecmascript::RUNTIME_CALLER_NUMBER, + "Invalid runtime caller number"); + runtimeStat_ = chunk_.New(runtimeCallerNames, ecmascript::RUNTIME_CALLER_NUMBER); + if (UNLIKELY(runtimeStat_ == nullptr)) { + LOG_ECMA(FATAL) << "alloc runtimeStat_ failed"; + UNREACHABLE(); + } +} + +void EcmaVM::SetRuntimeStatEnable(bool flag) +{ + if (flag) { + if (runtimeStat_ == nullptr) { + InitializeEcmaScriptRunStat(); + } + } else { + if (runtimeStatEnabled_) { + runtimeStat_->Print(); + runtimeStat_->ResetAllCount(); + } + } + runtimeStatEnabled_ = flag; +} + +bool EcmaVM::InitializeFinish() +{ + vmInitialized_ = true; + return true; +} + +void EcmaVM::UninitializeThreads() +{ + thread_->NativeCodeEnd(); + thread_->UpdateStatus(ThreadStatus::FINISHED); +} + +EcmaVM::~EcmaVM() +{ + vmInitialized_ = false; + Platform::GetCurrentPlatform()->Destroy(); + ClearNativeMethodsData(); + + if (panda::plugins::RuntimeTypeToLang(Runtime::GetCurrent()->GetRuntimeType()) != + panda_file::SourceLang::ECMASCRIPT) { + // If ecmavm is not main, it will be created and deleted via speacial api, so we need cleanup classlinker. + Runtime::GetCurrent()->GetClassLinker()->ResetExtension(panda_file::SourceLang::ECMASCRIPT); + } + + if (runtimeStat_ != nullptr && runtimeStatEnabled_) { + runtimeStat_->Print(); + } + + // clear c_address: c++ pointer delete + ClearBufferData(); + + if (gcStats_ != nullptr) { + if (options_.IsEnableGCStatsPrint()) { + gcStats_->PrintStatisticResult(true); + } + chunk_.Delete(gcStats_); + gcStats_ = nullptr; + } + + if (heap_ != nullptr) { + heap_->Destroy(); + delete heap_; + heap_ = nullptr; + } + + delete regExpParserCache_; + regExpParserCache_ = nullptr; + + if (notificationManager_ != nullptr) { + chunk_.Delete(notificationManager_); + notificationManager_ = nullptr; + } + + if (factory_ != nullptr) { + chunk_.Delete(factory_); + factory_ = nullptr; + } + + if (stringTable_ != nullptr) { + delete stringTable_; + stringTable_ = nullptr; + } + + if (runtimeStat_ != nullptr) { + chunk_.Delete(runtimeStat_); + runtimeStat_ = nullptr; + } + + if (moduleManager_ != nullptr) { + delete moduleManager_; + moduleManager_ = nullptr; + } + + if (thread_ != nullptr) { + thread_->DestroyInternalResources(); + delete thread_; + thread_ = nullptr; + } + + extractorCache_.clear(); + frameworkProgramMethods_.clear(); + + mem::InternalAllocatorPtr allocator = mm_->GetHeapManager()->GetInternalAllocator(); + allocator->Delete(runtime_iface_); + allocator->Delete(compiler_); + + mm_->Finalize(); + mem::MemoryManager::Destroy(mm_); +} + +bool EcmaVM::ExecuteFromPf(std::string_view filename, std::string_view entryPoint, const std::vector &args, + bool isModule) +{ + std::unique_ptr pf; + if (frameworkPandaFile_ == nullptr || !IsFrameworkPandaFile(filename)) { + pf = panda_file::OpenPandaFileOrZip(filename, panda_file::File::READ_WRITE); + if (pf == nullptr) { + return false; + } + AddPandaFile(pf.get(), isModule); // Store here prevent from being automatically cleared + notificationManager_->LoadModuleEvent(pf->GetFilename()); + } else { + pf.reset(frameworkPandaFile_); + } + + return Execute(std::move(pf), entryPoint, args, isModule); +} + +bool EcmaVM::ExecuteFromBuffer(const void *buffer, size_t size, std::string_view entryPoint, + const std::vector &args) +{ + auto pf = panda_file::OpenPandaFileFromMemory(buffer, size); + if (pf == nullptr) { + return false; + } + AddPandaFile(pf.get(), false); // Store here prevent from being automatically cleared + notificationManager_->LoadModuleEvent(pf->GetFilename()); + + return Execute(std::move(pf), entryPoint, args); +} + +tooling::ecmascript::PtJSExtractor *EcmaVM::GetDebugInfoExtractor(const panda_file::File *file) +{ + tooling::ecmascript::PtJSExtractor *res = nullptr; + auto it = extractorCache_.find(file); + if (it == extractorCache_.end()) { + auto extractor = std::make_unique(file); + res = extractor.get(); + extractorCache_[file] = std::move(extractor); + } else { + res = it->second.get(); + } + return res; +} + +bool EcmaVM::Execute(std::unique_ptr pf, std::string_view entryPoint, + const std::vector &args, bool isModule) +{ + if (pf == nullptr) { + return false; + } + + const panda_file::File *pf_ptr = pf.get(); + Runtime::GetCurrent()->GetClassLinker()->AddPandaFile(std::move(pf)); + // Get ClassName and MethodName + size_t pos = entryPoint.find_last_of("::"); + if (pos == std::string_view::npos) { + LOG_ECMA(ERROR) << "EntryPoint:" << entryPoint << " is illegal"; + return false; + } + CString methodName(entryPoint.substr(pos + 1)); + + // For Ark application startup + if (!isModule) { + thread_->ManagedCodeBegin(); + } + InvokeEcmaEntrypoint(*pf_ptr, methodName, args); + if (!isModule) { + thread_->ManagedCodeEnd(); + } + return true; +} + +JSHandle EcmaVM::GetGlobalEnv() const +{ + return JSHandle(reinterpret_cast(&globalEnv_)); +} + +JSHandle EcmaVM::GetMicroJobQueue() const +{ + return JSHandle(reinterpret_cast(µJobQueue_)); +} + +Method *EcmaVM::GetNativeMethodWrapper() +{ + if (nativeMethodWrapper_ != nullptr) { + return nativeMethodWrapper_; + } + auto mutf8_name = reinterpret_cast("LEcmascript/Intrinsics;"); + auto klass = + Runtime::GetCurrent()->GetClassLinker()->GetExtension(panda_file::SourceLang::ECMASCRIPT)->GetClass(mutf8_name); + if (klass == nullptr) { + return nullptr; + } + + mutf8_name = reinterpret_cast("NativeMethodWrapper"); + + Method::Proto proto; + auto &shorty = proto.GetShorty(); + + shorty.emplace_back(panda_file::Type::TypeId::VOID); + shorty.emplace_back(panda_file::Type::TypeId::TAGGED); + + nativeMethodWrapper_ = klass->GetDirectMethod(mutf8_name, proto); + return nativeMethodWrapper_; +} + +JSMethod *EcmaVM::GetMethodForNativeFunction(const void *func) +{ + Method *n_wrapper = GetNativeMethodWrapper(); + ASSERT(n_wrapper != nullptr); + auto method = chunk_.New(n_wrapper); + method->SetNativePointer(const_cast(func)); + + nativeMethods_.push_back(method); + return nativeMethods_.back(); +} + +void EcmaVM::RedirectMethod(const panda_file::File &pf) +{ + for (auto method : frameworkProgramMethods_) { + method->SetPandaFile(&pf); + } +} + +Expected EcmaVM::InvokeEntrypointImpl(Method *entrypoint, const std::vector &args) +{ + // For testcase startup + const panda_file::File *file = entrypoint->GetPandaFile(); + AddPandaFile(file, false); + ScopedManagedCodeThread managed_scope(thread_); + return InvokeEcmaEntrypoint(*file, utf::Mutf8AsCString(entrypoint->GetName().data), args); +} + +Expected EcmaVM::InvokeEcmaEntrypoint(const panda_file::File &pf, const CString &methodName, + const std::vector &args) +{ + [[maybe_unused]] EcmaHandleScope scope(thread_); + JSHandle program; + if (snapshotSerializeEnable_) { + program = PandaFileTranslator::TranslatePandaFile(this, pf, methodName); + auto string = EcmaString::Cast(program->GetLocation().GetTaggedObject()); + + auto index = ConvertToString(string).find(frameworkAbcFileName_); + if (index != CString::npos) { + LOG_ECMA(DEBUG) << "snapShot MakeSnapShotProgramObject abc " << ConvertToString(string); + SnapShot snapShot(this); + snapShot.MakeSnapShotProgramObject(*program, &pf, snapshotFileName_); + } + } else { + if (&pf != frameworkPandaFile_) { + program = PandaFileTranslator::TranslatePandaFile(this, pf, methodName); + } else { + JSHandle string = factory_->NewFromStdStringUnCheck(pf.GetFilename(), true); + program = JSHandle(thread_, frameworkProgram_); + program->SetLocation(thread_, string); + RedirectMethod(pf); + } + } + + SetProgram(*program, &pf); + if (program.IsEmpty()) { + LOG_ECMA(ERROR) << "program is empty, invoke entrypoint failed"; + return Unexpected(Runtime::Error::PANDA_FILE_LOAD_ERROR); + } + + JSHandle func = JSHandle(thread_, program->GetMainFunction()); + JSHandle global = GlobalEnv::Cast(globalEnv_.GetTaggedObject())->GetJSGlobalObject(); + JSHandle newTarget(thread_, JSTaggedValue::Undefined()); + JSHandle jsargs = factory_->NewTaggedArray(args.size()); + uint32_t i = 0; + for (const std::string &str : args) { + JSHandle strobj(factory_->NewFromStdString(str)); + jsargs->Set(thread_, i++, strobj); + } + + InternalCallParams *params = thread_->GetInternalCallParams(); + params->MakeArgList(*jsargs); + JSRuntimeOptions options = this->GetJSOptions(); + panda::ecmascript::InvokeJsFunction(thread_, func, global, newTarget, params); + if (!thread_->HasPendingException()) { + job::MicroJobQueue::ExecutePendingJob(thread_, GetMicroJobQueue()); + } + + return 0; +} + +void EcmaVM::AddPandaFile(const panda_file::File *pf, bool isModule) +{ + ASSERT(pf != nullptr); + os::memory::LockHolder lock(pandaFileWithProgramLock_); + pandaFileWithProgram_.push_back(std::make_tuple(nullptr, pf, isModule)); +} + +void EcmaVM::SetProgram(Program *program, const panda_file::File *pf) +{ + os::memory::LockHolder lock(pandaFileWithProgramLock_); + auto it = std::find_if(pandaFileWithProgram_.begin(), pandaFileWithProgram_.end(), + [pf](auto entry) { return std::get<1>(entry) == pf; }); + ASSERT(it != pandaFileWithProgram_.end()); + std::get<0>(*it) = program; +} + +bool EcmaVM::IsFrameworkPandaFile(std::string_view filename) const +{ + return filename.size() >= frameworkAbcFileName_.size() && + filename.substr(filename.size() - frameworkAbcFileName_.size()) == frameworkAbcFileName_; +} + +JSHandle EcmaVM::GetEcmaUncaughtException() const +{ + if (thread_->GetException().IsHole()) { + return JSHandle(); + } + JSHandle exceptionHandle(thread_, thread_->GetException()); + thread_->ClearException(); // clear for ohos app + if (exceptionHandle->IsObjectWrapper()) { + JSHandle wrapperValue = JSHandle::Cast(exceptionHandle); + JSHandle throwValue(thread_, wrapperValue->GetValue()); + return throwValue; + } + + return exceptionHandle; +} + +void EcmaVM::EnableUserUncaughtErrorHandler() +{ + isUncaughtExceptionRegistered_ = true; +} + +void EcmaVM::HandleUncaughtException() +{ + if (isUncaughtExceptionRegistered_) { + return; + } + ScopedManagedCodeThread s(thread_); + [[maybe_unused]] EcmaHandleScope handle_scope(thread_); + JSHandle exceptionHandle(thread_, JSTaggedValue(thread_->GetException())); + // if caught exceptionHandle type is JSError + thread_->ClearException(); + if (exceptionHandle->IsJSError()) { + PrintJSErrorInfo(exceptionHandle); + return; + } + if (exceptionHandle->IsObjectWrapper()) { + JSHandle wrapperValue = JSHandle::Cast(exceptionHandle); + JSHandle throwValue(thread_, wrapperValue->GetValue()); + if (throwValue->IsJSError()) { + PrintJSErrorInfo(throwValue); + } else { + JSHandle result = JSTaggedValue::ToString(thread_, throwValue); + CString string = ConvertToString(*result); + LOG(ERROR, RUNTIME) << string; + } + } +} + +void EcmaVM::PrintJSErrorInfo(const JSHandle &exceptionInfo) +{ + JSHandle nameKey = thread_->GlobalConstants()->GetHandledNameString(); + JSHandle name(JSObject::GetProperty(thread_, exceptionInfo, nameKey).GetValue()); + JSHandle msgKey = thread_->GlobalConstants()->GetHandledMessageString(); + JSHandle msg(JSObject::GetProperty(thread_, exceptionInfo, msgKey).GetValue()); + JSHandle stackKey = thread_->GlobalConstants()->GetHandledStackString(); + JSHandle stack(JSObject::GetProperty(thread_, exceptionInfo, stackKey).GetValue()); + + CString nameBuffer = ConvertToString(*name); + CString msgBuffer = ConvertToString(*msg); + CString stackBuffer = ConvertToString(*stack); + LOG(ERROR, RUNTIME) << nameBuffer << ": " << msgBuffer << "\n" << stackBuffer; +} + +void EcmaVM::ProcessReferences(const WeakRootVisitor &v0) +{ + if (regExpParserCache_ != nullptr) { + regExpParserCache_->Clear(); + } + + // array buffer + for (auto iter = arrayBufferDataList_.begin(); iter != arrayBufferDataList_.end();) { + JSNativePointer *object = *iter; + auto fwd = v0(reinterpret_cast(object)); + if (fwd == nullptr) { + object->Destroy(); + iter = arrayBufferDataList_.erase(iter); + } else if (fwd != reinterpret_cast(object)) { + *iter = JSNativePointer::Cast(fwd); + ++iter; + } else { + ++iter; + } + } + + ProcessPrograms(v0); + + // framework program + if (!frameworkProgram_.IsHole()) { + auto fwd = v0(frameworkProgram_.GetTaggedObject()); + if (fwd == nullptr) { + frameworkProgram_ = JSTaggedValue::Undefined(); + } else if (fwd != frameworkProgram_.GetTaggedObject()) { + frameworkProgram_ = JSTaggedValue(fwd); + } + } +} + +void EcmaVM::ProcessPrograms(const WeakRootVisitor &v0) +{ + os::memory::LockHolder lock(pandaFileWithProgramLock_); + // program vector + for (auto iter = pandaFileWithProgram_.begin(); iter != pandaFileWithProgram_.end();) { + auto object = std::get<0>(*iter); + if (object != nullptr) { + auto fwd = v0(object); + if (fwd == nullptr) { + object->FreeMethodData(regionFactory_.get()); + auto pf = std::get<1>(*iter); + extractorCache_.erase(pf); + delete pf; + iter = pandaFileWithProgram_.erase(iter); + } else if (fwd != object) { + *iter = std::make_tuple(reinterpret_cast(fwd), std::get<1>(*iter), + std::get<2>(*iter)); // 2: index + ++iter; + } else { + ++iter; + } + } else { + ++iter; + } + } +} + +void EcmaVM::PushToArrayDataList(JSNativePointer *array) +{ + if (std::find(arrayBufferDataList_.begin(), arrayBufferDataList_.end(), array) != arrayBufferDataList_.end()) { + return; + } + arrayBufferDataList_.emplace_back(array); +} + +void EcmaVM::RemoveArrayDataList(JSNativePointer *array) +{ + auto iter = std::find(arrayBufferDataList_.begin(), arrayBufferDataList_.end(), array); + if (iter != arrayBufferDataList_.end()) { + arrayBufferDataList_.erase(iter); + } +} + +bool EcmaVM::VerifyFilePath(const CString &filePath) const +{ + if (filePath.size() > PATH_MAX) { + return false; + } + + CVector resolvedPath(PATH_MAX); + auto result = realpath(filePath.c_str(), resolvedPath.data()); + if (result == nullptr) { + return false; + } + std::ifstream file(resolvedPath.data()); + if (!file.good()) { + return false; + } + file.close(); + return true; +} + +void EcmaVM::ClearBufferData() +{ + for (auto iter : arrayBufferDataList_) { + iter->Destroy(); + } + arrayBufferDataList_.clear(); + + os::memory::LockHolder lock(pandaFileWithProgramLock_); + for (auto iter = pandaFileWithProgram_.begin(); iter != pandaFileWithProgram_.end();) { + std::get<0>(*iter)->FreeMethodData(regionFactory_.get()); + iter = pandaFileWithProgram_.erase(iter); + } + pandaFileWithProgram_.clear(); +} + +bool EcmaVM::ExecutePromisePendingJob() const +{ + if (!thread_->HasPendingException()) { + job::MicroJobQueue::ExecutePendingJob(thread_, GetMicroJobQueue()); + return true; + } + return false; +} + +void EcmaVM::CollectGarbage([[maybe_unused]] TriggerGCType gcType) const +{ + mm_->GetGC()->WaitForGCInManaged(GCTask(GCTaskCause::EXPLICIT_CAUSE)); +} + +void EcmaVM::StartHeapTracking(HeapTracker *tracker) +{ + heap_->StartHeapTracking(tracker); +} + +void EcmaVM::StopHeapTracking() +{ + heap_->StopHeapTracking(); +} + +void EcmaVM::Iterate(const RootVisitor &v) +{ + v(Root::ROOT_VM, ObjectSlot(ToUintPtr(&globalEnv_))); + v(Root::ROOT_VM, ObjectSlot(ToUintPtr(µJobQueue_))); + v(Root::ROOT_VM, ObjectSlot(ToUintPtr(&moduleManager_->ecmaModules_))); + v(Root::ROOT_VM, ObjectSlot(ToUintPtr(®expCache_))); +} + +void EcmaVM::SetGlobalEnv(GlobalEnv *global) +{ + ASSERT(global != nullptr); + globalEnv_ = JSTaggedValue(global); +} + +void EcmaVM::SetMicroJobQueue(job::MicroJobQueue *queue) +{ + ASSERT(queue != nullptr); + microJobQueue_ = JSTaggedValue(queue); +} + +const panda_file::File *EcmaVM::GetLastLoadedPandaFile() +{ + os::memory::LockHolder lock(pandaFileWithProgramLock_); + auto currentFileTuple = pandaFileWithProgram_.back(); + return std::get<1>(currentFileTuple); +} + +JSHandle EcmaVM::GetModuleByName(JSHandle moduleName) +{ + const std::string ¤tPathFile = GetLastLoadedPandaFile()->GetFilename(); + CString relativeFile = ConvertToString(EcmaString::Cast(moduleName->GetTaggedObject())); + + // generate full path + CString abcPath = moduleManager_->GenerateModuleFullPath(currentPathFile, relativeFile); + + // Uniform module name + JSHandle abcModuleName = factory_->NewFromString(abcPath); + + JSHandle module = moduleManager_->GetModule(thread_, JSHandle::Cast(abcModuleName)); + if (module->IsUndefined()) { + CString file = ConvertToString(abcModuleName.GetObject()); + std::vector argv; + ExecuteModule(file, ENTRY_POINTER, argv); + module = moduleManager_->GetModule(thread_, JSHandle::Cast(abcModuleName)); + } + return module; +} + +void EcmaVM::ExecuteModule(std::string_view moduleFile, std::string_view entryPoint, + const std::vector &args) +{ + moduleManager_->SetCurrentExportModuleName(moduleFile); + // Update Current Module + EcmaVM::ExecuteFromPf(moduleFile, entryPoint, args, true); + // Restore Current Module + moduleManager_->RestoreCurrentExportModuleName(); +} + +void EcmaVM::ClearNativeMethodsData() +{ + for (auto iter : nativeMethods_) { + chunk_.Delete(iter); + } + nativeMethods_.clear(); +} + +void EcmaVM::SetupRegExpResultCache() +{ + regexpCache_ = builtins::RegExpExecResultCache::CreateCacheTable(thread_); +} + +void EcmaVM::HandleLdaStr(Frame *frame, BytecodeId string_id) +{ + auto value = GetConstantPool(GetJSThread())->GetObjectFromCache(string_id.AsIndex()); + frame->GetAcc().Set(value.GetRawData()); +} + +coretypes::String *EcmaVM::ResolveStringFromCompiledCode([[maybe_unused]] const panda_file::File &pf, + panda_file::File::EntityId id) +{ + auto value = GetConstantPool(GetJSThread())->GetObjectFromCache(id.GetOffset()); + return coretypes::String::Cast(value.GetHeapObject()); +} + +std::unique_ptr EcmaVM::OpenPandaFile(std::string_view location) +{ + panda_file::File::OpenMode open_mode = GetLanguageContext().GetBootPandaFilesOpenMode(); + auto pf = panda_file::OpenPandaFile(location, "", open_mode); + // PandaFileTranslator can trigger GC. + ScopedManagedCodeThread s(thread_); + PandaFileTranslator::QuickPandaFile(this, *pf); + return pf; +} + +coretypes::String *EcmaVM::GetNonMovableString([[maybe_unused]] const panda_file::File &pf, + [[maybe_unused]] panda_file::File::EntityId id) const +{ + return nullptr; +} + +void EcmaVM::HandleReturnFrame() +{ + JSThread *js_thread = GetAssociatedJSThread(); + + // Update EmcascriptEnv + js_thread->SetEcmascriptEnv(js_thread->GetEcmascriptEnv()->GetPrevEnvironment()); +} + +void EcmaVM::VisitVmRoots(const GCRootVisitor &visitor) +{ + ObjectXRay rootManager(this); + auto common_visitor = [&visitor](Root type, ObjectSlot slot) { + mem::RootType root; + switch (type) { + case Root::ROOT_FRAME: + root = mem::RootType::ROOT_FRAME; + break; + default: + root = mem::RootType::ROOT_VM; + break; + } + JSTaggedValue value(slot.GetTaggedType()); + if (value.IsHeapObject()) { + visitor(mem::GCRoot(root, value.GetHeapObject())); + } + }; + + auto single_visitor = [&common_visitor](Root type, ObjectSlot slot) { common_visitor(type, slot); }; + + auto range_visitor = [&common_visitor](Root type, ObjectSlot start, ObjectSlot end) { + for (ObjectSlot slot = start; slot < end; slot++) { + common_visitor(type, slot); + } + }; + rootManager.VisitVMRoots(single_visitor, range_visitor); +} + +void EcmaVM::UpdateVmRefs() +{ + ObjectXRay rootManager(this); + auto single_visitor = []([[maybe_unused]] Root type, ObjectSlot slot) { + JSTaggedValue value(slot.GetTaggedType()); + if (value.IsHeapObject()) { + ObjectHeader *object = value.GetHeapObject(); + if (object->IsForwarded()) { + slot.Update(TaggedObject::Cast(panda::mem::GetForwardAddress(object))); + } + } + }; + + auto range_visitor = [&single_visitor](Root type, ObjectSlot start, ObjectSlot end) { + for (ObjectSlot slot = start; slot < end; slot++) { + single_visitor(type, slot); + } + }; + rootManager.VisitVMRoots(single_visitor, range_visitor); + + // Special handling of arrayBufferDataList_ + // Don't handle it in ObjectXRay because these refs shouldn't be marked + // as gc roots + // NOLINTNEXTLINE(modernize-loop-convert) + for (size_t i = 0; i < arrayBufferDataList_.size(); ++i) { + single_visitor(Root::ROOT_VM, ObjectSlot(ToUintPtr(&arrayBufferDataList_[i]))); + } +} + +} // namespace panda::ecmascript diff --git a/runtime/ecma_vm.h b/runtime/ecma_vm.h new file mode 100644 index 000000000..6ecdf40b0 --- /dev/null +++ b/runtime/ecma_vm.h @@ -0,0 +1,592 @@ +/* + * Copyright (c) 2021-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_ECMA_VM_H +#define ECMASCRIPT_ECMA_VM_H + +#include + +#include "plugins/ecmascript/runtime/base/config.h" +#include "plugins/ecmascript/runtime/ecma_string_table.h" +#include "plugins/ecmascript/runtime/global_handle_collection.h" +#include "plugins/ecmascript/runtime/js_handle.h" +#include "plugins/ecmascript/runtime/js_method.h" +#include "plugins/ecmascript/runtime/js_runtime_options.h" +#include "plugins/ecmascript/runtime/mem/c_containers.h" +#include "plugins/ecmascript/runtime/mem/c_string.h" +#include "plugins/ecmascript/runtime/mem/chunk_containers.h" +#include "plugins/ecmascript/runtime/mem/gc_stats.h" +#include "plugins/ecmascript/runtime/platform/platform.h" +#include "plugins/ecmascript/runtime/mem/object_xray.h" +#include "plugins/ecmascript/runtime/mem/space.h" +#include "plugins/ecmascript/runtime/platform/task.h" +#include "plugins/ecmascript/runtime/snapshot/mem/snapshot_serialize.h" +#include "plugins/ecmascript/runtime/tooling/pt_js_extractor.h" +#include "include/panda_vm.h" +#include "libpandabase/macros.h" +#include "libpandabase/os/library_loader.h" + +#include "source_languages.h" + +namespace panda { +class JSNApi; +class RuntimeNotificationManager; +namespace panda_file { +class File; +} // namespace panda_file + +namespace ecmascript { +class GlobalEnv; +class ObjectFactory; +class RegExpParserCache; +class EcmaRuntimeStat; +class MemManager; +class Heap; +class HeapTracker; +class JSNativePointer; +class Program; +class JSPromise; +enum class PromiseRejectionEvent : uint32_t; + +namespace job { +class MicroJobQueue; +} // namespace job + +namespace builtins { +class RegExpExecResultCache; +} // namespace builtins + +template +class JSHandle; +class JSArrayBuffer; +class JSFunction; +class Program; +class ModuleManager; +class EcmaModule; +using HostPromiseRejectionTracker = void (*)(const EcmaVM *vm, const JSHandle promise, + const JSHandle reason, + const PromiseRejectionEvent operation, void *data); +using PromiseRejectCallback = void (*)(void *info); + +class EcmaRendezvous : public Rendezvous { + void SafepointBegin() ACQUIRE(*Locks::mutator_lock) override; + void SafepointEnd() RELEASE(*Locks::mutator_lock) override; +}; + +class EcmaVM final : public PandaVM { + using PtJSExtractor = tooling::ecmascript::PtJSExtractor; + +public: + static EcmaVM *Cast(PandaVM *object) + { + return reinterpret_cast(object); + } + + static EcmaVM *Create(const JSRuntimeOptions &options); + + static bool Destroy(PandaVM *vm); + + explicit EcmaVM(JSRuntimeOptions options); + + static Expected Create(Runtime *runtime, const JSRuntimeOptions &options); + + EcmaVM(); + + ~EcmaVM() override; + + bool ExecuteFromPf(std::string_view filename, std::string_view entryPoint, const std::vector &args, + bool isModule = false); + + bool ExecuteFromBuffer(const void *buffer, size_t size, std::string_view entryPoint, + const std::vector &args); + + PtJSExtractor *GetDebugInfoExtractor(const panda_file::File *file); + + bool IsInitialized() const + { + return vmInitialized_; + } + + void HandleLdaStr(Frame *frame, BytecodeId string_id) override; + + ObjectFactory *GetFactory() const + { + return factory_; + } + + bool Initialize() override; + + bool InitializeFinish() override; + void UninitializeThreads() override; + void PreStartup() override {} + void PreZygoteFork() override {} + void PostZygoteFork() override {} + void InitializeGC() override + { + mm_->InitializeGC(this); + } + void StartGC() override + { + mm_->StartGC(); + } + void StopGC() override + { + mm_->StopGC(); + } + + void VisitVmRoots(const GCRootVisitor &visitor) override; + void UpdateVmRefs() override; + + PandaVMType GetPandaVMType() const override + { + return PandaVMType::ECMA_VM; + } + + LanguageContext GetLanguageContext() const override + { + return Runtime::GetCurrent()->GetLanguageContext(panda_file::SourceLang::ECMASCRIPT); + } + + panda::mem::HeapManager *GetHeapManager() const override + { + return mm_->GetHeapManager(); + } + + panda::mem::GC *GetGC() const override + { + return mm_->GetGC(); + } + + panda::mem::GCTrigger *GetGCTrigger() const override + { + return mm_->GetGCTrigger(); + } + + StringTable *GetStringTable() const override + { + UNREACHABLE(); + } + + panda::mem::GCStats *GetGCStats() const override + { + return mm_->GetGCStats(); + } + + panda::mem::MemStatsType *GetMemStats() const override + { + return mm_->GetMemStats(); + } + + GCStats *GetEcmaGCStats() const + { + return gcStats_; + } + + panda::mem::GlobalObjectStorage *GetGlobalObjectStorage() const override + { + return nullptr; + } + + coretypes::String *ResolveString(const panda_file::File &pf, panda_file::File::EntityId id) override + { + ASSERT(stringTable_ != nullptr); + auto str = stringTable_->ResolveString(pf, id); + return coretypes::String::Cast(str); + } + + coretypes::String *ResolveStringFromCompiledCode(const panda_file::File &pf, + panda_file::File::EntityId id) override; + + void HandleReturnFrame() override; + + MonitorPool *GetMonitorPool() const override + { + UNREACHABLE(); + } + + ThreadManager *GetThreadManager() const override + { + UNREACHABLE(); + } + + void VisitStringTable(const StringTable::StringVisitor &visitor, mem::VisitGCRootFlags flags) override + { + GetEcmaStringTable()->VisitRoots(visitor, flags); + } + + void SweepStringTable(const GCObjectVisitor &gc_object_visitor) override + { + GetEcmaStringTable()->Sweep(gc_object_visitor); + } + + bool UpdateMovedStrings() override + { + return GetEcmaStringTable()->UpdateMoved(); + } + + ManagedThread *GetAssociatedThread() const override + { + return thread_; + } + + JSThread *GetAssociatedJSThread() const + { + return thread_; + } + + CompilerInterface *GetCompiler() const override + { + return compiler_; + } + + Rendezvous *GetRendezvous() const override + { + return rendezvous_; + } + + compiler::RuntimeInterface *GetCompilerRuntimeInterface() const override + { + return runtime_iface_; + } + + ObjectHeader *GetOOMErrorObject() override + { + // preallocated OOM is not implemented for JS + UNREACHABLE(); + } + + panda::mem::ReferenceProcessor *GetReferenceProcessor() const override + { + return ecma_reference_processor_.get(); + } + + const RuntimeOptions &GetOptions() const override + { + return Runtime::GetOptions(); + } + + static const JSRuntimeOptions &GetJSOptions() + { + return options_; + } + + JSHandle GetGlobalEnv() const; + + JSHandle GetMicroJobQueue() const; + + bool ExecutePromisePendingJob() const; + + RegExpParserCache *GetRegExpParserCache() const + { + ASSERT(regExpParserCache_ != nullptr); + return regExpParserCache_; + } + + JSMethod *GetMethodForNativeFunction(const void *func); + + EcmaStringTable *GetEcmaStringTable() const + { + ASSERT(stringTable_ != nullptr); + return stringTable_; + } + + JSThread *GetJSThread() const + { + return thread_; + } + + bool ICEnable() const + { + return icEnable_; + } + + void HandleReferences([[maybe_unused]] const GCTask &task, const mem::GC::ReferenceClearPredicateT &pred) override + { + LOG(DEBUG, REF_PROC) << "Start processing cleared references"; + mem::GC *gc = mm_->GetGC(); + gc->ProcessReferences(gc->GetGCPhase(), task, pred); + } + + void PushToArrayDataList(JSNativePointer *array); + void RemoveArrayDataList(JSNativePointer *array); + + JSHandle GetEcmaUncaughtException() const; + void EnableUserUncaughtErrorHandler(); + + template + void EnumeratePandaFiles(Callback cb) const + { + os::memory::LockHolder lock(pandaFileWithProgramLock_); + for (const auto &iter : pandaFileWithProgram_) { + if (!cb(std::get<0>(iter), std::get<1>(iter))) { + break; + } + } + } + + template + void EnumerateProgram(Callback cb, const std::string &pandaFile) const + { + os::memory::LockHolder lock(pandaFileWithProgramLock_); + for (const auto &iter : pandaFileWithProgram_) { + if (pandaFile == std::get<1>(iter)->GetFilename()) { + cb(std::get<0>(iter)); + break; + } + } + } + + EcmaRuntimeStat *GetRuntimeStat() const + { + return runtimeStat_; + } + + void SetRuntimeStatEnable(bool flag); + + bool IsRuntimeStatEnabled() const + { + return runtimeStatEnabled_; + } + + bool IsOptionalLogEnabled() const + { + return optionalLogEnabled_; + } + + void Iterate(const RootVisitor &v); + + const Heap *GetHeap() const + { + return heap_; + } + + void CollectGarbage(TriggerGCType gcType) const; + + void StartHeapTracking(HeapTracker *tracker); + + void StopHeapTracking(); + + RegionFactory *GetRegionFactory() const + { + return regionFactory_.get(); + } + + Chunk *GetChunk() const + { + return const_cast(&chunk_); + } + + void ProcessReferences(const WeakRootVisitor &v0); + + JSHandle GetModuleByName(JSHandle moduleName); + + void ExecuteModule(std::string_view moduleFile, std::string_view entryPoint, const std::vector &args); + + ModuleManager *GetModuleManager() const + { + return moduleManager_; + } + + void SetupRegExpResultCache(); + + JSHandle GetRegExpCache() const + { + return JSHandle(reinterpret_cast(®expCache_)); + } + + void SetRegExpCache(JSTaggedValue newCache) + { + regexpCache_ = newCache; + } + + RuntimeNotificationManager *GetNotificationManager() const + { + return notificationManager_; + } + + std::unique_ptr OpenPandaFile(std::string_view location) override; + + coretypes::String *GetNonMovableString(const panda_file::File &pf, panda_file::File::EntityId id) const override; + + const ChunkVector &GetNativeMethods() const + { + return nativeMethods_; + } + + void SetEnableForceGC(bool enable) + { + options_.SetEnableForceGC(enable); + } + + void SetData(void *data) + { + data_ = data; + } + + void SetPromiseRejectCallback(PromiseRejectCallback cb) + { + promiseRejectCallback_ = cb; + } + + PromiseRejectCallback GetPromiseRejectCallback() const + { + return promiseRejectCallback_; + } + + void SetHostPromiseRejectionTracker(HostPromiseRejectionTracker cb) + { + hostPromiseRejectionTracker_ = cb; + } + + void PromiseRejectionTracker(const JSHandle &promise, const JSHandle &reason, + const PromiseRejectionEvent operation) + { + if (hostPromiseRejectionTracker_ != nullptr) { + hostPromiseRejectionTracker_(this, promise, reason, operation, data_); + } + } + +protected: + bool CheckEntrypointSignature([[maybe_unused]] Method *entrypoint) override + { + return true; + } + + Expected InvokeEntrypointImpl(Method *entrypoint, + const std::vector &args) override; + + void HandleUncaughtException() override; + + void PrintJSErrorInfo(const JSHandle &exceptionInfo); + +private: + static constexpr uint32_t THREAD_SLEEP_TIME = 16 * 1000; + static constexpr uint32_t THREAD_SLEEP_COUNT = 100; + class TrimNewSpaceLimitTask : public Task { + public: + explicit TrimNewSpaceLimitTask(Heap *heap) : heap_(heap) {}; + ~TrimNewSpaceLimitTask() override = default; + bool Run(uint32_t threadIndex) override; + + NO_COPY_SEMANTIC(TrimNewSpaceLimitTask); + NO_MOVE_SEMANTIC(TrimNewSpaceLimitTask); + + private: + Heap *heap_; + }; + + void AddPandaFile(const panda_file::File *pf, bool isModule); + void SetProgram(Program *program, const panda_file::File *pf); + bool IsFrameworkPandaFile(std::string_view filename) const; + + void SetGlobalEnv(GlobalEnv *global); + + void SetMicroJobQueue(job::MicroJobQueue *queue); + + bool Execute(std::unique_ptr pf, std::string_view entryPoint, + const std::vector &args, bool isModule = false); + + Expected InvokeEcmaEntrypoint(const panda_file::File &pf, const CString &methodName, + const std::vector &args); + + void InitializeEcmaScriptRunStat(); + + void RedirectMethod(const panda_file::File &pf); + + bool VerifyFilePath(const CString &filePath) const; + + void ClearBufferData(); + + void ClearNativeMethodsData(); + + Method *GetNativeMethodWrapper(); + void LoadEcmaStdLib(); + const panda_file::File *GetLastLoadedPandaFile(); + void ProcessPrograms(const WeakRootVisitor &v0); + + NO_MOVE_SEMANTIC(EcmaVM); + NO_COPY_SEMANTIC(EcmaVM); + + mem::MemoryManager *mm_ {nullptr}; + PandaUniquePtr ecma_reference_processor_; + + EcmaRendezvous *rendezvous_ {nullptr}; + bool isTestMode_ {false}; + + // VM startup states. + static JSRuntimeOptions options_; + bool icEnable_ {true}; + bool vmInitialized_ {false}; + GCStats *gcStats_ {nullptr}; + bool snapshotSerializeEnable_ {false}; + bool snapshotDeserializeEnable_ {false}; + bool isUncaughtExceptionRegistered_ {false}; + + // VM memory management. + EcmaStringTable *stringTable_ {nullptr}; + std::unique_ptr regionFactory_; + Chunk chunk_; + Heap *heap_ {nullptr}; + ObjectFactory *factory_ {nullptr}; + CVector arrayBufferDataList_; + + // VM execution states. + JSThread *thread_ {nullptr}; + RegExpParserCache *regExpParserCache_ {nullptr}; + JSTaggedValue globalEnv_ {JSTaggedValue::Hole()}; + JSTaggedValue regexpCache_ {JSTaggedValue::Hole()}; + JSTaggedValue microJobQueue_ {JSTaggedValue::Hole()}; + bool runtimeStatEnabled_ {false}; + EcmaRuntimeStat *runtimeStat_ {nullptr}; + + // App framework resources. + JSTaggedValue frameworkProgram_ {JSTaggedValue::Hole()}; + CString frameworkAbcFileName_; + const panda_file::File *frameworkPandaFile_ {nullptr}; + CVector frameworkProgramMethods_; + + // VM resources. + CString snapshotFileName_; + ChunkVector nativeMethods_; + ModuleManager *moduleManager_ {nullptr}; + bool optionalLogEnabled_ {false}; + // weak reference need Redirect address + CVector> pandaFileWithProgram_ + GUARDED_BY(pandaFileWithProgramLock_); + mutable os::memory::Mutex pandaFileWithProgramLock_; + PandaMap>> + functions_arg_type_cache_; + Method *nativeMethodWrapper_ {nullptr}; + CompilerInterface *compiler_ {nullptr}; + compiler::RuntimeInterface *runtime_iface_ {nullptr}; + + // Debugger + RuntimeNotificationManager *notificationManager_ {nullptr}; + CUnorderedMap> extractorCache_; + + // Registered Callbacks + PromiseRejectCallback promiseRejectCallback_ {nullptr}; + HostPromiseRejectionTracker hostPromiseRejectionTracker_ {nullptr}; + void *data_ {nullptr}; + + friend class SnapShotSerialize; + friend class ObjectFactory; + friend class ValueSerializer; + friend class panda::JSNApi; +}; +} // namespace ecmascript +} // namespace panda + +#endif diff --git a/runtime/frames.h b/runtime/frames.h new file mode 100644 index 000000000..465de0ea4 --- /dev/null +++ b/runtime/frames.h @@ -0,0 +1,266 @@ +/* + * Copyright (c) 2021-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. + */ + +// in aot project, three Frame: Interpreter Frame、Runtime Frame、Optimized Frame. Optimized Frame +// call Runtime function, generate OptLeaveFrame Frame(gc related context (patchid、sp、fp) is saved to frame) +// then return Optimized thread.sp restore orignal sp. + +// Frame Layout +// Interpreter Frame(alias **iframe** ) Layout as follow: +// ``` +// +----------------------------------+-------------------+ +// | argv[n-1] | ^ +// |----------------------------------| | +// | ...... | | +// |----------------------------------| | +// | thisArg [maybe not exist] | | +// |----------------------------------| | +// | newTarget [maybe not exist] | | +// |----------------------------------| | +// | callTarget [deleted] | | +// |----------------------------------| | +// | ...... | | +// |----------------------------------| | +// | Vregs [not exist in native] | | +// +----------------------------------+--------+ interpreter frame +// | base.frameType | ^ | +// |----------------------------------| | | +// | base.prev(pre stack pointer) | | | +// |----------------------------------| | | +// | numActualArgs [deleted] | | | +// |----------------------------------| | | +// | env | | | +// |----------------------------------| | | +// | acc | | | +// |----------------------------------|InterpretedFrame | +// | profileTypeInfo | | | +// |----------------------------------| | | +// | constantpool | | | +// |----------------------------------| | | +// | method [changed to function] | | | +// |----------------------------------| | | +// | sp(current stack point) | | | +// |----------------------------------| | | +// | pc(bytecode addr) | v v +// +----------------------------------+--------+----------+ +// ``` +// address space grow from high address to low address.we add new field **FrameType** , +// the field's value is INTERPRETER_FRAME(represent interpreter frame). +// **currentsp** is pointer to callTarget field address, sp field 's value is **currentsp** , +// pre field pointer pre stack frame point. fill JSthread's sp field with iframe sp field +// by calling JSThread->SetCurrentSPFrame and save pre Frame address to iframe pre field. + +// For Example: +// ``` +// call call +// foo -----------------> bar -----------------------> rtfunc +// (interpret frame) (OptLeaveFrame) (Runtime Frame) +// ``` + +// Frame Layout as follow: +// ``` +// +----------------------------------+-------------------+ +// | argv[n-1] | ^ +// |----------------------------------| | +// | ...... | | +// |----------------------------------| | +// | thisArg [maybe not exist] | | +// |----------------------------------| | +// | newTarget [maybe not exist] | | +// |----------------------------------| | +// | ...... | | +// |----------------------------------| | +// | Vregs | | +// +----------------------------------+--------+ foo's frame +// | base.frameType | ^ | +// |----------------------------------| | | +// | base.prev(pre stack pointer) | | | +// |----------------------------------| | | +// | env | | | +// |----------------------------------| | | +// | acc | | | +// |----------------------------------| | | +// | profileTypeInfo |InterpretedFrame | +// |----------------------------------| | | +// | constantpool | | | +// |----------------------------------| | | +// | function | | | +// |----------------------------------| | | +// | sp(current stack point) | | | +// |----------------------------------| | | +// | pc(bytecode addr) | v v +// +----------------------------------+--------+----------+ +// | ............. | +// +--------------------------+---------------------------+ +// | patchID | ^ ^ +// |- - - - - - - - - | | | +// | fp | Fixed | +// |- - - - - - - - - | OptLeaveFrame | +// | sp | | bar's frame Header +// |- - - - - - - - - | | | +// | prev | | +// |- - - - - - - - - | | | +// | frameType | v | +// +--------------------------+---------------------------+ +// | ............. | +// +--------------------------+---------------------------+ +// | | +// | rtfunc's Frame | +// | | +// +------------------------------------------------------+ +// ``` +// Iterator: +// rtfunc get bar's Frame **currentfp** by calling GetCurrentSPFrame. +// then get bar's Frame pre field. +// bar's Frame pre field point to foo's Frame **currentsp**. +// finally we can iterator foo's Frame. + +#ifndef ECMASCRIPT_FRAMES_H +#define ECMASCRIPT_FRAMES_H + +#include "plugins/ecmascript/runtime/js_tagged_value.h" + +namespace panda::ecmascript { +class JSThread; +enum class FrameType : uint64_t { + OPTIMIZED_FRAME = 0, + OPTIMIZED_ENTRY_FRAME = 1, + INTERPRETER_FRAME = 2, + OPTIMIZED_LEAVE_FRAME = 3, +}; + +struct FrameConstants { +public: +#ifdef PANDA_TARGET_AMD64 + static constexpr int SP_DWARF_REG_NUM = 7; + static constexpr int FP_DWARF_REG_NUM = 6; + static constexpr int SP_OFFSET = 2; +#else +#ifdef PANDA_TARGET_ARM64 + static constexpr int SP_DWARF_REG_NUM = 31; /* x31 */ + static constexpr int FP_DWARF_REG_NUM = 29; /* x29 */ + static constexpr int SP_OFFSET = -3; +#else +#ifdef PANDA_TARGET_ARM32 + static constexpr int SP_DWARF_REG_NUM = 13; + static constexpr int FP_DWARF_REG_NUM = 11; + static constexpr int SP_OFFSET = 0; +#else + static constexpr int SP_DWARF_REG_NUM = 0; + static constexpr int FP_DWARF_REG_NUM = 0; + static constexpr int SP_OFFSET = 0; +#endif +#endif +#endif + static constexpr int AARCH64_SLOT_SIZE = sizeof(uint64_t); + static constexpr int AMD64_SLOT_SIZE = sizeof(uint64_t); + static constexpr int ARM32_SLOT_SIZE = sizeof(uint32_t); +}; + +class OptimizedFrameBase { +public: + OptimizedFrameBase() = default; + ~OptimizedFrameBase() = default; + // NOLINTNEXTLINE(misc-non-private-member-variables-in-classes) + FrameType type; + // NOLINTNEXTLINE(misc-non-private-member-variables-in-classes) + JSTaggedType *prev; // for llvm :c-fp ; for interrupt: thread-fp for gc + static OptimizedFrameBase *GetFrameFromSp(JSTaggedType *sp) + { + return reinterpret_cast(reinterpret_cast(sp) - + MEMBER_OFFSET(OptimizedFrameBase, prev)); + } + DEFAULT_MOVE_SEMANTIC(OptimizedFrameBase); + DEFAULT_COPY_SEMANTIC(OptimizedFrameBase); +}; + +class OptimizedEntryFrame { +public: + OptimizedEntryFrame() = default; + ~OptimizedEntryFrame() = default; + // NOLINTNEXTLINE(misc-non-private-member-variables-in-classes) + JSTaggedType *prevInterpretedFrameFp; + // NOLINTNEXTLINE(misc-non-private-member-variables-in-classes) + OptimizedFrameBase base; + static OptimizedEntryFrame *GetFrameFromSp(JSTaggedType *sp) + { + return reinterpret_cast(reinterpret_cast(sp) - + MEMBER_OFFSET(OptimizedEntryFrame, base.prev)); + } + DEFAULT_MOVE_SEMANTIC(OptimizedEntryFrame); + DEFAULT_COPY_SEMANTIC(OptimizedEntryFrame); +}; + +class InterpretedFrameBase { +public: + InterpretedFrameBase() = default; + ~InterpretedFrameBase() = default; + // NOLINTNEXTLINE(misc-non-private-member-variables-in-classes) + JSTaggedType *prev; // for llvm :c-fp ; for interrupt: thread-fp for gc + + // NOLINTNEXTLINE(misc-non-private-member-variables-in-classes) + FrameType type; + DEFAULT_MOVE_SEMANTIC(InterpretedFrameBase); + DEFAULT_COPY_SEMANTIC(InterpretedFrameBase); +}; + +// align with 8 +// NOLINTNEXTLINE(cppcoreguidelines-pro-type-member-init) +struct InterpretedFrame { + const uint8_t *pc; + JSTaggedType *sp; + // aligned with 8 bits + alignas(sizeof(uint64_t)) JSTaggedValue constpool; + JSTaggedValue function; + JSTaggedValue profileTypeInfo; + JSTaggedValue acc; + JSTaggedValue env; + InterpretedFrameBase base; + static InterpretedFrame *GetFrameFromSp(JSTaggedType *sp) + { + // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-pointer-arithmetic) + return reinterpret_cast(sp) - 1; + } + static constexpr uint32_t kSizeOn64Platform = + 2 * sizeof(int64_t) + 5 * sizeof(JSTaggedValue) + 2 * sizeof(uint64_t); + static constexpr uint32_t kSizeOn32Platform = + 2 * sizeof(int32_t) + 5 * sizeof(JSTaggedValue) + 2 * sizeof(uint64_t); +}; +static_assert(sizeof(InterpretedFrame) % sizeof(uint64_t) == 0U); + +struct OptLeaveFrame { + FrameType type; + JSTaggedType *prevFp; // set cursp here + uintptr_t sp; + uintptr_t fp; + uint64_t patchId; + static OptLeaveFrame *GetFrameFromSp(JSTaggedType *sp) + { + return reinterpret_cast(reinterpret_cast(sp) - + MEMBER_OFFSET(OptLeaveFrame, prevFp)); + } + static constexpr uint32_t kSizeOn64Platform = sizeof(FrameType) + 4 * sizeof(uint64_t); + static constexpr uint32_t kSizeOn32Platform = sizeof(FrameType) + 3 * sizeof(int32_t) + sizeof(uint64_t); + static constexpr uint32_t kPrevFpOffset = sizeof(FrameType); +}; + +#ifdef PANDA_TARGET_64 +static_assert(InterpretedFrame::kSizeOn64Platform == sizeof(InterpretedFrame)); +#endif +#ifdef PANDA_TARGET_32 +static_assert(InterpretedFrame::kSizeOn32Platform == sizeof(InterpretedFrame)); +#endif +} // namespace panda::ecmascript +#endif // ECMASCRIPT_FRAMES_H diff --git a/runtime/free_object.cpp b/runtime/free_object.cpp new file mode 100644 index 000000000..401fda58d --- /dev/null +++ b/runtime/free_object.cpp @@ -0,0 +1,24 @@ +/* + * Copyright (c) 2021-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 "plugins/ecmascript/runtime/free_object.h" +#include "plugins/ecmascript/runtime/object_factory.h" + +namespace panda::ecmascript { +FreeObject *FreeObject::FillFreeObject(EcmaVM *vm, uintptr_t address, size_t size) +{ + return vm->GetFactory()->FillFreeObject(address, size); +} +} // namespace panda::ecmascript diff --git a/runtime/free_object.h b/runtime/free_object.h new file mode 100644 index 000000000..079c65c37 --- /dev/null +++ b/runtime/free_object.h @@ -0,0 +1,81 @@ +/* + * Copyright (c) 2021-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_FREE_OBJECT_H +#define ECMASCRIPT_FREE_OBJECT_H + +#include "runtime/mem/free_object.h" + +#include "plugins/ecmascript/runtime/js_hclass.h" +#include "plugins/ecmascript/runtime/mem/barriers.h" +#include "plugins/ecmascript/runtime/mem/tagged_object-inl.h" + +namespace panda::ecmascript { +class FreeObject : public TaggedObject { +public: + static FreeObject *Cast(uintptr_t object) + { + return reinterpret_cast(object); + } + static FreeObject *FillFreeObject(EcmaVM *vm, uintptr_t address, size_t size); + + inline bool IsEmpty() const + { + return Available() == 0; + } + + inline uintptr_t GetBegin() const + { + return reinterpret_cast(this); + } + + inline uintptr_t GetEnd() const + { + return reinterpret_cast(this) + Available(); + } + + inline void SetAvailable(uint32_t size) + { + if (size >= SIZE) { + SetSize(JSTaggedValue(size)); + } + } + + inline uint32_t Available() const + { + auto hclass = GetClass(); + if (hclass != nullptr && (hclass->IsFreeObjectWithOneField() || hclass->IsFreeObjectWithNoneField())) { + return hclass->GetObjectSize(); + } + return GetSize().GetInt(); + } + + inline bool IsFreeObject() const + { + auto hclass = GetClass(); + return (hclass == nullptr) || (hclass->IsFreeObjectWithOneField() || hclass->IsFreeObjectWithTwoField() || + hclass->IsFreeObjectWithNoneField()); + } + + static constexpr size_t NEXT_OFFSET = TaggedObjectSize(); + SET_GET_NATIVE_FIELD(Next, FreeObject, NEXT_OFFSET, SIZE_OFFSET); + ACCESSORS(Size, SIZE_OFFSET, SIZE); +}; + +static_assert(FreeObject::NEXT_OFFSET == panda::mem::FreeObject::GetNextOffset()); +static_assert(FreeObject::SIZE_OFFSET == panda::mem::FreeObject::GetSizeOffset()); + +} // namespace panda::ecmascript +#endif // ECMASCRIPT_MEM_FREE_OBJECT_H diff --git a/runtime/generator_helper.cpp b/runtime/generator_helper.cpp new file mode 100644 index 000000000..808cfa5e1 --- /dev/null +++ b/runtime/generator_helper.cpp @@ -0,0 +1,38 @@ +/* + * Copyright (c) 2021-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 "plugins/ecmascript/runtime/generator_helper.h" + +#include "plugins/ecmascript/runtime/interpreter/interpreter-inl.h" +#include "plugins/ecmascript/runtime/js_iterator.h" +#include "libpandafile/bytecode_instruction-inl.h" + +namespace panda::ecmascript { +JSHandle GeneratorHelper::Continue(JSThread *thread, const JSHandle &genContext, + GeneratorResumeMode resumeMode, JSTaggedValue value) +{ + [[maybe_unused]] C2IBridge c2i; + EcmaInterpreter::ChangeGenContext(thread, genContext); + + JSHandle genObject(thread, genContext->GetGeneratorObject()); + genObject->SetResumeMode(thread, JSTaggedValue(static_cast(resumeMode))); + genObject->SetResumeResult(thread, value); + + JSHandle result(thread, EcmaInterpreter::GeneratorReEnterInterpreter(thread, genContext)); + + EcmaInterpreter::ResumeContext(thread); + return result; +} +} // namespace panda::ecmascript diff --git a/runtime/generator_helper.h b/runtime/generator_helper.h new file mode 100644 index 000000000..092c4616b --- /dev/null +++ b/runtime/generator_helper.h @@ -0,0 +1,40 @@ +/* + * Copyright (c) 2021-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_GENERATOR_HELPER_H +#define ECMASCRIPT_GENERATOR_HELPER_H + +#include + +#include "plugins/ecmascript/runtime/js_generator_object.h" +#include "plugins/ecmascript/runtime/js_handle.h" + +namespace panda::ecmascript { +enum class GeneratorResumeMode { + RETURN = 0, + THROW, + NEXT, +}; + +using C2IBridge = std::array; // 4: means array length + +class GeneratorHelper { +public: + static JSHandle Continue(JSThread *thread, const JSHandle &genContext, + GeneratorResumeMode resumeMode, JSTaggedValue value); +}; +} // namespace panda::ecmascript + +#endif // ECMASCRIPT_GENERATOR_HELPER_H diff --git a/runtime/global_dictionary-inl.h b/runtime/global_dictionary-inl.h new file mode 100644 index 000000000..b5e418457 --- /dev/null +++ b/runtime/global_dictionary-inl.h @@ -0,0 +1,187 @@ +/* + * Copyright (c) 2021-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_GLOBAL_DICTIONARY_INL_H +#define ECMASCRIPT_GLOBAL_DICTIONARY_INL_H + +#include "plugins/ecmascript/runtime/global_dictionary.h" +#include "plugins/ecmascript/runtime/ic/property_box.h" +#include "plugins/ecmascript/runtime/mem/c_containers.h" +#include "plugins/ecmascript/runtime/tagged_hash_table-inl.h" + +namespace panda { +namespace ecmascript { +int GlobalDictionary::Hash(const JSTaggedValue &key) +{ + if (key.IsHeapObject()) { + if (key.IsSymbol()) { + auto symbolString = JSSymbol::Cast(key.GetTaggedObject()); + return static_cast(symbolString->GetHashField()).GetInt(); + } + if (key.IsString()) { + auto keyString = EcmaString::Cast(key.GetTaggedObject()); + return keyString->GetHashcode(); + } + } + // key must be object + UNREACHABLE(); +} + +bool GlobalDictionary::IsMatch(const JSTaggedValue &key, const JSTaggedValue &other) +{ + return key == other; +} + +PropertyBox *GlobalDictionary::GetBox(int entry) const +{ + int index = GetEntryIndex(entry) + ENTRY_VALUE_INDEX; + return PropertyBox::Cast(Get(index).GetTaggedObject()); +} + +JSTaggedValue GlobalDictionary::GetValue(int entry) const +{ + return GetBox(entry)->GetValue(); +} + +PropertyAttributes GlobalDictionary::GetAttributes(int entry) const +{ + int index = GetEntryIndex(entry) + ENTRY_DETAILS_INDEX; + return PropertyAttributes(Get(index).GetInt()); +} + +void GlobalDictionary::SetEntry(const JSThread *thread, int entry, const JSTaggedValue &key, const JSTaggedValue &value, + const PropertyAttributes &attributes) +{ + SetKey(thread, entry, key); + SetAttributes(thread, entry, attributes); + UpdateValueAndAttributes(thread, entry, value, attributes); +} + +void GlobalDictionary::ClearEntry(const JSThread *thread, int entry) +{ + JSTaggedValue hole = JSTaggedValue::Hole(); + PropertyAttributes metaData; + SetEntry(thread, entry, hole, hole, metaData); +} + +void GlobalDictionary::UpdateValueAndAttributes(const JSThread *thread, int entry, const JSTaggedValue &value, + const PropertyAttributes &metaData) +{ + UpdateValue(thread, entry, value); + SetAttributes(thread, entry, metaData); +} + +void GlobalDictionary::SetAttributes(const JSThread *thread, int entry, const PropertyAttributes &metaData) +{ + int index = GetEntryIndex(entry) + ENTRY_DETAILS_INDEX; + Set(thread, index, metaData.GetTaggedValue()); +} + +void GlobalDictionary::UpdateValue(const JSThread *thread, int entry, const JSTaggedValue &value) +{ + SetValue(thread, entry, value); +} + +void GlobalDictionary::GetAllKeys(const JSThread *thread, int offset, TaggedArray *keyArray) const +{ + ASSERT_PRINT(offset + EntriesCount() <= static_cast(keyArray->GetLength()), + "keyArray capacity is not enough for dictionary"); + int arrayIndex = 0; + int size = Size(); + + CVector> sortArr; + for (int hashIndex = 0; hashIndex < size; hashIndex++) { + JSTaggedValue key = GetKey(hashIndex); + if (!key.IsUndefined() && !key.IsHole()) { + PropertyAttributes attr = GetAttributes(hashIndex); + std::pair pair(key, attr.GetOffset()); + sortArr.push_back(pair); + } + } + std::sort(sortArr.begin(), sortArr.end(), CompKey); + for (auto entry : sortArr) { + JSTaggedValue nameKey = entry.first; + keyArray->Set(thread, arrayIndex + offset, nameKey); + arrayIndex++; + } +} + +void GlobalDictionary::GetEnumAllKeys(const JSThread *thread, int offset, TaggedArray *keyArray, + uint32_t *keys) const +{ + ASSERT_PRINT(offset + EntriesCount() <= static_cast(keyArray->GetLength()), + "keyArray capacity is not enough for dictionary"); + int arrayIndex = 0; + int size = Size(); + + CVector> sortArr; + for (int hashIndex = 0; hashIndex < size; hashIndex++) { + JSTaggedValue key = GetKey(hashIndex); + if (key.IsString()) { + PropertyAttributes attr = GetAttributes(hashIndex); + if (attr.IsEnumerable()) { + std::pair pair(key, attr.GetOffset()); + sortArr.push_back(pair); + } + } + } + std::sort(sortArr.begin(), sortArr.end(), CompKey); + for (const auto &entry : sortArr) { + JSTaggedValue nameKey = entry.first; + keyArray->Set(thread, arrayIndex + offset, nameKey); + arrayIndex++; + } + *keys += arrayIndex; +} + +bool GlobalDictionary::CompKey(const std::pair &a, const std::pair &b) +{ + return a.second < b.second; +} + +void GlobalDictionary::InvalidatePropertyBox(JSThread *thread, const JSHandle &dictHandle, int entry, + const PropertyAttributes &metaData) +{ + PropertyBox *box = dictHandle->GetBox(entry); + PropertyAttributes originAttr = dictHandle->GetAttributes(entry); + PropertyAttributes newAttr(metaData); + + ASSERT(!box->GetValue().IsHole()); + int index = originAttr.GetDictionaryOrder(); + JSHandle oldValue(thread, box->GetValue()); + GlobalDictionary::InvalidateAndReplaceEntry(thread, dictHandle, index, oldValue); + dictHandle->SetAttributes(thread, entry, newAttr); +} + +void GlobalDictionary::InvalidateAndReplaceEntry(JSThread *thread, const JSHandle &dictHandle, + int entry, const JSHandle &oldValue) +{ + ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); + PropertyAttributes attr = dictHandle->GetAttributes(entry); + ASSERT_PRINT(attr.IsConfigurable(), "property must be configurable"); + ASSERT_PRINT(!dictHandle->GetBox(entry)->GetValue().IsHole(), "value must not be hole"); + + // Swap with a copy. + JSHandle newBox = factory->NewPropertyBox(oldValue); + // Cell is officially mutable henceforth. + attr.SetBoxType(PropertyBoxType::MUTABLE); + dictHandle->SetAttributes(thread, entry, attr); + dictHandle->UpdateValue(thread, entry, newBox.GetTaggedValue()); + dictHandle->GetBox(entry)->Clear(thread); +} +} // namespace ecmascript +} // namespace panda + +#endif diff --git a/runtime/global_dictionary.h b/runtime/global_dictionary.h new file mode 100644 index 000000000..7c9373a14 --- /dev/null +++ b/runtime/global_dictionary.h @@ -0,0 +1,93 @@ +/* + * Copyright (c) 2021-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_GLOBAL_DICTIONARY_H +#define ECMASCRIPT_GLOBAL_DICTIONARY_H + +#include "plugins/ecmascript/runtime/ecma_string.h" +#include "plugins/ecmascript/runtime/ic/property_box.h" +#include "plugins/ecmascript/runtime/js_handle.h" +#include "plugins/ecmascript/runtime/js_tagged_value.h" +#include "plugins/ecmascript/runtime/property_attributes.h" +#include "plugins/ecmascript/runtime/tagged_hash_table.h" + +namespace panda { +namespace ecmascript { +class GlobalDictionary : public OrderTaggedHashTable { +public: + using OrderHashTableT = OrderTaggedHashTable; + inline static int GetKeyIndex(int entry) + { + return OrderHashTableT::TABLE_HEADER_SIZE + entry * GetEntrySize() + ENTRY_KEY_INDEX; + } + inline static int GetValueIndex(int entry) + { + return OrderHashTableT::TABLE_HEADER_SIZE + entry * GetEntrySize() + ENTRY_VALUE_INDEX; + } + inline static int GetEntryIndex(int entry) + { + return OrderHashTableT::TABLE_HEADER_SIZE + entry * GetEntrySize(); + } + inline static int GetEntrySize() + { + return ENTRY_SIZE; + } + static inline bool IsMatch(const JSTaggedValue &key, const JSTaggedValue &other); + + static inline int Hash(const JSTaggedValue &key); + inline static void InvalidatePropertyBox(JSThread *thread, const JSHandle &dictHandle, int entry, + const PropertyAttributes &metaData); + + inline static void InvalidateAndReplaceEntry(JSThread *thread, const JSHandle &dictHandle, + int entry, const JSHandle &oldValue); + + inline PropertyBox *GetBox(int entry) const; + + inline JSTaggedValue GetValue(int entry) const; + + inline PropertyAttributes GetAttributes(int entry) const; + + inline void SetEntry(const JSThread *thread, int entry, const JSTaggedValue &key, const JSTaggedValue &value, + const PropertyAttributes &attributes); + + inline void ClearEntry([[maybe_unused]] const JSThread *thread, int entry); + + inline void UpdateValueAndAttributes(const JSThread *thread, int entry, const JSTaggedValue &value, + const PropertyAttributes &metaData); + + inline void UpdateValue(const JSThread *thread, int entry, const JSTaggedValue &value); + + inline void SetAttributes(const JSThread *thread, int entry, const PropertyAttributes &metaData); + + inline void GetAllKeys(const JSThread *thread, int offset, TaggedArray *keyArray) const; + + inline void GetEnumAllKeys(const JSThread *thread, int offset, TaggedArray *keyArray, uint32_t *keys) const; + + static bool inline CompKey(const std::pair &a, + const std::pair &b); + + DECL_DUMP() +private: + inline int GetKeysWithFilter(const JSThread *thread, int offset, TaggedArray *keyArray, + const std::function &filter) const; + + static constexpr int ENTRY_KEY_INDEX = 0; + static constexpr int ENTRY_VALUE_INDEX = 1; + static constexpr int ENTRY_DETAILS_INDEX = 2; + static constexpr int ENTRY_SIZE = 3; +}; +} // namespace ecmascript +} // namespace panda +#endif diff --git a/runtime/global_env.cpp b/runtime/global_env.cpp new file mode 100644 index 000000000..b3d807474 --- /dev/null +++ b/runtime/global_env.cpp @@ -0,0 +1,53 @@ +/* + * Copyright (c) 2021-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 "plugins/ecmascript/runtime/global_env.h" +#include "ecma_module.h" +#include "plugins/ecmascript/runtime/builtins/builtins_promise_handler.h" +#include "plugins/ecmascript/runtime/class_linker/program_object-inl.h" +#include "plugins/ecmascript/runtime/ic/ic_handler.h" +#include "plugins/ecmascript/runtime/ic/proto_change_details.h" +#include "plugins/ecmascript/runtime/jobs/micro_job_queue.h" +#include "plugins/ecmascript/runtime/js_generator_object.h" +#include "plugins/ecmascript/runtime/js_handle.h" +#include "plugins/ecmascript/runtime/js_promise.h" +#include "plugins/ecmascript/runtime/object_factory.h" +#include "js_array.h" +#include "js_realm.h" + +namespace panda::ecmascript { +JSHandle GlobalEnv::GetSymbol(JSThread *thread, const JSHandle &string) +{ + JSHandle symbolFunction(GetSymbolFunction()); + return JSObject::GetProperty(thread, symbolFunction, string).GetValue(); +} + +JSHandle GlobalEnv::GetStringPrototypeFunctionByName(JSThread *thread, const char *name) +{ + ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); + JSHandle stringFuncObj(GetStringFunction()); + JSHandle stringFuncPrototype(thread, stringFuncObj->GetPrototype(thread)); + JSHandle nameKey(factory->NewFromString(name)); + return JSObject::GetProperty(thread, stringFuncPrototype, nameKey).GetValue(); +} + +JSHandle GlobalEnv::GetStringFunctionByName(JSThread *thread, const char *name) +{ + ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); + JSHandle stringFuncObj = GetStringFunction(); + JSHandle nameKey(factory->NewFromString(name)); + return JSObject::GetProperty(thread, stringFuncObj, nameKey).GetValue(); +} +} // namespace panda::ecmascript diff --git a/runtime/global_env.h b/runtime/global_env.h new file mode 100644 index 000000000..24725689a --- /dev/null +++ b/runtime/global_env.h @@ -0,0 +1,220 @@ +/* + * Copyright (c) 2021-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_GLOBAL_ENV_H +#define ECMASCRIPT_GLOBAL_ENV_H + +#include "plugins/ecmascript/runtime/js_global_object.h" +#include "plugins/ecmascript/runtime/js_function.h" +#include "plugins/ecmascript/runtime/lexical_env.h" +#include "plugins/ecmascript/runtime/js_handle.h" +#include "plugins/ecmascript/runtime/global_env_constants-inl.h" + +namespace panda::ecmascript { +class JSThread; + +// NOLINTNEXTLINE(cppcoreguidelines-macro-usage) +#define GLOBAL_ENV_FIELDS(V) \ + /* Function */ \ + V(JSTaggedValue, ObjectFunction, OBJECT_FUNCTION_INDEX) \ + V(JSTaggedValue, ObjectFunctionPrototype, OBJECT_FUNCTION_PROTOTYPE_INDEX) \ + V(JSTaggedValue, ObjectFunctionPrototypeClass, OBJECT_FUNCTION_PROTOTYPE_CLASS_INDEX) \ + V(JSTaggedValue, FunctionFunction, FUNCTION_FUNCTION_INDEX) \ + V(JSTaggedValue, FunctionPrototype, FUNCTION_PROTOTYPE_INDEX) \ + V(JSTaggedValue, NumberFunction, NUMBER_FUNCTION_INDEX) \ + V(JSTaggedValue, DateFunction, DATE_FUNCTION_INDEX) \ + V(JSTaggedValue, BooleanFunction, BOOLEAN_FUNCTION_INDEX) \ + V(JSTaggedValue, ErrorFunction, ERROR_FUNCTION_INDEX) \ + V(JSTaggedValue, ArrayFunction, ARRAY_FUNCTION_INDEX) \ + V(JSTaggedValue, ArrayPrototype, ARRAY_PROTOTYPE_INDEX) \ + V(JSTaggedValue, TypedArrayFunction, TYPED_ARRAY_FUNCTION_INDEX) \ + V(JSTaggedValue, TypedArrayPrototype, TYPED_ARRAY_PROTOTYPE_INDEX) \ + V(JSTaggedValue, Int8ArrayFunction, INT8_ARRAY_FUNCTION_INDEX) \ + V(JSTaggedValue, Uint8ArrayFunction, UINT8_ARRAY_FUNCTION_INDEX) \ + V(JSTaggedValue, Uint8ClampedArrayFunction, UINT8_CLAMPED_ARRAY_FUNCTION_INDEX) \ + V(JSTaggedValue, Int16ArrayFunction, INT16_ARRAY_FUNCTION_INDEX) \ + V(JSTaggedValue, Uint16ArrayFunction, UINT16_ARRAY_FUNCTION_INDEX) \ + V(JSTaggedValue, Int32ArrayFunction, INT32_ARRAY_FUNCTION_INDEX) \ + V(JSTaggedValue, Uint32ArrayFunction, UINT32_ARRAY_FUNCTION_INDEX) \ + V(JSTaggedValue, Float32ArrayFunction, FLOAT32_ARRAY_FUNCTION_INDEX) \ + V(JSTaggedValue, Float64ArrayFunction, FLOAT64_ARRAY_FUNCTION_INDEX) \ + V(JSTaggedValue, ArrayBufferFunction, ARRAY_BUFFER_FUNCTION_INDEX) \ + V(JSTaggedValue, ArrayProtoValuesFunction, ARRAY_PROTO_VALUES_FUNCTION_INDEX) \ + V(JSTaggedValue, DataViewFunction, DATA_VIEW_FUNCTION_INDEX) \ + V(JSTaggedValue, SymbolFunction, SYMBOL_FUNCTION_INDEX) \ + V(JSTaggedValue, RangeErrorFunction, RANGE_ERROR_FUNCTION_INDEX) \ + V(JSTaggedValue, ReferenceErrorFunction, REFERENCE_ERROR_FUNCTION_INDEX) \ + V(JSTaggedValue, TypeErrorFunction, TYPE_ERROR_FUNCTION_INDEX) \ + V(JSTaggedValue, URIErrorFunction, URI_ERROR_FUNCTION_INDEX) \ + V(JSTaggedValue, SyntaxErrorFunction, SYNTAX_ERROR_FUNCTION_INDEX) \ + V(JSTaggedValue, EvalErrorFunction, EVAL_ERROR_FUNCTION_INDEX) \ + V(JSTaggedValue, IntlFunction, INTL_FUNCTION_INDEX) \ + V(JSTaggedValue, LocaleFunction, LOCALE_FUNCTION_INDEX) \ + V(JSTaggedValue, DateTimeFormatFunction, DATE_TIME_FORMAT_FUNCTION_INDEX) \ + V(JSTaggedValue, RelativeTimeFormatFunction, RELATIVE_TIME_FORMAT_FUNCTION_INDEX) \ + V(JSTaggedValue, NumberFormatFunction, NUMBER_FORMAT_FUNCTION_INDEX) \ + V(JSTaggedValue, CollatorFunction, COLLATOR_FUNCTION_INDEX) \ + V(JSTaggedValue, PluralRulesFunction, PLURAL_RULES_FUNCTION_INDEX) \ + V(JSTaggedValue, RegExpFunction, REGEXP_FUNCTION_INDEX) \ + V(JSTaggedValue, BuiltinsSetFunction, BUILTINS_SET_FUNCTION_INDEX) \ + V(JSTaggedValue, SetPrototype, SET_PROTOTYPE_INDEX) \ + V(JSTaggedValue, BuiltinsMapFunction, BUILTINS_MAP_FUNCTION_INDEX) \ + V(JSTaggedValue, BuiltinsWeakMapFunction, BUILTINS_WEAK_MAP_FUNCTION_INDEX) \ + V(JSTaggedValue, BuiltinsWeakSetFunction, BUILTINS_WEAK_SET_FUNCTION_INDEX) \ + V(JSTaggedValue, MapPrototype, MAP_PROTOTYPE_INDEX) \ + V(JSTaggedValue, MathFunction, MATH_FUNCTION_INDEX) \ + V(JSTaggedValue, JsonFunction, JSON_FUNCTION_INDEX) \ + V(JSTaggedValue, StringFunction, STRING_FUNCTION_INDEX) \ + V(JSTaggedValue, ProxyFunction, PROXY_FUNCTION_INDEX) \ + V(JSTaggedValue, GeneratorFunctionFunction, GENERATOR_FUNCTION_OFFSET) \ + V(JSTaggedValue, GeneratorFunctionPrototype, GENERATOR_FUNCTION_PROTOTYPE_OFFSET) \ + V(JSTaggedValue, InitialGenerator, INITIAL_GENERATOR_OFFSET) \ + V(JSTaggedValue, GeneratorPrototype, GENERATOR_PROTOTYPE_OFFSET) \ + V(JSTaggedValue, ReflectFunction, REFLECT_FUNCTION_INDEX) \ + V(JSTaggedValue, AsyncFunction, ASYNC_FUNCTION_INDEX) \ + V(JSTaggedValue, AsyncFunctionPrototype, ASYNC_FUNCTION_PROTOTYPE_INDEX) \ + V(JSTaggedValue, AsyncGeneratorFunctionFunction, ASYNC_GENERATOR_FUNCTION_INDEX) \ + V(JSTaggedValue, AsyncGeneratorFunctionPrototype, ASYNC_GENERATOR_FUNCTION_PROTOTYPE_INDEX) \ + V(JSTaggedValue, InitialAsyncGenerator, INITIAL_ASYNC_GENERATOR_OFFSET) \ + V(JSTaggedValue, AsyncGeneratorPrototype, ASYNC_GENERATOR_PROTOTYPE_OFFSET) \ + V(JSTaggedValue, AsyncFromSyncIteratorPrototype, ASYNC_FROM_SYNC_ITERATOR_PROTOTYPE_OFFSET) \ + V(JSTaggedValue, JSGlobalObject, JS_GLOBAL_OBJECT_INDEX) \ + V(JSTaggedValue, EmptyArray, EMPTY_ARRAY_OBJECT_INDEX) \ + V(JSTaggedValue, EmptyLayoutInfo, EMPTY_LAYOUT_INFO_OBJECT_INDEX) \ + V(JSTaggedValue, EmptyTaggedQueue, EMPTY_TAGGED_QUEUE_OBJECT_INDEX) \ + V(JSTaggedValue, HasInstanceSymbol, HASINSTANCE_SYMBOL_INDEX) \ + V(JSTaggedValue, IsConcatSpreadableSymbol, ISCONCAT_SYMBOL_INDEX) \ + V(JSTaggedValue, ToStringTagSymbol, TOSTRINGTAG_SYMBOL_INDEX) \ + V(JSTaggedValue, IteratorSymbol, ITERATOR_SYMBOL_INDEX) \ + V(JSTaggedValue, AsyncIteratorSymbol, ASYNC_ITERATOR_SYMBOL_INDEX) \ + V(JSTaggedValue, MatchSymbol, MATCH_SYMBOL_INDEX) \ + V(JSTaggedValue, ReplaceSymbol, REPLACE_SYMBOL_INDEX) \ + V(JSTaggedValue, SearchSymbol, SEARCH_SYMBOL_INDEX) \ + V(JSTaggedValue, SpeciesSymbol, SPECIES_SYMBOL_INDEX) \ + V(JSTaggedValue, SplitSymbol, SPLIT_SYMBOL_INDEX) \ + V(JSTaggedValue, ToPrimitiveSymbol, TOPRIMITIVE_SYMBOL_INDEX) \ + V(JSTaggedValue, UnscopablesSymbol, UNSCOPABLES_SYMBOL_INDEX) \ + V(JSTaggedValue, HoleySymbol, HOLEY_SYMBOL_OFFSET) \ + V(JSTaggedValue, ElementICSymbol, ELEMENT_IC_SYMBOL_OFFSET) \ + V(JSTaggedValue, IteratorPrototype, ITERATOR_PROTOTYPE_INDEX) \ + V(JSTaggedValue, AsyncIteratorPrototype, ASYNC_ITERATOR_PROTOTYPE_INDEX) \ + V(JSTaggedValue, ForinIteratorPrototype, FORIN_ITERATOR_PROTOTYPE_INDEX) \ + V(JSTaggedValue, ForinIteratorClass, FOR_IN_ITERATOR_CLASS_INDEX) \ + V(JSTaggedValue, StringIterator, STRING_ITERATOR_INDEX) \ + V(JSTaggedValue, MapIteratorPrototype, MAP_ITERATOR_PROTOTYPE_INDEX) \ + V(JSTaggedValue, SetIteratorPrototype, SET_ITERATOR_PROTOTYPE_INDEX) \ + V(JSTaggedValue, ArrayIteratorPrototype, ARRAY_ITERATOR_PROTOTYPE_INDEX) \ + V(JSTaggedValue, StringIteratorPrototype, STRING_ITERATOR_PROTOTYPE_INDEX) \ + /* SymbolTable *RegisterSymbols */ \ + V(JSTaggedValue, RegisterSymbols, SYMBOLS_INDEX) \ + V(JSTaggedValue, ThrowTypeError, THROW_TYPE_ERROR_INDEX) \ + V(JSTaggedValue, PromiseFunction, PROMISE_FUNCTION_INDEX) \ + V(JSTaggedValue, PromiseReactionJob, PROMISE_REACTION_JOB_INDEX) \ + V(JSTaggedValue, PromiseResolveThenableJob, PROMISE_REACTION_THENABLE_JOB_INDEX) \ + V(JSTaggedValue, TemplateMap, TEMPLATE_MAP_INDEX) \ + V(JSTaggedValue, FunctionClassWithProto, FUNCTION_CLASS_WITH_PROTO) \ + V(JSTaggedValue, FunctionClassWithoutProto, FUNCTION_CLASS_WITHOUT_PROTO) \ + V(JSTaggedValue, FunctionClassWithoutName, FUNCTION_CLASS_WITHOUT_NAME) \ + V(JSTaggedValue, ArgumentsClass, ARGUMENTS_CLASS) \ + V(JSTaggedValue, ArgumentsCallerAccessor, ARGUMENTSCALLERACCESSOR) \ + V(JSTaggedValue, ArgumentsCalleeAccessor, ARGUMENTSCALLEEACCESSOR) \ + V(JSTaggedValue, AsyncFunctionClass, ASYNC_FUNCTION_CLASS) \ + V(JSTaggedValue, AsyncAwaitStatusFunctionClass, ASYNC_AWAIT_STATUS_FUNCTION_CLASS) \ + V(JSTaggedValue, AsyncGeneratorResolveNextFunctionClass, ASYNC_GENERATOR_RESOLVE_NEXT_FUNCTION_CLASS) \ + V(JSTaggedValue, AsyncFromSyncIteratorValueUnwrapFunctionClass, \ + ASYNC_FROM_SYNC_ITERATOR_VALUE_UNWRAP_FUNCTION_CLASS) \ + V(JSTaggedValue, PromiseReactionFunctionClass, PROMISE_REACTION_FUNCTION_CLASS) \ + V(JSTaggedValue, PromiseExecutorFunctionClass, PROMISE_EXECUTOR_FUNCTION_CLASS) \ + V(JSTaggedValue, GeneratorFunctionClass, GENERATOR_FUNCTION_CLASS) \ + V(JSTaggedValue, AsyncGeneratorFunctionClass, ASYNC_GENERATOR_FUNCTION_CLASS) \ + V(JSTaggedValue, PromiseAllResolveElementFunctionClass, PROMISE_ALL_RESOLVE_ELEMENT_FUNC_CLASS) \ + V(JSTaggedValue, ProxyRevocFunctionClass, PROXY_REVOC_FUNCTION_CLASS) \ + V(JSTaggedValue, NativeErrorFunctionClass, NATIVE_ERROR_FUNCTION_CLASS) \ + V(JSTaggedValue, SpecificTypedArrayFunctionClass, SPERCIFIC_TYPED_ARRAY_FUNCTION_CLASS) \ + V(JSTaggedValue, ConstructorFunctionClass, CONSTRUCTOR_FUNCTION_CLASS) \ + V(JSTaggedValue, NormalFunctionClass, NORMAL_FUNCTION_CLASS) \ + V(JSTaggedValue, JSIntlBoundFunctionClass, JS_INTL_BOUND_FUNCTION_CLASS) \ + V(JSTaggedValue, NumberFormatLocales, NUMBER_FORMAT_LOCALES_INDEX) \ + V(JSTaggedValue, DateTimeFormatLocales, DATE_TIMEFORMAT_LOCALES_INDEX) \ + V(JSTaggedValue, GlobalRecord, GLOBAL_RECORD) + +class GlobalEnv : public TaggedObject { +public: + JSTaggedValue GetGlobalObject() const + { + return GetJSGlobalObject().GetTaggedValue(); + } + + void InitGlobalObject(); + + static GlobalEnv *Cast(ObjectHeader *object) + { + ASSERT(JSTaggedValue(object).IsJSGlobalEnv()); + return reinterpret_cast(object); + } + + JSHandle GetSymbol(JSThread *thread, const JSHandle &string); + JSHandle GetStringFunctionByName(JSThread *thread, const char *name); + JSHandle GetStringPrototypeFunctionByName(JSThread *thread, const char *name); + + enum Field { +// NOLINTNEXTLINE(cppcoreguidelines-macro-usage) +#define GLOBAL_ENV_SLOT(type, name, index) index, + GLOBAL_ENV_FIELDS(GLOBAL_ENV_SLOT) +#undef GLOBAL_ENV_SLOT + FINAL_INDEX + }; + +// NOLINTNEXTLINE(cppcoreguidelines-macro-usage) +#define GLOBAL_ENV_FIELD_ACCESSORS(type, name, index) \ + inline JSHandle Get##name() const \ + { \ + const uintptr_t address = \ + reinterpret_cast(this) + HEADER_SIZE + index * JSTaggedValue::TaggedTypeSize(); \ + JSHandle result(address); \ + return result; \ + } \ + template \ + inline void Set##name(const JSThread *thread, JSHandle value, BarrierMode mode = WRITE_BARRIER) \ + { \ + int offset = HEADER_SIZE + index * JSTaggedValue::TaggedTypeSize(); \ + if (mode == WRITE_BARRIER) { \ + ObjectAccessor::SetDynValue(thread, this, offset, value.GetTaggedValue().GetRawData()); \ + } else { \ + ObjectAccessor::SetDynValueWithoutBarrier(this, offset, value.GetTaggedValue().GetRawData()); \ + } \ + } \ + inline void Set##name(const JSThread *thread, type value, BarrierMode mode = WRITE_BARRIER) \ + { \ + int offset = HEADER_SIZE + index * JSTaggedValue::TaggedTypeSize(); \ + if (mode == WRITE_BARRIER) { \ + ObjectAccessor::SetDynValue(thread, this, offset, value.GetRawData()); \ + } else { \ + ObjectAccessor::SetDynValueWithoutBarrier(this, offset, value.GetRawData()); \ + } \ + } + GLOBAL_ENV_FIELDS(GLOBAL_ENV_FIELD_ACCESSORS) +#undef GLOBAL_ENV_FIELD_ACCESSORS + + static constexpr size_t HEADER_SIZE = TaggedObjectSize(); + static constexpr size_t SIZE = HEADER_SIZE + FINAL_INDEX * JSTaggedValue::TaggedTypeSize(); + + DECL_VISIT_OBJECT(HEADER_SIZE, SIZE); + + DECL_DUMP() +}; +} // namespace panda::ecmascript + +#endif // ECMASCRIPT_GLOBAL_ENV_H diff --git a/runtime/global_env_constants-inl.h b/runtime/global_env_constants-inl.h new file mode 100644 index 000000000..8607a738d --- /dev/null +++ b/runtime/global_env_constants-inl.h @@ -0,0 +1,65 @@ +/* + * Copyright (c) 2021-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 RUNTIME_ECMASCRIPT_GLOBAL_ENV_CONSTANTS_INL_H +#define RUNTIME_ECMASCRIPT_GLOBAL_ENV_CONSTANTS_INL_H + +#include "plugins/ecmascript/runtime/global_env_constants.h" +#include "plugins/ecmascript/runtime/ecma_macros.h" + +namespace panda::ecmascript { +inline const JSTaggedValue *GlobalEnvConstants::BeginSlot() const +{ + return &constants_[static_cast(ConstantIndex::CONSTATNT_BEGIN)]; +} + +inline const JSTaggedValue *GlobalEnvConstants::EndSlot() const +{ + return &constants_[static_cast(ConstantIndex::CONSTATNT_END)]; +} + +inline void GlobalEnvConstants::SetConstant(ConstantIndex index, JSTaggedValue value) +{ + DASSERT_PRINT(index >= ConstantIndex::CONSTATNT_BEGIN && index < ConstantIndex::CONSTATNT_END, + "Root Index out of bound"); + constants_[static_cast(index)] = value; +} + +inline uintptr_t GlobalEnvConstants::GetGlobalConstantAddr(ConstantIndex index) const +{ + return ToUintPtr(this) + sizeof(JSTaggedValue) * static_cast(index); +} + +// clang-format off +// NOLINTNEXTLINE(cppcoreguidelines-macro-usage) +#define DECL_GET_IMPL(Type, Name, Index, Desc) \ + inline const Type GlobalEnvConstants::Get##Name() const \ + { \ + return constants_[static_cast(ConstantIndex::Index)]; \ + } \ + inline const JSHandle GlobalEnvConstants::GetHandled##Name() const \ + { \ + return JSHandle(reinterpret_cast( \ + &constants_[static_cast(ConstantIndex::Index)])); \ + } + + GLOBAL_ENV_CONSTANT_CLASS(DECL_GET_IMPL) // NOLINT(readability-const-return-type) + GLOBAL_ENV_CONSTANT_SPECIAL(DECL_GET_IMPL) // NOLINT(readability-const-return-type) + GLOBAL_ENV_CONSTANT_CONSTANT(DECL_GET_IMPL) // NOLINT(readability-const-return-type) + GLOBAL_ENV_CONSTANT_ACCESSOR(DECL_GET_IMPL) // NOLINT(readability-const-return-type) +#undef DECL_GET_IMPL +// clang-format on +} // namespace panda::ecmascript +#endif // RUNTIME_ECMASCRIPT_GLOBAL_ENV_CONSTANTS_INL_H diff --git a/runtime/global_env_constants.cpp b/runtime/global_env_constants.cpp new file mode 100644 index 000000000..bda7c1b00 --- /dev/null +++ b/runtime/global_env_constants.cpp @@ -0,0 +1,511 @@ +/* + * Copyright (c) 2021-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 "plugins/ecmascript/runtime/global_env_constants.h" + +#include "plugins/ecmascript/runtime/accessor_data.h" +#include "plugins/ecmascript/runtime/builtins.h" +#include "plugins/ecmascript/runtime/builtins/builtins_global.h" +#include "plugins/ecmascript/runtime/class_info_extractor.h" +#include "plugins/ecmascript/runtime/class_linker/program_object.h" +#include "plugins/ecmascript/runtime/ecma_module.h" +#include "plugins/ecmascript/runtime/ecma_vm.h" +#include "plugins/ecmascript/runtime/free_object.h" +#include "plugins/ecmascript/runtime/global_env.h" +#include "plugins/ecmascript/runtime/global_env_constants-inl.h" +#include "plugins/ecmascript/runtime/ic/ic_handler.h" +#include "plugins/ecmascript/runtime/ic/property_box.h" +#include "plugins/ecmascript/runtime/ic/proto_change_details.h" +#include "plugins/ecmascript/runtime/jobs/micro_job_queue.h" +#include "plugins/ecmascript/runtime/jobs/pending_job.h" +#include "plugins/ecmascript/runtime/js_arguments.h" +#include "plugins/ecmascript/runtime/js_array.h" +#include "plugins/ecmascript/runtime/js_array_iterator.h" +#include "plugins/ecmascript/runtime/js_arraybuffer.h" +#include "plugins/ecmascript/runtime/js_async_generator_object.h" +#include "plugins/ecmascript/runtime/js_for_in_iterator.h" +#include "plugins/ecmascript/runtime/js_generator_object.h" +#include "plugins/ecmascript/runtime/js_hclass.h" +#include "plugins/ecmascript/runtime/js_iterator.h" +#include "plugins/ecmascript/runtime/js_map.h" +#include "plugins/ecmascript/runtime/js_map_iterator.h" +#include "plugins/ecmascript/runtime/js_primitive_ref.h" +#include "plugins/ecmascript/runtime/js_promise.h" +#include "plugins/ecmascript/runtime/js_proxy.h" +#include "plugins/ecmascript/runtime/js_realm.h" +#include "plugins/ecmascript/runtime/js_regexp.h" +#include "plugins/ecmascript/runtime/js_set.h" +#include "plugins/ecmascript/runtime/js_set_iterator.h" +#include "plugins/ecmascript/runtime/js_symbol.h" +#include "plugins/ecmascript/runtime/js_tagged_value.h" +#include "plugins/ecmascript/runtime/js_thread.h" +#include "plugins/ecmascript/runtime/object_factory.h" +#include "plugins/ecmascript/runtime/object_wrapper.h" + +namespace panda::ecmascript { +void GlobalEnvConstants::InitRootsClass([[maybe_unused]] JSThread *thread, JSHClass *dynClassClass) +{ + // Global constants are readonly. + ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); + + SetConstant(ConstantIndex::HCLASS_CLASS_INDEX, JSTaggedValue(dynClassClass)); + SetConstant(ConstantIndex::STRING_CLASS_INDEX, + factory->NewEcmaDynClass(dynClassClass, 0, JSType::STRING, HClass::STRING).GetTaggedValue()); + SetConstant(ConstantIndex::ARRAY_CLASS_INDEX, + factory->NewEcmaDynClass(dynClassClass, 0, JSType::TAGGED_ARRAY, HClass::ARRAY).GetTaggedValue()); + SetConstant( + ConstantIndex::DICTIONARY_CLASS_INDEX, + factory + ->NewEcmaDynClass(dynClassClass, 0, JSType::TAGGED_DICTIONARY, HClass::ARRAY | HClass::IS_DICTIONARY_ARRAY) + .GetTaggedValue()); + SetConstant( + ConstantIndex::JS_NATIVE_POINTER_CLASS_INDEX, + factory + ->NewEcmaDynClass(dynClassClass, JSNativePointer::SIZE, JSType::JS_NATIVE_POINTER, HClass::NATIVE_POINTER) + .GetTaggedValue()); + SetConstant(ConstantIndex::ENV_CLASS_INDEX, + factory->NewEcmaDynClass(dynClassClass, 0, JSType::TAGGED_ARRAY, HClass::ARRAY).GetTaggedValue()); + SetConstant(ConstantIndex::FREE_OBJECT_WITH_NONE_FIELD_CLASS_INDEX, + factory->NewEcmaDynClass(dynClassClass, FreeObject::NEXT_OFFSET, JSType::FREE_OBJECT_WITH_NONE_FIELD) + .GetTaggedValue()); + JSHClass *freeObject1Class = + *factory->NewEcmaDynClass(dynClassClass, FreeObject::SIZE_OFFSET, JSType::FREE_OBJECT_WITH_ONE_FIELD); + freeObject1Class->GetHClass()->MarkFieldAsNative(FreeObject::NEXT_OFFSET); + SetConstant(ConstantIndex::FREE_OBJECT_WITH_ONE_FIELD_CLASS_INDEX, JSTaggedValue(freeObject1Class)); + JSHClass *freeObject2Class = *factory->NewEcmaDynClass(dynClassClass, FreeObject::SIZE, + JSType::FREE_OBJECT_WITH_TWO_FIELD, HClass::IS_FREE_OBJECT); + freeObject2Class->GetHClass()->MarkFieldAsNative(FreeObject::SIZE_OFFSET); + SetConstant(ConstantIndex::FREE_OBJECT_WITH_TWO_FIELD_CLASS_INDEX, JSTaggedValue(freeObject2Class)); + SetConstant(ConstantIndex::SYMBOL_CLASS_INDEX, + factory->NewEcmaDynClass(dynClassClass, JSSymbol::SIZE, JSType::SYMBOL).GetTaggedValue()); + SetConstant(ConstantIndex::ACCESSOE_DATA_CLASS_INDEX, + factory->NewEcmaDynClass(dynClassClass, AccessorData::SIZE, JSType::ACCESSOR_DATA).GetTaggedValue()); + SetConstant( + ConstantIndex::INTERNAL_ACCESSOR_CLASS_INDEX, + factory->NewEcmaDynClass(dynClassClass, AccessorData::SIZE, JSType::INTERNAL_ACCESSOR).GetTaggedValue()); + JSHClass *jsProxyOrdinaryClass = *factory->NewEcmaDynClass(dynClassClass, JSProxy::SIZE, JSType::JS_PROXY); + jsProxyOrdinaryClass->GetHClass()->MarkFieldAsNative(JSProxy::METHOD_OFFSET); + SetConstant(ConstantIndex::JS_PROXY_ORDINARY_CLASS_INDEX, JSTaggedValue(jsProxyOrdinaryClass)); + SetConstant(ConstantIndex::OBJECT_WRAPPER_CLASS_INDEX, + factory->NewEcmaDynClass(dynClassClass, ObjectWrapper::SIZE, JSType::OBJECT_WRAPPER).GetTaggedValue()); + SetConstant( + ConstantIndex::COMPLETION_RECORD_CLASS_INDEX, + factory->NewEcmaDynClass(dynClassClass, CompletionRecord::SIZE, JSType::COMPLETION_RECORD).GetTaggedValue()); + SetConstant( + ConstantIndex::GENERATOR_CONTEST_INDEX, + factory->NewEcmaDynClass(dynClassClass, GeneratorContext::SIZE, JSType::JS_GENERATOR_CONTEXT).GetTaggedValue()); + SetConstant( + ConstantIndex::CAPABILITY_RECORD_CLASS_INDEX, + factory->NewEcmaDynClass(dynClassClass, PromiseCapability::SIZE, JSType::PROMISE_CAPABILITY).GetTaggedValue()); + SetConstant( + ConstantIndex::REACTIONS_RECORD_CLASS_INDEX, + factory->NewEcmaDynClass(dynClassClass, PromiseReaction::SIZE, JSType::PROMISE_REACTIONS).GetTaggedValue()); + SetConstant(ConstantIndex::PROMISE_ITERATOR_RECORD_CLASS_INDEX, + factory->NewEcmaDynClass(dynClassClass, PromiseIteratorRecord::SIZE, JSType::PROMISE_ITERATOR_RECORD) + .GetTaggedValue()); + SetConstant(ConstantIndex::PROMISE_RECORD_CLASS_INDEX, + factory->NewEcmaDynClass(dynClassClass, PromiseRecord::SIZE, JSType::PROMISE_RECORD).GetTaggedValue()); + SetConstant( + ConstantIndex::PROMISE_RESOLVING_FUNCTIONS_CLASS_INDEX, + factory->NewEcmaDynClass(dynClassClass, ResolvingFunctionsRecord::SIZE, JSType::RESOLVING_FUNCTIONS_RECORD) + .GetTaggedValue()); + SetConstant( + ConstantIndex::MICRO_JOB_QUEUE_CLASS_INDEX, + factory->NewEcmaDynClass(dynClassClass, job::MicroJobQueue::SIZE, JSType::MICRO_JOB_QUEUE).GetTaggedValue()); + SetConstant(ConstantIndex::PENDING_JOB_CLASS_INDEX, + factory->NewEcmaDynClass(dynClassClass, job::PendingJob::SIZE, JSType::PENDING_JOB).GetTaggedValue()); + JSHClass *protoChangeMarkerClass = + *factory->NewEcmaDynClass(dynClassClass, ProtoChangeMarker::SIZE, JSType::PROTO_CHANGE_MARKER); + protoChangeMarkerClass->GetHClass()->MarkFieldAsNative(ProtoChangeMarker::HAS_CHANGED_OFFSET); + SetConstant(ConstantIndex::PROTO_CHANGE_MARKER_CLASS_INDEX, JSTaggedValue(protoChangeMarkerClass)); + SetConstant( + ConstantIndex::PROTO_CHANGE_DETAILS_CLASS_INDEX, + factory->NewEcmaDynClass(dynClassClass, ProtoChangeDetails::SIZE, JSType::PROTOTYPE_INFO).GetTaggedValue()); + SetConstant( + ConstantIndex::PROTOTYPE_HANDLER_CLASS_INDEX, + factory->NewEcmaDynClass(dynClassClass, PrototypeHandler::SIZE, JSType::PROTOTYPE_HANDLER).GetTaggedValue()); + SetConstant( + ConstantIndex::TRANSITION_HANDLER_CLASS_INDEX, + factory->NewEcmaDynClass(dynClassClass, TransitionHandler::SIZE, JSType::TRANSITION_HANDLER).GetTaggedValue()); + SetConstant(ConstantIndex::PROPERTY_BOX_CLASS_INDEX, + factory->NewEcmaDynClass(dynClassClass, PropertyBox::SIZE, JSType::PROPERTY_BOX).GetTaggedValue()); + SetConstant(ConstantIndex::FUNCTION_EXTRA_INFO_INDEX, + factory->NewEcmaDynClass(dynClassClass, JSFunctionExtraInfo::SIZE, JSType::FUNCTION_EXTRA_INFO) + .GetTaggedValue()); + JSHClass *programClass = *factory->NewEcmaDynClass(dynClassClass, Program::SIZE, JSType::PROGRAM); + programClass->GetHClass()->MarkFieldAsNative(Program::METHODS_DATA_OFFSET); + programClass->GetHClass()->MarkFieldAsNative(Program::NUMBER_METHODS_OFFSET); + SetConstant(ConstantIndex::PROGRAM_CLASS_INDEX, JSTaggedValue(programClass)); + SetConstant(ConstantIndex::ECMA_MODULE_CLASS_INDEX, + factory->NewEcmaDynClass(dynClassClass, EcmaModule::SIZE, JSType::ECMA_MODULE).GetTaggedValue()); + + JSHClass *jsProxyCallableClass = + *factory->NewEcmaDynClass(dynClassClass, JSProxy::SIZE, JSType::JS_PROXY, HClass::IS_CALLABLE); + + jsProxyCallableClass->GetHClass()->MarkFieldAsNative(JSProxy::METHOD_OFFSET); + SetConstant(ConstantIndex::JS_PROXY_CALLABLE_CLASS_INDEX, JSTaggedValue(jsProxyCallableClass)); + + JSHClass *jsProxyConstructClass = + *factory->NewEcmaDynClass(dynClassClass, JSProxy::SIZE, JSType::JS_PROXY, HClass::IS_CALLABLE); + + jsProxyConstructClass->SetConstructor(true); + jsProxyConstructClass->GetHClass()->MarkFieldAsNative(JSProxy::METHOD_OFFSET); + SetConstant(ConstantIndex::JS_PROXY_CONSTRUCT_CLASS_INDEX, JSTaggedValue(jsProxyConstructClass)); + + SetConstant(ConstantIndex::JS_REALM_CLASS_INDEX, + factory->NewEcmaDynClass(dynClassClass, JSRealm::SIZE, JSType::JS_REALM).GetTaggedValue()); + SetConstant(ConstantIndex::MACHINE_CODE_CLASS_INDEX, + factory->NewEcmaDynClass(dynClassClass, 0, JSType::MACHINE_CODE_OBJECT).GetTaggedValue()); + + JSHClass *classInfoExtractorClass = + *factory->NewEcmaDynClass(dynClassClass, ClassInfoExtractor::SIZE, JSType::CLASS_INFO_EXTRACTOR); + classInfoExtractorClass->GetHClass()->MarkFieldAsNative(ClassInfoExtractor::BIT_FIELD_OFFSET); + classInfoExtractorClass->GetHClass()->MarkFieldAsNative(ClassInfoExtractor::CONSTRUCTOR_METHOD_OFFSET); + SetConstant(ConstantIndex::CLASS_INFO_EXTRACTOR_HCLASS_INDEX, JSTaggedValue(classInfoExtractorClass)); + + SetConstant(ConstantIndex::LINKED_HASH_SET_CLASS_INDEX, + factory->NewEcmaDynClass(dynClassClass, 0, JSType::LINKED_HASH_SET, HClass::ARRAY).GetTaggedValue()); + SetConstant(ConstantIndex::LINKED_HASH_MAP_CLASS_INDEX, + factory->NewEcmaDynClass(dynClassClass, 0, JSType::LINKED_HASH_MAP, HClass::ARRAY).GetTaggedValue()); + + JSHClass *weakHashSetClass = *factory->NewEcmaDynClass(dynClassClass, 0, JSType::LINKED_HASH_SET, HClass::ARRAY); + weakHashSetClass->SetWeakContainer(true); + SetConstant(ConstantIndex::WEAK_LINKED_HASH_SET_CLASS_INDEX, JSTaggedValue(weakHashSetClass)); + + JSHClass *weakHashMapClass = *factory->NewEcmaDynClass(dynClassClass, 0, JSType::LINKED_HASH_MAP, HClass::ARRAY); + weakHashMapClass->SetWeakContainer(true); + SetConstant(ConstantIndex::WEAK_LINKED_HASH_MAP_CLASS_INDEX, JSTaggedValue(weakHashMapClass)); +} + +// NOLINTNEXTLINE(readability-function-size) +void GlobalEnvConstants::InitGlobalConstant(JSThread *thread) +{ + ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); + // SPECIAL INIT + SetConstant(ConstantIndex::UNDEFINED_INDEX, JSTaggedValue::Undefined()); + SetConstant(ConstantIndex::NULL_INDEX, JSTaggedValue::Null()); + auto vm = thread->GetEcmaVM(); + SetConstant(ConstantIndex::EMPTY_STRING_OBJECT_INDEX, JSTaggedValue(EcmaString::CreateEmptyString(vm))); + [[maybe_unused]] auto test = EcmaString::Cast(GetHandledEmptyString().GetObject()); + SetConstant(ConstantIndex::CONSTRUCTOR_STRING_INDEX, + factory->NewFromCanBeCompressString("constructor").GetTaggedValue()); + SetConstant(ConstantIndex::PROTOTYPE_STRING_INDEX, + factory->NewFromCanBeCompressString("prototype").GetTaggedValue()); + SetConstant(ConstantIndex::LENGTH_STRING_INDEX, factory->NewFromCanBeCompressString("length").GetTaggedValue()); + SetConstant(ConstantIndex::VALUE_STRING_INDEX, factory->NewFromCanBeCompressString("value").GetTaggedValue()); + SetConstant(ConstantIndex::SET_STRING_INDEX, factory->NewFromCanBeCompressString("set").GetTaggedValue()); + SetConstant(ConstantIndex::GET_STRING_INDEX, factory->NewFromCanBeCompressString("get").GetTaggedValue()); + SetConstant(ConstantIndex::WRITABLE_STRING_INDEX, factory->NewFromCanBeCompressString("writable").GetTaggedValue()); + SetConstant(ConstantIndex::ENUMERABLE_STRING_INDEX, + factory->NewFromCanBeCompressString("enumerable").GetTaggedValue()); + SetConstant(ConstantIndex::CONFIGURABLE_STRING_INDEX, + factory->NewFromCanBeCompressString("configurable").GetTaggedValue()); + /* SymbolTable *RegisterSymbols */ + SetConstant(ConstantIndex::NAME_STRING_INDEX, factory->NewFromCanBeCompressString("name").GetTaggedValue()); + SetConstant(ConstantIndex::GETPROTOTYPEOF_STRING_INDEX, + factory->NewFromCanBeCompressString("getPrototypeOf").GetTaggedValue()); + SetConstant(ConstantIndex::SETPROTOTYPEOF_STRING_INDEX, + factory->NewFromCanBeCompressString("setPrototypeOf").GetTaggedValue()); + SetConstant(ConstantIndex::ISEXTENSIBLE_STRING_INDEX, + factory->NewFromCanBeCompressString("isExtensible").GetTaggedValue()); + SetConstant(ConstantIndex::PREVENTEXTENSIONS_STRING_INDEX, + factory->NewFromCanBeCompressString("preventExtensions").GetTaggedValue()); + SetConstant(ConstantIndex::GETOWNPROPERTYDESCRIPTOR_STRING_INDEX, + factory->NewFromCanBeCompressString("getOwnPropertyDescriptor").GetTaggedValue()); + SetConstant(ConstantIndex::DEFINEPROPERTY_STRING_INDEX, + factory->NewFromCanBeCompressString("defineProperty").GetTaggedValue()); + SetConstant(ConstantIndex::HAS_STRING_INDEX, factory->NewFromCanBeCompressString("has").GetTaggedValue()); + SetConstant(ConstantIndex::DELETEPROPERTY_STRING_INDEX, + factory->NewFromCanBeCompressString("deleteProperty").GetTaggedValue()); + SetConstant(ConstantIndex::ENUMERATE_STRING_INDEX, + factory->NewFromCanBeCompressString("enumerate").GetTaggedValue()); + SetConstant(ConstantIndex::OWNKEYS_STRING_INDEX, factory->NewFromCanBeCompressString("ownKeys").GetTaggedValue()); + SetConstant(ConstantIndex::APPLY_STRING_INDEX, factory->NewFromCanBeCompressString("apply").GetTaggedValue()); + SetConstant(ConstantIndex::NEGATIVE_ZERO_STRING_INDEX, factory->NewFromCanBeCompressString("-0").GetTaggedValue()); + SetConstant(ConstantIndex::DONE_STRING_INDEX, factory->NewFromCanBeCompressString("done").GetTaggedValue()); + SetConstant(ConstantIndex::PROXY_STRING_INDEX, factory->NewFromCanBeCompressString("proxy").GetTaggedValue()); + SetConstant(ConstantIndex::REVOKE_STRING_INDEX, factory->NewFromCanBeCompressString("revoke").GetTaggedValue()); + SetConstant(ConstantIndex::NEXT_STRING_INDEX, factory->NewFromCanBeCompressString("next").GetTaggedValue()); + SetConstant(ConstantIndex::TO_STRING_STRING_INDEX, + factory->NewFromCanBeCompressString("toString").GetTaggedValue()); + SetConstant(ConstantIndex::TO_LOCALE_STRING_STRING_INDEX, + factory->NewFromCanBeCompressString("toLocaleString").GetTaggedValue()); + SetConstant(ConstantIndex::VALUE_OF_STRING_INDEX, factory->NewFromCanBeCompressString("valueOf").GetTaggedValue()); + SetConstant(ConstantIndex::UNDEFINED_STRING_INDEX, + factory->NewFromCanBeCompressString("undefined").GetTaggedValue()); + SetConstant(ConstantIndex::NULL_STRING_INDEX, factory->NewFromCanBeCompressString("null").GetTaggedValue()); + SetConstant(ConstantIndex::BOOLEAN_STRING_INDEX, factory->NewFromCanBeCompressString("boolean").GetTaggedValue()); + SetConstant(ConstantIndex::NUMBER_STRING_INDEX, factory->NewFromCanBeCompressString("number").GetTaggedValue()); + SetConstant(ConstantIndex::FUNCTION_STRING_INDEX, factory->NewFromCanBeCompressString("function").GetTaggedValue()); + SetConstant(ConstantIndex::STRING_STRING_INDEX, factory->NewFromCanBeCompressString("string").GetTaggedValue()); + SetConstant(ConstantIndex::SYMBOL_STRING_INDEX, factory->NewFromCanBeCompressString("symbol").GetTaggedValue()); + SetConstant(ConstantIndex::OBJECT_STRING_INDEX, factory->NewFromCanBeCompressString("object").GetTaggedValue()); + SetConstant(ConstantIndex::TRUE_STRING_INDEX, factory->NewFromCanBeCompressString("true").GetTaggedValue()); + SetConstant(ConstantIndex::FALSE_STRING_INDEX, factory->NewFromCanBeCompressString("false").GetTaggedValue()); + SetConstant(ConstantIndex::RETURN_STRING_INDEX, factory->NewFromCanBeCompressString("return").GetTaggedValue()); + SetConstant(ConstantIndex::THROW_STRING_INDEX, factory->NewFromCanBeCompressString("throw").GetTaggedValue()); + SetConstant(ConstantIndex::PROXY_CONSTRUCT_STRING_INDEX, + factory->NewFromCanBeCompressString("construct").GetTaggedValue()); + SetConstant(ConstantIndex::PROXY_CALL_STRING_INDEX, factory->NewFromCanBeCompressString("call").GetTaggedValue()); + SetConstant(ConstantIndex::PROMISE_THEN_STRING_INDEX, factory->NewFromCanBeCompressString("then").GetTaggedValue()); + SetConstant(ConstantIndex::PROMISE_CATCH_STRING_INDEX, + factory->NewFromCanBeCompressString("catch").GetTaggedValue()); + SetConstant(ConstantIndex::SCRIPT_JOB_STRING_INDEX, + factory->NewFromCanBeCompressString("ScriptJobs").GetTaggedValue()); + SetConstant(ConstantIndex::PROMISE_STRING_INDEX, + factory->NewFromCanBeCompressString("PrimiseJobs").GetTaggedValue()); + SetConstant(ConstantIndex::THROWER_STRING_INDEX, factory->NewFromCanBeCompressString("Thrower").GetTaggedValue()); + SetConstant(ConstantIndex::IDENTITY_STRING_INDEX, factory->NewFromCanBeCompressString("Identity").GetTaggedValue()); + SetConstant(ConstantIndex::CALLER_STRING_INDEX, factory->NewFromCanBeCompressString("caller").GetTaggedValue()); + SetConstant(ConstantIndex::CALLEE_STRING_INDEX, factory->NewFromCanBeCompressString("callee").GetTaggedValue()); + SetConstant(ConstantIndex::INT8_ARRAY_STRING_INDEX, + factory->NewFromCanBeCompressString("Int8Array").GetTaggedValue()); + SetConstant(ConstantIndex::UINT8_ARRAY_STRING_INDEX, + factory->NewFromCanBeCompressString("Uint8Array").GetTaggedValue()); + SetConstant(ConstantIndex::UINT8_CLAMPED_ARRAY_STRING_INDEX, + factory->NewFromCanBeCompressString("Uint8ClampedArray").GetTaggedValue()); + SetConstant(ConstantIndex::INT16_ARRAY_STRING_INDEX, + factory->NewFromCanBeCompressString("Int16Array").GetTaggedValue()); + SetConstant(ConstantIndex::UINT16_ARRAY_STRING_INDEX, + factory->NewFromCanBeCompressString("Uint16Array").GetTaggedValue()); + SetConstant(ConstantIndex::INT32_ARRAY_STRING_INDEX, + factory->NewFromCanBeCompressString("Int32Array").GetTaggedValue()); + SetConstant(ConstantIndex::UINT32_ARRAY_STRING_INDEX, + factory->NewFromCanBeCompressString("Uint32Array").GetTaggedValue()); + SetConstant(ConstantIndex::FLOAT32_ARRAY_STRING_INDEX, + factory->NewFromCanBeCompressString("Float32Array").GetTaggedValue()); + SetConstant(ConstantIndex::FLOAT64_ARRAY_STRING_INDEX, + factory->NewFromCanBeCompressString("Float64Array").GetTaggedValue()); + SetConstant(ConstantIndex::ASYNC_FUNCTION_STRING_INDEX, + factory->NewFromCanBeCompressString("AsyncFunction").GetTaggedValue()); + SetConstant(ConstantIndex::PROMISE_RESOLVE_STRING_INDEX, + factory->NewFromCanBeCompressString("resolve").GetTaggedValue()); + SetConstant(ConstantIndex::ID_STRING_INDEX, factory->NewFromCanBeCompressString("id").GetTaggedValue()); + SetConstant(ConstantIndex::METHOD_STRING_INDEX, factory->NewFromCanBeCompressString("method").GetTaggedValue()); + SetConstant(ConstantIndex::PARAMS_STRING_INDEX, factory->NewFromCanBeCompressString("params").GetTaggedValue()); + SetConstant(ConstantIndex::RESULT_STRING_INDEX, factory->NewFromCanBeCompressString("result").GetTaggedValue()); + SetConstant(ConstantIndex::TO_JSON_STRING_INDEX, factory->NewFromCanBeCompressString("toJSON").GetTaggedValue()); + SetConstant(ConstantIndex::GLOBAL_STRING_INDEX, factory->NewFromCanBeCompressString("global").GetTaggedValue()); + SetConstant(ConstantIndex::MESSAGE_STRING_INDEX, factory->NewFromCanBeCompressString("message").GetTaggedValue()); + SetConstant(ConstantIndex::ERROR_STRING_INDEX, factory->NewFromCanBeCompressString("Error").GetTaggedValue()); + SetConstant(ConstantIndex::RANGE_ERROR_STRING_INDEX, + factory->NewFromCanBeCompressString("RangeError").GetTaggedValue()); + SetConstant(ConstantIndex::REFERENCE_ERROR_STRING_INDEX, + factory->NewFromCanBeCompressString("ReferenceError").GetTaggedValue()); + SetConstant(ConstantIndex::TYPE_ERROR_STRING_INDEX, + factory->NewFromCanBeCompressString("TypeError").GetTaggedValue()); + SetConstant(ConstantIndex::URI_ERROR_STRING_INDEX, + factory->NewFromCanBeCompressString("URIError").GetTaggedValue()); + SetConstant(ConstantIndex::SYNTAX_ERROR_STRING_INDEX, + factory->NewFromCanBeCompressString("SyntaxError").GetTaggedValue()); + SetConstant(ConstantIndex::EVAL_ERROR_STRING_INDEX, + factory->NewFromCanBeCompressString("EvalError").GetTaggedValue()); + SetConstant(ConstantIndex::STACK_STRING_INDEX, factory->NewFromCanBeCompressString("stack").GetTaggedValue()); + SetConstant(ConstantIndex::STACK_EMPTY_STRING_INDEX, + factory->NewFromCanBeCompressString("stackisempty").GetTaggedValue()); + SetConstant(ConstantIndex::OBJ_NOT_COERCIBLE_STRING_INDEX, + factory->NewFromCanBeCompressString("objectnotcoercible").GetTaggedValue()); + /* for Intl. */ + SetConstant(ConstantIndex::LANGUAGE_STRING_CLASS_INDEX, + factory->NewFromCanBeCompressString("language").GetTaggedValue()); + SetConstant(ConstantIndex::SCRIPT_STRING_CLASS_INDEX, + factory->NewFromCanBeCompressString("script").GetTaggedValue()); + SetConstant(ConstantIndex::REGION_STRING_CLASS_INDEX, + factory->NewFromCanBeCompressString("region").GetTaggedValue()); + SetConstant(ConstantIndex::BASE_NAME_STRING_CLASS_INDEX, + factory->NewFromCanBeCompressString("baseName").GetTaggedValue()); + SetConstant(ConstantIndex::CALENDAR_STRING_CLASS_INDEX, + factory->NewFromCanBeCompressString("calendar").GetTaggedValue()); + SetConstant(ConstantIndex::COLLATION_STRING_CLASS_INDEX, + factory->NewFromCanBeCompressString("collation").GetTaggedValue()); + SetConstant(ConstantIndex::HOUR_CYCLE_STRING_CLASS_INDEX, + factory->NewFromCanBeCompressString("hourCycle").GetTaggedValue()); + SetConstant(ConstantIndex::CASE_FIRST_STRING_CLASS_INDEX, + factory->NewFromCanBeCompressString("caseFirst").GetTaggedValue()); + SetConstant(ConstantIndex::NUMERIC_STRING_CLASS_INDEX, + factory->NewFromCanBeCompressString("numeric").GetTaggedValue()); + SetConstant(ConstantIndex::NUMBERING_SYSTEM_STRING_CLASS_INDEX, + factory->NewFromCanBeCompressString("numberingSystem").GetTaggedValue()); + SetConstant(ConstantIndex::TYPE_STRING_INDEX, factory->NewFromCanBeCompressString("type").GetTaggedValue()); + SetConstant(ConstantIndex::LOCALE_MATCHER_STRING_INDEX, + factory->NewFromCanBeCompressString("localeMatcher").GetTaggedValue()); + SetConstant(ConstantIndex::FORMAT_MATCHER_STRING_INDEX, + factory->NewFromCanBeCompressString("formatMatcher").GetTaggedValue()); + SetConstant(ConstantIndex::HOUR12_STRING_INDEX, factory->NewFromCanBeCompressString("hour12").GetTaggedValue()); + SetConstant(ConstantIndex::H11_STRING_INDEX, factory->NewFromCanBeCompressString("h11").GetTaggedValue()); + SetConstant(ConstantIndex::H12_STRING_INDEX, factory->NewFromCanBeCompressString("h12").GetTaggedValue()); + SetConstant(ConstantIndex::H23_STRING_INDEX, factory->NewFromCanBeCompressString("h23").GetTaggedValue()); + SetConstant(ConstantIndex::H24_STRING_INDEX, factory->NewFromCanBeCompressString("h24").GetTaggedValue()); + SetConstant(ConstantIndex::WEEK_DAY_STRING_INDEX, factory->NewFromCanBeCompressString("weekday").GetTaggedValue()); + SetConstant(ConstantIndex::ERA_STRING_INDEX, factory->NewFromCanBeCompressString("era").GetTaggedValue()); + SetConstant(ConstantIndex::YEAR_STRING_INDEX, factory->NewFromCanBeCompressString("year").GetTaggedValue()); + SetConstant(ConstantIndex::QUARTER_STRING_INDEX, factory->NewFromCanBeCompressString("quarter").GetTaggedValue()); + SetConstant(ConstantIndex::MONTH_STRING_INDEX, factory->NewFromCanBeCompressString("month").GetTaggedValue()); + SetConstant(ConstantIndex::DAY_STRING_INDEX, factory->NewFromCanBeCompressString("day").GetTaggedValue()); + SetConstant(ConstantIndex::HOUR_STRING_INDEX, factory->NewFromCanBeCompressString("hour").GetTaggedValue()); + SetConstant(ConstantIndex::MINUTE_STRING_INDEX, factory->NewFromCanBeCompressString("minute").GetTaggedValue()); + SetConstant(ConstantIndex::SECOND_STRING_INDEX, factory->NewFromCanBeCompressString("second").GetTaggedValue()); + SetConstant(ConstantIndex::YEARS_STRING_INDEX, factory->NewFromCanBeCompressString("years").GetTaggedValue()); + SetConstant(ConstantIndex::QUARTERS_STRING_INDEX, factory->NewFromCanBeCompressString("quarters").GetTaggedValue()); + SetConstant(ConstantIndex::MONTHS_STRING_INDEX, factory->NewFromCanBeCompressString("months").GetTaggedValue()); + SetConstant(ConstantIndex::DAYS_STRING_INDEX, factory->NewFromCanBeCompressString("days").GetTaggedValue()); + SetConstant(ConstantIndex::HOURS_STRING_INDEX, factory->NewFromCanBeCompressString("hours").GetTaggedValue()); + SetConstant(ConstantIndex::MINUTES_STRING_INDEX, factory->NewFromCanBeCompressString("minutes").GetTaggedValue()); + SetConstant(ConstantIndex::SECONDS_STRING_INDEX, factory->NewFromCanBeCompressString("seconds").GetTaggedValue()); + SetConstant(ConstantIndex::TIME_ZONE_NAME_STRING_INDEX, + factory->NewFromCanBeCompressString("timeZoneName").GetTaggedValue()); + SetConstant(ConstantIndex::LOCALE_STRING_INDEX, factory->NewFromCanBeCompressString("locale").GetTaggedValue()); + SetConstant(ConstantIndex::TIME_ZONE_STRING_INDEX, + factory->NewFromCanBeCompressString("timeZone").GetTaggedValue()); + SetConstant(ConstantIndex::LITERAL_STRING_INDEX, factory->NewFromCanBeCompressString("literal").GetTaggedValue()); + SetConstant(ConstantIndex::YEAR_NAME_STRING_INDEX, + factory->NewFromCanBeCompressString("yearName").GetTaggedValue()); + SetConstant(ConstantIndex::DAY_PERIOD_STRING_INDEX, + factory->NewFromCanBeCompressString("dayPeriod").GetTaggedValue()); + SetConstant(ConstantIndex::FRACTIONAL_SECOND_DIGITS_STRING_INDEX, + factory->NewFromCanBeCompressString("fractionalSecondDigits").GetTaggedValue()); + SetConstant(ConstantIndex::FRACTIONAL_SECOND_STRING_INDEX, + factory->NewFromCanBeCompressString("fractionalSecond").GetTaggedValue()); + SetConstant(ConstantIndex::RELATED_YEAR_STRING_INDEX, + factory->NewFromCanBeCompressString("relatedYear").GetTaggedValue()); + SetConstant(ConstantIndex::LOOK_UP_STRING_INDEX, factory->NewFromCanBeCompressString("lookup").GetTaggedValue()); + SetConstant(ConstantIndex::BEST_FIT_STRING_INDEX, factory->NewFromCanBeCompressString("bestfit").GetTaggedValue()); + SetConstant(ConstantIndex::DATE_STYLE_STRING_INDEX, + factory->NewFromCanBeCompressString("dateStyle").GetTaggedValue()); + SetConstant(ConstantIndex::TIME_STYLE_STRING_INDEX, + factory->NewFromCanBeCompressString("timeStyle").GetTaggedValue()); + SetConstant(ConstantIndex::UTC_STRING_INDEX, factory->NewFromCanBeCompressString("UTC").GetTaggedValue()); + SetConstant(ConstantIndex::INITIALIZED_RELATIVE_INDEX, + factory->NewFromCanBeCompressString("true").GetTaggedValue()); + SetConstant(ConstantIndex::WEEK_STRING_INDEX, factory->NewFromCanBeCompressString("week").GetTaggedValue()); + SetConstant(ConstantIndex::WEEKS_STRING_INDEX, factory->NewFromCanBeCompressString("weeks").GetTaggedValue()); + SetConstant(ConstantIndex::SOURCE_STRING_INDEX, factory->NewFromCanBeCompressString("source").GetTaggedValue()); + SetConstant(ConstantIndex::FORMAT_STRING_INDEX, factory->NewFromCanBeCompressString("format").GetTaggedValue()); + SetConstant(ConstantIndex::EN_US_STRING_INDEX, factory->NewFromCanBeCompressString("en-US").GetTaggedValue()); + SetConstant(ConstantIndex::UND_STRING_INDEX, factory->NewFromCanBeCompressString("und").GetTaggedValue()); + SetConstant(ConstantIndex::LATN_STRING_INDEX, factory->NewFromCanBeCompressString("latn").GetTaggedValue()); + SetConstant(ConstantIndex::STYLE_STRING_INDEX, factory->NewFromCanBeCompressString("style").GetTaggedValue()); + SetConstant(ConstantIndex::UNIT_STRING_INDEX, factory->NewFromCanBeCompressString("unit").GetTaggedValue()); + SetConstant(ConstantIndex::INTEGER_STRING_INDEX, factory->NewFromCanBeCompressString("integer").GetTaggedValue()); + SetConstant(ConstantIndex::NAN_STRING_INDEX, factory->NewFromCanBeCompressString("nan").GetTaggedValue()); + SetConstant(ConstantIndex::INFINITY_STRING_INDEX, factory->NewFromCanBeCompressString("infinity").GetTaggedValue()); + SetConstant(ConstantIndex::FRACTION_STRING_INDEX, factory->NewFromCanBeCompressString("fraction").GetTaggedValue()); + SetConstant(ConstantIndex::DECIMAL_STRING_INDEX, factory->NewFromCanBeCompressString("decimal").GetTaggedValue()); + SetConstant(ConstantIndex::GROUP_STRING_INDEX, factory->NewFromCanBeCompressString("group").GetTaggedValue()); + SetConstant(ConstantIndex::CURRENCY_STRING_INDEX, factory->NewFromCanBeCompressString("currency").GetTaggedValue()); + SetConstant(ConstantIndex::CURRENCY_SIGN_STRING_INDEX, + factory->NewFromCanBeCompressString("currencySign").GetTaggedValue()); + SetConstant(ConstantIndex::CURRENCY_DISPLAY_STRING_INDEX, + factory->NewFromCanBeCompressString("currencyDisplay").GetTaggedValue()); + SetConstant(ConstantIndex::PERCENT_SIGN_STRING_INDEX, + factory->NewFromCanBeCompressString("percentSign").GetTaggedValue()); + SetConstant(ConstantIndex::PERCENT_STRING_INDEX, factory->NewFromCanBeCompressString("percent").GetTaggedValue()); + SetConstant(ConstantIndex::MINUS_SIGN_STRING_INDEX, + factory->NewFromCanBeCompressString("minusSign").GetTaggedValue()); + SetConstant(ConstantIndex::PLUS_SIGN_STRING_INDEX, + factory->NewFromCanBeCompressString("plusSign").GetTaggedValue()); + SetConstant(ConstantIndex::EXPONENT_SEPARATOR_STRING_INDEX, + factory->NewFromCanBeCompressString("exponentSeparator").GetTaggedValue()); + SetConstant(ConstantIndex::EXPONENT_MINUS_SIGN_INDEX, + factory->NewFromCanBeCompressString("exponentMinusSign").GetTaggedValue()); + SetConstant(ConstantIndex::EXPONENT_INTEGER_STRING_INDEX, + factory->NewFromCanBeCompressString("exponentInteger").GetTaggedValue()); + SetConstant(ConstantIndex::LONG_STRING_INDEX, factory->NewFromCanBeCompressString("long").GetTaggedValue()); + SetConstant(ConstantIndex::SHORT_STRING_INDEX, factory->NewFromCanBeCompressString("short").GetTaggedValue()); + SetConstant(ConstantIndex::FULL_STRING_INDEX, factory->NewFromCanBeCompressString("full").GetTaggedValue()); + SetConstant(ConstantIndex::MEDIUM_STRING_INDEX, factory->NewFromCanBeCompressString("medium").GetTaggedValue()); + SetConstant(ConstantIndex::NARROW_STRING_INDEX, factory->NewFromCanBeCompressString("narrow").GetTaggedValue()); + SetConstant(ConstantIndex::ALWAYS_STRING_INDEX, factory->NewFromCanBeCompressString("always").GetTaggedValue()); + SetConstant(ConstantIndex::AUTO_STRING_INDEX, factory->NewFromCanBeCompressString("auto").GetTaggedValue()); + SetConstant(ConstantIndex::UNIT_DISPLAY_INDEX, factory->NewFromCanBeCompressString("unitDisplay").GetTaggedValue()); + SetConstant(ConstantIndex::NOTATION_INDEX, factory->NewFromCanBeCompressString("notation").GetTaggedValue()); + SetConstant(ConstantIndex::COMPACT_DISPALY_INDEX, + factory->NewFromCanBeCompressString("compactDisplay").GetTaggedValue()); + SetConstant(ConstantIndex::USER_GROUPING_INDEX, + factory->NewFromCanBeCompressString("useGrouping").GetTaggedValue()); + SetConstant(ConstantIndex::SIGN_DISPLAY_INDEX, factory->NewFromCanBeCompressString("signDisplay").GetTaggedValue()); + SetConstant(ConstantIndex::CODE_INDEX, factory->NewFromCanBeCompressString("code").GetTaggedValue()); + SetConstant(ConstantIndex::NARROW_SYMBOL_INDEX, + factory->NewFromCanBeCompressString("narrowSymbol").GetTaggedValue()); + SetConstant(ConstantIndex::STANDARD_INDEX, factory->NewFromCanBeCompressString("standard").GetTaggedValue()); + SetConstant(ConstantIndex::ACCOUNTING_INDEX, factory->NewFromCanBeCompressString("accounting").GetTaggedValue()); + SetConstant(ConstantIndex::SCIENTIFIC_INDEX, factory->NewFromCanBeCompressString("scientific").GetTaggedValue()); + SetConstant(ConstantIndex::ENGINEERING_INDEX, factory->NewFromCanBeCompressString("engineering").GetTaggedValue()); + SetConstant(ConstantIndex::COMPACT_STRING_INDEX, factory->NewFromCanBeCompressString("compact").GetTaggedValue()); + SetConstant(ConstantIndex::NEVER_INDEX, factory->NewFromCanBeCompressString("never").GetTaggedValue()); + SetConstant(ConstantIndex::EXPECT_ZERO_INDEX, factory->NewFromCanBeCompressString("exceptZero").GetTaggedValue()); + SetConstant(ConstantIndex::MINIMUM_INTEGER_DIGITS_INDEX, + factory->NewFromCanBeCompressString("minimumIntegerDigits").GetTaggedValue()); + SetConstant(ConstantIndex::MINIMUM_FRACTIONDIGITS_INDEX, + factory->NewFromCanBeCompressString("minimumFractionDigits").GetTaggedValue()); + SetConstant(ConstantIndex::MAXIMUM_FRACTIONDIGITS_INDEX, + factory->NewFromCanBeCompressString("maximumFractionDigits").GetTaggedValue()); + SetConstant(ConstantIndex::MINIMUM_SIGNIFICANTDIGITS_INDEX, + factory->NewFromCanBeCompressString("minimumSignificantDigits").GetTaggedValue()); + SetConstant(ConstantIndex::MAXIMUM_SIGNIFICANTDIGITS_INDEX, + factory->NewFromCanBeCompressString("maximumSignificantDigits").GetTaggedValue()); + SetConstant(ConstantIndex::INVALID_DATE_INDEX, + factory->NewFromCanBeCompressString("Invalid Date").GetTaggedValue()); + SetConstant(ConstantIndex::USAGE_INDEX, factory->NewFromCanBeCompressString("usage").GetTaggedValue()); + SetConstant(ConstantIndex::COMPARE_INDEX, factory->NewFromCanBeCompressString("compare").GetTaggedValue()); + SetConstant(ConstantIndex::SENSITIVITY_INDEX, factory->NewFromCanBeCompressString("sensitivity").GetTaggedValue()); + SetConstant(ConstantIndex::IGNORE_PUNCTUATION_INDEX, + factory->NewFromCanBeCompressString("ignorePunctuation").GetTaggedValue()); + SetConstant(ConstantIndex::CARDINAL_INDEX, factory->NewFromCanBeCompressString("cardinal").GetTaggedValue()); + SetConstant(ConstantIndex::ORDINAL_INDEX, factory->NewFromCanBeCompressString("ordinal").GetTaggedValue()); + SetConstant(ConstantIndex::EXEC_INDEX, factory->NewFromCanBeCompressString("exec").GetTaggedValue()); + SetConstant(ConstantIndex::LAST_INDEX_INDEX, factory->NewFromCanBeCompressString("lastIndex").GetTaggedValue()); + SetConstant(ConstantIndex::PLURAL_CATEGORIES_INDEX, + factory->NewFromCanBeCompressString("pluralCategories").GetTaggedValue()); + SetConstant(ConstantIndex::SORT_INDEX, factory->NewFromCanBeCompressString("sort").GetTaggedValue()); + SetConstant(ConstantIndex::SEARCH_INDEX, factory->NewFromCanBeCompressString("search").GetTaggedValue()); + SetConstant(ConstantIndex::BASE_INDEX, factory->NewFromCanBeCompressString("base").GetTaggedValue()); + SetConstant(ConstantIndex::ACCENT_INDEX, factory->NewFromCanBeCompressString("accent").GetTaggedValue()); + SetConstant(ConstantIndex::CASE_INDEX, factory->NewFromCanBeCompressString("case").GetTaggedValue()); + SetConstant(ConstantIndex::VARIANT_INDEX, factory->NewFromCanBeCompressString("variant").GetTaggedValue()); + SetConstant(ConstantIndex::EN_US_POSIX_STRING_INDEX, + factory->NewFromCanBeCompressString("en-US-POSIX").GetTaggedValue()); + SetConstant(ConstantIndex::UPPER_INDEX, factory->NewFromCanBeCompressString("upper").GetTaggedValue()); + SetConstant(ConstantIndex::LOWER_INDEX, factory->NewFromCanBeCompressString("lower").GetTaggedValue()); + SetConstant(ConstantIndex::DEFAULT_INDEX, factory->NewFromCanBeCompressString("default").GetTaggedValue()); + SetConstant(ConstantIndex::SHARED_INDEX, factory->NewFromCanBeCompressString("shared").GetTaggedValue()); + SetConstant(ConstantIndex::START_RANGE_INDEX, factory->NewFromCanBeCompressString("startRange").GetTaggedValue()); + SetConstant(ConstantIndex::END_RANGE_INDEX, factory->NewFromCanBeCompressString("endRange").GetTaggedValue()); + SetConstant(ConstantIndex::ISO8601_INDEX, factory->NewFromCanBeCompressString("iso8601").GetTaggedValue()); + SetConstant(ConstantIndex::GREGORY_INDEX, factory->NewFromCanBeCompressString("gregory").GetTaggedValue()); + SetConstant(ConstantIndex::ETHIOAA_INDEX, factory->NewFromCanBeCompressString("ethioaa").GetTaggedValue()); + SetConstant(ConstantIndex::STICKY_INDEX, factory->NewFromCanBeCompressString("sticky").GetTaggedValue()); + SetConstant(ConstantIndex::U_INDEX, factory->NewFromCanBeCompressString("u").GetTaggedValue()); + SetConstant(ConstantIndex::INDEX_INDEX, factory->NewFromCanBeCompressString("index").GetTaggedValue()); + SetConstant(ConstantIndex::INPUT_INDEX, factory->NewFromCanBeCompressString("input").GetTaggedValue()); + SetConstant(ConstantIndex::UNICODE_INDEX, factory->NewFromCanBeCompressString("unicode").GetTaggedValue()); + SetConstant(ConstantIndex::ZERO_INDEX, factory->NewFromCanBeCompressString("0").GetTaggedValue()); + SetConstant(ConstantIndex::VALUES_INDEX, factory->NewFromCanBeCompressString("values").GetTaggedValue()); + + auto accessor = factory->NewInternalAccessor(reinterpret_cast(JSFunction::PrototypeSetter), + reinterpret_cast(JSFunction::PrototypeGetter)); + SetConstant(ConstantIndex::FUNCTION_PROTOTYPE_ACCESSOR, accessor.GetTaggedValue()); + accessor = factory->NewInternalAccessor(nullptr, reinterpret_cast(JSFunction::NameGetter)); + SetConstant(ConstantIndex::FUNCTION_NAME_ACCESSOR, accessor.GetTaggedValue()); + accessor = factory->NewInternalAccessor(reinterpret_cast(JSArray::LengthSetter), + reinterpret_cast(JSArray::LengthGetter)); + SetConstant(ConstantIndex::ARRAY_LENGTH_ACCESSOR, accessor.GetTaggedValue()); +} + +void GlobalEnvConstants::InitGlobalUndefined() +{ + SetConstant(ConstantIndex::UNDEFINED_INDEX, JSTaggedValue::Undefined()); +} +} // namespace panda::ecmascript diff --git a/runtime/global_env_constants.h b/runtime/global_env_constants.h new file mode 100644 index 000000000..d4c192c6f --- /dev/null +++ b/runtime/global_env_constants.h @@ -0,0 +1,369 @@ +/* + * Copyright (c) 2021-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 RUNTIME_ECMASCRIPT_ECMA_ROOTS_H +#define RUNTIME_ECMASCRIPT_ECMA_ROOTS_H + +#include + +#include "plugins/ecmascript/runtime/mem/object_xray.h" +#include "libpandabase/macros.h" + +namespace panda::ecmascript { +// Forward Declaration +class ObjectFactory; +template +class JSHandle; +class JSHClass; +class JSThread; + +// NOLINTNEXTLINE(cppcoreguidelines-macro-usage) +#define GLOBAL_ENV_CONSTANT_CLASS(V) \ + /* GC Root */ \ + V(JSTaggedValue, HClassClass, HCLASS_CLASS_INDEX, ecma_roots_class) \ + V(JSTaggedValue, StringClass, STRING_CLASS_INDEX, ecma_roots_class) \ + V(JSTaggedValue, ArrayClass, ARRAY_CLASS_INDEX, ecma_roots_class) \ + V(JSTaggedValue, DictionaryClass, DICTIONARY_CLASS_INDEX, ecma_roots_class) \ + V(JSTaggedValue, JSNativePointerClass, JS_NATIVE_POINTER_CLASS_INDEX, ecma_roots_class) \ + V(JSTaggedValue, EnvClass, ENV_CLASS_INDEX, ecma_roots_class) \ + V(JSTaggedValue, FreeObjectWithNoneFieldClass, FREE_OBJECT_WITH_NONE_FIELD_CLASS_INDEX, ecma_roots_class) \ + V(JSTaggedValue, FreeObjectWithOneFieldClass, FREE_OBJECT_WITH_ONE_FIELD_CLASS_INDEX, ecma_roots_class) \ + V(JSTaggedValue, FreeObjectWithTwoFieldClass, FREE_OBJECT_WITH_TWO_FIELD_CLASS_INDEX, ecma_roots_class) \ + V(JSTaggedValue, SymbolClass, SYMBOL_CLASS_INDEX, ecma_roots_class) \ + V(JSTaggedValue, AccessorDataClass, ACCESSOE_DATA_CLASS_INDEX, ecma_roots_class) \ + V(JSTaggedValue, InternalAccessorClass, INTERNAL_ACCESSOR_CLASS_INDEX, ecma_roots_class) \ + V(JSTaggedValue, JSProxyOrdinaryClass, JS_PROXY_ORDINARY_CLASS_INDEX, ecma_roots_class) \ + V(JSTaggedValue, ObjectWrapperClass, OBJECT_WRAPPER_CLASS_INDEX, ecma_roots_class) \ + V(JSTaggedValue, CompletionRecordClass, COMPLETION_RECORD_CLASS_INDEX, ecma_roots_class) \ + V(JSTaggedValue, GeneratorContextClass, GENERATOR_CONTEST_INDEX, ecma_roots_class) \ + V(JSTaggedValue, CapabilityRecordClass, CAPABILITY_RECORD_CLASS_INDEX, ecma_roots_class) \ + V(JSTaggedValue, ReactionsRecordClass, REACTIONS_RECORD_CLASS_INDEX, ecma_roots_class) \ + V(JSTaggedValue, PromiseIteratorRecordClass, PROMISE_ITERATOR_RECORD_CLASS_INDEX, ecma_roots_class) \ + V(JSTaggedValue, PromiseRecordClass, PROMISE_RECORD_CLASS_INDEX, ecma_roots_class) \ + V(JSTaggedValue, PromiseResolvingFunctionsRecordClass, PROMISE_RESOLVING_FUNCTIONS_CLASS_INDEX, ecma_roots_class) \ + V(JSTaggedValue, MicroJobQueueClass, MICRO_JOB_QUEUE_CLASS_INDEX, ecma_roots_class) \ + V(JSTaggedValue, PendingJobClass, PENDING_JOB_CLASS_INDEX, ecma_roots_class) \ + V(JSTaggedValue, ProtoChangeMarkerClass, PROTO_CHANGE_MARKER_CLASS_INDEX, ecma_roots_class) \ + V(JSTaggedValue, ProtoChangeDetailsClass, PROTO_CHANGE_DETAILS_CLASS_INDEX, ecma_roots_class) \ + V(JSTaggedValue, PrototypeHandlerClass, PROTOTYPE_HANDLER_CLASS_INDEX, ecma_roots_class) \ + V(JSTaggedValue, TransitionHandlerClass, TRANSITION_HANDLER_CLASS_INDEX, ecma_roots_class) \ + V(JSTaggedValue, PropertyBoxClass, PROPERTY_BOX_CLASS_INDEX, ecma_roots_class) \ + V(JSTaggedValue, FunctionExtraInfoClass, FUNCTION_EXTRA_INFO_INDEX, ecma_roots_class) \ + V(JSTaggedValue, ProgramClass, PROGRAM_CLASS_INDEX, ecma_roots_class) \ + V(JSTaggedValue, EcmaModuleClass, ECMA_MODULE_CLASS_INDEX, ecma_roots_class) \ + V(JSTaggedValue, JSProxyCallableClass, JS_PROXY_CALLABLE_CLASS_INDEX, ecma_roots_class) \ + V(JSTaggedValue, JSProxyConstructClass, JS_PROXY_CONSTRUCT_CLASS_INDEX, ecma_roots_class) \ + V(JSTaggedValue, JSRealmClass, JS_REALM_CLASS_INDEX, ecma_roots_class) \ + V(JSTaggedValue, LinkedHashSetClass, LINKED_HASH_SET_CLASS_INDEX, ecma_roots_class) \ + V(JSTaggedValue, WeakLinkedHashSetClass, WEAK_LINKED_HASH_SET_CLASS_INDEX, ecma_roots_class) \ + V(JSTaggedValue, LinkedHashMapClass, LINKED_HASH_MAP_CLASS_INDEX, ecma_roots_class) \ + V(JSTaggedValue, WeakLinkedHashMapClass, WEAK_LINKED_HASH_MAP_CLASS_INDEX, ecma_roots_class) \ + V(JSTaggedValue, JSRegExpClass, JS_REGEXP_CLASS_INDEX, ecma_roots_class) \ + V(JSTaggedValue, MachineCodeClass, MACHINE_CODE_CLASS_INDEX, ecma_roots_class) \ + V(JSTaggedValue, ClassInfoExtractorHClass, CLASS_INFO_EXTRACTOR_HCLASS_INDEX, ecma_roots_class) + +// NOLINTNEXTLINE(cppcoreguidelines-macro-usage) +#define GLOBAL_ENV_CONSTANT_SPECIAL(V) \ + V(JSTaggedValue, Undefined, UNDEFINED_INDEX, ecma_roots_special) \ + V(JSTaggedValue, Null, NULL_INDEX, ecma_roots_special) \ + V(JSTaggedValue, EmptyString, EMPTY_STRING_OBJECT_INDEX, ecma_roots_special) +/* GlobalConstant */ +// NOLINTNEXTLINE(cppcoreguidelines-macro-usage) +#define GLOBAL_ENV_CONSTANT_CONSTANT(V) \ + V(JSTaggedValue, ConstructorString, CONSTRUCTOR_STRING_INDEX, constructor) \ + V(JSTaggedValue, PrototypeString, PROTOTYPE_STRING_INDEX, prototype) \ + V(JSTaggedValue, LengthString, LENGTH_STRING_INDEX, length) \ + V(JSTaggedValue, ValueString, VALUE_STRING_INDEX, value) \ + V(JSTaggedValue, GetString, GET_STRING_INDEX, set) \ + V(JSTaggedValue, SetString, SET_STRING_INDEX, get) \ + V(JSTaggedValue, WritableString, WRITABLE_STRING_INDEX, writable) \ + V(JSTaggedValue, EnumerableString, ENUMERABLE_STRING_INDEX, enumerable) \ + V(JSTaggedValue, ConfigurableString, CONFIGURABLE_STRING_INDEX, configurable) \ + /* SymbolTable*RegisterSymbols */ \ + V(JSTaggedValue, NameString, NAME_STRING_INDEX, name) \ + V(JSTaggedValue, GetPrototypeOfString, GETPROTOTYPEOF_STRING_INDEX, getPrototypeOf) \ + V(JSTaggedValue, SetPrototypeOfString, SETPROTOTYPEOF_STRING_INDEX, setPrototypeOf) \ + V(JSTaggedValue, IsExtensibleString, ISEXTENSIBLE_STRING_INDEX, isExtensible) \ + V(JSTaggedValue, PreventExtensionsString, PREVENTEXTENSIONS_STRING_INDEX, preventExtensions) \ + V(JSTaggedValue, GetOwnPropertyDescriptorString, GETOWNPROPERTYDESCRIPTOR_STRING_INDEX, getOwnPropertyDescriptor) \ + V(JSTaggedValue, DefinePropertyString, DEFINEPROPERTY_STRING_INDEX, defineProperty) \ + V(JSTaggedValue, HasString, HAS_STRING_INDEX, has) \ + V(JSTaggedValue, DeletePropertyString, DELETEPROPERTY_STRING_INDEX, deleteProperty) \ + V(JSTaggedValue, EnumerateString, ENUMERATE_STRING_INDEX, enumerate) \ + V(JSTaggedValue, OwnKeysString, OWNKEYS_STRING_INDEX, ownKeys) \ + V(JSTaggedValue, ApplyString, APPLY_STRING_INDEX, apply) \ + V(JSTaggedValue, NegativeZeroString, NEGATIVE_ZERO_STRING_INDEX, -0) \ + V(JSTaggedValue, DoneString, DONE_STRING_INDEX, done) \ + V(JSTaggedValue, ProxyString, PROXY_STRING_INDEX, proxy) \ + V(JSTaggedValue, RevokeString, REVOKE_STRING_INDEX, revoke) \ + V(JSTaggedValue, NextString, NEXT_STRING_INDEX, next) \ + V(JSTaggedValue, ToStringString, TO_STRING_STRING_INDEX, toString) \ + V(JSTaggedValue, ToLocaleStringString, TO_LOCALE_STRING_STRING_INDEX, toLocaleString) \ + V(JSTaggedValue, ValueOfString, VALUE_OF_STRING_INDEX, valueOf) \ + V(JSTaggedValue, UndefinedString, UNDEFINED_STRING_INDEX, undefined) \ + V(JSTaggedValue, NullString, NULL_STRING_INDEX, null) \ + V(JSTaggedValue, BooleanString, BOOLEAN_STRING_INDEX, boolean) \ + V(JSTaggedValue, NumberString, NUMBER_STRING_INDEX, number) \ + V(JSTaggedValue, FunctionString, FUNCTION_STRING_INDEX, function) \ + V(JSTaggedValue, StringString, STRING_STRING_INDEX, string) \ + V(JSTaggedValue, SymbolString, SYMBOL_STRING_INDEX, symbol) \ + V(JSTaggedValue, ObjectString, OBJECT_STRING_INDEX, object) \ + V(JSTaggedValue, TrueString, TRUE_STRING_INDEX, true) \ + V(JSTaggedValue, FalseString, FALSE_STRING_INDEX, false) \ + V(JSTaggedValue, ReturnString, RETURN_STRING_INDEX, return) \ + V(JSTaggedValue, ThrowString, THROW_STRING_INDEX, throw) \ + V(JSTaggedValue, ProxyConstructString, PROXY_CONSTRUCT_STRING_INDEX, construct) \ + V(JSTaggedValue, ProxyCallString, PROXY_CALL_STRING_INDEX, call) \ + V(JSTaggedValue, PromiseThenString, PROMISE_THEN_STRING_INDEX, then) \ + V(JSTaggedValue, PromiseCatchString, PROMISE_CATCH_STRING_INDEX, catch) \ + V(JSTaggedValue, ScriptJobString, SCRIPT_JOB_STRING_INDEX, ScriptJobs) \ + V(JSTaggedValue, PromiseString, PROMISE_STRING_INDEX, PrimiseJobs) \ + V(JSTaggedValue, ThrowerString, THROWER_STRING_INDEX, Thrower) \ + V(JSTaggedValue, IdentityString, IDENTITY_STRING_INDEX, Identity) \ + V(JSTaggedValue, CallerString, CALLER_STRING_INDEX, caller) \ + V(JSTaggedValue, CalleeString, CALLEE_STRING_INDEX, callee) \ + V(JSTaggedValue, Int8ArrayString, INT8_ARRAY_STRING_INDEX, Int8Array) \ + V(JSTaggedValue, Uint8ArrayString, UINT8_ARRAY_STRING_INDEX, Uint8Array) \ + V(JSTaggedValue, Uint8ClampedArrayString, UINT8_CLAMPED_ARRAY_STRING_INDEX, Uint8ClampedArray) \ + V(JSTaggedValue, Int16ArrayString, INT16_ARRAY_STRING_INDEX, Int16Array) \ + V(JSTaggedValue, Uint16ArrayString, UINT16_ARRAY_STRING_INDEX, Uint16Array) \ + V(JSTaggedValue, Int32ArrayString, INT32_ARRAY_STRING_INDEX, Int32Array) \ + V(JSTaggedValue, Uint32ArrayString, UINT32_ARRAY_STRING_INDEX, Uint32Array) \ + V(JSTaggedValue, Float32ArrayString, FLOAT32_ARRAY_STRING_INDEX, Float32Array) \ + V(JSTaggedValue, Float64ArrayString, FLOAT64_ARRAY_STRING_INDEX, Float64Array) \ + V(JSTaggedValue, AsyncFunctionString, ASYNC_FUNCTION_STRING_INDEX, AsyncFunction) \ + V(JSTaggedValue, PromiseResolveString, PROMISE_RESOLVE_STRING_INDEX, resolve) \ + V(JSTaggedValue, IdString, ID_STRING_INDEX, id) \ + V(JSTaggedValue, MethodString, METHOD_STRING_INDEX, method) \ + V(JSTaggedValue, ParamsString, PARAMS_STRING_INDEX, params) \ + V(JSTaggedValue, ResultString, RESULT_STRING_INDEX, result) \ + V(JSTaggedValue, ToJsonString, TO_JSON_STRING_INDEX, toJSON) \ + V(JSTaggedValue, GlobalString, GLOBAL_STRING_INDEX, global) \ + V(JSTaggedValue, MessageString, MESSAGE_STRING_INDEX, message) \ + V(JSTaggedValue, ErrorString, ERROR_STRING_INDEX, Error) \ + V(JSTaggedValue, RangeErrorString, RANGE_ERROR_STRING_INDEX, RangeError) \ + V(JSTaggedValue, ReferenceErrorString, REFERENCE_ERROR_STRING_INDEX, ReferenceError) \ + V(JSTaggedValue, TypeErrorString, TYPE_ERROR_STRING_INDEX, TypeError) \ + V(JSTaggedValue, URIErrorString, URI_ERROR_STRING_INDEX, URIError) \ + V(JSTaggedValue, SyntaxErrorString, SYNTAX_ERROR_STRING_INDEX, SyntaxError) \ + V(JSTaggedValue, EvalErrorString, EVAL_ERROR_STRING_INDEX, EvalError) \ + V(JSTaggedValue, StackString, STACK_STRING_INDEX, stack) \ + V(JSTaggedValue, StackEmptyString, STACK_EMPTY_STRING_INDEX, stackisempty) \ + V(JSTaggedValue, ObjNotCoercibleString, OBJ_NOT_COERCIBLE_STRING_INDEX, objectnotcoercible) \ + /* forIntl. */ \ + V(JSTaggedValue, LanguageString, LANGUAGE_STRING_CLASS_INDEX, language) \ + V(JSTaggedValue, ScriptString, SCRIPT_STRING_CLASS_INDEX, script) \ + V(JSTaggedValue, RegionString, REGION_STRING_CLASS_INDEX, region) \ + V(JSTaggedValue, BaseNameString, BASE_NAME_STRING_CLASS_INDEX, baseName) \ + V(JSTaggedValue, CalendarString, CALENDAR_STRING_CLASS_INDEX, calendar) \ + V(JSTaggedValue, CollationString, COLLATION_STRING_CLASS_INDEX, collation) \ + V(JSTaggedValue, HourCycleString, HOUR_CYCLE_STRING_CLASS_INDEX, hourCycle) \ + V(JSTaggedValue, CaseFirstString, CASE_FIRST_STRING_CLASS_INDEX, caseFirst) \ + V(JSTaggedValue, NumericString, NUMERIC_STRING_CLASS_INDEX, numeric) \ + V(JSTaggedValue, NumberingSystemString, NUMBERING_SYSTEM_STRING_CLASS_INDEX, numberingSystem) \ + V(JSTaggedValue, TypeString, TYPE_STRING_INDEX, type) \ + V(JSTaggedValue, LocaleMatcherString, LOCALE_MATCHER_STRING_INDEX, localeMatcher) \ + V(JSTaggedValue, FormatMatcherString, FORMAT_MATCHER_STRING_INDEX, formatMatcher) \ + V(JSTaggedValue, Hour12String, HOUR12_STRING_INDEX, hour12) \ + V(JSTaggedValue, H11String, H11_STRING_INDEX, h11) \ + V(JSTaggedValue, H12String, H12_STRING_INDEX, h12) \ + V(JSTaggedValue, H23String, H23_STRING_INDEX, h23) \ + V(JSTaggedValue, H24String, H24_STRING_INDEX, h24) \ + V(JSTaggedValue, WeekdayString, WEEK_DAY_STRING_INDEX, weekday) \ + V(JSTaggedValue, EraString, ERA_STRING_INDEX, era) \ + V(JSTaggedValue, YearString, YEAR_STRING_INDEX, year) \ + V(JSTaggedValue, QuarterString, QUARTER_STRING_INDEX, quarter) \ + V(JSTaggedValue, MonthString, MONTH_STRING_INDEX, month) \ + V(JSTaggedValue, DayString, DAY_STRING_INDEX, day) \ + V(JSTaggedValue, HourString, HOUR_STRING_INDEX, hour) \ + V(JSTaggedValue, MinuteString, MINUTE_STRING_INDEX, minute) \ + V(JSTaggedValue, SecondString, SECOND_STRING_INDEX, second) \ + V(JSTaggedValue, YearsString, YEARS_STRING_INDEX, years) \ + V(JSTaggedValue, QuartersString, QUARTERS_STRING_INDEX, quarters) \ + V(JSTaggedValue, MonthsString, MONTHS_STRING_INDEX, months) \ + V(JSTaggedValue, DaysString, DAYS_STRING_INDEX, days) \ + V(JSTaggedValue, HoursString, HOURS_STRING_INDEX, hours) \ + V(JSTaggedValue, MinutesString, MINUTES_STRING_INDEX, minutes) \ + V(JSTaggedValue, SecondsString, SECONDS_STRING_INDEX, seconds) \ + V(JSTaggedValue, TimeZoneNameString, TIME_ZONE_NAME_STRING_INDEX, timeZoneName) \ + V(JSTaggedValue, LocaleString, LOCALE_STRING_INDEX, locale) \ + V(JSTaggedValue, TimeZoneString, TIME_ZONE_STRING_INDEX, timeZone) \ + V(JSTaggedValue, LiteralString, LITERAL_STRING_INDEX, literal) \ + V(JSTaggedValue, YearNameString, YEAR_NAME_STRING_INDEX, yearName) \ + V(JSTaggedValue, DayPeriodString, DAY_PERIOD_STRING_INDEX, dayPeriod) \ + V(JSTaggedValue, FractionalSecondDigitsString, FRACTIONAL_SECOND_DIGITS_STRING_INDEX, fractionalSecondDigits) \ + V(JSTaggedValue, FractionalSecondString, FRACTIONAL_SECOND_STRING_INDEX, fractionalSecond) \ + V(JSTaggedValue, RelatedYearString, RELATED_YEAR_STRING_INDEX, relatedYear) \ + V(JSTaggedValue, LookUpString, LOOK_UP_STRING_INDEX, lookup) \ + V(JSTaggedValue, BestFitString, BEST_FIT_STRING_INDEX, bestfit) \ + V(JSTaggedValue, DateStyleString, DATE_STYLE_STRING_INDEX, dateStyle) \ + V(JSTaggedValue, TimeStyleString, TIME_STYLE_STRING_INDEX, timeStyle) \ + V(JSTaggedValue, UTCString, UTC_STRING_INDEX, UTC) \ + V(JSTaggedValue, InitializedRelativeTimeFormatString, INITIALIZED_RELATIVE_INDEX, true) \ + V(JSTaggedValue, WeekString, WEEK_STRING_INDEX, week) \ + V(JSTaggedValue, WeeksString, WEEKS_STRING_INDEX, weeks) \ + V(JSTaggedValue, SourceString, SOURCE_STRING_INDEX, source) \ + V(JSTaggedValue, FormatString, FORMAT_STRING_INDEX, format) \ + V(JSTaggedValue, EnUsString, EN_US_STRING_INDEX, en - US) \ + V(JSTaggedValue, UndString, UND_STRING_INDEX, und) \ + V(JSTaggedValue, LatnString, LATN_STRING_INDEX, latn) \ + V(JSTaggedValue, StyleString, STYLE_STRING_INDEX, style) \ + V(JSTaggedValue, UnitString, UNIT_STRING_INDEX, unit) \ + V(JSTaggedValue, IntegerString, INTEGER_STRING_INDEX, integer) \ + V(JSTaggedValue, NanString, NAN_STRING_INDEX, nan) \ + V(JSTaggedValue, InfinityString, INFINITY_STRING_INDEX, infinity) \ + V(JSTaggedValue, FractionString, FRACTION_STRING_INDEX, fraction) \ + V(JSTaggedValue, DecimalString, DECIMAL_STRING_INDEX, decimal) \ + V(JSTaggedValue, GroupString, GROUP_STRING_INDEX, group) \ + V(JSTaggedValue, CurrencyString, CURRENCY_STRING_INDEX, currency) \ + V(JSTaggedValue, CurrencySignString, CURRENCY_SIGN_STRING_INDEX, currencySign) \ + V(JSTaggedValue, CurrencyDisplayString, CURRENCY_DISPLAY_STRING_INDEX, currencyDisplay) \ + V(JSTaggedValue, PercentSignString, PERCENT_SIGN_STRING_INDEX, percentSign) \ + V(JSTaggedValue, PercentString, PERCENT_STRING_INDEX, percent) \ + V(JSTaggedValue, MinusSignString, MINUS_SIGN_STRING_INDEX, minusSign) \ + V(JSTaggedValue, PlusSignString, PLUS_SIGN_STRING_INDEX, plusSign) \ + V(JSTaggedValue, ExponentSeparatorString, EXPONENT_SEPARATOR_STRING_INDEX, exponentSeparator) \ + V(JSTaggedValue, ExponentMinusSignString, EXPONENT_MINUS_SIGN_INDEX, exponentMinusSign) \ + V(JSTaggedValue, ExponentIntegerString, EXPONENT_INTEGER_STRING_INDEX, exponentInteger) \ + V(JSTaggedValue, LongString, LONG_STRING_INDEX, long) \ + V(JSTaggedValue, ShortString, SHORT_STRING_INDEX, short) \ + V(JSTaggedValue, FullString, FULL_STRING_INDEX, full) \ + V(JSTaggedValue, MediumString, MEDIUM_STRING_INDEX, medium) \ + V(JSTaggedValue, NarrowString, NARROW_STRING_INDEX, narrow) \ + V(JSTaggedValue, AlwaysString, ALWAYS_STRING_INDEX, always) \ + V(JSTaggedValue, AutoString, AUTO_STRING_INDEX, auto) \ + V(JSTaggedValue, UnitDisplayString, UNIT_DISPLAY_INDEX, unitDisplay) \ + V(JSTaggedValue, NotationString, NOTATION_INDEX, notation) \ + V(JSTaggedValue, CompactDisplayString, COMPACT_DISPALY_INDEX, compactDisplay) \ + V(JSTaggedValue, UserGroupingString, USER_GROUPING_INDEX, useGrouping) \ + V(JSTaggedValue, SignDisplayString, SIGN_DISPLAY_INDEX, signDisplay) \ + V(JSTaggedValue, CodeString, CODE_INDEX, code) \ + V(JSTaggedValue, NarrowSymbolString, NARROW_SYMBOL_INDEX, narrowSymbol) \ + V(JSTaggedValue, StandardString, STANDARD_INDEX, standard) \ + V(JSTaggedValue, AccountingString, ACCOUNTING_INDEX, accounting) \ + V(JSTaggedValue, ScientificString, SCIENTIFIC_INDEX, scientific) \ + V(JSTaggedValue, EngineeringString, ENGINEERING_INDEX, engineering) \ + V(JSTaggedValue, CompactString, COMPACT_STRING_INDEX, compact) \ + V(JSTaggedValue, NeverString, NEVER_INDEX, never) \ + V(JSTaggedValue, ExceptZeroString, EXPECT_ZERO_INDEX, exceptZero) \ + V(JSTaggedValue, MinimumIntegerDigitsString, MINIMUM_INTEGER_DIGITS_INDEX, minimumIntegerDigits) \ + V(JSTaggedValue, MinimumFractionDigitsString, MINIMUM_FRACTIONDIGITS_INDEX, minimumFractionDigits) \ + V(JSTaggedValue, MaximumFractionDigitsString, MAXIMUM_FRACTIONDIGITS_INDEX, maximumFractionDigits) \ + V(JSTaggedValue, MinimumSignificantDigitsString, MINIMUM_SIGNIFICANTDIGITS_INDEX, minimumSignificantDigits) \ + V(JSTaggedValue, MaximumSignificantDigitsString, MAXIMUM_SIGNIFICANTDIGITS_INDEX, maximumSignificantDigits) \ + V(JSTaggedValue, InvalidDateString, INVALID_DATE_INDEX, InvalidDate) \ + V(JSTaggedValue, UsageString, USAGE_INDEX, usage) \ + V(JSTaggedValue, CompareString, COMPARE_INDEX, compare) \ + V(JSTaggedValue, SensitivityString, SENSITIVITY_INDEX, sensitivity) \ + V(JSTaggedValue, IgnorePunctuationString, IGNORE_PUNCTUATION_INDEX, ignorePunctuation) \ + V(JSTaggedValue, CardinalString, CARDINAL_INDEX, cardinal) \ + V(JSTaggedValue, OrdinalString, ORDINAL_INDEX, ordinal) \ + V(JSTaggedValue, PluralCategoriesString, PLURAL_CATEGORIES_INDEX, pluralCategories) \ + V(JSTaggedValue, SortString, SORT_INDEX, sort) \ + V(JSTaggedValue, SearchString, SEARCH_INDEX, search) \ + V(JSTaggedValue, BaseString, BASE_INDEX, base) \ + V(JSTaggedValue, AccentString, ACCENT_INDEX, accent) \ + V(JSTaggedValue, CaseString, CASE_INDEX, Case) \ + V(JSTaggedValue, VariantString, VARIANT_INDEX, variant) \ + V(JSTaggedValue, EnUsPosixString, EN_US_POSIX_STRING_INDEX, en - US - POSIX) \ + V(JSTaggedValue, UpperString, UPPER_INDEX, upper) \ + V(JSTaggedValue, LowerString, LOWER_INDEX, lower) \ + V(JSTaggedValue, DefaultString, DEFAULT_INDEX, Default) \ + V(JSTaggedValue, SharedString, SHARED_INDEX, shared) \ + V(JSTaggedValue, StartRangeString, START_RANGE_INDEX, startRange) \ + V(JSTaggedValue, EndRangeString, END_RANGE_INDEX, endRange) \ + V(JSTaggedValue, Iso8601String, ISO8601_INDEX, iso8601) \ + V(JSTaggedValue, GregoryString, GREGORY_INDEX, gregory) \ + V(JSTaggedValue, EthioaaString, ETHIOAA_INDEX, ethioaa) \ + V(JSTaggedValue, ValuesString, VALUES_INDEX, values) \ + /* for regexp. */ \ + V(JSTaggedValue, ExecString, EXEC_INDEX, exec) \ + V(JSTaggedValue, LastIndexString, LAST_INDEX_INDEX, lastIndex) \ + V(JSTaggedValue, StickyString, STICKY_INDEX, sticky) \ + V(JSTaggedValue, UString, U_INDEX, u) \ + V(JSTaggedValue, IndexString, INDEX_INDEX, index) \ + V(JSTaggedValue, InputString, INPUT_INDEX, input) \ + V(JSTaggedValue, UnicodeString, UNICODE_INDEX, unicode) \ + V(JSTaggedValue, ZeroString, ZERO_INDEX, zero) + +// 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 */ + +// ConstantIndex used for explicit visit each constant. +enum class ConstantIndex : uint16_t { +// NOLINTNEXTLINE(cppcoreguidelines-macro-usage) +#define INDEX_FILTER(Type, Name, Index, Desc) Index, + GLOBAL_ENV_CONSTANT_CLASS(INDEX_FILTER) GLOBAL_ENV_CONSTANT_SPECIAL(INDEX_FILTER) + GLOBAL_ENV_CONSTANT_CONSTANT(INDEX_FILTER) GLOBAL_ENV_CONSTANT_ACCESSOR(INDEX_FILTER) + +#undef INDEX_FILTER + CONSTATNT_COUNT, + + CONSTATNT_BEGIN = 0, + CONSTATNT_END = CONSTATNT_COUNT, + + READ_ONLY_CONSTATNT_BEGIN = CONSTATNT_BEGIN, + READ_ONLY_CONSTATNT_END = CONSTATNT_END, + // ... +}; +// clang-format on + +class GlobalEnvConstants { +public: + explicit GlobalEnvConstants() = default; + + DEFAULT_MOVE_SEMANTIC(GlobalEnvConstants); + DEFAULT_COPY_SEMANTIC(GlobalEnvConstants); + + ~GlobalEnvConstants() = default; + + const JSTaggedValue *BeginSlot() const; + + const JSTaggedValue *EndSlot() const; + + void InitRootsClass(JSThread *thread, JSHClass *dynClassClass); + + void InitGlobalConstant(JSThread *thread); + + void InitGlobalUndefined(); + + void SetConstant(ConstantIndex index, JSTaggedValue value); + + uintptr_t GetGlobalConstantAddr(ConstantIndex index) const; + +// NOLINTNEXTLINE(cppcoreguidelines-macro-usage) +#define DECL_GET(Type, Name, Index, Desc) \ + const Type Get##Name() const; \ + const JSHandle GetHandled##Name() const; + GLOBAL_ENV_CONSTANT_CLASS(DECL_GET) + GLOBAL_ENV_CONSTANT_SPECIAL(DECL_GET) + GLOBAL_ENV_CONSTANT_CONSTANT(DECL_GET) + GLOBAL_ENV_CONSTANT_ACCESSOR(DECL_GET) +#undef DECL_GET + + void VisitRangeSlot(const RootRangeVisitor &visitor) + { + visitor(ecmascript::Root::ROOT_VM, ObjectSlot(ToUintPtr(BeginSlot())), ObjectSlot(ToUintPtr(EndSlot()))); + } + +private: + JSTaggedValue constants_[static_cast(ConstantIndex::CONSTATNT_COUNT)]; // NOLINT(modernize-avoid-c-arrays) +}; +} // namespace panda::ecmascript +#endif // RUNTIME_ECMASCRIPT_ECMA_ROOTS_H diff --git a/runtime/global_handle_collection.h b/runtime/global_handle_collection.h new file mode 100644 index 000000000..d7557b133 --- /dev/null +++ b/runtime/global_handle_collection.h @@ -0,0 +1,51 @@ +/* + * Copyright (c) 2021-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_GLOBAL_HANDLE_COLLECTION_H +#define ECMASCRIPT_GLOBAL_HANDLE_COLLECTION_H + +#include "plugins/ecmascript/runtime/js_thread.h" + +namespace panda::ecmascript { +template +class JSHandle; +class GlobalHandleCollection { +public: + explicit GlobalHandleCollection(JSThread *thread) : thread_(thread) {} + + ~GlobalHandleCollection() = default; + + DEFAULT_MOVE_SEMANTIC(GlobalHandleCollection); + DEFAULT_COPY_SEMANTIC(GlobalHandleCollection); + + template + JSHandle NewHandle(JSTaggedType value) + { + uintptr_t addr = thread_->GetEcmaGlobalStorage()->NewGlobalHandle(value); + return JSHandle(addr); + } + + template + void Dispose(JSHandle handle) + { + thread_->GetEcmaGlobalStorage()->DisposeGlobalHandle(handle.GetAddress()); + } + +private: + JSThread *thread_{nullptr}; +}; +} // namespace panda::ecmascript + +#endif // ECMASCRIPT_GLOBAL_HANDLE_COLLECTION_H diff --git a/runtime/hprof/heap_profiler.cpp b/runtime/hprof/heap_profiler.cpp new file mode 100644 index 000000000..0f3178b20 --- /dev/null +++ b/runtime/hprof/heap_profiler.cpp @@ -0,0 +1,209 @@ +/* + * Copyright (c) 2021-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 "plugins/ecmascript/runtime/hprof/heap_profiler.h" + +#include +#include +#include + +#include "plugins/ecmascript/runtime/ecma_vm.h" +#include "plugins/ecmascript/runtime/hprof/heap_snapshot.h" +#include "plugins/ecmascript/runtime/js_thread.h" +#include "plugins/ecmascript/runtime/mem/assert_scope-inl.h" +#include "plugins/ecmascript/runtime/mem/concurrent_sweeper.h" +#include "plugins/ecmascript/runtime/mem/heap-inl.h" + +namespace panda::ecmascript { +HeapProfiler::~HeapProfiler() +{ + ClearSnapShot(); + const_cast(heap_->GetRegionFactory())->Delete(jsonSerializer_); + jsonSerializer_ = nullptr; +} + +bool HeapProfiler::DumpHeapSnapShot(JSThread *thread, DumpFormat dumpFormat, const std::string &filePath, bool isVmMode) +{ + [[maybe_unused]] bool heapClean = ForceFullGC(thread); + ASSERT(heapClean); + size_t heapSize = thread->GetEcmaVM()->GetHeap()->GetHeapObjectSize(); + LOG(ERROR, RUNTIME) << "HeapProfiler DumpSnapshot heap size " << heapSize; + HeapSnapShot *snapShot = MakeHeapSnapShot(thread, SampleType::ONE_SHOT, isVmMode); + ASSERT(snapShot != nullptr); + std::pair realPath = FilePathValid(filePath); + if (realPath.first) { + return jsonSerializer_->Serialize(snapShot, realPath.second); + } + + std::pair realGenPath = FilePathValid(GenDumpFileName(dumpFormat)); + if (realGenPath.first) { + return jsonSerializer_->Serialize(snapShot, realGenPath.second); + } + UNREACHABLE(); +} + +// NOLINTNEXTLINE(google-default-arguments) +bool HeapProfiler::StartHeapTracking(JSThread *thread, double timeInterval, bool isVmMode) +{ + HeapSnapShot *snapShot = MakeHeapSnapShot(thread, SampleType::REAL_TIME, isVmMode); + if (snapShot == nullptr) { + return false; + } + heapTracker_ = std::make_unique(snapShot, timeInterval); + thread->GetEcmaVM()->StartHeapTracking(heapTracker_.get()); + heapTracker_->StartTracing(); + return true; +} + +bool HeapProfiler::StopHeapTracking(JSThread *thread, [[maybe_unused]] DumpFormat dumpFormat, const std::string &path) +{ + if (heapTracker_ == nullptr) { + return false; + } + thread->GetEcmaVM()->StopHeapTracking(); + heapTracker_->StopTracing(); + + // check path + if (path.empty()) { + return false; + } + std::pair realPath = FilePathValid(path); + if (!realPath.first) { + return false; + } + + HeapSnapShot *snapShot = hprofs_.at(0); + if (snapShot == nullptr) { + return false; + } + snapShot->FinishSnapShot(); + return jsonSerializer_->Serialize(snapShot, realPath.second); +} + +std::pair HeapProfiler::FilePathValid(const std::string &filePath) +{ + if (filePath.size() > PATH_MAX) { + return std::make_pair(false, ""); + } + CVector resolvedPath(PATH_MAX); + auto result = realpath(filePath.c_str(), resolvedPath.data()); + if (result == resolvedPath.data() || errno == ENOENT) { + return std::make_pair(true, CString(resolvedPath.data())); + } + return std::make_pair(false, ""); +} + +std::string HeapProfiler::GenDumpFileName(DumpFormat dumpFormat) +{ + CString filename("hprof_"); + switch (dumpFormat) { + case DumpFormat::JSON: + filename.append(GetTimeStamp()); + break; + case DumpFormat::BINARY: + filename.append("unimplemented"); + break; + case DumpFormat::OTHER: + filename.append("unimplemented"); + break; + default: + filename.append("unimplemented"); + break; + } + filename.append(".heapsnapshot"); + return CstringConvertToString(filename); +} + +CString HeapProfiler::GetTimeStamp() +{ + std::time_t timeSource = std::time(nullptr); + tm *timeData = localtime(&timeSource); + CString stamp; + const int TIME_START = 1900; + stamp.append(ToCString(timeData->tm_year + TIME_START)) + .append("-") + .append(ToCString(timeData->tm_mon + 1)) + .append("-") + .append(ToCString(timeData->tm_mday)) + .append("_") + .append(ToCString(timeData->tm_hour)) + .append("-") + .append(ToCString(timeData->tm_min)) + .append("-") + .append(ToCString(timeData->tm_sec)); + return stamp; +} + +bool HeapProfiler::ForceFullGC(JSThread *thread) +{ + auto vm = thread->GetEcmaVM(); + if (vm->IsInitialized()) { + const_cast(vm->GetHeap())->CollectGarbage(TriggerGCType::SEMI_GC); + const_cast(vm->GetHeap())->CollectGarbage(TriggerGCType::OLD_GC); + return true; + } + return false; +} + +HeapSnapShot *HeapProfiler::MakeHeapSnapShot(JSThread *thread, SampleType sampleType, bool isVmMode) +{ + LOG(ERROR, RUNTIME) << "HeapProfiler::MakeHeapSnapShot"; + DISALLOW_GARBAGE_COLLECTION; + heap_->GetSweeper()->EnsureAllTaskFinished(); + switch (sampleType) { + case SampleType::ONE_SHOT: { + auto *snapShot = + const_cast(heap_->GetRegionFactory())->New(thread, heap_, isVmMode); + if (snapShot == nullptr) { + LOG_ECMA(FATAL) << "alloc snapshot failed"; + UNREACHABLE(); + } + snapShot->BuildUp(thread); + AddSnapShot(snapShot); + return snapShot; + } + case SampleType::REAL_TIME: { + auto *snapShot = + const_cast(heap_->GetRegionFactory())->New(thread, heap_, isVmMode); + if (snapShot == nullptr) { + LOG_ECMA(FATAL) << "alloc snapshot failed"; + UNREACHABLE(); + } + AddSnapShot(snapShot); + snapShot->PrepareSnapShot(); + return snapShot; + } + default: + return nullptr; + } +} + +void HeapProfiler::AddSnapShot(HeapSnapShot *snapshot) +{ + if (hprofs_.size() >= MAX_NUM_HPROF) { + ClearSnapShot(); + } + ASSERT(snapshot != nullptr); + hprofs_.emplace_back(snapshot); +} + +void HeapProfiler::ClearSnapShot() +{ + for (auto *snapshot : hprofs_) { + const_cast(heap_->GetRegionFactory())->Delete(snapshot); + } + hprofs_.clear(); +} +} // namespace panda::ecmascript diff --git a/runtime/hprof/heap_profiler.h b/runtime/hprof/heap_profiler.h new file mode 100644 index 000000000..0efef6266 --- /dev/null +++ b/runtime/hprof/heap_profiler.h @@ -0,0 +1,76 @@ +/* + * Copyright (c) 2021-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_HPROF_HEAP_PROFILER_H +#define ECMASCRIPT_HPROF_HEAP_PROFILER_H + +#include "plugins/ecmascript/runtime/ecma_macros.h" +#include "plugins/ecmascript/runtime/hprof/heap_profiler_interface.h" +#include "plugins/ecmascript/runtime/hprof/heap_snapshot_json_serializer.h" +#include "plugins/ecmascript/runtime/hprof/heap_tracker.h" +#include "plugins/ecmascript/runtime/mem/c_containers.h" +#include "plugins/ecmascript/runtime/mem/heap.h" +#include "os/mem.h" + +namespace panda::ecmascript { +class HeapSnapShot; +class HeapProfiler : public HeapProfilerInterface { +public: + NO_MOVE_SEMANTIC(HeapProfiler); + NO_COPY_SEMANTIC(HeapProfiler); + explicit HeapProfiler(const Heap *heap) : heap_(heap) + { + jsonSerializer_ = const_cast(heap->GetRegionFactory())->New(); + if (UNLIKELY(jsonSerializer_ == nullptr)) { + LOG_ECMA(FATAL) << "alloc snapshot json serializer failed"; + UNREACHABLE(); + } + } + ~HeapProfiler() override; + + enum class SampleType { ONE_SHOT, REAL_TIME }; + /** + * dump the specific snapshot in target format + */ + bool DumpHeapSnapShot(JSThread *thread, DumpFormat dumpFormat, const std::string &path, bool isVmMode = true); + void AddSnapShot(HeapSnapShot *snapshot); + + // NOLINTNEXTLINE(google-default-arguments) + bool StartHeapTracking(JSThread *thread, double timeInterval, bool isVmMode = true) override; + bool StopHeapTracking(JSThread *thread, DumpFormat dumpFormat, const std::string &filePath) override; + +private: + /** + * tigger full gc to make sure no unreachable objects in heap + */ + bool ForceFullGC(JSThread *thread); + + /** + * make a new heap snapshot and put it into a container eg, vector + */ + HeapSnapShot *MakeHeapSnapShot(JSThread *thread, SampleType sampleType, bool isVmMode = true); + std::pair FilePathValid(const std::string &filePath); + std::string GenDumpFileName(DumpFormat dumpFormat); + CString GetTimeStamp(); + void ClearSnapShot(); + + const size_t MAX_NUM_HPROF = 5; // ~10MB + CVector hprofs_; + HeapSnapShotJSONSerializer *jsonSerializer_{nullptr}; + std::unique_ptr heapTracker_; + const Heap *heap_; +}; +} // namespace panda::ecmascript +#endif // ECMASCRIPT_HPROF_HEAP_PROFILER_H diff --git a/runtime/hprof/heap_profiler_interface.cpp b/runtime/hprof/heap_profiler_interface.cpp new file mode 100644 index 000000000..24011bc90 --- /dev/null +++ b/runtime/hprof/heap_profiler_interface.cpp @@ -0,0 +1,57 @@ +/* + * Copyright (c) 2021-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 "plugins/ecmascript/runtime/hprof/heap_profiler.h" +#include "plugins/ecmascript/runtime/js_thread.h" +#include "plugins/ecmascript/runtime/mem/heap.h" + +namespace panda::ecmascript { +HeapProfilerInterface *HeapProfilerInterface::heapProfile_ = nullptr; +HeapProfilerInterface *HeapProfilerInterface::GetInstance(JSThread *thread) +{ + if (HeapProfilerInterface::heapProfile_ == nullptr) { + heapProfile_ = HeapProfilerInterface::CreateHeapProfiler(thread); + } + return HeapProfilerInterface::heapProfile_; +} + +void HeapProfilerInterface::DumpHeapSnapShot(JSThread *thread, DumpFormat dumpFormat, + const std::string &filePath, bool isVmMode) +{ + LOG(ERROR, RUNTIME) << "HeapProfilerInterface::DumpHeapSnapshot"; + const Heap *heap = thread->GetEcmaVM()->GetHeap(); + auto *hprof = const_cast(heap->GetRegionFactory())->New(heap); + if (UNLIKELY(hprof == nullptr)) { + LOG_ECMA(FATAL) << "alloc hprof failed"; + UNREACHABLE(); + } + hprof->DumpHeapSnapShot(thread, dumpFormat, filePath, isVmMode); + const_cast(heap->GetRegionFactory())->Delete(hprof); +} + +HeapProfilerInterface *HeapProfilerInterface::CreateHeapProfiler(JSThread *thread) +{ + const Heap *heap = thread->GetEcmaVM()->GetHeap(); + auto *hprof = const_cast(heap->GetRegionFactory())->New(heap); + ASSERT(hprof != nullptr); + return hprof; +} + +void HeapProfilerInterface::Destroy(JSThread *thread, HeapProfilerInterface *heapProfiler) +{ + const Heap *heap = thread->GetEcmaVM()->GetHeap(); + const_cast(heap->GetRegionFactory())->Delete(heapProfiler); +} +} // namespace panda::ecmascript diff --git a/runtime/hprof/heap_profiler_interface.h b/runtime/hprof/heap_profiler_interface.h new file mode 100644 index 000000000..08436af57 --- /dev/null +++ b/runtime/hprof/heap_profiler_interface.h @@ -0,0 +1,47 @@ +/* + * Copyright (c) 2021-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_HPROF_HEAP_PROFILER_INTERFACE_H +#define ECMASCRIPT_HPROF_HEAP_PROFILER_INTERFACE_H + +#include "plugins/ecmascript/runtime/mem/c_string.h" +#include "plugins/ecmascript/runtime/js_thread.h" + +namespace panda::ecmascript { +enum class DumpFormat { JSON, BINARY, OTHER }; + +class HeapProfilerInterface { +public: + static HeapProfilerInterface *GetInstance(JSThread *thread); + static void DumpHeapSnapShot(JSThread *thread, DumpFormat dumpFormat, + const std::string &filePath, bool isVmMode = true); + + static HeapProfilerInterface *CreateHeapProfiler(JSThread *thread); + static void Destroy(JSThread *thread, HeapProfilerInterface *heapProfiler); + + HeapProfilerInterface() = default; + virtual ~HeapProfilerInterface() = default; + + // NOLINTNEXTLINE(google-default-arguments) + virtual bool StartHeapTracking(JSThread *thread, double timeInterval, bool isVmMode = true) = 0; + virtual bool StopHeapTracking(JSThread *thread, DumpFormat dumpFormat, const std::string &filePath) = 0; + + NO_MOVE_SEMANTIC(HeapProfilerInterface); + NO_COPY_SEMANTIC(HeapProfilerInterface); +private: + static HeapProfilerInterface *heapProfile_; +}; +} // namespace panda::ecmascript +#endif // ECMASCRIPT_HPROF_HEAP_PROFILER_INTERFACE_H diff --git a/runtime/hprof/heap_root_visitor.cpp b/runtime/hprof/heap_root_visitor.cpp new file mode 100644 index 000000000..16de662d7 --- /dev/null +++ b/runtime/hprof/heap_root_visitor.cpp @@ -0,0 +1,33 @@ +/* + * Copyright (c) 2021-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 "plugins/ecmascript/runtime/hprof/heap_root_visitor.h" +#include "plugins/ecmascript/runtime/ecma_vm.h" +#include "plugins/ecmascript/runtime/js_thread.h" + +namespace panda::ecmascript { +void HeapRootVisitor::VisitHeapRoots(JSThread *thread, const RootVisitor &visitor, + const RootRangeVisitor &range_visitor) +{ + auto ecma_vm = GetVMInstance(thread); + ecma_vm->Iterate(visitor); + thread->Iterate(visitor, range_visitor); +} + +EcmaVM *HeapRootVisitor::GetVMInstance(JSThread *thread) const +{ + return thread->GetEcmaVM(); +} +} // namespace panda::ecmascript diff --git a/runtime/hprof/heap_root_visitor.h b/runtime/hprof/heap_root_visitor.h new file mode 100644 index 000000000..0f1f44b33 --- /dev/null +++ b/runtime/hprof/heap_root_visitor.h @@ -0,0 +1,36 @@ +/* + * Copyright (c) 2021-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_HPROF_HEAP_ROOT_VISITOR_H +#define ECMASCRIPT_HPROF_HEAP_ROOT_VISITOR_H + +#include "plugins/ecmascript/runtime/mem/object_xray.h" + +namespace panda::ecmascript { +class JSThread; + +class HeapRootVisitor { +public: + HeapRootVisitor() = default; + ~HeapRootVisitor() = default; + NO_MOVE_SEMANTIC(HeapRootVisitor); + NO_COPY_SEMANTIC(HeapRootVisitor); + void VisitHeapRoots(JSThread *thread, const RootVisitor &visitor, const RootRangeVisitor &range_visitor); + +private: + EcmaVM *GetVMInstance(JSThread *thread) const; +}; +} // namespace panda::ecmascript +#endif // ECMASCRIPT_HPROF_HEAP_ROOT_VISITOR_H diff --git a/runtime/hprof/heap_snapshot.cpp b/runtime/hprof/heap_snapshot.cpp new file mode 100644 index 000000000..74bf6b08e --- /dev/null +++ b/runtime/hprof/heap_snapshot.cpp @@ -0,0 +1,737 @@ +/* + * Copyright (c) 2021-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 "plugins/ecmascript/runtime/hprof/heap_snapshot.h" + +#include + +#include "plugins/ecmascript/runtime/ecma_string-inl.h" +#include "plugins/ecmascript/runtime/global_dictionary.h" +#include "plugins/ecmascript/runtime/global_env.h" +#include "plugins/ecmascript/runtime/hprof/heap_root_visitor.h" +#include "plugins/ecmascript/runtime/ic/property_box.h" +#include "plugins/ecmascript/runtime/js_array.h" +#include "plugins/ecmascript/runtime/js_handle.h" +#include "plugins/ecmascript/runtime/js_hclass-inl.h" +#include "plugins/ecmascript/runtime/js_object-inl.h" +#include "plugins/ecmascript/runtime/js_symbol.h" +#include "plugins/ecmascript/runtime/js_tagged_value-inl.h" +#include "plugins/ecmascript/runtime/js_thread.h" +#include "plugins/ecmascript/runtime/mem/assert_scope-inl.h" +#include "plugins/ecmascript/runtime/property_attributes.h" +#include "plugins/ecmascript/runtime/tagged_array.h" +#include "plugins/ecmascript/runtime/tagged_dictionary.h" + +namespace panda::ecmascript { +CString *HeapSnapShot::GetString(const CString &as) +{ + return stringTable_.GetString(as); +} + +Node *Node::NewNode(const Heap *heap, size_t id, size_t index, CString *name, NodeType type, size_t size, + TaggedObject *entry, bool isLive) +{ + auto node = const_cast(heap->GetRegionFactory()) + ->New(id, index, name, type, size, 0, NewAddress(entry), isLive); + if (UNLIKELY(node == nullptr)) { + LOG_ECMA(FATAL) << "internal allocator failed"; + UNREACHABLE(); + } + return node; +} + +Edge *Edge::NewEdge(const Heap *heap, uint64_t id, EdgeType type, Node *from, Node *to, CString *name) +{ + auto edge = const_cast(heap->GetRegionFactory())->New(id, type, from, to, name); + if (UNLIKELY(edge == nullptr)) { + LOG_ECMA(FATAL) << "internal allocator failed"; + UNREACHABLE(); + } + return edge; +} + +HeapSnapShot::~HeapSnapShot() +{ + const Heap *heap = thread_->GetEcmaVM()->GetHeap(); + for (Node *node : nodes_) { + const_cast(heap->GetRegionFactory())->Delete(node); + } + for (Edge *edge : edges_) { + const_cast(heap->GetRegionFactory())->Delete(edge); + } + nodes_.clear(); + edges_.clear(); +} + +bool HeapSnapShot::BuildUp(JSThread *thread) +{ + FillNodes(thread); + FillEdges(thread); + AddSyntheticRoot(thread); + return Verify(); +} + +bool HeapSnapShot::Verify() +{ + GetString(CString("HeapVerify:").append(ToCString(totalNodesSize_))); + return (edgeCount_ > nodeCount_) && (totalNodesSize_ > 0); +} + +void HeapSnapShot::PrepareSnapShot() +{ + FillNodes(thread_); +} + +void HeapSnapShot::UpdateNode() +{ + for (Node *node : nodes_) { + node->SetLive(false); + } + FillNodes(thread_); + + for (auto iter = nodes_.begin(); iter != nodes_.end();) { + if (!(*iter)->IsLive()) { + iter = nodes_.erase(iter); + } else { + iter++; + } + } +} + +bool HeapSnapShot::FinishSnapShot() +{ + UpdateNode(); + FillEdges(thread_); + AddSyntheticRoot(thread_); + return Verify(); +} + +void HeapSnapShot::RecordSampleTime() +{ + timeStamps_.emplace_back(sequenceId_); +} + +void HeapSnapShot::AddNode(uintptr_t address) +{ + GenerateNode(thread_, JSTaggedValue(address)); +} + +void HeapSnapShot::MoveNode(uintptr_t address, uintptr_t forward_address) +{ + int sequenceId = -1; + Node *node = entryMap_.FindAndEraseNode(address); + if (node != nullptr) { + sequenceId = node->GetId(); + EraseNodeUnique(node); + } + GenerateNode(thread_, JSTaggedValue(forward_address), sequenceId); +} + +// NOLINTNEXTLINE(readability-function-size) +CString *HeapSnapShot::GenerateNodeName([[maybe_unused]] JSThread *thread, const JSHandle &entry) +{ + auto *hCls = entry->GetClass(); + JSType type = hCls->GetObjectType(); + switch (type) { + case JSType::TAGGED_ARRAY: { + CString arrayName; + TaggedArray *array = TaggedArray::Cast(entry.GetObject()); + arrayName = "TaggedArray["; + arrayName.append(ToCString(array->GetLength())); + arrayName.append("]"); + return GetString(arrayName); // String type was handled singly, see#GenerateStringNode + } + case JSType::HCLASS: + return GetString("HiddenClass"); + case JSType::TAGGED_DICTIONARY: { + CString dictName; + TaggedArray *dict = TaggedArray::Cast(entry.GetObject()); + dictName = "TaggedDict["; + dictName.append(ToCString(dict->GetLength())); + dictName.append("]"); + return GetString(dictName); + } + case JSType::STRING: + return GetString("BaseString"); + case JSType::JS_OBJECT: { + CString objName = CString("JSOBJECT(Ctor="); // Ctor-name + return GetString(objName); + } + case JSType::FREE_OBJECT_WITH_ONE_FIELD: + case JSType::FREE_OBJECT_WITH_NONE_FIELD: + case JSType::FREE_OBJECT_WITH_TWO_FIELD: + case JSType::JS_NATIVE_POINTER: { + break; + } + case JSType::JS_FUNCTION_BASE: + return GetString("JSFunctionBase"); + case JSType::JS_FUNCTION: + return GetString("JSFunction"); + case JSType::JS_ERROR: + return GetString("Error"); + case JSType::JS_EVAL_ERROR: + return GetString("Eval Error"); + case JSType::JS_RANGE_ERROR: + return GetString("Range Error"); + case JSType::JS_TYPE_ERROR: + return GetString("Type Error"); + case JSType::JS_REFERENCE_ERROR: + return GetString("Reference Error"); + case JSType::JS_URI_ERROR: + return GetString("Uri Error"); + case JSType::JS_SYNTAX_ERROR: + return GetString("Syntax Error"); + case JSType::JS_REG_EXP: + return GetString("Regexp"); + case JSType::JS_SET: + return GetString("Set"); + case JSType::JS_MAP: + return GetString("Map"); + case JSType::JS_WEAK_SET: + return GetString("WeakSet"); + case JSType::JS_WEAK_MAP: + return GetString("WeakMap"); + case JSType::JS_DATE: + return GetString("Date"); + case JSType::JS_BOUND_FUNCTION: + return GetString("Bound Function"); + case JSType::JS_ARRAY: { + JSArray *jsArray = JSArray::Cast(entry.GetObject()); + CString jsArrayName("JSArray["); + jsArrayName.append(ToCString(jsArray->GetLength().GetInt())); + jsArrayName.append("]"); + return GetString(jsArrayName); + } + case JSType::JS_TYPED_ARRAY: + return GetString("Typed Array"); + case JSType::JS_INT8_ARRAY: + return GetString("Int8 Array"); + case JSType::JS_UINT8_ARRAY: + return GetString("Uint8 Array"); + case JSType::JS_UINT8_CLAMPED_ARRAY: + return GetString("Uint8 Clamped Array"); + case JSType::JS_INT16_ARRAY: + return GetString("Int16 Array"); + case JSType::JS_UINT16_ARRAY: + return GetString("Uint16 Array"); + case JSType::JS_INT32_ARRAY: + return GetString("Int32 Array"); + case JSType::JS_UINT32_ARRAY: + return GetString("Uint32 Array"); + case JSType::JS_FLOAT32_ARRAY: + return GetString("Float32 Array"); + case JSType::JS_FLOAT64_ARRAY: + return GetString("Float64 Array"); + case JSType::JS_ARGUMENTS: + return GetString("Arguments"); + case JSType::JS_PROXY: + return GetString("Proxy"); + case JSType::JS_PRIMITIVE_REF: + return GetString("Primitive"); + case JSType::JS_DATA_VIEW: + return GetString("DataView"); + case JSType::JS_ITERATOR: + return GetString("Iterator"); + case JSType::JS_FORIN_ITERATOR: + return GetString("ForinInterator"); + case JSType::JS_MAP_ITERATOR: + return GetString("MapIterator"); + case JSType::JS_SET_ITERATOR: + return GetString("SetIterator"); + case JSType::JS_ARRAY_ITERATOR: + return GetString("ArrayIterator"); + case JSType::JS_STRING_ITERATOR: + return GetString("StringIterator"); + case JSType::JS_ARRAY_BUFFER: + return GetString("ArrayBuffer"); + case JSType::JS_PROXY_REVOC_FUNCTION: + return GetString("ProxyRevocFunction"); + case JSType::PROMISE_REACTIONS: + return GetString("PromiseReaction"); + case JSType::PROMISE_CAPABILITY: + return GetString("PromiseCapability"); + case JSType::PROMISE_ITERATOR_RECORD: + return GetString("PromiseIteratorRecord"); + case JSType::PROMISE_RECORD: + return GetString("PromiseRecord"); + case JSType::RESOLVING_FUNCTIONS_RECORD: + return GetString("ResolvingFunctionsRecord"); + case JSType::JS_PROMISE: + return GetString("Promise"); + case JSType::JS_PROMISE_REACTIONS_FUNCTION: + return GetString("PromiseReactionsFunction"); + case JSType::JS_PROMISE_EXECUTOR_FUNCTION: + return GetString("PromiseExecutorFunction"); + case JSType::JS_PROMISE_ALL_RESOLVE_ELEMENT_FUNCTION: + return GetString("PromiseAllResolveElementFunction"); + case JSType::JS_GENERATOR_FUNCTION: + return GetString("JSGeneratorFunction"); + case JSType::SYMBOL: + return GetString("Symbol"); + case JSType::JS_ASYNC_FUNCTION: + return GetString("AsyncFunction"); + case JSType::JS_INTL_BOUND_FUNCTION: + return GetString("JSIntlBoundFunction"); + case JSType::JS_ASYNC_AWAIT_STATUS_FUNCTION: + return GetString("AsyncAwaitStatusFunction"); + case JSType::JS_ASYNC_FUNC_OBJECT: + return GetString("AsyncFunctionObject"); + case JSType::JS_REALM: + return GetString("Realm"); + case JSType::JS_GLOBAL_OBJECT: + return GetString("GlobalObject"); + case JSType::JS_INTL: + return GetString("JSIntl"); + case JSType::JS_LOCALE: + return GetString("JSLocale"); + case JSType::JS_DATE_TIME_FORMAT: + return GetString("JSDateTimeFormat"); + case JSType::JS_RELATIVE_TIME_FORMAT: + return GetString("JSRelativeTimeFormat"); + case JSType::JS_NUMBER_FORMAT: + return GetString("JSNumberFormat"); + case JSType::JS_COLLATOR: + return GetString("JSCollator"); + case JSType::JS_PLURAL_RULES: + return GetString("JSPluralRules"); + case JSType::JS_GENERATOR_OBJECT: + return GetString("JSGeneratorObject"); + case JSType::JS_GENERATOR_CONTEXT: + return GetString("JSGeneratorContext"); + case JSType::ACCESSOR_DATA: + return GetString("AccessorData"); + case JSType::INTERNAL_ACCESSOR: + return GetString("InternalAccessor"); + case JSType::FUNCTION_EXTRA_INFO: + return GetString("FunctionExtraInfo"); + case JSType::MICRO_JOB_QUEUE: + return GetString("MicroJobQueue"); + case JSType::PENDING_JOB: + return GetString("PendingJob"); + case JSType::COMPLETION_RECORD: + return GetString("CompletionRecord"); + case JSType::ECMA_MODULE: + return GetString("EcmaModule"); + case JSType::JS_ARRAY_LIST: + return GetString("ArrayList"); + default: + break; + } + if (IsInVmMode()) { + switch (type) { + case JSType::PROPERTY_BOX: + return GetString("PropertyBox"); + case JSType::GLOBAL_ENV: + return GetString("GlobalEnv"); + case JSType::PROTOTYPE_HANDLER: + return GetString("ProtoTypeHandler"); + case JSType::TRANSITION_HANDLER: + return GetString("TransitionHandler"); + case JSType::PROTO_CHANGE_MARKER: + return GetString("ProtoChangeMarker"); + case JSType::PROTOTYPE_INFO: + return GetString("ProtoChangeDetails"); + case JSType::TEMPLATE_MAP: + return GetString("TemplateMap"); + case JSType::PROGRAM: + return GetString("Program"); + case JSType::LEXICAL_FUNCTION: + return GetString("LexicalFunction"); + case JSType::MACHINE_CODE_OBJECT: + return GetString("MachineCode"); + default: + break; + } + } else { + return GetString("Hidden Object"); + } + return GetString("UnKnownType"); +} + +NodeType HeapSnapShot::GenerateNodeType(TaggedObject *entry) +{ + NodeType nodeType = NodeType::INVALID; + auto *hCls = entry->GetClass(); + if (hCls->IsTaggedArray()) { + nodeType = NodeType::JS_ARRAY; + } else if (hCls->IsHClass()) { + nodeType = NodeType::HCLASS; + } else { + nodeType = NodeType(hCls->GetObjectType()); + } + return nodeType; +} + +void HeapSnapShot::FillNodes(JSThread *thread) +{ + // Iterate Heap Object + auto heap = thread_->GetEcmaVM()->GetHeap(); + if (heap != nullptr) { + heap->IteratorOverObjects( + [this, &thread](TaggedObject *obj) { this->GenerateNode(thread, JSTaggedValue(obj)); }); + } +} + +Node *HeapSnapShot::GenerateNode(JSThread *thread, JSTaggedValue entry, int sequenceId) +{ + Node *node = nullptr; + if (sequenceId == -1) { + sequenceId = sequenceId_ + SEQ_STEP; + } + if (entry.IsHeapObject()) { + if (entry.IsWeak()) { + entry.RemoveWeakTag(); + } + if (entry.IsString()) { + node = GenerateStringNode(entry, sequenceId); + if (node == nullptr) { + LOG(DEBUG, RUNTIME) << "string node nullptr"; + } + } + JSHandle obj(thread, entry.GetTaggedObject()); + auto *baseClass = obj->GetClass(); + if (baseClass != nullptr) { + auto name = GenerateNodeName(thread, obj); + node = Node::NewNode(heap_, sequenceId, nodeCount_, name, GenerateNodeType(obj.GetObject()), + obj->GetClass()->SizeFromJSHClass(obj.GetObject()), + obj.GetObject()); + Node *existNode = entryMap_.FindOrInsertNode(node); // Fast Index + if (existNode == node) { + if (sequenceId == sequenceId_ + SEQ_STEP) { + sequenceId_ = sequenceId; // Odd Digit + } + InsertNodeUnique(node); + ASSERT(entryMap_.FindEntry(node->GetAddress())->GetAddress() == node->GetAddress()); + } else { + existNode->SetLive(true); + ASSERT(entryMap_.FindEntry(node->GetAddress())->GetAddress() == node->GetAddress()); + const_cast(heap_->GetRegionFactory())->Delete(node); + return nullptr; + } + } + } else { + CString primitiveName; + // A primitive value with tag will be regarded as a pointer + auto *obj = reinterpret_cast(entry.GetRawData()); + if (entry.IsInt()) { + primitiveName.append("Int:" + ToCString(entry.GetInt())); + } else if (entry.IsDouble()) { + primitiveName.append("Double:"); + } else if (entry.IsHole()) { + primitiveName.append("Hole"); + } else if (entry.IsNull()) { + primitiveName.append("Null"); + } else if (entry.IsTrue()) { + primitiveName.append("Boolean:true"); + } else if (entry.IsFalse()) { + primitiveName.append("Boolean:false"); + } else if (entry.IsException()) { + primitiveName.append("Exception"); + } else if (entry.IsUndefined()) { + primitiveName.append("Undefined"); + } else { + primitiveName.append("Illegal_Primitive"); + } + + node = + Node::NewNode(heap_, sequenceId, nodeCount_, GetString(primitiveName), NodeType::JS_PRIMITIVE_REF, 0, obj); + entryMap_.InsertEntry(node); // Fast Index + if (sequenceId == sequenceId_ + SEQ_STEP) { + sequenceId_ = sequenceId; // Odd Digit + } + InsertNodeUnique(node); + } + return node; +} + +Node *HeapSnapShot::GenerateStringNode(JSTaggedValue entry, int sequenceId) +{ + Node *node = nullptr; + auto originStr = static_cast(entry.GetTaggedObject()); + size_t selfsize = originStr->ObjectSize(); + CString strContent; + strContent.append(EntryVisitor::ConvertKey(entry)); + node = Node::NewNode(heap_, sequenceId, nodeCount_, GetString(strContent), NodeType::PRIM_STRING, selfsize, + entry.GetTaggedObject()); + Node *existNode = entryMap_.FindOrInsertNode(node); // Fast Index + if (existNode == node) { + if (sequenceId == sequenceId_ + SEQ_STEP) { + sequenceId_ = sequenceId; // Odd Digit + } + InsertNodeUnique(node); + } else { + existNode->SetLive(true); + } + ASSERT(entryMap_.FindEntry(node->GetAddress())->GetAddress() == node->GetAddress()); + if (existNode != node) { + const_cast(heap_->GetRegionFactory())->Delete(node); + return nullptr; + } + return node; +} + +void HeapSnapShot::FillEdges(JSThread *thread) +{ + size_t length = nodes_.size(); + auto iter = nodes_.begin(); + size_t count = 0; + while (++count < length) { + ASSERT(*iter != nullptr); + auto *objFrom = reinterpret_cast((*iter)->GetAddress()); + std::vector> nameResources; + JSTaggedValue(objFrom).DumpForSnapshot(thread, nameResources, isVmMode_); + JSTaggedValue objValue(objFrom); + for (auto const &it : nameResources) { + JSTaggedValue toValue = it.second; + Node *entryTo = nullptr; + if (toValue.IsHeapObject()) { + auto *to = reinterpret_cast(toValue.GetHeapObject()); + entryTo = entryMap_.FindEntry(Node::NewAddress(to)); + } + if (entryTo == nullptr) { + entryTo = GenerateNode(thread, toValue); + } + if (entryTo != nullptr) { + Edge *edge = Edge::NewEdge(heap_, edgeCount_, EdgeType::DEFAULT, *iter, entryTo, GetString(it.first)); + InsertEdgeUnique(edge); + (*iter)->IncEdgeCount(); // Update Node's edgeCount_ here + } + } + iter++; + } + // Fill Primitive Edge + size_t lengthExtend = nodes_.size(); + while (++count < lengthExtend) { + ASSERT(*iter != nullptr); + if ((*iter)->GetType() == NodeType::JS_PRIMITIVE_REF) { + JSTaggedValue jsFrom(reinterpret_cast((*iter)->GetAddress())); + CString valueName; + if (jsFrom.IsInt()) { + valueName.append(ToCString(jsFrom.GetInt())); + } else if (jsFrom.IsDouble()) { + valueName.append(FloatToCString(jsFrom.GetDouble())); + } else { + valueName.append("NaN"); + } + Edge *edge = Edge::NewEdge(heap_, edgeCount_, EdgeType::DEFAULT, (*iter), (*iter), GetString(valueName)); + InsertEdgeUnique(edge); + (*iter)->IncEdgeCount(); // Update Node's edgeCount_ here + } + iter++; + } +} + +void HeapSnapShot::BridgeAllReferences() +{ + // This Function is Unused + for (Edge *edge : edges_) { + auto *from = reinterpret_cast(edge->GetFrom()->GetAddress()); + auto *to = reinterpret_cast(edge->GetTo()->GetAddress()); + if (!JSTaggedValue(from).IsECMAObject()) { + continue; // named it by other way + } + edge->SetName(GenerateEdgeName(from, to)); + } +} + +CString *HeapSnapShot::GenerateEdgeName([[maybe_unused]] TaggedObject *from, [[maybe_unused]] TaggedObject *to) +{ + // This Function is Unused + ASSERT(from != nullptr && from != to); + return GetString("[]"); // unAnalysed +} + +Node *HeapSnapShot::InsertNodeUnique(Node *node) +{ + AccumulateNodeSize(node->GetSelfSize()); + nodes_.emplace_back(node); + nodeCount_++; + return node; +} + +void HeapSnapShot::EraseNodeUnique(Node *node) +{ + auto iter = std::find(nodes_.begin(), nodes_.end(), node); + if (iter != nodes_.end()) { + DecreaseNodeSize(node->GetSelfSize()); + nodes_.erase(iter); + nodeCount_--; + } +} + +Edge *HeapSnapShot::InsertEdgeUnique(Edge *edge) +{ + edges_.emplace_back(edge); + edgeCount_++; + return edge; +} + +void HeapSnapShot::AddSyntheticRoot(JSThread *thread) +{ + Node *syntheticRoot = + Node::NewNode(heap_, 1, nodeCount_, GetString("SyntheticRoot"), NodeType::SYNTHETIC, 0, nullptr); + InsertNodeAt(0, syntheticRoot); + + int edgeOffset = 0; +// NOLINTNEXTLINE(cppcoreguidelines-macro-usage) +#define ROOT_EDGE_BUILDER_CORE(type, slot) \ + JSTaggedValue value(slot.GetTaggedType()); \ + if (value.IsHeapObject()) { \ + TaggedObject *root = value.GetTaggedObject(); \ + Node *rootNode = entryMap_.FindEntry(Node::NewAddress(root)); \ + if (rootNode != nullptr) { \ + Edge *edge = \ + Edge::NewEdge(heap_, edgeCount_, EdgeType::SHORTCUT, syntheticRoot, rootNode, GetString("-subroot-")); \ + InsertEdgeAt(edgeOffset, edge); \ + edgeOffset++; \ + syntheticRoot->IncEdgeCount(); \ + } \ + } + + RootVisitor rootEdgeBuilder = [this, syntheticRoot, &edgeOffset]([[maybe_unused]] Root type, ObjectSlot slot) { + ROOT_EDGE_BUILDER_CORE(type, slot); + }; + + RootRangeVisitor rootRangeEdgeBuilder = [this, syntheticRoot, &edgeOffset]([[maybe_unused]] Root type, + ObjectSlot start, ObjectSlot end) { + for (ObjectSlot slot = start; slot < end; slot++) { + ROOT_EDGE_BUILDER_CORE(type, slot); + } + }; +#undef ROOT_EDGE_BUILDER_CORE + rootVisitor_.VisitHeapRoots(thread, rootEdgeBuilder, rootRangeEdgeBuilder); + + int reindex = 0; + for (Node *node : nodes_) { + node->SetIndex(reindex); + reindex++; + } +} + +Node *HeapSnapShot::InsertNodeAt(size_t pos, Node *node) +{ + ASSERT(node != nullptr); + auto iter = nodes_.begin(); + std::advance(iter, pos); + nodes_.insert(iter, node); + nodeCount_++; + return node; +} + +Edge *HeapSnapShot::InsertEdgeAt(size_t pos, Edge *edge) +{ + ASSERT(edge != nullptr); + edges_.insert(edges_.begin() + pos, edge); + edgeCount_++; + return edge; +} + +CString EntryVisitor::ConvertKey(JSTaggedValue key) +{ + ASSERT(key.GetTaggedObject() != nullptr); + EcmaString *keyString = EcmaString::Cast(key.GetTaggedObject()); + if (key.IsSymbol()) { + JSSymbol *symbol = JSSymbol::Cast(key.GetTaggedObject()); + keyString = EcmaString::Cast(symbol->GetDescription().GetTaggedObject()); + } + // convert, expensive but safe + int length = 0; + if (keyString->IsUtf8()) { + length = keyString->GetUtf8Length(); + std::vector buffer(length); + [[maybe_unused]] int size = keyString->CopyDataUtf8(buffer.data(), length); + ASSERT(size == length); + CString keyCopy(reinterpret_cast(buffer.data())); + return keyCopy; + } + length = keyString->GetLength(); + std::vector buffer(length); + [[maybe_unused]] int size = keyString->CopyDataUtf16(buffer.data(), length); + ASSERT(size == length); + CString keyCopy(reinterpret_cast(buffer.data())); + return keyCopy; +} + +Node *HeapEntryMap::FindOrInsertNode(Node *node) +{ + ASSERT(node != nullptr); + auto it = nodesMap_.find(node->GetAddress()); + if (it != nodesMap_.end()) { + return it->second; + } + InsertEntry(node); + return node; +} + +Node *HeapEntryMap::FindAndEraseNode(Address addr) +{ + auto it = nodesMap_.find(addr); + if (it != nodesMap_.end()) { + Node *node = it->second; + nodesMap_.erase(it); + nodeEntryCount_--; + return node; + } + return nullptr; +} + +Node *HeapEntryMap::FindEntry(Address addr) +{ + auto it = nodesMap_.find(addr); + return it != nodesMap_.end() ? it->second : nullptr; +} + +void HeapEntryMap::InsertEntry(Node *node) +{ + nodeEntryCount_++; + nodesMap_.insert(std::make_pair(node->GetAddress(), node)); +} + +FrontType NodeTypeConverter::Convert(NodeType type) +{ + FrontType fType = FrontType::DEFAULT; + if (type == NodeType::PROPERTY_BOX) { + fType = FrontType::HIDDEN; + } else if (type == NodeType::JS_ARRAY || type == NodeType::JS_TYPED_ARRAY) { + fType = FrontType::ARRAY; + } else if (type == NodeType::PRIM_STRING) { // STRING + fType = FrontType::STRING; + } else if (type == NodeType::JS_OBJECT) { + fType = FrontType::OBJECT; + } else if (type >= NodeType::JS_FUNCTION_BEGIN && type <= NodeType::JS_FUNCTION_END) { + fType = FrontType::CLOSURE; + } else if (type == NodeType::JS_BOUND_FUNCTION) { + fType = FrontType::CLOSURE; + } else if (type == NodeType::JS_FUNCTION_BASE) { + fType = FrontType::CLOSURE; + } else if (type == NodeType::JS_REG_EXP) { + fType = FrontType::REGEXP; + } else if (type == NodeType::SYMBOL) { + fType = FrontType::SYMBOL; + } else if (type == NodeType::JS_PRIMITIVE_REF) { + fType = FrontType::HEAPNUMBER; + } else if (type == NodeType::SYNTHETIC) { + fType = FrontType::SYNTHETIC; + } else { + fType = FrontType::DEFAULT; + // NATIVE, /* kNative */ + // CONSSTRING, /* kConsString */ + // SLICEDSTRING, /* kSlicedString */ + // SYMBOL, /* kSymbol */ + // BIGINT, /* kBigInt */ + } + return fType; +} +} // namespace panda::ecmascript diff --git a/runtime/hprof/heap_snapshot.h b/runtime/hprof/heap_snapshot.h new file mode 100644 index 000000000..f3992946b --- /dev/null +++ b/runtime/hprof/heap_snapshot.h @@ -0,0 +1,378 @@ +/* + * Copyright (c) 2021-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_HPROF_HEAP_SNAPSHOT_H +#define ECMASCRIPT_HPROF_HEAP_SNAPSHOT_H + +#include +#include +#include +#include + +#include "plugins/ecmascript/runtime/mem/c_containers.h" +#include "os/mem.h" +#include "plugins/ecmascript/runtime/hprof/heap_profiler.h" +#include "plugins/ecmascript/runtime/hprof/heap_root_visitor.h" +#include "plugins/ecmascript/runtime/hprof/string_hashmap.h" +#include "plugins/ecmascript/runtime/js_hclass.h" +#include "plugins/ecmascript/runtime/js_object.h" +#include "plugins/ecmascript/runtime/js_tagged_value.h" + +namespace panda::ecmascript { +// Define the Object Graphic +using Address = uintptr_t; + +enum class NodeType : uint8_t { + JSTYPE_DECL, + PRIM_STRING, /* Primitive String */ + PRIM_ARRAY, /* Primitive Array */ + SYNTHETIC /* For Synthetic Root */ +}; + +enum class EdgeType { CONTEXT, ELEMENT, PROPERTY, INTERNAL, HIDDEN, SHORTCUT, WEAK, DEFAULT = PROPERTY }; + +class Node { +public: + explicit Node(uint64_t id, uint64_t index, CString *name, NodeType type, size_t size, uint64_t traceId, + Address address, bool isLive = true) + : id_(id), + index_(index), + name_(name), + type_(type), + size_(size), + traceId_(traceId), + address_(address), + isLive_(isLive) + { + } + uint64_t GetId() const + { + return id_; + } + void SetIndex(uint64_t index) + { + index_ = index; + } + uint64_t GetIndex() const + { + return index_; + } + + const CString *GetName() const + { + return name_; + } + NodeType GetType() const + { + return type_; + } + size_t GetSelfSize() const + { + return size_; + } + size_t GetEdgeCount() const + { + return edgeCount_; + } + void IncEdgeCount() + { + edgeCount_++; + } + uint64_t GetStackTraceId() const + { + return traceId_; + } + Address GetAddress() const + { + return address_; + } + bool IsLive() const + { + return isLive_; + } + void SetLive(bool isLive) + { + isLive_ = isLive; + } + static Node *NewNode(const Heap *heap, size_t id, size_t index, CString *name, NodeType type, size_t size, + TaggedObject *entry, bool isLive = true); + template + static Address NewAddress(T *addr) + { + return reinterpret_cast
(addr); + } + static constexpr int NODE_FIELD_COUNT = 7; + ~Node() = default; + + DEFAULT_MOVE_SEMANTIC(Node); + DEFAULT_COPY_SEMANTIC(Node); + +private: + uint64_t id_ {0}; // Range from 1 + uint64_t index_ {0}; + CString *name_ {nullptr}; + NodeType type_ {NodeType::INVALID}; + size_t size_ {0}; + size_t edgeCount_ {0}; + uint64_t traceId_ {0}; + Address address_ {0x0}; + bool isLive_ {true}; +}; + +class Edge { +public: + explicit Edge(uint64_t id, EdgeType type, Node *from, Node *to, CString *name) + : id_(id), edgeType_(type), from_(from), to_(to), name_(name) + { + } + uint64_t GetId() const + { + return id_; + } + EdgeType GetType() const + { + return edgeType_; + } + const Node *GetFrom() const + { + return from_; + } + const Node *GetTo() const + { + return to_; + } + const CString *GetName() const + { + return name_; + } + void SetName(CString *name) + { + name_ = name; + } + void UpdateFrom(Node *node) + { + from_ = node; + } + void UpdateTo(Node *node) + { + to_ = node; + } + static Edge *NewEdge(const Heap *heap, uint64_t id, EdgeType type, Node *from, Node *to, CString *name); + static constexpr int EDGE_FIELD_COUNT = 3; + ~Edge() = default; + + DEFAULT_MOVE_SEMANTIC(Edge); + DEFAULT_COPY_SEMANTIC(Edge); + +private: + uint64_t id_ {-1ULL}; + EdgeType edgeType_ {EdgeType::DEFAULT}; + Node *from_ {nullptr}; + Node *to_ {nullptr}; + CString *name_ {nullptr}; +}; + +class TimeStamp { +public: + explicit TimeStamp(int sequenceId) : lastSequenceId_(sequenceId), timeStampUs_(TimeStamp::Now()) {} + ~TimeStamp() = default; + + DEFAULT_MOVE_SEMANTIC(TimeStamp); + DEFAULT_COPY_SEMANTIC(TimeStamp); + + int GetLastSequenceId() const + { + return lastSequenceId_; + } + + int64_t GetTimeStamp() const + { + return timeStampUs_; + } + +private: + static int64_t Now() + { + struct timeval tv = {0, 0}; + gettimeofday(&tv, nullptr); + const int THOUSAND = 1000; + return tv.tv_usec + tv.tv_sec * THOUSAND * THOUSAND; + } + + int lastSequenceId_ {0}; + int64_t timeStampUs_ {0}; +}; + +class HeapEntryMap { +public: + HeapEntryMap() = default; + ~HeapEntryMap() = default; + NO_MOVE_SEMANTIC(HeapEntryMap); + NO_COPY_SEMANTIC(HeapEntryMap); + Node *FindOrInsertNode(Node *node); + Node *FindAndEraseNode(Address addr); + Node *FindEntry(Address addr); + size_t GetCapcity() const + { + return nodesMap_.size(); + } + size_t GetEntryCount() const + { + return nodeEntryCount_; + } + void InsertEntry(Node *node); + +private: + size_t nodeEntryCount_ {0}; + CUnorderedMap nodesMap_ {}; +}; + +class HeapSnapShot { +public: + static constexpr int SEQ_STEP = 2; + NO_MOVE_SEMANTIC(HeapSnapShot); + NO_COPY_SEMANTIC(HeapSnapShot); + explicit HeapSnapShot(JSThread *thread, const Heap *heap, const bool isVmMode) + : stringTable_(heap), thread_(thread), heap_(heap), isVmMode_(isVmMode) + { + } + ~HeapSnapShot(); + bool BuildUp(JSThread *thread); + bool Verify(); + + void PrepareSnapShot(); + void UpdateNode(); + void AddNode(uintptr_t address); + void MoveNode(uintptr_t address, uintptr_t forward_address); + void RecordSampleTime(); + bool FinishSnapShot(); + + const CVector &GetTimeStamps() const + { + return timeStamps_; + } + + size_t GetNodeCount() const + { + return nodeCount_; + } + size_t GetEdgeCount() const + { + return edgeCount_; + } + size_t GetTotalNodeSize() const + { + return totalNodesSize_; + } + void AccumulateNodeSize(size_t size) + { + totalNodesSize_ += size; + } + void DecreaseNodeSize(size_t size) + { + totalNodesSize_ -= size; + } + CString *GenerateNodeName(JSThread *thread, const JSHandle &entry); + NodeType GenerateNodeType(TaggedObject *entry); + const CList *GetNodes() const + { + return &nodes_; + } + const CVector *GetEdges() const + { + return &edges_; + } + const StringHashMap *GetEcmaStringTable() const + { + return &stringTable_; + } + + CString *GetString(const CString &as); + + bool IsInVmMode() const + { + return isVmMode_; + } + +private: + void FillNodes(JSThread *thread); + Node *GenerateNode(JSThread *thread, JSTaggedValue entry, int sequenceId = -1); + Node *GenerateStringNode(JSTaggedValue entry, int sequenceId); + void FillEdges(JSThread *thread); + void BridgeAllReferences(); + CString *GenerateEdgeName(TaggedObject *from, TaggedObject *to); + + Node *InsertNodeUnique(Node *node); + void EraseNodeUnique(Node *node); + Edge *InsertEdgeUnique(Edge *edge); + void AddSyntheticRoot(JSThread *thread); + Node *InsertNodeAt(size_t pos, Node *node); + Edge *InsertEdgeAt(size_t pos, Edge *edge); + + StringHashMap stringTable_; + CList nodes_ {}; + CVector edges_ {}; + CVector timeStamps_ {}; + std::atomic_int sequenceId_ {1}; // 1 Reversed for SyntheticRoot + int nodeCount_ {0}; + int edgeCount_ {0}; + int totalNodesSize_ {0}; + HeapEntryMap entryMap_; + panda::ecmascript::HeapRootVisitor rootVisitor_; + JSThread *thread_; + const Heap *heap_; + bool isVmMode_ {true}; +}; + +class EntryVisitor { +public: + NO_MOVE_SEMANTIC(EntryVisitor); + NO_COPY_SEMANTIC(EntryVisitor); + explicit EntryVisitor() = default; + ~EntryVisitor() = default; + static CString ConvertKey(JSTaggedValue key); +}; + +enum class FrontType { + HIDDEN, /* kHidden */ + ARRAY, /* kArray */ + STRING, /* kString */ + OBJECT, /* kObject */ + CODE, /* kCode */ + CLOSURE, /* kClosure */ + REGEXP, /* kRegExp */ + HEAPNUMBER, /* kHeapNumber */ + NATIVE, /* kNative */ + SYNTHETIC, /* kSynthetic */ + CONSSTRING, /* kConsString */ + SLICEDSTRING, /* kSlicedString */ + SYMBOL, /* kSymbol */ + BIGINT, /* kBigInt */ + DEFAULT = NATIVE, /* kDefault */ +}; + +class NodeTypeConverter { +public: + explicit NodeTypeConverter() = default; + ~NodeTypeConverter() = default; + NO_MOVE_SEMANTIC(NodeTypeConverter); + NO_COPY_SEMANTIC(NodeTypeConverter); + /* + * For Front-End to Show Statistics Correctly + */ + static FrontType Convert(NodeType type); +}; +} // namespace panda::ecmascript +#endif // ECMASCRIPT_HPROF_HEAP_SNAPSHOT_H diff --git a/runtime/hprof/heap_snapshot_json_serializer.cpp b/runtime/hprof/heap_snapshot_json_serializer.cpp new file mode 100644 index 000000000..93d50aa05 --- /dev/null +++ b/runtime/hprof/heap_snapshot_json_serializer.cpp @@ -0,0 +1,206 @@ +/* + * Copyright (c) 2021-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 "plugins/ecmascript/runtime/hprof/heap_snapshot_json_serializer.h" +#include "plugins/ecmascript/runtime/mem/c_containers.h" +#include "plugins/ecmascript/runtime/hprof/heap_snapshot.h" +#include "plugins/ecmascript/runtime/hprof/string_hashmap.h" + +namespace panda::ecmascript { +bool HeapSnapShotJSONSerializer::Serialize(HeapSnapShot *snapShot, const CString &fileName) +{ + // Serialize Node/Edge/String-Table + LOG(ERROR, RUNTIME) << "HeapSnapShotJSONSerializer::Serialize begin"; + snapShot_ = snapShot; + ASSERT(snapShot_->GetNodes() != nullptr && snapShot_->GetEdges() != nullptr && + snapShot_->GetEcmaStringTable() != nullptr); + stringBuffer_.str(""); // Clear Buffer + + SerializeSnapShotHeader(); // 1. + SerializeNodes(); // 2. + SerializeEdges(); // 3. + SerializeTraceFunctionInfo(); // 4. + SerializeTraceTree(); // 5. + SerializeSamples(); // 6. + SerializeLocations(); // 7. + SerializeStringTable(); // 8. + SerializerSnapShotClosure(); // 9. + + WriteJSON(fileName); // 10. + LOG(ERROR, RUNTIME) << "HeapSnapShotJSONSerializer::Serialize exit"; + return true; +} + +void HeapSnapShotJSONSerializer::SerializeSnapShotHeader() +{ + stringBuffer_ << "{\"snapshot\":\n"; // 1. + stringBuffer_ << "{\"meta\":\n"; // 2. + // NOLINTNEXTLINE(modernize-raw-string-literal) + stringBuffer_ << "{\"node_fields\":[\"type\",\"name\",\"id\",\"self_size\",\"edge_count\",\"trace_node_id\","; + stringBuffer_ << "\"detachedness\"],\n"; // 3. + // NOLINTNEXTLINE(modernize-raw-string-literal) + stringBuffer_ << "\"node_types\":[[\"hidden\",\"array\",\"string\",\"object\",\"code\",\"closure\",\"regexp\","; + // NOLINTNEXTLINE(modernize-raw-string-literal) + stringBuffer_ << "\"number\",\"native\",\"synthetic\",\"concatenated string\",\"slicedstring\",\"symbol\","; + // NOLINTNEXTLINE(modernize-raw-string-literal) + stringBuffer_ << "\"bigint\"],\"string\",\"number\",\"number\",\"number\",\"number\",\"number\"],\n"; // 4. + // NOLINTNEXTLINE(modernize-raw-string-literal) + stringBuffer_ << "\"edge_fields\":[\"type\",\"name_or_index\",\"to_node\"],\n"; // 5. + // NOLINTNEXTLINE(modernize-raw-string-literal) + stringBuffer_ << "\"edge_types\":[[\"context\",\"element\",\"property\",\"internal\",\"hidden\",\"shortcut\","; + // NOLINTNEXTLINE(modernize-raw-string-literal) + stringBuffer_ << "\"weak\"],\"string_or_number\",\"node\"],\n"; // 6. + // NOLINTNEXTLINE(modernize-raw-string-literal) + stringBuffer_ << "\"trace_function_info_fields\":[\"function_id\",\"name\",\"script_name\",\"script_id\","; + // NOLINTNEXTLINE(modernize-raw-string-literal) + stringBuffer_ << "\"line\",\"column\"],\n"; // 7. + // NOLINTNEXTLINE(modernize-raw-string-literal) + stringBuffer_ << "\"trace_node_fields\":[\"id\",\"function_info_index\",\"count\",\"size\",\"children\"],\n"; + // NOLINTNEXTLINE(modernize-raw-string-literal) + stringBuffer_ << "\"sample_fields\":[\"timestamp_us\",\"last_assigned_id\"],\n"; // 9. + // NOLINTNEXTLINE(modernize-raw-string-literal) + stringBuffer_ << "\"location_fields\":[\"object_index\",\"script_id\",\"line\",\"column\"]},\n"; // 10. + stringBuffer_ << "\"node_count\":" << snapShot_->GetNodeCount() << ",\n"; // 11. + stringBuffer_ << "\"edge_count\":" << snapShot_->GetEdgeCount() << ",\n"; // 12. + stringBuffer_ << "\"trace_function_count\":" + << "0\n"; // 13. + stringBuffer_ << "},\n"; // 14. +} + +void HeapSnapShotJSONSerializer::SerializeNodes() +{ + const CList *nodes = snapShot_->GetNodes(); + const StringHashMap *stringTable = snapShot_->GetEcmaStringTable(); + ASSERT(nodes != nullptr); + stringBuffer_ << "\"nodes\":["; // Section Header + size_t i = 0; + for (auto *node : *nodes) { + if (i > 0) { + stringBuffer_ << ","; // add comma except first line + } + stringBuffer_ << static_cast(NodeTypeConverter::Convert(node->GetType())) << ","; // 1. + stringBuffer_ << stringTable->GetStringId(node->GetName()) << ","; // 2. + stringBuffer_ << node->GetId() << ","; // 3. + stringBuffer_ << node->GetSelfSize() << ","; // 4. + stringBuffer_ << node->GetEdgeCount() << ","; // 5. + stringBuffer_ << node->GetStackTraceId() << ","; // 6. + if (i == nodes->size() - 1) { // add comma at last the line + stringBuffer_ << "0" + << "],\n"; // 7. detachedness default + } else { + stringBuffer_ << "0\n"; // 7. + } + i++; + } +} + +void HeapSnapShotJSONSerializer::SerializeEdges() +{ + const CVector *edges = snapShot_->GetEdges(); + const StringHashMap *stringTable = snapShot_->GetEcmaStringTable(); + ASSERT(edges != nullptr); + stringBuffer_ << "\"edges\":["; + size_t i = 0; + for (auto *edge : *edges) { + if (i > 0) { // add comma except the first line + stringBuffer_ << ","; + } + stringBuffer_ << static_cast(edge->GetType()) << ","; // 1. + stringBuffer_ << stringTable->GetStringId(edge->GetName()) << ","; // 2. Use StringId + + if (i == edges->size() - 1) { // add comma at last the line + stringBuffer_ << edge->GetTo()->GetIndex() * Node::NODE_FIELD_COUNT << "],\n"; // 3. + } else { + stringBuffer_ << edge->GetTo()->GetIndex() * Node::NODE_FIELD_COUNT << "\n"; // 3. + } + i++; + } +} + +void HeapSnapShotJSONSerializer::SerializeTraceFunctionInfo() +{ + stringBuffer_ << "\"trace_function_infos\":[],\n"; // Empty +} + +void HeapSnapShotJSONSerializer::SerializeTraceTree() +{ + stringBuffer_ << "\"trace_tree\":[],\n"; // Empty +} + +void HeapSnapShotJSONSerializer::SerializeSamples() +{ + stringBuffer_ << "\"samples\":["; + const CVector &timeStamps = snapShot_->GetTimeStamps(); + if (!timeStamps.empty()) { + auto firstTimeStamp = timeStamps[0]; + bool isFirst = true; + for (auto timeStamp : timeStamps) { + if (!isFirst) { + stringBuffer_ << "\n, "; + } else { + isFirst = false; + } + stringBuffer_ << timeStamp.GetTimeStamp() - firstTimeStamp.GetTimeStamp() << ", "; + stringBuffer_ << timeStamp.GetLastSequenceId(); + } + } + stringBuffer_ << "],\n"; +} + +void HeapSnapShotJSONSerializer::SerializeLocations() +{ + stringBuffer_ << "\"locations\":[],\n"; +} + +void HeapSnapShotJSONSerializer::SerializeStringTable() +{ + const StringHashMap *stringTable = snapShot_->GetEcmaStringTable(); + ASSERT(stringTable != nullptr); + stringBuffer_ << "\"strings\":[\"\",\n"; + stringBuffer_ << "\"\",\n"; + stringBuffer_ << "\"GC roots\",\n"; + // StringId Range from 3 + size_t capcity = stringTable->GetCapcity(); + size_t i = 0; + for (auto key : stringTable->GetOrderedKeyStorage()) { + if (i == capcity - 1) { + stringBuffer_ << "\"" << *(stringTable->GetStringByKey(key)) << "\"\n"; // No Comma for the last line + } else { + stringBuffer_ << "\"" << *(stringTable->GetStringByKey(key)) << "\",\n"; + } + i++; + } + stringBuffer_ << "]\n"; +} + +void HeapSnapShotJSONSerializer::SerializerSnapShotClosure() +{ + stringBuffer_ << "}\n"; +} + +void HeapSnapShotJSONSerializer::WriteJSON(const CString &fileName) +{ + std::string fName(fileName); + LOG(ERROR, RUNTIME) << "HeapSnapShotJSONSerializer::WriteJSON" << fName; + outputStream_.open(fName, std::ios::out); + if (!outputStream_.good()) { + LOG_ECMA(ERROR) << "open file failed"; + return; + } + outputStream_ << stringBuffer_.str(); + outputStream_.close(); + outputStream_.clear(); // Make sure the next open operation success +} +} // namespace panda::ecmascript diff --git a/runtime/hprof/heap_snapshot_json_serializer.h b/runtime/hprof/heap_snapshot_json_serializer.h new file mode 100644 index 000000000..b23a29f43 --- /dev/null +++ b/runtime/hprof/heap_snapshot_json_serializer.h @@ -0,0 +1,56 @@ +/* + * Copyright (c) 2021-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_HPROF_HEAP_SNAPSHOT_SERIALIZER_H +#define ECMASCRIPT_HPROF_HEAP_SNAPSHOT_SERIALIZER_H + +#include +#include + +#include "plugins/ecmascript/runtime/mem/c_string.h" +#include "os/mem.h" + +namespace panda::ecmascript { +using fstream = std::fstream; +using stringstream = std::stringstream; + +class HeapSnapShot; + +class HeapSnapShotJSONSerializer { +public: + explicit HeapSnapShotJSONSerializer() = default; + ~HeapSnapShotJSONSerializer() = default; + NO_MOVE_SEMANTIC(HeapSnapShotJSONSerializer); + NO_COPY_SEMANTIC(HeapSnapShotJSONSerializer); + bool Serialize(HeapSnapShot *snapShot, const CString &fileName); + +private: + void SerializeSnapShotHeader(); + void SerializeNodes(); + void SerializeEdges(); + void SerializeTraceFunctionInfo(); + void SerializeTraceTree(); + void SerializeSamples(); + void SerializeLocations(); + void SerializeStringTable(); + void SerializerSnapShotClosure(); + + void WriteJSON(const CString &fileName); + fstream outputStream_; + HeapSnapShot *snapShot_{nullptr}; + stringstream stringBuffer_; +}; +} // namespace panda::ecmascript +#endif // ECMASCRIPT_HPROF_HEAP_SNAPSHOT_SERIALIZER_H diff --git a/runtime/hprof/heap_tracker.cpp b/runtime/hprof/heap_tracker.cpp new file mode 100644 index 000000000..c15fd68e9 --- /dev/null +++ b/runtime/hprof/heap_tracker.cpp @@ -0,0 +1,44 @@ +/* + * Copyright (c) 2021-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 "plugins/ecmascript/runtime/hprof/heap_tracker.h" +#include "plugins/ecmascript/runtime/hprof/heap_snapshot.h" +#include "plugins/ecmascript/runtime/mem/space.h" + +namespace panda::ecmascript { +static constexpr int32_t MILLI_TO_MICRO = 1000; + +void HeapTrackerSample::Run() +{ + while (!isInterrupt_) { + snapShot_->RecordSampleTime(); + usleep(timeInterval_ * MILLI_TO_MICRO); + } +} + +void HeapTracker::AllocationEvent(uintptr_t address) +{ + if (snapShot_ != nullptr) { + snapShot_->AddNode(address); + } +} + +void HeapTracker::MoveEvent(uintptr_t address, uintptr_t forward_address) +{ + if (snapShot_ != nullptr) { + snapShot_->MoveNode(address, forward_address); + } +} +} // namespace panda::ecmascript diff --git a/runtime/hprof/heap_tracker.h b/runtime/hprof/heap_tracker.h new file mode 100644 index 000000000..f0d98e1e3 --- /dev/null +++ b/runtime/hprof/heap_tracker.h @@ -0,0 +1,92 @@ +/* + * Copyright (c) 2021-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_HPROF_HEAP_TRACKER_H +#define ECMASCRIPT_HPROF_HEAP_TRACKER_H + +#include +#include +#include + +#include "libpandabase/macros.h" + +namespace panda::ecmascript { +class HeapSnapShot; + +class HeapTrackerSample { +public: + explicit HeapTrackerSample(HeapSnapShot *snapShot, double timeInterval) + : timeInterval_(timeInterval), snapShot_(snapShot) + { + } + + ~HeapTrackerSample() + { + isInterrupt_ = true; + } + + void Start() + { + isInterrupt_ = false; + thread_ = std::thread(&HeapTrackerSample::Run, this); + } + + void Stop() + { + isInterrupt_ = true; + if (thread_.joinable()) { + thread_.join(); + } + } + + void Run(); + + NO_COPY_SEMANTIC(HeapTrackerSample); + NO_MOVE_SEMANTIC(HeapTrackerSample); + +private: + std::thread thread_; + std::atomic_bool isInterrupt_ = true; + double timeInterval_ = 0; + HeapSnapShot *snapShot_; +}; + +class HeapTracker { +public: + HeapTracker(HeapSnapShot *snapShot, double timeInterval) : snapShot_(snapShot), sample_(snapShot, timeInterval) {} + ~HeapTracker() = default; + + void StartTracing() + { + sample_.Start(); + } + + void StopTracing() + { + sample_.Stop(); + } + + void AllocationEvent(uintptr_t address); + void MoveEvent(uintptr_t address, uintptr_t forward_address); + + NO_COPY_SEMANTIC(HeapTracker); + NO_MOVE_SEMANTIC(HeapTracker); + +private: + HeapSnapShot *snapShot_; + HeapTrackerSample sample_; +}; +} // namespace panda::ecmascript +#endif // ECMASCRIPT_HPROF_HEAP_TRACKER_H diff --git a/runtime/hprof/string_hashmap.cpp b/runtime/hprof/string_hashmap.cpp new file mode 100644 index 000000000..43c0218ee --- /dev/null +++ b/runtime/hprof/string_hashmap.cpp @@ -0,0 +1,99 @@ +/* + * Copyright (c) 2021-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 "plugins/ecmascript/runtime/hprof/string_hashmap.h" + +namespace panda::ecmascript { +CString *StringHashMap::FindOrInsertString(CString *string) +{ + StringKey key = GenerateStringKey(string); + auto it = hashmap_.find(key); + if (it != hashmap_.end()) { + return it->second; + } + index_++; + hashmap_.insert(std::make_pair(key, string)); + orderedKey_.emplace_back(key); + indexMap_.insert(std::make_pair(key, index_)); + return string; +} + +StringId StringHashMap::GetStringId(const CString *string) const +{ + auto it = indexMap_.find(GenerateStringKey(string)); + return it != indexMap_.end() ? it->second : 1; // "" +} + +CString *StringHashMap::GetStringByKey(StringKey key) const +{ + auto it = hashmap_.find(key); + if (it != hashmap_.end()) { + return FormatString(it->second); + } + return nullptr; +} + +CString *StringHashMap::FormatString(CString *string) const +{ + // remove "\"" | "\r\n" | "\\" | "\t" | "\f" + int length = string->length(); + char *charSeq = const_cast(string->c_str()); + for (int i = 0; i < length; i++) { + if (charSeq[i] == '\"') { // NOLINT(cppcoreguidelines-pro-bounds-pointer-arithmetic) + charSeq[i] = '`'; // NOLINT(cppcoreguidelines-pro-bounds-pointer-arithmetic) + } else if (charSeq[i] == '\r') { // NOLINT(cppcoreguidelines-pro-bounds-pointer-arithmetic) + charSeq[i] = '`'; // NOLINT(cppcoreguidelines-pro-bounds-pointer-arithmetic) + } else if (charSeq[i] == '\n') { // NOLINT(cppcoreguidelines-pro-bounds-pointer-arithmetic) + charSeq[i] = '`'; // NOLINT(cppcoreguidelines-pro-bounds-pointer-arithmetic) + } else if (charSeq[i] == '\\') { // NOLINT(cppcoreguidelines-pro-bounds-pointer-arithmetic) + charSeq[i] = '`'; // NOLINT(cppcoreguidelines-pro-bounds-pointer-arithmetic) + } else if (charSeq[i] == '\t') { // NOLINT(cppcoreguidelines-pro-bounds-pointer-arithmetic) + charSeq[i] = '`'; // NOLINT(cppcoreguidelines-pro-bounds-pointer-arithmetic) + } else if (charSeq[i] == '\f') { // NOLINT(cppcoreguidelines-pro-bounds-pointer-arithmetic) + charSeq[i] = '`'; // NOLINT(cppcoreguidelines-pro-bounds-pointer-arithmetic) + } else if (charSeq[i] < ' ') { // NOLINT(cppcoreguidelines-pro-bounds-pointer-arithmetic) + // ctrl chars 0~31 + charSeq[i] = '`'; // NOLINT(cppcoreguidelines-pro-bounds-pointer-arithmetic) + } + } + *string = charSeq; + return string; +} + +StringKey StringHashMap::GenerateStringKey(const CString *string) const +{ + return std::hash {}(std::string(*string)); +} + +CString *StringHashMap::GetString(CString as) +{ + auto *tempString = const_cast(heap_->GetRegionFactory())->New(std::move(as)); + CString *oldString = FindOrInsertString(tempString); + if (tempString != oldString) { + const_cast(heap_->GetRegionFactory())->Delete(tempString); + return oldString; + } + return tempString; +} + +void StringHashMap::Clear() +{ + for (auto it : hashmap_) { + if (it.second != nullptr) { + const_cast(heap_->GetRegionFactory())->Delete(it.second); + } + } +} +} // namespace panda::ecmascript diff --git a/runtime/hprof/string_hashmap.h b/runtime/hprof/string_hashmap.h new file mode 100644 index 000000000..b24a9ac4e --- /dev/null +++ b/runtime/hprof/string_hashmap.h @@ -0,0 +1,83 @@ +/* + * Copyright (c) 2021-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_HPROF_STRING_HASHMAP_H +#define ECMASCRIPT_HPROF_STRING_HASHMAP_H + +#include "plugins/ecmascript/runtime/js_thread.h" +#include "plugins/ecmascript/runtime/mem/c_containers.h" +#include "plugins/ecmascript/runtime/mem/c_string.h" +#include "plugins/ecmascript/runtime/mem/heap.h" +#include "os/mem.h" + +namespace panda::ecmascript { +using StringKey = uint64_t; +using StringId = uint64_t; + +// An Implementation for Native StringTable without Auto Mem-Management +// To make sure when using String, it still stays where it was. +class StringHashMap { +public: + explicit StringHashMap(const Heap *heap) : heap_(heap) + { + ASSERT(heap_ != nullptr); + } + ~StringHashMap() + { + Clear(); + } + NO_MOVE_SEMANTIC(StringHashMap); + NO_COPY_SEMANTIC(StringHashMap); + /* + * The ID is the seat number in JSON file Range from 0~string_table_.size() + */ + StringId GetStringId(const CString *string) const; + /* + * Get all keys sorted by insert order + */ + const CVector &GetOrderedKeyStorage() const + { + return orderedKey_; + } + /* + * Get string by its hash key + */ + CString *GetStringByKey(StringKey key) const; + size_t GetCapcity() const + { + ASSERT(orderedKey_.size() == hashmap_.size()); + return orderedKey_.size(); + } + /* + * For external call to use this StringTable + */ + CString *GetString(CString as); + +private: + StringKey GenerateStringKey(const CString *string) const; + CString *FindOrInsertString(CString *string); + CString *FormatString(CString *string) const; + /* + * Free all memory + */ + void Clear(); + const Heap *heap_; + CVector orderedKey_; // Used for Serialize Order + size_t index_{2}; // 2: Offset the String-Table Header + CUnorderedMap indexMap_; + CUnorderedMap hashmap_; +}; +} // namespace panda::ecmascript +#endif // ECMASCRIPT_HPROF_STRING_HASHMAP_H diff --git a/runtime/ic/ic_binary_op-inl.h b/runtime/ic/ic_binary_op-inl.h new file mode 100644 index 000000000..4b49c2619 --- /dev/null +++ b/runtime/ic/ic_binary_op-inl.h @@ -0,0 +1,355 @@ +/* + * Copyright (c) 2021-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_IC_IC_BINARY_OP_INL_H_ +#define ECMASCRIPT_IC_IC_BINARY_OP_INL_H_ + +#include "ic_binary_op.h" +#include "plugins/ecmascript/runtime/ic/proto_change_details.h" +#include "plugins/ecmascript/runtime/interpreter/interpreter-inl.h" +#include "plugins/ecmascript/runtime/interpreter/slow_runtime_stub.h" +#include "plugins/ecmascript/runtime/js_array.h" +#include "plugins/ecmascript/runtime/js_handle.h" +#include "plugins/ecmascript/runtime/js_tagged_value-inl.h" +#include "plugins/ecmascript/runtime/napi/include/jsnapi.h" + +namespace panda::ecmascript { +JSTaggedValue ICBinaryOP::AddWithTSType(JSThread *thread, EcmaVM *ecma_vm, JSTaggedValue left, JSTaggedValue right, + JSTaggedValue argType) +{ + INTERPRETER_TRACE(thread, AddWithTSType); + BinaryType addType = static_cast(argType.GetInt()); + switch (addType) { + // Support cases, such as: int + double, int + int, double + double + case BinaryType::NUMBER: { + double a0Double = left.IsInt() ? left.GetInt() : left.GetDouble(); + double a1Double = right.IsInt() ? right.GetInt() : right.GetDouble(); + double ret = a0Double + a1Double; + return JSTaggedValue(ret); + } + // Support cases, such as: number + null, undefined + null, boolean + number, etc. + case BinaryType::NUMBER_GEN: { + JSHandle leftValue(thread, left); + JSHandle rightValue(thread, right); + JSHandle primitiveA0(thread, JSTaggedValue::ToPrimitive(thread, leftValue)); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + JSHandle primitiveA1(thread, JSTaggedValue::ToPrimitive(thread, rightValue)); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + + JSTaggedNumber taggedValueA0 = JSTaggedValue::ToNumber(thread, primitiveA0); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + JSTaggedNumber taggedValueA1 = JSTaggedValue::ToNumber(thread, primitiveA1); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + double a0Double = taggedValueA0.GetNumber(); + double a1Double = taggedValueA1.GetNumber(); + return JSTaggedValue(a0Double + a1Double); + } + // Support case: string + string. + case BinaryType::STRING: { + JSHandle stringA0 = JSHandle(JSHandle(thread, left)); + JSHandle stringA1 = JSHandle(JSHandle(thread, right)); + EcmaString *ret = EcmaString::Concat(stringA0, stringA1, ecma_vm); + return JSTaggedValue(ret); + } + // Support cases, such as: string + null, string + object, string + boolean, string + number, etc. + case BinaryType::STRING_GEN: { + JSHandle leftValue(thread, left); + JSHandle rightValue(thread, right); + if (left.IsString()) { + JSHandle stringA0 = JSHandle(leftValue); + JSHandle stringA1 = JSTaggedValue::ToString(thread, rightValue); + EcmaString *ret = EcmaString::Concat(stringA0, stringA1, ecma_vm); + return JSTaggedValue(ret); + } + JSHandle stringA0 = JSTaggedValue::ToString(thread, leftValue); + JSHandle stringA1 = JSHandle(rightValue); + EcmaString *ret = EcmaString::Concat(stringA0, stringA1, ecma_vm); + return JSTaggedValue(ret); + } + // Some special cases, such as: object + undefined, object + boolean, etc. + case BinaryType::GENERIC: { + JSTaggedValue res = SlowRuntimeStub::Add2Dyn(thread, ecma_vm, left, right); + return res; + } + default: { + UNREACHABLE(); + } + } +} + +JSTaggedValue ICBinaryOP::SubWithTSType(JSThread *thread, EcmaVM *ecma_vm, JSTaggedValue left, JSTaggedValue right, + JSTaggedValue argType) +{ + INTERPRETER_TRACE(thread, SubWithTSType); + BinaryType subType = static_cast(argType.GetInt()); + switch (subType) { + // Support int or number + case BinaryType::NUMBER: { + double a0Double = left.IsInt() ? left.GetInt() : left.GetDouble(); + double a1Double = right.IsInt() ? right.GetInt() : right.GetDouble(); + double ret = a0Double - a1Double; + return JSTaggedValue(ret); + } + // Support cases, such as: string like '2333', boolean, null + case BinaryType::GENERIC: { + JSHandle leftValue(thread, left); + JSHandle rightValue(thread, right); + JSTaggedNumber number0 = JSTaggedValue::ToNumber(thread, leftValue); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + JSTaggedNumber number1 = JSTaggedValue::ToNumber(thread, rightValue); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + auto ret = number0 - number1; + return JSTaggedValue(ret); + } + case BinaryType::NUMBER_GEN: + case BinaryType::STRING: + case BinaryType::STRING_GEN: + default: { + UNREACHABLE(); + } + } +} + +JSTaggedValue ICBinaryOP::MulWithTSType(JSThread *thread, EcmaVM *ecma_vm, JSTaggedValue left, JSTaggedValue right, + JSTaggedValue argType) +{ + INTERPRETER_TRACE(thread, MulWithTSType); + BinaryType mulType = static_cast(argType.GetInt()); + switch (mulType) { + // Support int or number + case BinaryType::NUMBER: { + return JSTaggedValue(left.GetNumber() * right.GetNumber()); + } + // Support cases, such as: string like '2333', boolean, null + case BinaryType::GENERIC: { + JSHandle leftValue(thread, left); + JSHandle rightValue(thread, right); + // 6. Let lnum be ToNumber(leftValue). + JSTaggedNumber primitiveA = JSTaggedValue::ToNumber(thread, leftValue); + // 7. ReturnIfAbrupt(lnum). + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + // 8. Let rnum be ToNumber(rightValue). + JSTaggedNumber primitiveB = JSTaggedValue::ToNumber(thread, rightValue); + // 9. ReturnIfAbrupt(rnum). + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + // 12.6.3.1 Applying the * Operator + return primitiveA * primitiveB; + } + case BinaryType::NUMBER_GEN: + case BinaryType::STRING: + case BinaryType::STRING_GEN: + default: { + UNREACHABLE(); + } + } +} + +JSTaggedValue ICBinaryOP::DivWithTSType(JSThread *thread, EcmaVM *ecma_vm, JSTaggedValue left, JSTaggedValue right, + JSTaggedValue argType) +{ + INTERPRETER_TRACE(thread, DivWithTSType); + BinaryType divType = static_cast(argType.GetInt()); + switch (divType) { + // Support int or number + case BinaryType::NUMBER: { + double dLeft = left.IsInt() ? left.GetInt() : left.GetDouble(); + double dRight = right.IsInt() ? right.GetInt() : right.GetDouble(); + if (UNLIKELY(dRight == 0.0)) { + if (dLeft == 0.0 || std::isnan(dLeft)) { + return JSTaggedValue(base::NAN_VALUE); + } + uint64_t flagBit = + ((bit_cast(dLeft)) ^ (bit_cast(dRight))) & base::DOUBLE_SIGN_MASK; + return JSTaggedValue(bit_cast(flagBit ^ (bit_cast(base::POSITIVE_INFINITY)))); + } + return JSTaggedValue(dLeft / dRight); + } + // Support special cases, such as: string like '2333', boolean, null + case BinaryType::GENERIC: { + auto res = SlowRuntimeStub::Div2Dyn(thread, left, right); + return res; + } + case BinaryType::NUMBER_GEN: + case BinaryType::STRING: + case BinaryType::STRING_GEN: + default: { + UNREACHABLE(); + } + } +} + +JSTaggedValue ICBinaryOP::ModWithTSType(JSThread *thread, EcmaVM *ecma_vm, JSTaggedValue left, JSTaggedValue right, + JSTaggedValue argType) +{ + INTERPRETER_TRACE(thread, ModWithTSType); + BinaryType modType = static_cast(argType.GetInt()); + switch (modType) { + // Support int or number + case BinaryType::NUMBER: { + double dLeft = left.IsInt() ? left.GetInt() : left.GetDouble(); + double dRight = right.IsInt() ? right.GetInt() : right.GetDouble(); + if (dRight == 0.0 || std::isnan(dRight) || std::isnan(dLeft) || std::isinf(dLeft)) { + return JSTaggedValue(base::NAN_VALUE); + } + if (dLeft == 0.0 || std::isinf(dRight)) { + return JSTaggedValue(dLeft); + } + return JSTaggedValue(std::fmod(dLeft, dRight)); + } + // Support special cases, such as: string like '2333', boolean, null + case BinaryType::GENERIC: { + JSHandle leftValue(thread, left); + JSHandle rightValue(thread, right); + JSTaggedNumber leftNumber = JSTaggedValue::ToNumber(thread, leftValue); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + double dLeft = leftNumber.GetNumber(); + JSTaggedNumber rightNumber = JSTaggedValue::ToNumber(thread, rightValue); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + double dRight = rightNumber.GetNumber(); + // 12.6.3.3 Applying the % Operator + if ((dRight == 0.0) || std::isnan(dRight) || std::isnan(dLeft) || std::isinf(dLeft)) { + return JSTaggedValue(base::NAN_VALUE); + } + if ((dLeft == 0.0) || std::isinf(dRight)) { + return JSTaggedValue(dLeft); + } + return JSTaggedValue(std::fmod(dLeft, dRight)); + } + case BinaryType::NUMBER_GEN: + case BinaryType::STRING: + case BinaryType::STRING_GEN: + default: { + UNREACHABLE(); + } + } +} + +void ICBinaryOP::GetBitOPDate(JSThread *thread, JSTaggedValue left, JSTaggedValue right, int32_t &opNumber0, + int32_t &opNumber1, BinaryType opType) +{ + INTERPRETER_TRACE(thread, GetBitOPDate); + switch (opType) { + case BinaryType::NUMBER: { + opNumber0 = + left.IsInt() ? left.GetInt() : base::NumberHelper::DoubleToInt(left.GetDouble(), base::INT32_BITS); + opNumber1 = + right.IsInt() ? right.GetInt() : base::NumberHelper::DoubleToInt(right.GetDouble(), base::INT32_BITS); + break; + } + // Support special cases, such as: string like '2333', boolean, null + case BinaryType::GENERIC: { + JSHandle leftValue(thread, left); + JSHandle rightValue(thread, right); + JSTaggedValue taggedNumber0 = SlowRuntimeStub::ToJSTaggedValueWithInt32(thread, leftValue.GetTaggedValue()); + JSTaggedValue taggedNumber1 = + SlowRuntimeStub::ToJSTaggedValueWithUint32(thread, rightValue.GetTaggedValue()); + opNumber0 = taggedNumber0.GetInt(); + opNumber1 = taggedNumber1.GetInt(); + break; + } + case BinaryType::NUMBER_GEN: + case BinaryType::STRING: + case BinaryType::STRING_GEN: + default: { + UNREACHABLE(); + } + } + return; +} + +JSTaggedValue ICBinaryOP::ShlWithTSType(JSThread *thread, EcmaVM *ecma_vm, JSTaggedValue left, JSTaggedValue right, + JSTaggedValue argType) +{ + INTERPRETER_TRACE(thread, ShlWithTSType); + BinaryType shlType = static_cast(argType.GetInt()); + int32_t opNumber0; + int32_t opNumber1; + GetBitOPDate(thread, left, right, opNumber0, opNumber1, shlType); + uint32_t shift = + static_cast(opNumber1) & 0x1f; // NOLINT(hicpp-signed-bitwise, readability-magic-numbers) + using unsigned_type = std::make_unsigned_t; + auto ret = static_cast(static_cast(opNumber0) << shift); // NOLINT(hicpp-signed-bitwise) + return JSTaggedValue(ret); +} + +JSTaggedValue ICBinaryOP::ShrWithTSType(JSThread *thread, EcmaVM *ecma_vm, JSTaggedValue left, JSTaggedValue right, + JSTaggedValue argType) +{ + INTERPRETER_TRACE(thread, ShrWithTSType); + BinaryType shrType = static_cast(argType.GetInt()); + int32_t opNumber0; + int32_t opNumber1; + GetBitOPDate(thread, left, right, opNumber0, opNumber1, shrType); + uint32_t shift = + static_cast(opNumber1) & 0x1f; // NOLINT(hicpp-signed-bitwise, readability-magic-numbers) + auto ret = static_cast(opNumber0 >> shift); // NOLINT(hicpp-signed-bitwise) + return JSTaggedValue(ret); +} + +JSTaggedValue ICBinaryOP::AshrWithTSType(JSThread *thread, EcmaVM *ecma_vm, JSTaggedValue left, JSTaggedValue right, + JSTaggedValue argType) +{ + INTERPRETER_TRACE(thread, AshrWithTSType); + BinaryType ashrType = static_cast(argType.GetInt()); + int32_t opNumber0; + int32_t opNumber1; + GetBitOPDate(thread, left, right, opNumber0, opNumber1, ashrType); + uint32_t shift = + static_cast(opNumber1) & 0x1f; // NOLINT(hicpp-signed-bitwise, readability-magic-numbers) + using unsigned_type = std::make_unsigned_t; + auto ret = static_cast(static_cast(opNumber0) >> shift); // NOLINT(hicpp-signed-bitwise) + return JSTaggedValue(ret); +} + +JSTaggedValue ICBinaryOP::AndWithTSType(JSThread *thread, EcmaVM *ecma_vm, JSTaggedValue left, JSTaggedValue right, + JSTaggedValue argType) +{ + INTERPRETER_TRACE(thread, AndWithTSType); + BinaryType andType = static_cast(argType.GetInt()); + int32_t opNumber0; + int32_t opNumber1; + GetBitOPDate(thread, left, right, opNumber0, opNumber1, andType); + // NOLINT(hicpp-signed-bitwise) + auto ret = static_cast(opNumber0) & static_cast(opNumber1); + return JSTaggedValue(ret); +} + +JSTaggedValue ICBinaryOP::OrWithTSType(JSThread *thread, EcmaVM *ecma_vm, JSTaggedValue left, JSTaggedValue right, + JSTaggedValue argType) +{ + INTERPRETER_TRACE(thread, OrWithTSType); + BinaryType orType = static_cast(argType.GetInt()); + int32_t opNumber0; + int32_t opNumber1; + GetBitOPDate(thread, left, right, opNumber0, opNumber1, orType); + // NOLINT(hicpp-signed-bitwise) + auto ret = static_cast(opNumber0) | static_cast(opNumber1); + return JSTaggedValue(ret); +} + +JSTaggedValue ICBinaryOP::XorWithTSType(JSThread *thread, EcmaVM *ecma_vm, JSTaggedValue left, JSTaggedValue right, + JSTaggedValue argType) +{ + INTERPRETER_TRACE(thread, XorWithTSType); + BinaryType xorType = static_cast(argType.GetInt()); + int32_t opNumber0; + int32_t opNumber1; + GetBitOPDate(thread, left, right, opNumber0, opNumber1, xorType); + // NOLINT(hicpp-signed-bitwise) + auto ret = static_cast(opNumber0) ^ static_cast(opNumber1); + return JSTaggedValue(ret); +} +} // namespace panda::ecmascript +#endif // ECMASCRIPT_IC_IC_BINARY_OP_INL_H_ diff --git a/runtime/ic/ic_binary_op.h b/runtime/ic/ic_binary_op.h new file mode 100644 index 000000000..bef23af87 --- /dev/null +++ b/runtime/ic/ic_binary_op.h @@ -0,0 +1,62 @@ +/* + * Copyright (c) 2021-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_IC_IC_BINARY_OP_H_ +#define ECMASCRIPT_IC_IC_BINARY_OP_H_ + +#include "plugins/ecmascript/runtime/js_tagged_value.h" +#include "plugins/ecmascript/runtime/property_attributes.h" +#include "plugins/ecmascript/runtime/ic/profile_type_info.h" +#include "plugins/ecmascript/runtime/runtime_call_id.h" + +namespace panda::ecmascript { +enum class BinaryType : uint8_t { + NUMBER, + NUMBER_GEN, + STRING, + STRING_GEN, + GENERIC, +}; + +class ICBinaryOP { +public: + static inline JSTaggedValue AddWithTSType(JSThread *thread, EcmaVM *ecma_vm, JSTaggedValue left, + JSTaggedValue right, JSTaggedValue argType); + static inline JSTaggedValue SubWithTSType(JSThread *thread, EcmaVM *ecma_vm, JSTaggedValue left, + JSTaggedValue right, JSTaggedValue argType); + static inline JSTaggedValue MulWithTSType(JSThread *thread, EcmaVM *ecma_vm, JSTaggedValue left, + JSTaggedValue right, JSTaggedValue argType); + static inline JSTaggedValue DivWithTSType(JSThread *thread, EcmaVM *ecma_vm, JSTaggedValue left, + JSTaggedValue right, JSTaggedValue argType); + static inline JSTaggedValue ModWithTSType(JSThread *thread, EcmaVM *ecma_vm, JSTaggedValue left, + JSTaggedValue right, JSTaggedValue argType); + static inline void GetBitOPDate(JSThread *thread, JSTaggedValue left, JSTaggedValue right, + int32_t &opNumber0, int32_t &opNumber1, BinaryType opType); + static inline JSTaggedValue ShlWithTSType(JSThread *thread, EcmaVM *ecma_vm, JSTaggedValue left, + JSTaggedValue right, JSTaggedValue argType); + static inline JSTaggedValue ShrWithTSType(JSThread *thread, EcmaVM *ecma_vm, JSTaggedValue left, + JSTaggedValue right, JSTaggedValue argType); + static inline JSTaggedValue AshrWithTSType(JSThread *thread, EcmaVM *ecma_vm, JSTaggedValue left, + JSTaggedValue right, JSTaggedValue argType); + static inline JSTaggedValue AndWithTSType(JSThread *thread, EcmaVM *ecma_vm, JSTaggedValue left, + JSTaggedValue right, JSTaggedValue argType); + static inline JSTaggedValue OrWithTSType(JSThread *thread, EcmaVM *ecma_vm, JSTaggedValue left, + JSTaggedValue right, JSTaggedValue argType); + static inline JSTaggedValue XorWithTSType(JSThread *thread, EcmaVM *ecma_vm, JSTaggedValue left, + JSTaggedValue right, JSTaggedValue argType); +}; +} // namespace panda::ecmascript + +#endif // ECMASCRIPT_IC_IC_BINARY_OP_H_ diff --git a/runtime/ic/ic_compare_op.cpp b/runtime/ic/ic_compare_op.cpp new file mode 100644 index 000000000..2ecb230fd --- /dev/null +++ b/runtime/ic/ic_compare_op.cpp @@ -0,0 +1,470 @@ +/* + * Copyright (c) 2021-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 "plugins/ecmascript/runtime/ic/ic_compare_op.h" + +#include "plugins/ecmascript/runtime/accessor_data.h" +#include "plugins/ecmascript/runtime/base/error_helper.h" +#include "plugins/ecmascript/runtime/base/number_helper.h" +#include "plugins/ecmascript/runtime/base/string_helper.h" +#include "plugins/ecmascript/runtime/ecma_runtime_call_info.h" +#include "plugins/ecmascript/runtime/ecma_string-inl.h" +#include "plugins/ecmascript/runtime/interpreter/interpreter-inl.h" +#include "plugins/ecmascript/runtime/mem/c_containers.h" +#include "plugins/ecmascript/runtime/mem/tagged_object-inl.h" +#include "plugins/ecmascript/runtime/runtime_call_id.h" + +namespace panda::ecmascript { +JSTaggedValue CompareOp::EqualWithIC(JSThread* thread, JSTaggedValue left, + JSTaggedValue right, CompareOpType operationType) +{ + INTERPRETER_TRACE(thread, EqualWithIC); + double leftDouble = 0; + double rightDouble = 0; + JSTaggedValue ret = JSTaggedValue::False(); + switch (operationType) { + case CompareOpType::NUMBER_NUMBER: { + leftDouble = left.GetNumber(); + rightDouble = right.GetNumber(); + ret = JSTaggedValue(JSTaggedValue::StrictNumberEquals(leftDouble, rightDouble)); + break; + } + case CompareOpType::STRING_NUMBER: { + JSTaggedValue temp = left; + left = right; + right = temp; + [[fallthrough]]; + } + case CompareOpType::NUMBER_STRING: { + JSHandle leftHandle(thread, left); + JSHandle rightHandle(thread, right); + rightDouble = JSTaggedValue::ToNumber(thread, rightHandle).GetNumber(); + RETURN_VALUE_IF_ABRUPT_COMPLETION(thread, JSTaggedValue::False()); + leftDouble = leftHandle.GetTaggedValue().GetNumber(); + ret = JSTaggedValue(JSTaggedValue::StrictNumberEquals(leftDouble, rightDouble)); + break; + } + case CompareOpType::BOOLEAN_NUMBER: { + JSTaggedValue temp = left; + left = right; + right = temp; + [[fallthrough]]; + } + case CompareOpType::NUMBER_BOOLEAN: { + leftDouble = left.GetNumber(); + if (right.GetRawData() == JSTaggedValue::VALUE_TRUE) { + rightDouble = 1; + } + ret = JSTaggedValue(JSTaggedValue::StrictNumberEquals(leftDouble, rightDouble)); + break; + } + case CompareOpType::OBJ_NUMBER: { + JSTaggedValue temp = left; + left = right; + right = temp; + [[fallthrough]]; + } + case CompareOpType::NUMBER_OBJ: { + JSHandle leftHandle(thread, left); + JSHandle rightHandle(thread, right); + JSHandle rightPrimitive(thread, JSTaggedValue::ToPrimitive(thread, rightHandle)); + RETURN_VALUE_IF_ABRUPT_COMPLETION(thread, JSTaggedValue::False()); + if (rightPrimitive->IsNumber()) { + ret = EqualWithIC(thread, leftHandle.GetTaggedValue(), + rightPrimitive.GetTaggedValue(), CompareOpType::NUMBER_NUMBER); + } else if (rightPrimitive->IsString()) { + ret = EqualWithIC(thread, leftHandle.GetTaggedValue(), + rightPrimitive.GetTaggedValue(), CompareOpType::NUMBER_STRING); + } else if (rightPrimitive->IsBoolean()) { + ret = EqualWithIC(thread, leftHandle.GetTaggedValue(), + rightPrimitive.GetTaggedValue(), CompareOpType::NUMBER_BOOLEAN); + } + break; + } + case CompareOpType::STRING_STRING: { + bool result = EcmaString::StringsAreEqual(static_cast(left.GetTaggedObject()), + static_cast(right.GetTaggedObject())); + ret = result ? JSTaggedValue::True() : JSTaggedValue::False(); + break; + } + case CompareOpType::BOOLEAN_STRING: { + JSTaggedValue temp = left; + left = right; + right = temp; + [[fallthrough]]; + } + case CompareOpType::STRING_BOOLEAN: { + JSHandle leftHandle(thread, left); + JSHandle rightHandle(thread, right); + leftDouble = JSTaggedValue::ToNumber(thread, leftHandle).GetNumber(); + RETURN_VALUE_IF_ABRUPT_COMPLETION(thread, JSTaggedValue::False()); + if (rightHandle.GetTaggedValue().GetRawData() == JSTaggedValue::VALUE_TRUE) { + rightDouble = 1; + } + ret = JSTaggedValue(JSTaggedValue::StrictNumberEquals(leftDouble, rightDouble)); + break; + } + case CompareOpType::OBJ_STRING: { + JSTaggedValue temp = left; + left = right; + right = temp; + [[fallthrough]]; + } + case CompareOpType::STRING_OBJ: { + JSHandle leftHandle(thread, left); + JSHandle rightHandle(thread, right); + JSHandle rightPrimitive(thread, JSTaggedValue::ToPrimitive(thread, rightHandle)); + RETURN_VALUE_IF_ABRUPT_COMPLETION(thread, JSTaggedValue::False()); + if (rightPrimitive->IsNumber()) { + ret = EqualWithIC(thread, leftHandle.GetTaggedValue(), + rightPrimitive.GetTaggedValue(), CompareOpType::NUMBER_STRING); + } else if (rightPrimitive->IsString()) { + ret = EqualWithIC(thread, leftHandle.GetTaggedValue(), + rightPrimitive.GetTaggedValue(), CompareOpType::STRING_STRING); + } else if (rightPrimitive->IsBoolean()) { + ret = EqualWithIC(thread, leftHandle.GetTaggedValue(), + rightPrimitive.GetTaggedValue(), CompareOpType::STRING_BOOLEAN); + } + break; + } + case CompareOpType::BOOLEAN_BOOLEAN: { + if (left.GetRawData() == JSTaggedValue::VALUE_TRUE) { + leftDouble = 1; + } + if (right.GetRawData() == JSTaggedValue::VALUE_TRUE) { + rightDouble = 1; + } + ret = JSTaggedValue(JSTaggedValue::StrictNumberEquals(leftDouble, rightDouble)); + break; + } + case CompareOpType::OBJ_BOOLEAN: { + JSHandle leftHandle(thread, left); + JSHandle rightHandle(thread, right); + JSHandle leftPrimitive(thread, JSTaggedValue::ToPrimitive(thread, leftHandle)); + RETURN_VALUE_IF_ABRUPT_COMPLETION(thread, JSTaggedValue::False()); + if (leftPrimitive->IsNumber()) { + ret = EqualWithIC(thread, leftPrimitive.GetTaggedValue(), + rightHandle.GetTaggedValue(), CompareOpType::NUMBER_BOOLEAN); + } else if (leftPrimitive->IsString()) { + ret = EqualWithIC(thread, leftPrimitive.GetTaggedValue(), + rightHandle.GetTaggedValue(), CompareOpType::STRING_BOOLEAN); + } else if (leftPrimitive->IsBoolean()) { + ret = EqualWithIC(thread, leftPrimitive.GetTaggedValue(), + rightHandle.GetTaggedValue(), CompareOpType::BOOLEAN_BOOLEAN); + } + break; + } + case CompareOpType::BOOLEAN_OBJ: { + JSHandle leftHandle(thread, left); + JSHandle rightHandle(thread, right); + JSHandle rightPrimitive(thread, JSTaggedValue::ToPrimitive(thread, rightHandle)); + RETURN_VALUE_IF_ABRUPT_COMPLETION(thread, JSTaggedValue::False()); + if (rightPrimitive->IsNumber()) { + ret = EqualWithIC(thread, rightPrimitive.GetTaggedValue(), + leftHandle.GetTaggedValue(), CompareOpType::NUMBER_BOOLEAN); + } else if (rightPrimitive->IsString()) { + ret = EqualWithIC(thread, rightPrimitive.GetTaggedValue(), + leftHandle.GetTaggedValue(), CompareOpType::STRING_BOOLEAN); + } else if (rightPrimitive->IsBoolean()) { + ret = EqualWithIC(thread, rightPrimitive.GetTaggedValue(), + leftHandle.GetTaggedValue(), CompareOpType::BOOLEAN_BOOLEAN); + } + break; + } + case CompareOpType::OBJ_OBJ: { + // if same type, must call Type::StrictEqual() + JSType xType = left.GetTaggedObject()->GetClass()->GetObjectType(); + JSType yType = right.GetTaggedObject()->GetClass()->GetObjectType(); + bool resultObj = false; + if (xType == yType) { + resultObj = JSTaggedValue::StrictEqual(thread, JSHandle(thread, left), + JSHandle(thread, right)); + } + ret = resultObj ? JSTaggedValue::True() : JSTaggedValue::False(); + break; + } + case CompareOpType::SYMBOL_SYMBOL: { + ret = left == right ? JSTaggedValue::True() : JSTaggedValue::False(); + break; + } + case CompareOpType::NULL_NULL: + case CompareOpType::NULL_UNDEFINED: + case CompareOpType::UNDEFINED_UNDEFINED: + case CompareOpType::UNDEFINED_NULL: { + ret = JSTaggedValue::True(); + break; + } + default: + ret = JSTaggedValue::False(); + } + return ret; +} + +JSTaggedValue CompareOp::NotEqualWithIC(JSThread *thread, JSTaggedValue left, + JSTaggedValue right, CompareOpType operationType) +{ + INTERPRETER_TRACE(thread, NotEqualWithIC); + JSTaggedValue res = EqualWithIC(thread, left, right, operationType); + return res == JSTaggedValue::True() ? JSTaggedValue::False() : JSTaggedValue::True(); +} + +ComparisonResult CompareOp::Compare(JSThread *thread, JSTaggedValue left, + JSTaggedValue right, CompareOpType operationType) +{ + INTERPRETER_TRACE(thread, Compare); + double leftDouble = 0; + double rightDouble = 0; + ComparisonResult ret = ComparisonResult::UNDEFINED; + switch (operationType) { + case CompareOpType::NUMBER_NUMBER: { + leftDouble = left.IsInt() ? static_cast(left.GetInt()) : left.GetDouble(); + rightDouble = right.IsInt() ? static_cast(right.GetInt()) : right.GetDouble(); + ret = JSTaggedValue::StrictNumberCompare(leftDouble, rightDouble); + break; + } + case CompareOpType::NUMBER_STRING: { + JSHandle leftHandle(thread, left); + JSHandle rightHandle(thread, right); + + rightDouble = JSTaggedValue::ToNumber(thread, rightHandle).GetNumber(); + RETURN_VALUE_IF_ABRUPT_COMPLETION(thread, ComparisonResult::UNDEFINED); + leftDouble = leftHandle.GetTaggedValue().GetNumber(); + ret = JSTaggedValue::StrictNumberCompare(leftDouble, rightDouble); + break; + } + case CompareOpType::NUMBER_BOOLEAN: { + leftDouble = left.GetNumber(); + if (right.GetRawData() == JSTaggedValue::VALUE_TRUE) { + rightDouble = 1; + } + ret = JSTaggedValue::StrictNumberCompare(leftDouble, rightDouble); + break; + } + case CompareOpType::NUMBER_OBJ: { + JSHandle leftHandle(thread, left); + JSHandle rightHandle(thread, right); + JSHandle rightPrimitive(thread, JSTaggedValue::ToPrimitive(thread, rightHandle)); + RETURN_VALUE_IF_ABRUPT_COMPLETION(thread, ComparisonResult::UNDEFINED); + if (rightPrimitive->IsNumber()) { + ret = Compare(thread, leftHandle.GetTaggedValue(), + rightPrimitive.GetTaggedValue(), CompareOpType::NUMBER_NUMBER); + } else if (rightPrimitive->IsString()) { + ret = Compare(thread, leftHandle.GetTaggedValue(), + rightPrimitive.GetTaggedValue(), CompareOpType::NUMBER_STRING); + } else if (rightPrimitive->IsBoolean()) { + ret = Compare(thread, leftHandle.GetTaggedValue(), + rightPrimitive.GetTaggedValue(), CompareOpType::NUMBER_BOOLEAN); + } + break; + } + case CompareOpType::STRING_STRING: { + auto xString = static_cast(left.GetTaggedObject()); + auto yString = static_cast(right.GetTaggedObject()); + int result = xString->Compare(yString); + if (result < 0) { + ret = ComparisonResult::LESS; + } else if (result == 0) { + ret = ComparisonResult::EQUAL; + } else { + ret = ComparisonResult::GREAT; + } + break; + } + case CompareOpType::STRING_NUMBER: { + JSHandle leftHandle(thread, left); + JSHandle rightHandle(thread, right); + leftDouble = JSTaggedValue::ToNumber(thread, leftHandle).GetNumber(); + RETURN_VALUE_IF_ABRUPT_COMPLETION(thread, ComparisonResult::UNDEFINED); + rightDouble = rightHandle.GetTaggedValue().GetNumber(); + ret = JSTaggedValue::StrictNumberCompare(leftDouble, rightDouble); + break; + } + case CompareOpType::STRING_BOOLEAN: { + JSHandle leftHandle(thread, left); + JSHandle rightHandle(thread, right); + leftDouble = JSTaggedValue::ToNumber(thread, leftHandle).GetNumber(); + RETURN_VALUE_IF_ABRUPT_COMPLETION(thread, ComparisonResult::UNDEFINED); + if (rightHandle.GetTaggedValue().GetRawData() == JSTaggedValue::VALUE_TRUE) { + rightDouble = 1; + } + ret = JSTaggedValue::StrictNumberCompare(leftDouble, rightDouble); + break; + } + case CompareOpType::STRING_OBJ: { + JSHandle leftHandle(thread, left); + JSHandle rightHandle(thread, right); + JSHandle rightPrimitive(thread, JSTaggedValue::ToPrimitive(thread, rightHandle)); + RETURN_VALUE_IF_ABRUPT_COMPLETION(thread, ComparisonResult::UNDEFINED); + if (rightPrimitive->IsNumber()) { + ret = Compare(thread, leftHandle.GetTaggedValue(), + rightPrimitive.GetTaggedValue(), CompareOpType::NUMBER_STRING); + } else if (rightPrimitive->IsString()) { + ret = Compare(thread, leftHandle.GetTaggedValue(), + rightPrimitive.GetTaggedValue(), CompareOpType::STRING_STRING); + } else if (rightPrimitive->IsBoolean()) { + ret = Compare(thread, leftHandle.GetTaggedValue(), + rightPrimitive.GetTaggedValue(), CompareOpType::STRING_BOOLEAN); + } + break; + } + case CompareOpType::BOOLEAN_BOOLEAN: { + if (left.GetRawData() == JSTaggedValue::VALUE_TRUE) { + leftDouble = 1; + } + if (right.GetRawData() == JSTaggedValue::VALUE_TRUE) { + rightDouble = 1; + } + ret = JSTaggedValue::StrictNumberCompare(leftDouble, rightDouble); + break; + } + + case CompareOpType::BOOLEAN_NUMBER: { + if (left.GetRawData() == JSTaggedValue::VALUE_TRUE) { + leftDouble = 1; + } + rightDouble = right.GetNumber(); + ret = JSTaggedValue::StrictNumberCompare(leftDouble, rightDouble); + break; + } + + case CompareOpType::BOOLEAN_STRING: { + JSHandle leftHandle(thread, left); + JSHandle rightHandle(thread, right); + rightDouble = JSTaggedValue::ToNumber(thread, rightHandle).GetNumber(); + RETURN_VALUE_IF_ABRUPT_COMPLETION(thread, ComparisonResult::UNDEFINED); + if (leftHandle.GetTaggedValue().GetRawData() == JSTaggedValue::VALUE_TRUE) { + leftDouble = 1; + } + ret = JSTaggedValue::StrictNumberCompare(leftDouble, rightDouble); + break; + } + + case CompareOpType::BOOLEAN_OBJ: { + JSHandle leftHandle(thread, left); + JSHandle rightHandle(thread, right); + JSHandle rightPrimitive(thread, JSTaggedValue::ToPrimitive(thread, rightHandle)); + RETURN_VALUE_IF_ABRUPT_COMPLETION(thread, ComparisonResult::UNDEFINED); + if (rightPrimitive->IsNumber()) { + ret = Compare(thread, rightPrimitive.GetTaggedValue(), + leftHandle.GetTaggedValue(), CompareOpType::NUMBER_BOOLEAN); + } else if (rightPrimitive->IsString()) { + ret = Compare(thread, rightPrimitive.GetTaggedValue(), + leftHandle.GetTaggedValue(), CompareOpType::STRING_BOOLEAN); + } else if (rightPrimitive->IsBoolean()) { + ret = Compare(thread, rightPrimitive.GetTaggedValue(), + leftHandle.GetTaggedValue(), CompareOpType::BOOLEAN_BOOLEAN); + } + break; + } + case CompareOpType::OBJ_OBJ: { + JSHandle leftHandle(thread, left); + JSHandle rightHandle(thread, right); + ret = JSTaggedValue::Compare(thread, leftHandle, rightHandle); + break; + } + case CompareOpType::OBJ_NUMBER: { + JSHandle leftHandle(thread, left); + JSHandle rightHandle(thread, right); + JSHandle leftPrimitive(thread, JSTaggedValue::ToPrimitive(thread, leftHandle)); + RETURN_VALUE_IF_ABRUPT_COMPLETION(thread, ComparisonResult::UNDEFINED); + if (leftPrimitive->IsNumber()) { + ret = Compare(thread, leftPrimitive.GetTaggedValue(), + rightHandle.GetTaggedValue(), CompareOpType::NUMBER_NUMBER); + } else if (leftPrimitive->IsString()) { + ret = Compare(thread, leftPrimitive.GetTaggedValue(), + rightHandle.GetTaggedValue(), CompareOpType::STRING_NUMBER); + } else if (leftPrimitive->IsBoolean()) { + ret = Compare(thread, leftPrimitive.GetTaggedValue(), + rightHandle.GetTaggedValue(), CompareOpType::BOOLEAN_NUMBER); + } + break; + } + case CompareOpType::OBJ_STRING: { + JSHandle leftHandle(thread, left); + JSHandle rightHandle(thread, right); + JSHandle leftPrimitive(thread, JSTaggedValue::ToPrimitive(thread, leftHandle)); + RETURN_VALUE_IF_ABRUPT_COMPLETION(thread, ComparisonResult::UNDEFINED); + if (leftPrimitive->IsNumber()) { + ret = Compare(thread, leftPrimitive.GetTaggedValue(), + rightHandle.GetTaggedValue(), CompareOpType::NUMBER_STRING); + } else if (leftPrimitive->IsString()) { + ret = Compare(thread, leftPrimitive.GetTaggedValue(), + rightHandle.GetTaggedValue(), CompareOpType::STRING_STRING); + } else if (leftPrimitive->IsBoolean()) { + ret = Compare(thread, leftPrimitive.GetTaggedValue(), + rightHandle.GetTaggedValue(), CompareOpType::BOOLEAN_STRING); + } + break; + } + case CompareOpType::OBJ_BOOLEAN: { + JSHandle leftHandle(thread, left); + JSHandle rightHandle(thread, right); + JSHandle leftPrimitive(thread, JSTaggedValue::ToPrimitive(thread, leftHandle)); + RETURN_VALUE_IF_ABRUPT_COMPLETION(thread, ComparisonResult::UNDEFINED); + if (leftPrimitive->IsNumber()) { + ret = Compare(thread, leftPrimitive.GetTaggedValue(), + rightHandle.GetTaggedValue(), CompareOpType::NUMBER_BOOLEAN); + } else if (leftPrimitive->IsString()) { + ret = Compare(thread, leftPrimitive.GetTaggedValue(), + rightHandle.GetTaggedValue(), CompareOpType::STRING_BOOLEAN); + } else if (leftPrimitive->IsBoolean()) { + ret = Compare(thread, leftPrimitive.GetTaggedValue(), + rightHandle.GetTaggedValue(), CompareOpType::BOOLEAN_BOOLEAN); + } + break; + } + default: + ret = ComparisonResult::UNDEFINED; + } + return ret; +} + +JSTaggedValue CompareOp::LessDynWithIC(JSThread *thread, JSTaggedValue left, + JSTaggedValue right, CompareOpType operationType) +{ + INTERPRETER_TRACE(thread, LessDynWithIC); + bool ret = CompareOp::Compare(thread, left, right, operationType) == ComparisonResult::LESS; + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + return (ret ? JSTaggedValue::True() : JSTaggedValue::False()); +} + +JSTaggedValue CompareOp::LessEqDynWithIC(JSThread *thread, JSTaggedValue left, + JSTaggedValue right, CompareOpType operationType) +{ + INTERPRETER_TRACE(thread, LessEqDynWithIC); + bool ret = CompareOp::Compare(thread, left, right, operationType) <= ComparisonResult::EQUAL; + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + return (ret ? JSTaggedValue::True() : JSTaggedValue::False()); +} + +JSTaggedValue CompareOp::GreaterDynWithIC(JSThread *thread, JSTaggedValue left, + JSTaggedValue right, CompareOpType operationType) +{ + INTERPRETER_TRACE(thread, GreaterDynWithIC); + bool ret = CompareOp::Compare(thread, left, right, operationType) == ComparisonResult::GREAT; + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + return (ret ? JSTaggedValue::True() : JSTaggedValue::False()); +} + +JSTaggedValue CompareOp::GreaterEqDynWithIC(JSThread *thread, JSTaggedValue left, + JSTaggedValue right, CompareOpType operationType) +{ + INTERPRETER_TRACE(thread, GreaterEqDynWithIC); + ComparisonResult comparison = CompareOp::Compare(thread, left, right, operationType); + bool ret = (comparison == ComparisonResult::GREAT) || (comparison == ComparisonResult::EQUAL); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + return (ret ? JSTaggedValue::True() : JSTaggedValue::False()); +} +} // namespace panda::ecmascript diff --git a/runtime/ic/ic_compare_op.h b/runtime/ic/ic_compare_op.h new file mode 100644 index 000000000..b88e0752c --- /dev/null +++ b/runtime/ic/ic_compare_op.h @@ -0,0 +1,78 @@ +/* + * Copyright (c) 2021-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_IC_IC_COMPARE_H +#define ECMASCRIPT_IC_IC_COMPARE_H + +#include "plugins/ecmascript/runtime/js_function.h" +#include "plugins/ecmascript/runtime/js_thread.h" +#include "plugins/ecmascript/runtime/object_factory.h" + +namespace panda::ecmascript { +enum class CompareOpType { + NUMBER_NUMBER, + NUMBER_STRING, + NUMBER_BOOLEAN, + NUMBER_OBJ, + STRING_STRING, + STRING_NUMBER, + STRING_BOOLEAN, + STRING_OBJ, + BOOLEAN_BOOLEAN, + BOOLEAN_NUMBER, + BOOLEAN_STRING, + BOOLEAN_OBJ, + OBJ_OBJ, + OBJ_NUMBER, + OBJ_STRING, + OBJ_BOOLEAN, + SYMBOL_SYMBOL, + NULL_NULL, + NULL_UNDEFINED, + UNDEFINED_UNDEFINED, + UNDEFINED_NULL, + UNDEFINED_BLLEAN, + OTHER, +}; + +class CompareOp { +public: + + CompareOp() = default; + ~CompareOp() = default; + + static JSTaggedValue EqualWithIC(JSThread* thread, JSTaggedValue left, + JSTaggedValue right, CompareOpType operationType); + + static JSTaggedValue NotEqualWithIC(JSThread *thread, JSTaggedValue left, + JSTaggedValue right, CompareOpType operationType); + + static ComparisonResult Compare(JSThread *thread, JSTaggedValue left, + JSTaggedValue right, CompareOpType operationType); + + static JSTaggedValue LessDynWithIC(JSThread *thread, JSTaggedValue left, + JSTaggedValue right, CompareOpType operationType); + + static JSTaggedValue LessEqDynWithIC(JSThread *thread, JSTaggedValue left, + JSTaggedValue right, CompareOpType operationType); + + static JSTaggedValue GreaterDynWithIC(JSThread *thread, JSTaggedValue left, + JSTaggedValue right, CompareOpType operationType); + + static JSTaggedValue GreaterEqDynWithIC(JSThread *thread, JSTaggedValue left, + JSTaggedValue right, CompareOpType operationType); +}; +} // namespace panda::ecmascript +#endif // ECMASCRIPT_IC_IC_COMPAREOP_H diff --git a/runtime/ic/ic_handler-inl.h b/runtime/ic/ic_handler-inl.h new file mode 100644 index 000000000..f888ec865 --- /dev/null +++ b/runtime/ic/ic_handler-inl.h @@ -0,0 +1,144 @@ +/* + * Copyright (c) 2021-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_IC_IC_HANDLER_INL_H +#define ECMASCRIPT_IC_IC_HANDLER_INL_H + +#include "plugins/ecmascript/runtime/ecma_vm.h" +#include "plugins/ecmascript/runtime/global_env.h" +#include "plugins/ecmascript/runtime/js_object-inl.h" +#include "ic_handler.h" + +namespace panda::ecmascript { +JSHandle LoadHandler::LoadElement(const JSThread *thread) +{ + uint32_t handler = 0; + KindBit::Set(HandlerKind::ELEMENT, &handler); + return JSHandle(thread, JSTaggedValue(handler)); +} + +JSHandle LoadHandler::LoadProperty(const JSThread *thread, const ObjectOperator &op) +{ + uint32_t handler = 0; + ASSERT(!op.IsElement()); + if (!op.IsFound()) { + KindBit::Set(HandlerKind::NON_EXIST, &handler); + return JSHandle(thread, JSTaggedValue(handler)); + } + ASSERT(op.IsFastMode()); + + JSTaggedValue val = op.GetValue(); + if (val.IsPropertyBox()) { + return JSHandle(thread, val); + } + bool hasAccessor = op.IsAccessorDescriptor(); + AccessorBit::Set(hasAccessor, &handler); + if (!hasAccessor) { + KindBit::Set(HandlerKind::FIELD, &handler); + } + + if (op.IsInlinedProps()) { + InlinedPropsBit::Set(true, &handler); + JSHandle holder = JSHandle::Cast(op.GetHolder()); + auto index = holder->GetJSHClass()->GetInlinedPropertiesIndex(op.GetIndex()); + OffsetBit::Set(index, &handler); + return JSHandle(thread, JSTaggedValue(handler)); + } + if (op.IsFastMode()) { + OffsetBit::Set(op.GetIndex(), &handler); + return JSHandle(thread, JSTaggedValue(handler)); + } + UNREACHABLE(); +} + +JSHandle PrototypeHandler::LoadPrototype(const JSThread *thread, + const ObjectOperator &op, + const JSHandle &hclass) +{ + ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); + JSHandle handlerInfo = LoadHandler::LoadProperty(thread, op); + JSHandle handler = factory->NewPrototypeHandler(); + handler->SetHandlerInfo(thread, handlerInfo); + if (op.IsFound()) { + handler->SetHolder(thread, op.GetHolder()); + } + auto result = JSHClass::EnableProtoChangeMarker(thread, hclass); + handler->SetProtoCell(thread, result); + return JSHandle::Cast(handler); +} + +JSHandle PrototypeHandler::StorePrototype(const JSThread *thread, + const ObjectOperator &op, + const JSHandle &hclass) +{ + ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); + JSHandle handler = factory->NewPrototypeHandler(); + JSHandle handlerInfo = StoreHandler::StoreProperty(thread, op); + handler->SetHandlerInfo(thread, handlerInfo); + handler->SetHolder(thread, op.GetHolder()); + auto result = JSHClass::EnableProtoChangeMarker(thread, hclass); + handler->SetProtoCell(thread, result); + return JSHandle::Cast(handler); +} + +JSHandle StoreHandler::StoreElement(const JSThread *thread, JSHandle receiver) +{ + uint32_t handler = 0; + KindBit::Set(HandlerKind::ELEMENT, &handler); + + if (receiver->IsJSArray()) { + IsJSArrayBit::Set(true, &handler); + } + return JSHandle(thread, JSTaggedValue(handler)); +} + +JSHandle StoreHandler::StoreProperty(const JSThread *thread, const ObjectOperator &op) +{ + ASSERT(!op.IsElement()); + uint32_t handler = 0; + JSTaggedValue val = op.GetValue(); + if (val.IsPropertyBox()) { + return JSHandle(thread, val); + } + bool hasSetter = op.IsAccessorDescriptor(); + AccessorBit::Set(hasSetter, &handler); + if (!hasSetter) { + KindBit::Set(HandlerKind::FIELD, &handler); + } + if (op.IsInlinedProps()) { + InlinedPropsBit::Set(true, &handler); + JSHandle receiver = JSHandle::Cast(op.GetReceiver()); + auto index = receiver->GetJSHClass()->GetInlinedPropertiesIndex(op.GetIndex()); + OffsetBit::Set(index, &handler); + return JSHandle(thread, JSTaggedValue(handler)); + } + ASSERT(op.IsFastMode()); + OffsetBit::Set(op.GetIndex(), &handler); + return JSHandle(thread, JSTaggedValue(handler)); +} + +JSHandle TransitionHandler::StoreTransition(const JSThread *thread, const ObjectOperator &op) +{ + ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); + JSHandle handler = factory->NewTransitionHandler(); + JSHandle handlerInfo = StoreHandler::StoreProperty(thread, op); + handler->SetHandlerInfo(thread, handlerInfo); + auto hclass = JSObject::Cast(op.GetReceiver()->GetHeapObject())->GetJSHClass(); + handler->SetTransitionHClass(thread, JSTaggedValue(hclass)); + return JSHandle::Cast(handler); +} +} // namespace panda::ecmascript + +#endif // ECMASCRIPT_IC_IC_HANDLER_INL_H diff --git a/runtime/ic/ic_handler.h b/runtime/ic/ic_handler.h new file mode 100644 index 000000000..a90011770 --- /dev/null +++ b/runtime/ic/ic_handler.h @@ -0,0 +1,155 @@ +/* + * Copyright (c) 2021-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_IC_IC_HANDLER_H +#define ECMASCRIPT_IC_IC_HANDLER_H + +#include "plugins/ecmascript/runtime/ecma_macros.h" +#include "plugins/ecmascript/runtime/js_tagged_value-inl.h" +#include "plugins/ecmascript/runtime/mem/tagged_object.h" + +namespace panda::ecmascript { +class HandlerBase { +public: + static constexpr uint32_t KIND_BIT_LENGTH = 3; + enum HandlerKind { + NONE = 0, + FIELD, + ELEMENT, + DICTIONARY, + NON_EXIST, + }; + + using KindBit = BitField; + using InlinedPropsBit = KindBit::NextFlag; + using AccessorBit = InlinedPropsBit::NextFlag; + using InternalAccessorBit = AccessorBit::NextFlag; + using IsJSArrayBit = InternalAccessorBit::NextFlag; + using OffsetBit = IsJSArrayBit::NextField; + + HandlerBase() = default; + virtual ~HandlerBase() = default; + + DEFAULT_MOVE_SEMANTIC(HandlerBase); + DEFAULT_COPY_SEMANTIC(HandlerBase); + + static inline bool IsAccessor(uint32_t handler) + { + return AccessorBit::Get(handler); + } + + static inline bool IsInternalAccessor(uint32_t handler) + { + return InternalAccessorBit::Get(handler); + } + + static inline bool IsNonExist(uint32_t handler) + { + return GetKind(handler) == HandlerKind::NON_EXIST; + } + + static inline bool IsField(uint32_t handler) + { + return GetKind(handler) == HandlerKind::FIELD; + } + + static inline bool IsElement(uint32_t handler) + { + return GetKind(handler) == HandlerKind::ELEMENT; + } + + static inline bool IsDictionary(uint32_t handler) + { + return GetKind(handler) == HandlerKind::DICTIONARY; + } + + static inline bool IsInlinedProps(uint32_t handler) + { + return InlinedPropsBit::Get(handler); + } + + static inline HandlerKind GetKind(uint32_t handler) + { + return KindBit::Get(handler); + } + + static inline bool IsJSArray(uint32_t handler) + { + return IsJSArrayBit::Get(handler); + } + + static inline int GetOffset(uint32_t handler) + { + return OffsetBit::Get(handler); + } +}; + +class LoadHandler final : public HandlerBase { +public: + static inline JSHandle LoadProperty(const JSThread *thread, const ObjectOperator &op); + static inline JSHandle LoadElement(const JSThread *thread); +}; + +class StoreHandler final : public HandlerBase { +public: + static inline JSHandle StoreProperty(const JSThread *thread, const ObjectOperator &op); + static inline JSHandle StoreElement(const JSThread *thread, JSHandle receiver); +}; + +class TransitionHandler : public TaggedObject { +public: + static TransitionHandler *Cast(TaggedObject *object) + { + ASSERT(JSTaggedValue(object).IsTransitionHandler()); + return static_cast(object); + } + + static inline JSHandle StoreTransition(const JSThread *thread, const ObjectOperator &op); + + static constexpr size_t HANDLER_INFO_OFFSET = TaggedObjectSize(); + ACCESSORS(HandlerInfo, HANDLER_INFO_OFFSET, TRANSITION_HCLASS_OFFSET) + + ACCESSORS(TransitionHClass, TRANSITION_HCLASS_OFFSET, SIZE) + + DECL_VISIT_OBJECT(HANDLER_INFO_OFFSET, SIZE) + DECL_DUMP() +}; + +class PrototypeHandler : public TaggedObject { +public: + static PrototypeHandler *Cast(TaggedObject *object) + { + ASSERT(JSTaggedValue(object).IsPrototypeHandler()); + return static_cast(object); + } + + static inline JSHandle LoadPrototype(const JSThread *thread, const ObjectOperator &op, + const JSHandle &hclass); + static inline JSHandle StorePrototype(const JSThread *thread, const ObjectOperator &op, + const JSHandle &hclass); + + static constexpr size_t HANDLER_INFO_OFFSET = TaggedObjectSize(); + + ACCESSORS(HandlerInfo, HANDLER_INFO_OFFSET, PROTO_CELL_OFFSET) + + ACCESSORS(ProtoCell, PROTO_CELL_OFFSET, HOLDER_OFFSET) + + ACCESSORS(Holder, HOLDER_OFFSET, SIZE) + + DECL_VISIT_OBJECT(HANDLER_INFO_OFFSET, SIZE) + DECL_DUMP() +}; +} // namespace panda::ecmascript +#endif // ECMASCRIPT_IC_IC_HANDLER_H diff --git a/runtime/ic/ic_runtime.cpp b/runtime/ic/ic_runtime.cpp new file mode 100644 index 000000000..90e857ef1 --- /dev/null +++ b/runtime/ic/ic_runtime.cpp @@ -0,0 +1,229 @@ +/* + * Copyright (c) 2021-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 "plugins/ecmascript/runtime/ic/ic_runtime.h" +#include "plugins/ecmascript/runtime/global_dictionary-inl.h" +#include "plugins/ecmascript/runtime/global_env.h" +#include "plugins/ecmascript/runtime/ic/ic_handler-inl.h" +#include "plugins/ecmascript/runtime/ic/profile_type_info.h" +#include "plugins/ecmascript/runtime/interpreter/fast_runtime_stub-inl.h" +#include "plugins/ecmascript/runtime/interpreter/slow_runtime_stub.h" +#include "plugins/ecmascript/runtime/js_function.h" +#include "plugins/ecmascript/runtime/js_hclass-inl.h" +#include "plugins/ecmascript/runtime/js_proxy.h" +#include "plugins/ecmascript/runtime/js_tagged_value-inl.h" +#include "plugins/ecmascript/runtime/js_typed_array.h" +#include "plugins/ecmascript/runtime/object_factory-inl.h" +#include "plugins/ecmascript/runtime/tagged_dictionary.h" +namespace panda::ecmascript { +#define TRACE_IC 0 // NOLINTNEXTLINE(cppcoreguidelines-macro-usage) + +void ICRuntime::UpdateLoadHandler(const ObjectOperator &op, JSHandle key, + JSHandle receiver) +{ + if (icAccessor_.GetICState() == ProfileTypeAccessor::ICState::MEGA) { + return; + } + if (IsNamedIC(GetICKind())) { + key = JSHandle(); + } + JSHandle handlerValue; + JSHandle hclass(GetThread(), JSHandle::Cast(receiver)->GetClass()); + if (op.IsElement()) { + if (!op.IsFound() && hclass->IsDictionaryElement()) { + return; + } + handlerValue = LoadHandler::LoadElement(thread_); + } else { + if (!op.IsFound()) { + handlerValue = PrototypeHandler::LoadPrototype(thread_, op, hclass); + } else if (!op.IsOnPrototype()) { + handlerValue = LoadHandler::LoadProperty(thread_, op); + } else { + // do not support global prototype ic + if (IsGlobalLoadIC(GetICKind())) { + return; + } + handlerValue = PrototypeHandler::LoadPrototype(thread_, op, hclass); + } + } + + if (key.IsEmpty()) { + icAccessor_.AddHandlerWithoutKey(JSHandle::Cast(hclass), handlerValue); + } else if (op.IsElement()) { + // do not support global element ic + if (IsGlobalLoadIC(GetICKind())) { + return; + } + icAccessor_.AddElementHandler(JSHandle::Cast(hclass), handlerValue); + } else { + icAccessor_.AddHandlerWithKey(key, JSHandle::Cast(hclass), handlerValue); + } +} + +void ICRuntime::UpdateStoreHandler(const ObjectOperator &op, JSHandle key, + JSHandle receiver) +{ + if (icAccessor_.GetICState() == ProfileTypeAccessor::ICState::MEGA) { + return; + } + if (IsNamedIC(GetICKind())) { + key = JSHandle(); + } + JSHandle handlerValue; + if (op.IsElement()) { + handlerValue = StoreHandler::StoreElement(thread_, receiver); + } else { + ASSERT(op.IsFound()); + if (op.IsOnPrototype()) { + // do not support global prototype ic + if (IsGlobalStoreIC(GetICKind())) { + return; + } + JSHandle hclass(thread_, JSHandle::Cast(receiver)->GetClass()); + handlerValue = PrototypeHandler::StorePrototype(thread_, op, hclass); + } else if (op.IsTransition()) { + handlerValue = TransitionHandler::StoreTransition(thread_, op); + } else { + handlerValue = StoreHandler::StoreProperty(thread_, op); + } + } + + if (key.IsEmpty()) { + icAccessor_.AddHandlerWithoutKey(receiverHClass_, handlerValue); + } else if (op.IsElement()) { + // do not support global element ic + if (IsGlobalStoreIC(GetICKind())) { + return; + } + icAccessor_.AddElementHandler(receiverHClass_, handlerValue); + } else { + icAccessor_.AddHandlerWithKey(key, receiverHClass_, handlerValue); + } +} + +void ICRuntime::TraceIC([[maybe_unused]] JSHandle receiver, + [[maybe_unused]] JSHandle key) const +{ +#if TRACE_IC + auto kind = ICKindToString(GetICKind()); + auto state = ProfileTypeAccessor::ICStateToString(icAccessor_.GetICState()); + if (key->IsString()) { + LOG(ERROR, RUNTIME) << kind << " miss key is: " << JSHandle::Cast(key)->GetCString().get() + << ", receiver is " << receiver->GetHeapObject()->GetClass()->IsDictionaryMode() + << ", state is " << state; + } else { + LOG(ERROR, RUNTIME) << kind << " miss " + << ", state is " + << ", receiver is " << receiver->GetHeapObject()->GetClass()->IsDictionaryMode() << state; + } +#endif +} + +JSTaggedValue LoadICRuntime::LoadMiss(JSHandle receiver, JSHandle key) +{ + if (receiver->IsTypedArray() || !receiver->IsJSObject()) { + return JSTaggedValue::GetProperty(thread_, receiver, key).GetValue().GetTaggedValue(); + } + + // global variable find from global record firstly + if (GetICKind() == ICKind::TRY_NAMED_GLOBAL_LOAD_IC) { + bool found = false; + JSTaggedValue box = SlowRuntimeStub::LdGlobalRecord(thread_, key.GetTaggedValue(), &found); + if (found) { + ASSERT(box.IsPropertyBox()); + icAccessor_.AddGlobalRecordHandler(JSHandle(thread_, box)); + return PropertyBox::Cast(box.GetTaggedObject())->GetValue(); + } + } + + ObjectOperator op(GetThread(), receiver, key); + auto result = JSHandle(thread_, JSObject::GetProperty(GetThread(), &op)); + if (!op.IsFound() && + (GetICKind() == ICKind::NAMED_GLOBAL_LOAD_IC || GetICKind() == ICKind::TRY_NAMED_GLOBAL_LOAD_IC)) { + return SlowRuntimeStub::ThrowReferenceError(GetThread(), key.GetTaggedValue(), " is not definded"); + } + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(GetThread()); + // ic-switch + if (!GetThread()->GetEcmaVM()->ICEnable()) { + icAccessor_.SetAsMega(); + return result.GetTaggedValue(); + } +#ifndef NDEBUG + TraceIC(receiver, key); +#endif + // do not cache element + if (!op.IsFastMode() && op.IsFound()) { + icAccessor_.SetAsMega(); + return result.GetTaggedValue(); + } + + UpdateLoadHandler(op, key, receiver); + return result.GetTaggedValue(); +} + +JSTaggedValue StoreICRuntime::StoreMiss(JSHandle receiver, JSHandle key, + JSHandle value) +{ + if (receiver->IsTypedArray() || !receiver->IsJSObject()) { + bool success = JSTaggedValue::SetProperty(GetThread(), receiver, key, value, true); + return success ? JSTaggedValue::Undefined() : JSTaggedValue::Exception(); + } + + // global variable find from global record firstly + if (GetICKind() == ICKind::TRY_NAMED_GLOBAL_STORE_IC) { + bool found = false; + JSTaggedValue box = SlowRuntimeStub::LdGlobalRecord(thread_, key.GetTaggedValue(), &found); + if (found) { + ASSERT(box.IsPropertyBox()); + SlowRuntimeStub::TryUpdateGlobalRecord(thread_, key.GetTaggedValue(), value.GetTaggedValue()); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread_); + icAccessor_.AddGlobalRecordHandler(JSHandle(thread_, box)); + return JSTaggedValue::Undefined(); + } + FastRuntimeStub::GetGlobalOwnProperty(receiver.GetTaggedValue(), key.GetTaggedValue(), &found); + if (!found) { + return SlowRuntimeStub::ThrowReferenceError(GetThread(), key.GetTaggedValue(), " is not defined"); + } + } + UpdateReceiverHClass(JSHandle(GetThread(), JSHandle::Cast(receiver)->GetClass())); + + ObjectOperator op(GetThread(), receiver, key); + bool success = JSObject::SetProperty(&op, value, true); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread_); + if (!success && + (GetICKind() == ICKind::NAMED_GLOBAL_STORE_IC || GetICKind() == ICKind::TRY_NAMED_GLOBAL_STORE_IC)) { + return SlowRuntimeStub::ThrowReferenceError(GetThread(), key.GetTaggedValue(), " is not defined"); + } + // ic-switch + if (!GetThread()->GetEcmaVM()->ICEnable()) { + icAccessor_.SetAsMega(); + return success ? JSTaggedValue::Undefined() : JSTaggedValue::Exception(); + } +#ifndef NDEBUG + TraceIC(receiver, key); +#endif + // do not cache element + if (!op.IsFastMode()) { + icAccessor_.SetAsMega(); + return success ? JSTaggedValue::Undefined() : JSTaggedValue::Exception(); + } + if (success) { + UpdateStoreHandler(op, key, receiver); + return JSTaggedValue::Undefined(); + } + return JSTaggedValue::Exception(); +} +} // namespace panda::ecmascript diff --git a/runtime/ic/ic_runtime.h b/runtime/ic/ic_runtime.h new file mode 100644 index 000000000..67a097272 --- /dev/null +++ b/runtime/ic/ic_runtime.h @@ -0,0 +1,100 @@ +/* + * Copyright (c) 2021-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_IC_IC_RUNTIME_H +#define ECMASCRIPT_IC_IC_RUNTIME_H + +#include "plugins/ecmascript/runtime/ic/profile_type_info.h" +#include "plugins/ecmascript/runtime/accessor_data.h" +#include "plugins/ecmascript/runtime/ecma_vm.h" +#include "plugins/ecmascript/runtime/js_tagged_value.h" +#include "plugins/ecmascript/runtime/js_handle.h" + +namespace panda::ecmascript { +class ProfileTypeInfo; +class JSThread; +class ObjectOperator; + +class ICRuntime { +public: + ICRuntime(JSThread *thread, JSHandle profileTypeInfo, uint32_t slotId, ICKind kind) + : thread_(thread), icAccessor_(thread, profileTypeInfo, slotId, kind) + { + } + + ~ICRuntime() = default; + + void UpdateLoadHandler(const ObjectOperator &op, JSHandle key, JSHandle receiver); + void UpdateStoreHandler(const ObjectOperator &op, JSHandle key, JSHandle receiver); + + JSThread *GetThread() const + { + return thread_; + } + + void UpdateReceiverHClass(JSHandle receiverHClass) + { + receiverHClass_ = receiverHClass; + } + + ICKind GetICKind() const + { + return icAccessor_.GetKind(); + } + + void TraceIC(JSHandle receiver, JSHandle key) const; + + NO_MOVE_SEMANTIC(ICRuntime); + NO_COPY_SEMANTIC(ICRuntime); + +protected: + JSThread *thread_; // NOLINT(misc-non-private-member-variables-in-classes) + JSHandle receiverHClass_ {}; // NOLINT(misc-non-private-member-variables-in-classes) + ProfileTypeAccessor icAccessor_; // NOLINT(misc-non-private-member-variables-in-classes) +}; + +class LoadICRuntime : public ICRuntime { +public: + LoadICRuntime(JSThread *thread, JSHandle profileTypeInfo, uint32_t slotId, ICKind kind) + : ICRuntime(thread, profileTypeInfo, slotId, kind) + { + } + + ~LoadICRuntime() = default; + + JSTaggedValue LoadMiss(JSHandle receiver, JSHandle key); + + NO_MOVE_SEMANTIC(LoadICRuntime); + NO_COPY_SEMANTIC(LoadICRuntime); +}; + +class StoreICRuntime : public ICRuntime { +public: + StoreICRuntime(JSThread *thread, JSHandle profileTypeInfo, uint32_t slotId, ICKind kind) + : ICRuntime(thread, profileTypeInfo, slotId, kind) + { + } + + ~StoreICRuntime() = default; + + JSTaggedValue StoreMiss(JSHandle receiver, JSHandle key, + JSHandle value); + + NO_MOVE_SEMANTIC(StoreICRuntime); + NO_COPY_SEMANTIC(StoreICRuntime); +}; +} // namespace panda::ecmascript + +#endif // ECMASCRIPT_IC_IC_RUNTIME_H diff --git a/runtime/ic/ic_runtime_stub-inl.h b/runtime/ic/ic_runtime_stub-inl.h new file mode 100644 index 000000000..ef77544f4 --- /dev/null +++ b/runtime/ic/ic_runtime_stub-inl.h @@ -0,0 +1,422 @@ +/* + * Copyright (c) 2021-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_IC_IC_RUNTIME_STUB_INL_H_ +#define ECMASCRIPT_IC_IC_RUNTIME_STUB_INL_H_ + +#include "ic_runtime_stub.h" +#include "ic_handler.h" +#include "ic_runtime.h" +#include "profile_type_info.h" +#include "plugins/ecmascript/runtime/js_tagged_value-inl.h" +#include "plugins/ecmascript/runtime/js_array.h" +#include "plugins/ecmascript/runtime/js_hclass-inl.h" +#include "plugins/ecmascript/runtime/global_dictionary-inl.h" +#include "plugins/ecmascript/runtime/js_function.h" +#include "plugins/ecmascript/runtime/js_proxy.h" +#include "plugins/ecmascript/runtime/global_env.h" +#include "plugins/ecmascript/runtime/object_factory-inl.h" +#include "plugins/ecmascript/runtime/js_handle.h" +#include "plugins/ecmascript/runtime/interpreter/fast_runtime_stub-inl.h" +#include "plugins/ecmascript/runtime/ic/proto_change_details.h" + +#include "plugins/ecmascript/runtime/vmstat/runtime_stat.h" +#include "plugins/ecmascript/runtime/runtime_call_id.h" + +namespace panda::ecmascript { +JSTaggedValue ICRuntimeStub::LoadGlobalICByName(JSThread *thread, ProfileTypeInfo *profileTypeInfo, + JSTaggedValue globalValue, JSTaggedValue key, uint32_t slotId, + bool isTryLoad) +{ + INTERPRETER_TRACE(thread, LoadGlobalICByName); + JSTaggedValue handler = profileTypeInfo->Get(slotId); + if (handler.IsHeapObject()) { + auto result = LoadGlobal(handler); + if (!result.IsHole()) { + return result; + } + } + + [[maybe_unused]] EcmaHandleScope handleScope(thread); + auto keyHandle = JSHandle(thread, key); + auto receiverHandle = JSHandle(thread, globalValue); + auto profileInfoHandle = JSHandle(thread, profileTypeInfo); + LoadICRuntime icRuntime(thread, JSHandle::Cast(profileInfoHandle), slotId, + isTryLoad ? ICKind::TRY_NAMED_GLOBAL_LOAD_IC : ICKind::NAMED_GLOBAL_LOAD_IC); + return icRuntime.LoadMiss(receiverHandle, keyHandle); +} + +JSTaggedValue ICRuntimeStub::StoreGlobalICByName(JSThread *thread, ProfileTypeInfo *profileTypeInfo, + JSTaggedValue globalValue, JSTaggedValue key, JSTaggedValue value, + uint32_t slotId, bool isTryStore) +{ + INTERPRETER_TRACE(thread, StoreGlobalICByName); + JSTaggedValue handler = profileTypeInfo->Get(slotId); + if (handler.IsHeapObject()) { + auto result = StoreGlobal(thread, value, handler); + if (!result.IsHole()) { + return result; + } + } + + [[maybe_unused]] EcmaHandleScope handleScope(thread); + auto keyHandle = JSHandle(thread, key); + auto receiverHandle = JSHandle(thread, globalValue); + auto valueHandle = JSHandle(thread, value); + auto profileInfoHandle = JSHandle(thread, profileTypeInfo); + StoreICRuntime icRuntime(thread, JSHandle::Cast(profileInfoHandle), slotId, + isTryStore ? ICKind::TRY_NAMED_GLOBAL_STORE_IC : ICKind::NAMED_GLOBAL_STORE_IC); + return icRuntime.StoreMiss(receiverHandle, keyHandle, valueHandle); +} + +JSTaggedValue ICRuntimeStub::CheckPolyHClass(JSTaggedValue cachedValue, JSHClass *hclass) +{ + if (!cachedValue.IsWeak()) { + ASSERT(cachedValue.IsTaggedArray()); + TaggedArray *array = TaggedArray::Cast(cachedValue.GetHeapObject()); + 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); + if (result != JSTaggedValue::Undefined() && result.GetWeakReferent() == hclass) { + return array->Get(i + 1); + } + } + } + return JSTaggedValue::Hole(); +} + +JSTaggedValue ICRuntimeStub::TryLoadICByName(JSThread *thread, JSTaggedValue receiver, JSTaggedValue firstValue, + JSTaggedValue secondValue) +{ + INTERPRETER_TRACE(thread, TryLoadICByName); + if (LIKELY(receiver.IsHeapObject())) { + auto hclass = receiver.GetTaggedObject()->GetClass(); + if (LIKELY(firstValue.GetWeakReferentUnChecked() == hclass)) { + return LoadICWithHandler(thread, receiver, receiver, secondValue); + } + JSTaggedValue cachedHandler = CheckPolyHClass(firstValue, hclass); + if (!cachedHandler.IsHole()) { + return LoadICWithHandler(thread, receiver, receiver, cachedHandler); + } + } + return JSTaggedValue::Hole(); +} + +JSTaggedValue ICRuntimeStub::TryLoadICByValue(JSThread *thread, JSTaggedValue receiver, JSTaggedValue key, + JSTaggedValue firstValue, JSTaggedValue secondValue) +{ + INTERPRETER_TRACE(thread, TryLoadICByValue); + if (receiver.IsHeapObject()) { + auto hclass = receiver.GetTaggedObject()->GetClass(); + if (firstValue.GetWeakReferentUnChecked() == hclass) { + ASSERT(HandlerBase::IsElement(secondValue.GetInt())); + return LoadElement(JSObject::Cast(receiver.GetHeapObject()), key); + } + // Check key + if (firstValue == key) { + JSTaggedValue cachedHandler = CheckPolyHClass(secondValue, hclass); + if (!cachedHandler.IsHole()) { + return LoadICWithHandler(thread, receiver, receiver, cachedHandler); + } + } + } + return JSTaggedValue::Hole(); +} + +JSTaggedValue ICRuntimeStub::TryStoreICByValue(JSThread *thread, JSTaggedValue receiver, JSTaggedValue key, + JSTaggedValue firstValue, JSTaggedValue secondValue, JSTaggedValue value) +{ + INTERPRETER_TRACE(thread, TryStoreICByValue); + if (receiver.IsHeapObject()) { + auto hclass = receiver.GetTaggedObject()->GetClass(); + if (firstValue.GetWeakReferentUnChecked() == hclass) { + auto handlerInfo = static_cast(secondValue.GetInt()); + return StoreElement(thread, JSObject::Cast(receiver.GetHeapObject()), key, value, handlerInfo); + } + // Check key + if (firstValue == key) { + JSTaggedValue cachedHandler = CheckPolyHClass(secondValue, hclass); + if (!cachedHandler.IsHole()) { + return StoreICWithHandler(thread, receiver, receiver, value, cachedHandler); + } + } + } + + return JSTaggedValue::Hole(); +} + +JSTaggedValue ICRuntimeStub::TryStoreICByName(JSThread *thread, JSTaggedValue receiver, JSTaggedValue firstValue, + JSTaggedValue secondValue, JSTaggedValue value) +{ + INTERPRETER_TRACE(thread, TryStoreICByName); + if (receiver.IsHeapObject()) { + auto hclass = receiver.GetTaggedObject()->GetClass(); + if (firstValue.GetWeakReferentUnChecked() == hclass) { + return StoreICWithHandler(thread, receiver, receiver, value, secondValue); + } + JSTaggedValue cachedHandler = CheckPolyHClass(firstValue, hclass); + if (!cachedHandler.IsHole()) { + return StoreICWithHandler(thread, receiver, receiver, value, cachedHandler); + } + } + return JSTaggedValue::Hole(); +} + +JSTaggedValue ICRuntimeStub::StoreICWithHandler(JSThread *thread, JSTaggedValue receiver, JSTaggedValue holder, + JSTaggedValue value, JSTaggedValue handler) +{ + INTERPRETER_TRACE(thread, StoreICWithHandler); + if (handler.IsInt()) { + auto handlerInfo = static_cast(handler.GetInt()); + if (HandlerBase::IsField(handlerInfo)) { + StoreField(thread, JSObject::Cast(receiver.GetHeapObject()), value, handlerInfo); + return JSTaggedValue::Undefined(); + } + ASSERT(HandlerBase::IsAccessor(handlerInfo) || HandlerBase::IsInternalAccessor(handlerInfo)); + auto accessor = LoadFromField(JSObject::Cast(holder.GetHeapObject()), handlerInfo); + return FastRuntimeStub::CallSetter(thread, JSTaggedValue(receiver), value, accessor); + } + if (handler.IsTransitionHandler()) { + StoreWithTransition(thread, JSObject::Cast(receiver.GetHeapObject()), value, handler); + return JSTaggedValue::Undefined(); + } + if (handler.IsPrototypeHandler()) { + return StorePrototype(thread, receiver, value, handler); + } + if (handler.IsPropertyBox()) { + return StoreGlobal(thread, value, handler); + } + return JSTaggedValue::Undefined(); +} + +JSTaggedValue ICRuntimeStub::StorePrototype(JSThread *thread, JSTaggedValue receiver, JSTaggedValue value, + JSTaggedValue handler) +{ + INTERPRETER_TRACE(thread, StorePrototype); + ASSERT(handler.IsPrototypeHandler()); + PrototypeHandler *prototypeHandler = PrototypeHandler::Cast(handler.GetTaggedObject()); + auto cellValue = prototypeHandler->GetProtoCell(); + ASSERT(cellValue.IsProtoChangeMarker()); + ProtoChangeMarker *cell = ProtoChangeMarker::Cast(cellValue.GetHeapObject()); + if (cell->GetHasChanged()) { + return JSTaggedValue::Hole(); + } + auto holder = prototypeHandler->GetHolder(); + JSTaggedValue handlerInfo = prototypeHandler->GetHandlerInfo(); + return StoreICWithHandler(thread, receiver, holder, value, handlerInfo); +} + +void ICRuntimeStub::StoreWithTransition(JSThread *thread, JSObject *receiver, JSTaggedValue value, + JSTaggedValue handler) +{ + INTERPRETER_TRACE(thread, StoreWithTransition); + TransitionHandler *transitionHandler = TransitionHandler::Cast(handler.GetTaggedObject()); + JSHClass *newHClass = JSHClass::Cast(transitionHandler->GetTransitionHClass().GetTaggedObject()); + receiver->SetClass(newHClass); + uint32_t handlerInfo = transitionHandler->GetHandlerInfo().GetInt(); + ASSERT(HandlerBase::IsField(handlerInfo)); + + if (!HandlerBase::IsInlinedProps(handlerInfo)) { + TaggedArray *array = TaggedArray::Cast(receiver->GetProperties().GetHeapObject()); + int capacity = array->GetLength(); + int index = HandlerBase::GetOffset(handlerInfo); + if (index >= capacity) { + ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); + JSHandle properties; + JSHandle objHandle(thread, receiver); + JSHandle valueHandle(thread, value); + if (capacity == 0) { + capacity = JSObject::MIN_PROPERTIES_LENGTH; + properties = factory->NewTaggedArray(capacity); + } else { + auto arrayHandle = JSHandle(thread, array); + properties = factory->CopyArray(arrayHandle, capacity, JSObject::ComputePropertyCapacity(capacity)); + } + properties->Set(thread, index, valueHandle); + objHandle->SetProperties(thread, properties); + return; + } + array->Set(thread, index, value); + return; + } + StoreField(thread, receiver, value, handlerInfo); +} + +void ICRuntimeStub::StoreField(JSThread *thread, JSObject *receiver, JSTaggedValue value, uint32_t handler) +{ + INTERPRETER_TRACE(thread, StoreField); + int index = HandlerBase::GetOffset(handler); + if (HandlerBase::IsInlinedProps(handler)) { + SET_VALUE_WITH_BARRIER(thread, receiver, index * JSTaggedValue::TaggedTypeSize(), value); + return; + } + TaggedArray *array = TaggedArray::Cast(receiver->GetProperties().GetHeapObject()); + ASSERT(index < static_cast(array->GetLength())); + array->Set(thread, index, value); +} + +JSTaggedValue ICRuntimeStub::LoadFromField(JSObject *receiver, uint32_t handlerInfo) +{ + int index = HandlerBase::GetOffset(handlerInfo); + if (HandlerBase::IsInlinedProps(handlerInfo)) { + return JSTaggedValue(GET_VALUE(receiver, index * JSTaggedValue::TaggedTypeSize())); + } + return TaggedArray::Cast(receiver->GetProperties().GetHeapObject())->Get(index); +} + +JSTaggedValue ICRuntimeStub::LoadGlobal(JSTaggedValue handler) +{ + ASSERT(handler.IsPropertyBox()); + PropertyBox *cell = PropertyBox::Cast(handler.GetHeapObject()); + if (cell->IsInvalid()) { + return JSTaggedValue::Hole(); + } + JSTaggedValue ret = cell->GetValue(); + ASSERT(!ret.IsAccessorData()); + return ret; +} + +JSTaggedValue ICRuntimeStub::StoreGlobal(JSThread *thread, JSTaggedValue value, JSTaggedValue handler) +{ + INTERPRETER_TRACE(thread, StoreGlobal); + ASSERT(handler.IsPropertyBox()); + PropertyBox *cell = PropertyBox::Cast(handler.GetHeapObject()); + if (cell->IsInvalid()) { + return JSTaggedValue::Hole(); + } + ASSERT(!cell->GetValue().IsAccessorData()); + cell->SetValue(thread, value); + return JSTaggedValue::Undefined(); +} + +JSTaggedValue ICRuntimeStub::LoadPrototype(JSThread *thread, JSTaggedValue receiver, JSTaggedValue handler) +{ + INTERPRETER_TRACE(thread, LoadPrototype); + ASSERT(handler.IsPrototypeHandler()); + PrototypeHandler *prototypeHandler = PrototypeHandler::Cast(handler.GetTaggedObject()); + auto cellValue = prototypeHandler->GetProtoCell(); + ASSERT(cellValue.IsProtoChangeMarker()); + ProtoChangeMarker *cell = ProtoChangeMarker::Cast(cellValue.GetHeapObject()); + if (cell->GetHasChanged()) { + return JSTaggedValue::Hole(); + } + auto holder = prototypeHandler->GetHolder(); + JSTaggedValue handlerInfo = prototypeHandler->GetHandlerInfo(); + return LoadICWithHandler(thread, receiver, holder, handlerInfo); +} + +JSTaggedValue ICRuntimeStub::LoadICWithHandler(JSThread *thread, JSTaggedValue receiver, JSTaggedValue holder, + JSTaggedValue handler) +{ + INTERPRETER_TRACE(thread, LoadICWithHandler); + if (LIKELY(handler.IsInt())) { + auto handlerInfo = static_cast(handler.GetInt()); + if (LIKELY(HandlerBase::IsField(handlerInfo))) { + return LoadFromField(JSObject::Cast(holder.GetHeapObject()), handlerInfo); + } + if (HandlerBase::IsNonExist(handlerInfo)) { + return JSTaggedValue::Hole(); + } + ASSERT(HandlerBase::IsAccessor(handlerInfo) || HandlerBase::IsInternalAccessor(handlerInfo)); + auto accessor = LoadFromField(JSObject::Cast(holder.GetHeapObject()), handlerInfo); + return FastRuntimeStub::CallGetter(thread, receiver, holder, accessor); + } + + if (handler.IsPrototypeHandler()) { + return LoadPrototype(thread, receiver, handler); + } + + return LoadGlobal(handler); +} + +JSTaggedValue ICRuntimeStub::LoadElement(JSObject *receiver, JSTaggedValue key) +{ + auto index = TryToElementsIndex(key); + if (index >= JSObject::MAX_ELEMENT_INDEX) { + return JSTaggedValue::Hole(); + } + uint32_t elementIndex = index; + TaggedArray *elements = TaggedArray::Cast(receiver->GetElements().GetHeapObject()); + if (elements->GetLength() <= elementIndex) { + return JSTaggedValue::Hole(); + } + + JSTaggedValue value = elements->Get(elementIndex); + // TaggedArray elements + return value; +} + +JSTaggedValue ICRuntimeStub::StoreElement(JSThread *thread, JSObject *receiver, JSTaggedValue key, JSTaggedValue value, + uint32_t handlerInfo) +{ + INTERPRETER_TRACE(thread, StoreElement); + ASSERT(HandlerBase::IsElement(handlerInfo)); + auto index = TryToElementsIndex(key); + if (index >= JSObject::MAX_ELEMENT_INDEX) { + return JSTaggedValue::Hole(); + } + uint32_t elementIndex = index; + if (HandlerBase::IsJSArray(handlerInfo)) { + JSArray *arr = JSArray::Cast(receiver); + uint32_t oldLength = arr->GetArrayLength(); + if (elementIndex >= oldLength) { + arr->SetArrayLength(thread, elementIndex + 1); + } + } + TaggedArray *elements = TaggedArray::Cast(receiver->GetElements().GetHeapObject()); + uint32_t capacity = elements->GetLength(); + if (elementIndex >= capacity) { + if (JSObject::ShouldTransToDict(capacity, elementIndex)) { + return JSTaggedValue::Hole(); + } + [[maybe_unused]] EcmaHandleScope handleScope(thread); + JSHandle receiverHandle(thread, receiver); + JSHandle valueHandle(thread, value); + elements = + *JSObject::GrowElementsCapacity(thread, receiverHandle, JSObject::ComputeElementCapacity(elementIndex + 1)); + receiverHandle->SetElements(thread, JSTaggedValue(elements)); + elements->Set(thread, elementIndex, valueHandle); + return JSTaggedValue::Undefined(); + } + elements->Set(thread, elementIndex, value); + return JSTaggedValue::Undefined(); +} + +uint32_t ICRuntimeStub::TryToElementsIndex(JSTaggedValue key) +{ + if (LIKELY(key.IsInt())) { + return key.GetInt(); + } + + if (key.IsString()) { + uint32_t index = 0; + if (JSTaggedValue::StringToElementIndex(key, &index)) { + return index; + } + } + + if (key.IsDouble()) { + double number = key.GetDouble(); + auto integer = static_cast(number); + if (number == integer) { + return integer; + } + } + + return JSObject::MAX_ELEMENT_INDEX; +} +} // namespace panda::ecmascript + +#endif // ECMASCRIPT_IC_IC_RUNTIME_STUB_INL_H_ diff --git a/runtime/ic/ic_runtime_stub.cpp b/runtime/ic/ic_runtime_stub.cpp new file mode 100644 index 000000000..3f99ce7a4 --- /dev/null +++ b/runtime/ic/ic_runtime_stub.cpp @@ -0,0 +1,91 @@ +/* + * Copyright (c) 2021-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 "plugins/ecmascript/runtime/base/config.h" +#include "ic_runtime_stub.h" +#include "ic_handler.h" +#include "ic_runtime.h" +#include "profile_type_info.h" +#include "plugins/ecmascript/runtime/js_tagged_value-inl.h" +#include "plugins/ecmascript/runtime/js_array.h" +#include "plugins/ecmascript/runtime/js_hclass-inl.h" +#include "plugins/ecmascript/runtime/global_dictionary-inl.h" +#include "plugins/ecmascript/runtime/js_function.h" +#include "plugins/ecmascript/runtime/js_proxy.h" +#include "plugins/ecmascript/runtime/global_env.h" +#include "plugins/ecmascript/runtime/object_factory-inl.h" +#include "plugins/ecmascript/runtime/js_handle.h" +#include "plugins/ecmascript/runtime/interpreter/fast_runtime_stub-inl.h" +#include "plugins/ecmascript/runtime/ic/proto_change_details.h" + +#include "plugins/ecmascript/runtime/vmstat/runtime_stat.h" +#include "plugins/ecmascript/runtime/runtime_call_id.h" + +namespace panda::ecmascript { +JSTaggedValue ICRuntimeStub::LoadICByName(JSThread *thread, ProfileTypeInfo *profileTypeInfo, JSTaggedValue receiver, + JSTaggedValue key, uint32_t slotId) +{ + INTERPRETER_TRACE(thread, LoadICByName); + [[maybe_unused]] EcmaHandleScope handleScope(thread); + auto keyHandle = JSHandle(thread, key); + auto receiverHandle = JSHandle(thread, receiver); + auto profileInfoHandle = JSHandle(thread, profileTypeInfo); + LoadICRuntime icRuntime(thread, profileInfoHandle, slotId, ICKind::NAMED_LOAD_IC); + return icRuntime.LoadMiss(receiverHandle, keyHandle); +} + +JSTaggedValue ICRuntimeStub::LoadICByValue(JSThread *thread, ProfileTypeInfo *profileTypeInfo, JSTaggedValue receiver, + JSTaggedValue key, uint32_t slotId) +{ + INTERPRETER_TRACE(thread, LoadICByValue); + + [[maybe_unused]] EcmaHandleScope handleScope(thread); + auto keyHandle = JSHandle(thread, key); + auto receiverHandle = JSHandle(thread, receiver); + auto profileInfoHandle = JSHandle(thread, profileTypeInfo); + LoadICRuntime icRuntime(thread, JSHandle::Cast(profileInfoHandle), slotId, ICKind::LOAD_IC); + return icRuntime.LoadMiss(receiverHandle, keyHandle); +} + +JSTaggedValue ICRuntimeStub::StoreICByValue(JSThread *thread, ProfileTypeInfo *profileTypeInfo, JSTaggedValue receiver, + JSTaggedValue key, JSTaggedValue value, uint32_t slotId) +{ + INTERPRETER_TRACE(thread, StoreICByValue); + + [[maybe_unused]] EcmaHandleScope handleScope(thread); + auto keyHandle = JSHandle(thread, key); + auto receiverHandle = JSHandle(thread, receiver); + auto valueHandle = JSHandle(thread, value); + auto profileInfoHandle = JSHandle(thread, profileTypeInfo); + StoreICRuntime icRuntime(thread, JSHandle::Cast(profileInfoHandle), slotId, ICKind::STORE_IC); + return icRuntime.StoreMiss(receiverHandle, keyHandle, valueHandle); +} + +JSTaggedValue ICRuntimeStub::StoreICByName(JSThread *thread, ProfileTypeInfo *profileTypeInfo, JSTaggedValue receiver, + JSTaggedValue key, JSTaggedValue value, uint32_t slotId) +{ + INTERPRETER_TRACE(thread, StoreICByName); + + [[maybe_unused]] EcmaHandleScope handleScope(thread); + auto keyHandle = JSHandle(thread, key); + auto receiverHandle = JSHandle(thread, receiver); + auto valueHandle = JSHandle(thread, value); + auto profileInfoHandle = JSHandle(thread, profileTypeInfo); + StoreICRuntime icRuntime(thread, JSHandle::Cast(profileInfoHandle), slotId, + ICKind::NAMED_STORE_IC); + return icRuntime.StoreMiss(receiverHandle, keyHandle, valueHandle); +} + +} // namespace panda::ecmascript diff --git a/runtime/ic/ic_runtime_stub.h b/runtime/ic/ic_runtime_stub.h new file mode 100644 index 000000000..d2740c331 --- /dev/null +++ b/runtime/ic/ic_runtime_stub.h @@ -0,0 +1,78 @@ +/* + * Copyright (c) 2021-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_IC_IC_RUNTIME_STUB_H_ +#define ECMASCRIPT_IC_IC_RUNTIME_STUB_H_ + +#include "plugins/ecmascript/runtime/base/config.h" +#include "plugins/ecmascript/runtime/js_tagged_value.h" +#include "plugins/ecmascript/runtime/property_attributes.h" + +namespace panda::ecmascript { +class ProfileTypeInfo; + +class ICRuntimeStub { +public: + ARK_INLINE static inline JSTaggedValue LoadGlobalICByName(JSThread *thread, ProfileTypeInfo *profileTypeInfo, + JSTaggedValue globalValue, JSTaggedValue key, + uint32_t slotId, bool isTryLoad = false); + ARK_INLINE static inline JSTaggedValue StoreGlobalICByName(JSThread *thread, ProfileTypeInfo *profileTypeInfo, + JSTaggedValue globalValue, JSTaggedValue key, + JSTaggedValue value, uint32_t slotId, + bool isTryStore = false); + ARK_NOINLINE static JSTaggedValue LoadICByName(JSThread *thread, ProfileTypeInfo *profileTypeInfo, + JSTaggedValue receiver, JSTaggedValue key, uint32_t slotId); + ARK_INLINE static inline JSTaggedValue TryLoadICByName(JSThread *thread, JSTaggedValue receiver, + JSTaggedValue firstValue, JSTaggedValue secondValue); + ARK_INLINE static inline JSTaggedValue TryStoreICByName(JSThread *thread, JSTaggedValue receiver, + JSTaggedValue firstValue, JSTaggedValue secondValue, + JSTaggedValue value); + ARK_NOINLINE static JSTaggedValue StoreICByName(JSThread *thread, ProfileTypeInfo *profileTypeInfo, + JSTaggedValue receiver, JSTaggedValue key, JSTaggedValue value, + uint32_t slotId); + ARK_INLINE static inline JSTaggedValue CheckPolyHClass(JSTaggedValue cachedValue, JSHClass *hclass); + static inline JSTaggedValue LoadICWithHandler(JSThread *thread, JSTaggedValue receiver, JSTaggedValue holder, + JSTaggedValue handler); + static inline JSTaggedValue StoreICWithHandler(JSThread *thread, JSTaggedValue receiver, JSTaggedValue holder, + JSTaggedValue value, JSTaggedValue handler); + ARK_INLINE static inline void StoreWithTransition(JSThread *thread, JSObject *receiver, JSTaggedValue value, + JSTaggedValue handler); + ARK_INLINE static inline JSTaggedValue StorePrototype(JSThread *thread, JSTaggedValue receiver, JSTaggedValue value, + JSTaggedValue handler); + ARK_INLINE static inline JSTaggedValue LoadFromField(JSObject *receiver, uint32_t handlerInfo); + ARK_INLINE static inline void StoreField(JSThread *thread, JSObject *receiver, JSTaggedValue value, + uint32_t handler); + ARK_INLINE static inline JSTaggedValue LoadGlobal(JSTaggedValue handler); + ARK_INLINE static inline JSTaggedValue StoreGlobal(JSThread *thread, JSTaggedValue value, JSTaggedValue handler); + ARK_INLINE static inline JSTaggedValue LoadPrototype(JSThread *thread, JSTaggedValue receiver, + JSTaggedValue handler); + ARK_INLINE static inline JSTaggedValue TryLoadICByValue(JSThread *thread, JSTaggedValue receiver, JSTaggedValue key, + JSTaggedValue firstValue, JSTaggedValue secondValue); + ARK_NOINLINE static JSTaggedValue LoadICByValue(JSThread *thread, ProfileTypeInfo *profileTypeInfo, + JSTaggedValue receiver, JSTaggedValue key, uint32_t slotId); + ARK_INLINE static inline JSTaggedValue TryStoreICByValue(JSThread *thread, JSTaggedValue receiver, + JSTaggedValue key, JSTaggedValue firstValue, + JSTaggedValue secondValue, JSTaggedValue value); + ARK_NOINLINE static JSTaggedValue StoreICByValue(JSThread *thread, ProfileTypeInfo *profileTypeInfo, + JSTaggedValue receiver, JSTaggedValue key, JSTaggedValue value, + uint32_t slotId); + ARK_INLINE static inline JSTaggedValue LoadElement(JSObject *receiver, JSTaggedValue key); + ARK_INLINE static inline JSTaggedValue StoreElement(JSThread *thread, JSObject *receiver, JSTaggedValue key, + JSTaggedValue value, uint32_t handlerInfo); + ARK_INLINE static inline uint32_t TryToElementsIndex(JSTaggedValue key); +}; +} // namespace panda::ecmascript + +#endif // ECMASCRIPT_IC_IC_RUNTIME_STUB_H_ diff --git a/runtime/ic/invoke_cache.h b/runtime/ic/invoke_cache.h new file mode 100644 index 000000000..02435983f --- /dev/null +++ b/runtime/ic/invoke_cache.h @@ -0,0 +1,51 @@ +/* + * Copyright (c) 2021-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_IC_INVOKE_CACHE_H_ +#define ECMASCRIPT_IC_INVOKE_CACHE_H_ + +#include "plugins/ecmascript/runtime/ic/profile_type_info.h" +#include "plugins/ecmascript/runtime/js_tagged_value.h" + +namespace panda::ecmascript { +class InvokeCache { +public: + static constexpr size_t MONO_CASE_NUM = 2; + static constexpr size_t POLY_CASE_NUM = 4; + + static bool SetMonoConstuctCacheSlot(JSThread *thread, ProfileTypeInfo *profileTypeInfo, uint32_t slotId, + JSTaggedValue newTarget, JSTaggedValue initialHClass); + + static bool SetPolyConstuctCacheSlot(JSThread *thread, ProfileTypeInfo *profileTypeInfo, uint32_t slotId, + uint8_t length, JSTaggedValue newTargetArray, + JSTaggedValue initialHClassArray); + + static JSTaggedValue CheckPolyInvokeCache(JSTaggedValue cachedArray, JSTaggedValue func); + + static JSTaggedValue Construct(JSThread *thread, JSTaggedValue firstValue, JSTaggedValue secondValue, + JSTaggedValue ctor, JSTaggedValue newTarget, uint16_t firstArgIdx, + uint16_t length); + + static bool SetMonoInlineCallCacheSlot(JSThread *thread, ProfileTypeInfo *profileTypeInfo, uint32_t slotId, + JSTaggedValue callee); + + static bool SetPolyInlineCallCacheSlot(JSThread *thread, ProfileTypeInfo *profileTypeInfo, uint32_t slotId, + uint8_t length, JSTaggedValue calleeArray); + + static bool DecideCanBeInlined(JSMethod *method); +}; +} // namespace panda::ecmascript + +#endif // ECMASCRIPT_IC_INVOKE_CACHE_H_ diff --git a/runtime/ic/profile_type_info.cpp b/runtime/ic/profile_type_info.cpp new file mode 100644 index 000000000..8176a9b97 --- /dev/null +++ b/runtime/ic/profile_type_info.cpp @@ -0,0 +1,287 @@ +/* + * Copyright (c) 2021-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 "plugins/ecmascript/runtime/ic/profile_type_info.h" +#include "plugins/ecmascript/runtime/ic/ic_handler-inl.h" +#include "plugins/ecmascript/runtime/js_function.h" +#include "plugins/ecmascript/runtime/tagged_array-inl.h" + +namespace panda::ecmascript { +void ProfileTypeAccessor::AddElementHandler(JSHandle dynclass, JSHandle handler) const +{ + auto profileData = profileTypeInfo_->Get(slotId_); + ASSERT(!profileData.IsHole()); + auto index = slotId_; + if (profileData.IsUndefined()) { + profileTypeInfo_->Set(thread_, index, GetWeakRef(dynclass.GetTaggedValue())); + profileTypeInfo_->Set(thread_, index + 1, handler.GetTaggedValue()); + return; + } + // clear key ic + if (!profileData.IsWeak() && (profileData.IsString() || profileData.IsSymbol())) { + profileTypeInfo_->Set(thread_, index, GetWeakRef(dynclass.GetTaggedValue())); + profileTypeInfo_->Set(thread_, index + 1, handler.GetTaggedValue()); + return; + } + AddHandlerWithoutKey(dynclass, handler); +} + +void ProfileTypeAccessor::AddHandlerWithoutKey(JSHandle dynclass, JSHandle handler) const +{ + auto index = slotId_; + if (IsNamedGlobalIC(GetKind())) { + profileTypeInfo_->Set(thread_, index, handler.GetTaggedValue()); + return; + } + auto profileData = profileTypeInfo_->Get(slotId_); + ASSERT(!profileData.IsHole()); + if (profileData.IsUndefined()) { + profileTypeInfo_->Set(thread_, index, GetWeakRef(dynclass.GetTaggedValue())); + profileTypeInfo_->Set(thread_, index + 1, handler.GetTaggedValue()); + return; + } + if (!profileData.IsWeak() && profileData.IsTaggedArray()) { // POLY + ASSERT(profileTypeInfo_->Get(index + 1) == JSTaggedValue::Hole()); + JSHandle arr(thread_, profileData); + const uint32_t step = 2; + uint32_t newLen = arr->GetLength() + step; + if (newLen > CACHE_MAX_LEN) { + profileTypeInfo_->Set(thread_, index, JSTaggedValue::Hole()); + profileTypeInfo_->Set(thread_, index + 1, JSTaggedValue::Hole()); + return; + } + auto factory = thread_->GetEcmaVM()->GetFactory(); + JSHandle newArr = factory->NewTaggedArray(newLen); + uint32_t i = 0; + for (; i < arr->GetLength(); i += step) { + newArr->Set(thread_, i, arr->Get(i)); + newArr->Set(thread_, i + 1, arr->Get(i + 1)); + } + newArr->Set(thread_, i, GetWeakRef(dynclass.GetTaggedValue())); + newArr->Set(thread_, i + 1, handler.GetTaggedValue()); + profileTypeInfo_->Set(thread_, index, newArr.GetTaggedValue()); + profileTypeInfo_->Set(thread_, index + 1, JSTaggedValue::Hole()); + return; + } + // MONO to POLY + auto factory = thread_->GetEcmaVM()->GetFactory(); + JSHandle newArr = factory->NewTaggedArray(POLY_CASE_NUM); + uint32_t arrIndex = 0; + newArr->Set(thread_, arrIndex++, profileTypeInfo_->Get(index)); + newArr->Set(thread_, arrIndex++, profileTypeInfo_->Get(index + 1)); + newArr->Set(thread_, arrIndex++, GetWeakRef(dynclass.GetTaggedValue())); + newArr->Set(thread_, arrIndex, handler.GetTaggedValue()); + + profileTypeInfo_->Set(thread_, index, newArr.GetTaggedValue()); + profileTypeInfo_->Set(thread_, index + 1, JSTaggedValue::Hole()); +} + +void ProfileTypeAccessor::AddHandlerWithKey(JSHandle key, JSHandle dynclass, + JSHandle handler) const +{ + if (IsValueGlobalIC(GetKind())) { + AddGlobalHandlerKey(key, handler); + return; + } + auto profileData = profileTypeInfo_->Get(slotId_); + ASSERT(!profileData.IsHole()); + auto index = slotId_; + if (profileData.IsUndefined() && profileTypeInfo_->Get(index + 1) == JSTaggedValue::Undefined()) { + profileTypeInfo_->Set(thread_, index, key.GetTaggedValue()); + const int arrayLength = 2; + JSHandle newArr = thread_->GetEcmaVM()->GetFactory()->NewTaggedArray(arrayLength); + newArr->Set(thread_, 0, GetWeakRef(dynclass.GetTaggedValue())); + newArr->Set(thread_, 1, handler.GetTaggedValue()); + profileTypeInfo_->Set(thread_, index + 1, newArr.GetTaggedValue()); + return; + } + // for element ic, profileData may dynclass or taggedarray + if (key.GetTaggedValue() != profileData) { + profileTypeInfo_->Set(thread_, index, JSTaggedValue::Hole()); + profileTypeInfo_->Set(thread_, index + 1, JSTaggedValue::Hole()); + return; + } + JSTaggedValue patchValue = profileTypeInfo_->Get(index + 1); + ASSERT(patchValue.IsTaggedArray()); + JSHandle arr(thread_, patchValue); + const uint32_t step = 2; + if (arr->GetLength() > step) { // POLY + uint32_t newLen = arr->GetLength() + step; + if (newLen > CACHE_MAX_LEN) { + profileTypeInfo_->Set(thread_, index, JSTaggedValue::Hole()); + profileTypeInfo_->Set(thread_, index + 1, JSTaggedValue::Hole()); + return; + } + auto factory = thread_->GetEcmaVM()->GetFactory(); + JSHandle newArr = factory->NewTaggedArray(newLen); + newArr->Set(thread_, 0, GetWeakRef(dynclass.GetTaggedValue())); + newArr->Set(thread_, 1, handler.GetTaggedValue()); + for (uint32_t i = 0; i < arr->GetLength(); i += step) { + newArr->Set(thread_, i + step, arr->Get(i)); + newArr->Set(thread_, i + step + 1, arr->Get(i + 1)); + } + profileTypeInfo_->Set(thread_, index + 1, newArr.GetTaggedValue()); + return; + } + // MONO + auto factory = thread_->GetEcmaVM()->GetFactory(); + JSHandle newArr = factory->NewTaggedArray(POLY_CASE_NUM); + uint32_t arrIndex = 0; + newArr->Set(thread_, arrIndex++, arr->Get(0)); + newArr->Set(thread_, arrIndex++, arr->Get(1)); + newArr->Set(thread_, arrIndex++, GetWeakRef(dynclass.GetTaggedValue())); + newArr->Set(thread_, arrIndex++, handler.GetTaggedValue()); + + profileTypeInfo_->Set(thread_, index + 1, newArr.GetTaggedValue()); +} + +void ProfileTypeAccessor::AddGlobalHandlerKey(JSHandle key, JSHandle handler) const +{ + auto index = slotId_; + const uint8_t step = 2; // key and value pair + JSTaggedValue indexVal = profileTypeInfo_->Get(index); + if (indexVal.IsUndefined()) { + auto factory = thread_->GetEcmaVM()->GetFactory(); + JSHandle newArr = factory->NewTaggedArray(step); + newArr->Set(thread_, 0, GetWeakRef(key.GetTaggedValue())); + newArr->Set(thread_, 1, handler.GetTaggedValue()); + profileTypeInfo_->Set(thread_, index, newArr.GetTaggedValue()); + return; + } + ASSERT(indexVal.IsTaggedArray()); + JSHandle arr(thread_, indexVal); + uint32_t newLen = arr->GetLength() + step; + if (newLen > CACHE_MAX_LEN) { + profileTypeInfo_->Set(thread_, index, JSTaggedValue::Hole()); + return; + } + auto factory = thread_->GetEcmaVM()->GetFactory(); + JSHandle newArr = factory->NewTaggedArray(newLen); + newArr->Set(thread_, 0, GetWeakRef(key.GetTaggedValue())); + newArr->Set(thread_, 1, handler.GetTaggedValue()); + + for (uint32_t i = 0; i < arr->GetLength(); i += step) { + newArr->Set(thread_, i + step, arr->Get(i)); + newArr->Set(thread_, i + step + 1, arr->Get(i + 1)); + } + profileTypeInfo_->Set(thread_, index, newArr.GetTaggedValue()); +} + +void ProfileTypeAccessor::AddGlobalRecordHandler(JSHandle handler) const +{ + uint32_t index = slotId_; + profileTypeInfo_->Set(thread_, index, handler.GetTaggedValue()); +} + +void ProfileTypeAccessor::SetAsMega() const +{ + profileTypeInfo_->Set(thread_, slotId_, JSTaggedValue::Hole()); + profileTypeInfo_->Set(thread_, slotId_ + 1, JSTaggedValue::Hole()); +} + +std::string ICKindToString(ICKind kind) +{ + switch (kind) { + case ICKind::NAMED_LOAD_IC: + return "NamedLoadIC"; + case ICKind::NAMED_STORE_IC: + return "NamedStoreIC"; + case ICKind::LOAD_IC: + return "LoadIC"; + case ICKind::STORE_IC: + return "StoreIC"; + case ICKind::NAMED_GLOBAL_LOAD_IC: + return "NamedGlobalLoadIC"; + case ICKind::NAMED_GLOBAL_STORE_IC: + return "NamedGlobalStoreIC"; + case ICKind::TRY_NAMED_GLOBAL_LOAD_IC: + return "TryNamedGlobalLoadIC"; + case ICKind::TRY_NAMED_GLOBAL_STORE_IC: + return "TryNamedGlobalStoreIC"; + case ICKind::GLOBAL_LOAD_IC: + return "GlobalLoadIC"; + case ICKind::GLOBAL_STORE_IC: + return "GlobalStoreIC"; + default: + UNREACHABLE(); + } + UNREACHABLE(); +} + +std::string ProfileTypeAccessor::ICStateToString(ProfileTypeAccessor::ICState state) +{ + switch (state) { + case ICState::UNINIT: + return "uninit"; + case ICState::MONO: + return "mono"; + case ICState::POLY: + return "poly"; + case ICState::MEGA: + return "mega"; + default: + UNREACHABLE(); + } + UNREACHABLE(); +} + +ProfileTypeAccessor::ICState ProfileTypeAccessor::GetICState() const +{ + auto profileData = profileTypeInfo_->Get(slotId_); + if (profileData.IsUndefined()) { + return ICState::UNINIT; + } + + if (profileData.IsHole()) { + return ICState::MEGA; + } + + switch (kind_) { + case ICKind::NAMED_LOAD_IC: + case ICKind::NAMED_STORE_IC: + if (profileData.IsWeak()) { + return ICState::MONO; + } + ASSERT(profileData.IsTaggedArray()); + return ICState::POLY; + case ICKind::LOAD_IC: + case ICKind::STORE_IC: { + if (profileData.IsWeak()) { + return ICState::MONO; + } + if (profileData.IsTaggedArray()) { + TaggedArray *array = TaggedArray::Cast(profileData.GetHeapObject()); + return array->GetLength() == MONO_CASE_NUM ? ICState::MONO : ICState::POLY; // 2 : test case + } + profileData = profileTypeInfo_->Get(slotId_ + 1); + TaggedArray *array = TaggedArray::Cast(profileData.GetHeapObject()); + return array->GetLength() == MONO_CASE_NUM ? ICState::MONO : ICState::POLY; // 2 : test case + } + case ICKind::NAMED_GLOBAL_LOAD_IC: + case ICKind::NAMED_GLOBAL_STORE_IC: + ASSERT(profileData.IsPropertyBox()); + return ICState::MONO; + case ICKind::GLOBAL_LOAD_IC: + case ICKind::GLOBAL_STORE_IC: { + ASSERT(profileData.IsTaggedArray()); + TaggedArray *array = TaggedArray::Cast(profileData.GetHeapObject()); + return array->GetLength() == MONO_CASE_NUM ? ICState::MONO : ICState::POLY; // 2 : test case + } + default: + UNREACHABLE(); + } + return ICState::UNINIT; +} +} // namespace panda::ecmascript diff --git a/runtime/ic/profile_type_info.h b/runtime/ic/profile_type_info.h new file mode 100644 index 000000000..ac9a673b3 --- /dev/null +++ b/runtime/ic/profile_type_info.h @@ -0,0 +1,155 @@ +/* + * Copyright (c) 2021-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_IC_PROFILE_TYPE_INFO_H +#define ECMASCRIPT_IC_PROFILE_TYPE_INFO_H + +#include "plugins/ecmascript/runtime/js_function.h" +#include "plugins/ecmascript/runtime/tagged_array.h" + +namespace panda::ecmascript { +enum class ICKind { + NAMED_LOAD_IC, + NAMED_STORE_IC, + LOAD_IC, + STORE_IC, + NAMED_GLOBAL_LOAD_IC, + NAMED_GLOBAL_STORE_IC, + TRY_NAMED_GLOBAL_LOAD_IC, + TRY_NAMED_GLOBAL_STORE_IC, + GLOBAL_LOAD_IC, + GLOBAL_STORE_IC, +}; + +static inline bool IsNamedGlobalIC(ICKind kind) +{ + return (kind == ICKind::NAMED_GLOBAL_LOAD_IC) || (kind == ICKind::NAMED_GLOBAL_STORE_IC) || + (kind == ICKind::TRY_NAMED_GLOBAL_LOAD_IC) || (kind == ICKind::TRY_NAMED_GLOBAL_STORE_IC); +} + +static inline bool IsValueGlobalIC(ICKind kind) +{ + return (kind == ICKind::GLOBAL_LOAD_IC) || (kind == ICKind::GLOBAL_STORE_IC); +} + +static inline bool IsValueNormalIC(ICKind kind) +{ + return (kind == ICKind::LOAD_IC) || (kind == ICKind::STORE_IC); +} + +static inline bool IsValueIC(ICKind kind) +{ + return IsValueNormalIC(kind) || IsValueGlobalIC(kind); +} + +static inline bool IsNamedNormalIC(ICKind kind) +{ + return (kind == ICKind::NAMED_LOAD_IC) || (kind == ICKind::NAMED_STORE_IC); +} + +static inline bool IsNamedIC(ICKind kind) +{ + return IsNamedNormalIC(kind) || IsNamedGlobalIC(kind); +} + +static inline bool IsGlobalLoadIC(ICKind kind) +{ + return (kind == ICKind::NAMED_GLOBAL_LOAD_IC) || + (kind == ICKind::GLOBAL_LOAD_IC) || + (kind == ICKind::TRY_NAMED_GLOBAL_LOAD_IC); +} + +static inline bool IsGlobalStoreIC(ICKind kind) +{ + return (kind == ICKind::NAMED_GLOBAL_STORE_IC) || + (kind == ICKind::GLOBAL_STORE_IC) || + (kind == ICKind::TRY_NAMED_GLOBAL_STORE_IC); +} + +static inline bool IsGlobalIC(ICKind kind) +{ + return IsValueGlobalIC(kind) || IsNamedGlobalIC(kind); +} + +std::string ICKindToString(ICKind kind); + +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 ProfileTypeInfo *Cast(TaggedObject *object) + { + ASSERT(JSTaggedValue(object).IsTaggedArray()); + return static_cast(object); + } +}; + +class ProfileTypeAccessor { +public: + static constexpr size_t CACHE_MAX_LEN = 8; + static constexpr size_t MONO_CASE_NUM = 2; + static constexpr size_t POLY_CASE_NUM = 4; + + enum ICState { + UNINIT, + MONO, + POLY, + MEGA, + }; + + ProfileTypeAccessor(JSThread *thread, JSHandle profileTypeInfo, uint32_t slotId, ICKind kind) + : thread_(thread), profileTypeInfo_(profileTypeInfo), slotId_(slotId), kind_(kind) + { + } + ~ProfileTypeAccessor() = default; + + ICState GetICState() const; + static std::string ICStateToString(ICState state); + void AddHandlerWithoutKey(JSHandle dynclass, JSHandle handler) const; + void AddElementHandler(JSHandle dynclass, JSHandle handler) const; + void AddHandlerWithKey(JSHandle key, JSHandle dynclass, + JSHandle handler) const; + void AddGlobalHandlerKey(JSHandle key, JSHandle handler) const; + void AddGlobalRecordHandler(JSHandle handler) const; + + JSTaggedValue GetWeakRef(JSTaggedValue value) const + { + return JSTaggedValue(value.CreateAndGetWeakRef()); + } + + JSTaggedValue GetRefFromWeak(const JSTaggedValue &value) const + { + return JSTaggedValue(value.GetWeakReferent()); + } + void SetAsMega() const; + + ICKind GetKind() const + { + return kind_; + } + + NO_MOVE_SEMANTIC(ProfileTypeAccessor); + NO_COPY_SEMANTIC(ProfileTypeAccessor); + +private: + JSThread *thread_; + JSHandle profileTypeInfo_; + uint32_t slotId_; + ICKind kind_; +}; +} // namespace panda::ecmascript + +#endif // ECMASCRIPT_IC_PROFILE_TYPE_INFO_H diff --git a/runtime/ic/properties_cache-inl.h b/runtime/ic/properties_cache-inl.h new file mode 100644 index 000000000..ec5060607 --- /dev/null +++ b/runtime/ic/properties_cache-inl.h @@ -0,0 +1,56 @@ +/* + * Copyright (c) 2021-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_IC_PROPERTIES_CACHE_INL_H +#define ECMASCRIPT_IC_PROPERTIES_CACHE_INL_H + +#include "plugins/ecmascript/runtime/ic/properties_cache.h" +#include "plugins/ecmascript/runtime/js_tagged_value-inl.h" + +namespace panda::ecmascript { +int PropertiesCache::Get(JSHClass *jsHclass, JSTaggedValue key) +{ + int hash = Hash(jsHclass, key); + PropertyKey &prop = keys_[hash]; + if ((prop.hclass_ == jsHclass) && (prop.key_ == key)) { + return keys_[hash].results_; + } + return NOT_FOUND; +} + +void PropertiesCache::Set(JSHClass *jsHclass, JSTaggedValue key, int index) +{ + int hash = Hash(jsHclass, key); + PropertyKey &prop = keys_[hash]; + prop.hclass_ = jsHclass; + prop.key_ = key; + keys_[hash].results_ = index; +} + +void PropertiesCache::Clear() +{ + for (auto &key : keys_) { + key.hclass_ = nullptr; + } +} + +int PropertiesCache::Hash(JSHClass *cls, JSTaggedValue key) +{ + uint32_t clsHash = static_cast(reinterpret_cast(cls)) >> 3U; // skip 8bytes + uint32_t keyHash = key.GetKeyHashCode(); + return static_cast((clsHash ^ keyHash) & CACHE_LENGTH_MASK); +} +} // namespace panda::ecmascript +#endif // ECMASCRIPT_IC_PROPERTIES_CACHE_INL_H diff --git a/runtime/ic/properties_cache.h b/runtime/ic/properties_cache.h new file mode 100644 index 000000000..f676db144 --- /dev/null +++ b/runtime/ic/properties_cache.h @@ -0,0 +1,66 @@ +/* + * Copyright (c) 2021-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_IC_PROPERTIES_CACHE_H +#define ECMASCRIPT_IC_PROPERTIES_CACHE_H + +#include + +#include "plugins/ecmascript/runtime/js_hclass.h" +#include "plugins/ecmascript/runtime/js_tagged_value.h" +#include "plugins/ecmascript/runtime/ecma_macros.h" + +namespace panda::ecmascript { +class EcmaVM; +class PropertiesCache { +public: + inline int Get(JSHClass *jsHclass, JSTaggedValue key); + inline void Set(JSHClass *jsHclass, JSTaggedValue key, int index); + inline void Clear(); + + static const int NOT_FOUND = -1; + + DEFAULT_MOVE_SEMANTIC(PropertiesCache); + DEFAULT_COPY_SEMANTIC(PropertiesCache); + +private: + PropertiesCache() + { + for (uint32_t i = 0; i < CACHE_LENGTH; ++i) { + keys_[i].hclass_ = nullptr; + keys_[i].key_ = JSTaggedValue::Hole(); + keys_[i].results_ = NOT_FOUND; + } + } + ~PropertiesCache() = default; + + struct PropertyKey { + JSHClass *hclass_{nullptr}; + JSTaggedValue key_{JSTaggedValue::Hole()}; + int results_{NOT_FOUND}; + }; + + static inline int Hash(JSHClass *cls, JSTaggedValue key); + + static const uint32_t CACHE_LENGTH_BIT = 10; + static const uint32_t CACHE_LENGTH = (1U << CACHE_LENGTH_BIT); + static const uint32_t CACHE_LENGTH_MASK = CACHE_LENGTH - 1; + + std::array keys_{}; + + friend class JSThread; +}; +} // namespace panda::ecmascript +#endif // ECMASCRIPT_IC_PROPERTIES_CACHE_H diff --git a/runtime/ic/property_box.cpp b/runtime/ic/property_box.cpp new file mode 100644 index 000000000..05067a2d2 --- /dev/null +++ b/runtime/ic/property_box.cpp @@ -0,0 +1,25 @@ +/* + * Copyright (c) 2021-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 "plugins/ecmascript/runtime/js_tagged_value-inl.h" +#include "property_box.h" + +namespace panda::ecmascript { +void PropertyBox::Clear(const JSThread *thread) +{ + ASSERT_PRINT(!GetValue().IsHole(), "value must not be hole"); + SetValue(thread, JSTaggedValue::Hole()); +} +} // namespace panda::ecmascript diff --git a/runtime/ic/property_box.h b/runtime/ic/property_box.h new file mode 100644 index 000000000..063d7e891 --- /dev/null +++ b/runtime/ic/property_box.h @@ -0,0 +1,50 @@ +/* + * Copyright (c) 2021-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_IC_PROPERTY_BOX_H +#define ECMASCRIPT_IC_PROPERTY_BOX_H + +#include "plugins/ecmascript/runtime/ecma_macros.h" +#include "plugins/ecmascript/runtime/js_hclass.h" +#include "plugins/ecmascript/runtime/js_tagged_value.h" +#include "plugins/ecmascript/runtime/property_attributes.h" + +namespace panda { +namespace ecmascript { +class PropertyBox : public TaggedObject { +public: + static PropertyBox *Cast(ObjectHeader *object) + { + ASSERT(JSTaggedValue(object).IsPropertyBox()); + return static_cast(object); + } + + void Clear(const JSThread *thread); + + inline bool IsInvalid() const + { + return GetValue().IsHole(); + } + + static constexpr size_t VALUE_OFFSET = TaggedObjectSize(); + ACCESSORS(Value, VALUE_OFFSET, SIZE); + + DECL_VISIT_OBJECT(VALUE_OFFSET, SIZE) + DECL_DUMP() +}; +} // namespace ecmascript +} // namespace panda + +#endif diff --git a/runtime/ic/proto_change_details.cpp b/runtime/ic/proto_change_details.cpp new file mode 100644 index 000000000..1fd1b38ef --- /dev/null +++ b/runtime/ic/proto_change_details.cpp @@ -0,0 +1,75 @@ +/* + * Copyright (c) 2021-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 "plugins/ecmascript/runtime/ic/proto_change_details.h" +#include "plugins/ecmascript/runtime/weak_vector-inl.h" + +namespace panda::ecmascript { +JSHandle ChangeListener::Add(const JSThread *thread, const JSHandle &array, + const JSHandle &value, uint32_t *index) +{ + JSTaggedValue weakValue; + if (!array->Full()) { + weakValue = JSTaggedValue(value.GetTaggedValue().CreateAndGetWeakRef()); + uint32_t arrayIndex = array->PushBack(thread, weakValue); + if (arrayIndex != TaggedArray::MAX_ARRAY_INDEX) { + if (index != nullptr) { + *index = arrayIndex; + } + return array; + } + UNREACHABLE(); + } + // if exist hole, use it. + uint32_t holeIndex = CheckHole(array); + if (holeIndex != TaggedArray::MAX_ARRAY_INDEX) { + weakValue = JSTaggedValue(value.GetTaggedValue().CreateAndGetWeakRef()); + array->Set(thread, holeIndex, weakValue); + if (index != nullptr) { + *index = holeIndex; + } + return array; + } + // the vector is full and no hole exists. + JSHandle newArray = WeakVector::Grow(thread, JSHandle(array), array->GetCapacity() + 1); + weakValue = JSTaggedValue(value.GetTaggedValue().CreateAndGetWeakRef()); + uint32_t arrayIndex = newArray->PushBack(thread, weakValue); + ASSERT(arrayIndex != TaggedArray::MAX_ARRAY_INDEX); + if (index != nullptr) { + *index = arrayIndex; + } + return JSHandle(newArray); +} + +uint32_t ChangeListener::CheckHole(const JSHandle &array) +{ + for (uint32_t i = 0; i < array->GetEnd(); i++) { + JSTaggedValue value = array->Get(i); + if (value == JSTaggedValue::Hole() || value == JSTaggedValue::Undefined()) { + return i; + } + } + return TaggedArray::MAX_ARRAY_INDEX; +} + +JSTaggedValue ChangeListener::Get(uint32_t index) +{ + JSTaggedValue value = WeakVector::Get(index); + if (!value.IsHeapObject()) { + return value; + } + return JSTaggedValue(value.GetTaggedWeakRef()); +} +} // namespace panda::ecmascript diff --git a/runtime/ic/proto_change_details.h b/runtime/ic/proto_change_details.h new file mode 100644 index 000000000..dfd8bb2a6 --- /dev/null +++ b/runtime/ic/proto_change_details.h @@ -0,0 +1,75 @@ +/* + * Copyright (c) 2021-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_IC_PROTOTYPE_CHANGE_DETAILS_H +#define ECMASCRIPT_IC_PROTOTYPE_CHANGE_DETAILS_H + +#include "plugins/ecmascript/runtime/ecma_macros.h" +#include "plugins/ecmascript/runtime/js_handle.h" +#include "plugins/ecmascript/runtime/js_tagged_value.h" +#include "plugins/ecmascript/runtime/weak_vector.h" + +namespace panda { +namespace ecmascript { +class ProtoChangeMarker : public TaggedObject { +public: + + using HasChangedField = BitField; + static ProtoChangeMarker *Cast(ObjectHeader *object) + { + ASSERT(JSTaggedValue(object).IsProtoChangeMarker()); + return static_cast(object); + } + + static constexpr size_t HAS_CHANGED_OFFSET = TaggedObjectSize(); + SET_GET_PRIMITIVE_FIELD(HasChanged, bool, HAS_CHANGED_OFFSET, SIZE); + DECL_DUMP() +}; + +class ProtoChangeDetails : public TaggedObject { +public: + static constexpr int UNREGISTERED = -1; + static ProtoChangeDetails *Cast(ObjectHeader *object) + { + ASSERT(JSTaggedValue(object).IsProtoChangeDetails()); + return static_cast(object); + } + + static constexpr size_t CHANGE_LISTENER_OFFSET = TaggedObjectSize(); + ACCESSORS(ChangeListener, CHANGE_LISTENER_OFFSET, REGISTER_INDEX_OFFSET); + ACCESSORS(RegisterIndex, REGISTER_INDEX_OFFSET, SIZE); + + DECL_VISIT_OBJECT(CHANGE_LISTENER_OFFSET, SIZE) + DECL_DUMP() +}; + +class ChangeListener : public WeakVector { +public: + static ChangeListener *Cast(ObjectHeader *object) + { + return static_cast(object); + } + + static JSHandle Add(const JSThread *thread, const JSHandle &array, + const JSHandle &value, uint32_t *index); + + static uint32_t CheckHole(const JSHandle &array); + + JSTaggedValue Get(uint32_t index); +}; +} // namespace ecmascript +} // namespace panda + +#endif // ECMASCRIPT_IC_PROTOTYPE_CHANGE_DETAILS_H diff --git a/runtime/include/tooling/pt_ecmascript_extension.h b/runtime/include/tooling/pt_ecmascript_extension.h new file mode 100644 index 000000000..582f59f5a --- /dev/null +++ b/runtime/include/tooling/pt_ecmascript_extension.h @@ -0,0 +1,33 @@ +/** + * Copyright (c) 2021-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 PANDA_TOOLING_ECMASCRIPT_EXTENSION_H +#define PANDA_TOOLING_ECMASCRIPT_EXTENSION_H + +#include "runtime/include/tooling/vreg_value.h" +#include "runtime/include/tooling/debug_interface.h" +#include "plugins/ecmascript/runtime/js_tagged_value.h" + +namespace panda::tooling::ecmascript { +using JSTaggedValue = panda::ecmascript::JSTaggedValue; +class PtEcmaScriptExtension { +public: + static VRegValue TaggedValueToVRegValue(JSTaggedValue value); + static JSTaggedValue VRegValueToTaggedValue(VRegValue value); +}; + +} // namespace panda::tooling::ecmascript + +#endif // PANDA_TOOLING_ECMASCRIPT_EXTENSION_H diff --git a/runtime/internal_call_params.cpp b/runtime/internal_call_params.cpp new file mode 100644 index 000000000..012509c3a --- /dev/null +++ b/runtime/internal_call_params.cpp @@ -0,0 +1,132 @@ +/* + * Copyright (c) 2021-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 "plugins/ecmascript/runtime/internal_call_params.h" + +namespace panda::ecmascript { +void InternalCallParams::MakeArgv(const EcmaRuntimeCallInfo *info, uint32_t position) +{ + int32_t mayLenth = info->GetArgsNumber() - position; + uint32_t length = mayLenth > 0 ? mayLenth : 0; + if (LIKELY(length <= InternalCallParams::RESERVE_INTERNAL_CALL_PARAMS_FIXED_LENGTH)) { + EnableFixedModeAndSetLength(length); + for (uint32_t index = 0; index < length; ++index) { + SetFixedBuffer(index, info->GetCallArg(index + position)); + } + return; + } + + EnableVariableModeAndSetLength(length); + for (uint32_t index = 0; index < length; ++index) { + SetVariableBuffer(index, info->GetCallArg(index + position)); + } +} + +void InternalCallParams::MakeArgListWithHole(const TaggedArray *argv, uint32_t length) +{ + if (length > argv->GetLength()) { + length = argv->GetLength(); + } + ASSERT(length <= argv->GetLength()); + if (LIKELY(length <= InternalCallParams::RESERVE_INTERNAL_CALL_PARAMS_FIXED_LENGTH)) { + EnableFixedModeAndSetLength(length); + for (uint32_t index = 0; index < length; ++index) { + auto value = argv->Get(index); + SetFixedBuffer(index, value.IsHole() ? JSTaggedValue::Undefined() : value); + } + return; + } + + EnableVariableModeAndSetLength(length); + for (uint32_t index = 0; index < length; ++index) { + auto value = argv->Get(index); + SetVariableBuffer(index, value.IsHole() ? JSTaggedValue::Undefined() : value); + } +} + +void InternalCallParams::MakeArgList(const TaggedArray *argv) +{ + uint32_t length = argv->GetLength(); + if (LIKELY(length <= InternalCallParams::RESERVE_INTERNAL_CALL_PARAMS_FIXED_LENGTH)) { + EnableFixedModeAndSetLength(length); + for (uint32_t index = 0; index < length; ++index) { + SetFixedBuffer(index, argv->Get(index)); + } + return; + } + + EnableVariableModeAndSetLength(length); + for (uint32_t index = 0; index < length; ++index) { + SetVariableBuffer(index, argv->Get(index)); + } +} + +void InternalCallParams::MakeBoundArgv(const JSThread *thread, const JSHandle &boundFunc) +{ + JSHandle boundArgs(thread, boundFunc->GetBoundArguments()); + uint32_t boundLength = boundArgs->GetLength(); + uint32_t length = IsFixedMode() ? boundLength + GetFixedLength() + : boundLength + GetVariableLength(); + if (LIKELY(length <= InternalCallParams::RESERVE_INTERNAL_CALL_PARAMS_FIXED_LENGTH)) { + EnableFixedModeAndSetLength(length); + + // Prevent override, reverse write order + for (array_ssize_t index = length - 1; index >= static_cast(boundLength); --index) { + SetFixedBuffer(index, GetFixedBuffer(index - boundLength)); + } + + for (uint32_t index = 0; index < boundLength; ++index) { + SetFixedBuffer(index, boundArgs->Get(index)); + } + return; + } + + // need cross mode: fixed -> variable + if (IsFixedMode()) { + // enable variable mode not clear fixed buffer + EnableVariableModeAndSetLength(length); + for (uint32_t index = 0; index < boundLength; ++index) { + SetVariableBuffer(index, boundArgs->Get(index)); + } + + for (uint32_t index = boundLength; index < length; ++index) { + SetVariableBuffer(index, GetFixedBuffer(index - boundLength)); + } + return; + } + + EnableVariableModeAndSetLength(length); + for (array_ssize_t index = boundLength - 1; index >= 0; --index) { + InsertVariableBuffer(boundArgs->Get(index)); + } +} + +void InternalCallParams::Iterate(const RootRangeVisitor &v) const +{ + if (GetLength() == 0) { + return; + } + uintptr_t start = 0U; + uintptr_t end = 0U; + if (LIKELY(IsFixedMode())) { + start = GetFixedDataAddress(); + end = start + sizeof(TaggedType) * fixed_length_; + } else { + start = GetVariableDataAddress(); + end = start + sizeof(TaggedType) * variable_length_; + } + v(Root::ROOT_INTERNAL_CALL_PARAMS, ObjectSlot(start), ObjectSlot(end)); +} +} // namespace panda::ecmascript diff --git a/runtime/internal_call_params.h b/runtime/internal_call_params.h new file mode 100644 index 000000000..d4faf3a0a --- /dev/null +++ b/runtime/internal_call_params.h @@ -0,0 +1,232 @@ +/* + * Copyright (c) 2021-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_INTERNAL_CALL_PARAMS_H +#define ECMASCRIPT_INTERNAL_CALL_PARAMS_H + +#include "plugins/ecmascript/runtime/ecma_runtime_call_info.h" +#include "plugins/ecmascript/runtime/js_tagged_value.h" +#include "plugins/ecmascript/runtime/js_handle.h" +#include "plugins/ecmascript/runtime/js_function.h" +#include "plugins/ecmascript/runtime/mem/c_containers.h" + +namespace panda::ecmascript { +class InternalCallParams { +public: + static constexpr uint8_t RESERVE_INTERNAL_CALL_PARAMS_FIXED_LENGTH = 128; + + InternalCallParams() = default; + + ~InternalCallParams() = default; + + inline const JSTaggedType *GetArgv() const + { + if (IsFixedMode()) { + return &fixed_data_.front(); + } + + ASSERT_PRINT(variable_length_ > RESERVE_INTERNAL_CALL_PARAMS_FIXED_LENGTH, "internal call params mode error"); + return variable_data_.data(); + } + + inline uint32_t GetLength() const + { + if (IsFixedMode()) { + return fixed_length_; + } + + ASSERT_PRINT(variable_length_ > RESERVE_INTERNAL_CALL_PARAMS_FIXED_LENGTH, "internal call params mode error"); + return variable_length_; + } + + template + inline void MakeArgv(const JSHandle &arg) + { + EnableFixedModeAndSetLength(1); + fixed_data_[0] = arg.GetTaggedType(); + } + + template + inline void MakeArgv(const JSHandle &arg0, const JSHandle &arg1) + { + EnableFixedModeAndSetLength(2); + fixed_data_[0] = arg0.GetTaggedType(); + fixed_data_[1] = arg1.GetTaggedType(); + } + + template + inline void MakeArgv(const JSHandle &arg0, const JSHandle &arg1, const JSHandle &arg2) + { + EnableFixedModeAndSetLength(3); + fixed_data_[0] = arg0.GetTaggedType(); + fixed_data_[1] = arg1.GetTaggedType(); + fixed_data_[2] = arg2.GetTaggedType(); + } + + template + inline void MakeArgv(const JSHandle &arg0, const JSHandle &arg1, const JSHandle &arg2, + const JSHandle &arg3) + { + EnableFixedModeAndSetLength(4); + fixed_data_[0] = arg0.GetTaggedType(); + fixed_data_[1] = arg1.GetTaggedType(); + fixed_data_[2] = arg2.GetTaggedType(); + fixed_data_[3] = arg3.GetTaggedType(); + } + + inline void MakeEmptyArgv() + { + EnableFixedModeAndSetLength(0); + } + + inline void MakeArgv(const JSTaggedValue arg) + { + EnableFixedModeAndSetLength(1); + fixed_data_[0] = arg.GetRawData(); + } + + inline void MakeArgv(const JSTaggedValue arg0, const JSTaggedValue arg1) + { + EnableFixedModeAndSetLength(2); + fixed_data_[0] = arg0.GetRawData(); + fixed_data_[1] = arg1.GetRawData(); + } + + inline void MakeArgv(const JSTaggedValue arg0, const JSTaggedValue arg1, const JSTaggedValue arg2) + { + EnableFixedModeAndSetLength(3); + fixed_data_[0] = arg0.GetRawData(); + fixed_data_[1] = arg1.GetRawData(); + fixed_data_[2] = arg2.GetRawData(); + } + + inline void MakeArgv(const JSTaggedValue arg0, const JSTaggedValue arg1, const JSTaggedValue arg2, + const JSTaggedValue arg3) + { + EnableFixedModeAndSetLength(4); + fixed_data_[0] = arg0.GetRawData(); + fixed_data_[1] = arg1.GetRawData(); + fixed_data_[2] = arg2.GetRawData(); + fixed_data_[3] = arg3.GetRawData(); + } + + void MakeArgv(const EcmaRuntimeCallInfo *info, uint32_t position); + + void MakeArgList(const TaggedArray *argv); + void MakeArgListWithHole(const TaggedArray *argv, uint32_t length); + + void MakeBoundArgv(const JSThread *thread, const JSHandle &boundFunc); + + void Iterate(const RootRangeVisitor &v) const; + +private: + DEFAULT_COPY_SEMANTIC(InternalCallParams); + DEFAULT_MOVE_SEMANTIC(InternalCallParams); + + inline bool IsFixedMode() const + { + return !variable_mode_; + } + + inline void EnableFixedModeAndSetLength(uint32_t length) + { + variable_mode_ = false; + variable_data_.clear(); + variable_length_ = 0; + fixed_length_ = length; + } + + inline uint32_t GetFixedLength() const + { + return fixed_length_; + } + + inline uintptr_t GetFixedDataAddress() const + { + return ToUintPtr(&fixed_data_); + } + + inline JSTaggedType GetFixedBuffer(uint32_t idx) const + { + return fixed_data_[idx]; + } + + inline void SetFixedBuffer(uint32_t idx, JSHandle val) + { + fixed_data_[idx] = val.GetTaggedType(); + } + + inline void SetFixedBuffer(uint32_t idx, JSTaggedValue val) + { + fixed_data_[idx] = val.GetRawData(); + } + + inline void SetFixedBuffer(uint32_t idx, JSTaggedType val) + { + fixed_data_[idx] = val; + } + + inline void EnableVariableModeAndSetLength(uint32_t length) + { + variable_mode_ = true; + fixed_length_ = 0; + variable_length_ = length; + variable_data_.resize(variable_length_); + } + + inline uint32_t GetVariableLength() const + { + return variable_length_; + } + + inline uintptr_t GetVariableDataAddress() const + { + return ToUintPtr(variable_data_.data()); + } + + inline JSTaggedType GetVariableBuffer(uint32_t idx) const + { + return variable_data_[idx]; + } + + inline void SetVariableBuffer(uint32_t idx, JSHandle val) + { + variable_data_[idx] = val.GetTaggedType(); + } + + inline void SetVariableBuffer(uint32_t idx, JSTaggedValue val) + { + variable_data_[idx] = val.GetRawData(); + } + + inline void SetVariableBuffer(uint32_t idx, JSTaggedType val) + { + variable_data_[idx] = val; + } + + inline void InsertVariableBuffer(JSTaggedValue val) + { + variable_data_.insert(variable_data_.begin(), val.GetRawData()); + } + + std::array fixed_data_{}; + CVector variable_data_{}; + uint32_t fixed_length_{0}; + uint32_t variable_length_{0}; + bool variable_mode_{false}; +}; +} // namespace panda::ecmascript + +#endif // ECMASCRIPT_INTERNAL_CALL_PARAMS_H diff --git a/runtime/interpreter/ecma-interpreter-inl.h b/runtime/interpreter/ecma-interpreter-inl.h new file mode 100644 index 000000000..f4bac392c --- /dev/null +++ b/runtime/interpreter/ecma-interpreter-inl.h @@ -0,0 +1,2215 @@ +/** + * Copyright (c) Huawei Technologies Co., Ltd. 2019-2021. All rights reserved. + */ + +#ifndef PANDA_ECMASCRIPT_INTERPRETER_INL_H +#define PANDA_ECMASCRIPT_INTERPRETER_INL_H + +#include "plugins/ecmascript/runtime/ecma_string.h" +#include "plugins/ecmascript/runtime/interpreter/ecma-interpreter.h" +#include "plugins/ecmascript/runtime/interpreter/js_frame-inl.h" +#include "plugins/ecmascript/runtime/interpreter/slow_runtime_stub.h" +#include "plugins/ecmascript/runtime/interpreter/js_decode_call_instr.h" +#include "plugins/ecmascript/runtime/interpreter/fast_runtime_stub-inl.h" +#include "plugins/ecmascript/runtime/ecma_vm.h" +#include "plugins/ecmascript/runtime/intrinsics-inl.h" +#include "plugins/ecmascript/runtime/js_generator_object.h" +#include "plugins/ecmascript/runtime/js_array.h" +#include "plugins/ecmascript/compiler/ecmascript_extensions/thread_environment_api.h" +#include "runtime/interpreter/interpreter-inl.h" +#include "runtime/interpreter/instruction_handler_base.h" + +namespace panda::ecmascript { + +// NOLINTNEXTLINE(cppcoreguidelines-macro-usage) +#define INTRINSIC_CALL_SETACC(intrinsic_call) \ + { \ + JSTaggedValue result(intrinsic_call); \ + SetAccFromTaggedValue(result); \ + } + +// NOLINTNEXTLINE(cppcoreguidelines-macro-usage) +#define INTRINSIC_CALL_CHECK(intrinsic_call) \ + { \ + JSTaggedValue result(intrinsic_call); \ + if (UNLIKELY(result.IsException())) { \ + this->MoveToExceptionHandler(); \ + return; \ + } \ + } + +// NOLINTNEXTLINE(cppcoreguidelines-macro-usage) +#define INTRINSIC_CALL_CHECK_SETACC(intrinsic_call) \ + { \ + JSTaggedValue result(intrinsic_call); \ + if (UNLIKELY(result.IsException())) { \ + this->MoveToExceptionHandler(); \ + return; \ + } \ + SetAccFromTaggedValue(result); \ + } + +// NOLINTNEXTLINE(cppcoreguidelines-macro-usage) +#define INTERPRETER_RETURN_IF_ABRUPT(result) \ + do { \ + if (result.IsException()) { \ + this->MoveToExceptionHandler(); \ + return; \ + } \ + } while (false) + +template +class JSFrameHelper { +public: + template + ALWAYS_INLINE static uint32_t GetNumberActualArgsDyn([[maybe_unused]] InstructionHandler *instr_handler) + { + return JSGetNumberActualArgsDyn(instr_handler->GetInst()); + } + + template + ALWAYS_INLINE static void CopyArgumentsDyn(InstructionHandler *instr_handler, Frame *new_frame, uint32_t num_vregs, + uint32_t num_actual_args) + { + uint16_t prev_v0 = instr_handler->GetInst().template GetVReg(); + Frame *prev_frame = instr_handler->GetFrame(); + BytecodeInstruction prev_inst = instr_handler->GetInst(); + auto *thread = JSThread::Cast(instr_handler->GetThread()); + + JSCopyArgumets(thread, prev_frame, prev_v0, prev_inst, new_frame, num_vregs, num_actual_args); + } + + template + ALWAYS_INLINE static Frame *CreateFrame(ManagedThread *thread, uint32_t nregs_size, Method *method, Frame *prev, + uint32_t nregs, uint32_t num_actual_args) + { + return ::panda::CreateFrame(thread->GetStackFrameAllocator(), nregs_size, method, prev, nregs, + num_actual_args); + } +}; + +template +class InstructionHandler : public interpreter::InstructionHandler { +public: + ALWAYS_INLINE inline InstructionHandler(interpreter::InstructionHandlerState *state) + : interpreter::InstructionHandler(state) + { + } + + ALWAYS_INLINE JSThread *GetJSThread() const + { + return JSThread::Cast(interpreter::InstructionHandler::GetThread()); + } + + ALWAYS_INLINE JSTaggedValue GetRegAsTaggedValue(uint16_t v) + { + return VRegAsTaggedValue(this->GetFrame()->GetVReg(v)); + } + + ALWAYS_INLINE JSTaggedType GetRegAsTaggedType(uint16_t v) + { + return VRegAsTaggedType(this->GetFrame()->GetVReg(v)); + } + + ALWAYS_INLINE JSTaggedValue GetAccAsTaggedValue() + { + return VRegAsTaggedValue(this->GetAcc()); + } + + JSTaggedType *GetStkArgs(uint16_t firstArgIdx) + { + static_assert(sizeof(interpreter::VRegister) == sizeof(JSTaggedType)); + auto thread = JSThread::Cast(this->GetThread()); + return reinterpret_cast(&thread->GetCurrentFrame()->GetVReg(firstArgIdx)); + } + + JSTaggedValue GetThisFunction() + { + // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-pointer-arithmetic) + auto numVregs = this->GetFrame()->GetMethod()->GetNumVregs(); + return GetRegAsTaggedValue(numVregs); + } + + JSTaggedValue GetGlobalObject() + { + auto thread = JSThread::Cast(this->GetThread()); + return thread->GetEcmaVM()->GetGlobalEnv()->GetGlobalObject(); + } + + panda::ecmascript::JSHandle GetGlobalEnv() + { + auto thread = JSThread::Cast(this->GetThread()); + return thread->GetEcmaVM()->GetGlobalEnv(); + } + + ObjectFactory *GetFactory() + { + auto thread = JSThread::Cast(this->GetThread()); + return thread->GetEcmaVM()->GetFactory(); + } + + ALWAYS_INLINE void SetAccFromTaggedValue(JSTaggedValue v) + { + this->GetAcc().Set(v.GetRawData()); + } + + ALWAYS_INLINE void SaveAccToFrame() + { + this->GetFrame()->SetAcc(this->GetAcc()); + } + + ALWAYS_INLINE void RestoreAccFromFrame() + { + this->GetAcc() = this->GetFrame()->GetAcc(); + } + + JSHandle GetMethodName(JSHandle callTarget) + { + auto thread = JSThread::Cast(this->GetThread()); + JSHandle thisFunc = JSHandle::Cast(callTarget); + return JSFunctionBase::GetFunctionName(thread, JSHandle(thisFunc)); + } + + template + ALWAYS_INLINE void HandleEcmaReturnDyn() + { + LOG_INST() << "ecma.return.dyn"; + this->template DoReturnDyn(); + } + + // Redefine method from base class + ALWAYS_INLINE void HandleReturnStackless() + { + JSThread *js_thread = GetJSThread(); + + // Update EcmascirptEnv + js_thread->SetEcmascriptEnv(js_thread->GetEcmascriptEnv()->GetPrevEnvironment()); + + interpreter::InstructionHandler::HandleReturnStackless(); + } + + template + ALWAYS_INLINE void DoEcmaCallDyn() + { + this->UpdateBytecodeOffset(); + + uint16_t v0 = this->GetInst().template GetVReg(); + uint64_t function = GetRegAsTaggedValue(v0).GetRawData(); + JSThread *js_thread = this->GetJSThread(); + if (UNLIKELY(!JSTaggedValue(function).IsCallable())) { + JSHandle error = GetFactory()->GetJSError(ErrorType::TYPE_ERROR, "is not callable"); + js_thread->SetException(error.GetTaggedValue()); + this->MoveToExceptionHandler(); + return; + } + + ECMAObject *this_func = ECMAObject::Cast(JSTaggedValue(function).GetHeapObject()); + Method *method = this_func->GetCallTarget(); + if (method->IsNative()) { + // Native + ASSERT(method->GetNumVregs() == 0); + + uint32_t num_declared_args = method->GetNumArgs(); + uint32_t num_actual_args = JSGetNumberActualArgsDyn(this->GetInst()); + uint32_t num_args = std::max(num_declared_args, num_actual_args); + + Frame *prev_frame = this->GetFrame(); + BytecodeInstruction prev_inst = this->GetInst(); + Frame *frame = + JSFrame::CreateNativeFrame(js_thread, method, js_thread->GetCurrentFrame(), num_args, num_actual_args); + + JSCopyArgumets(this->GetJSThread(), prev_frame, v0, prev_inst, frame, 0, num_actual_args); + + // Call native method + JSTaggedValue ret_value = JSFrame::ExecuteNativeMethod(js_thread, frame, method, num_actual_args); + + JSFrame::DestroyNativeFrame(js_thread, frame); + if (UNLIKELY(js_thread->HasPendingException())) { + this->MoveToExceptionHandler(); + return; + } + ASSERT(JSTaggedValue(ret_value).IsException() == false); + SetAccFromTaggedValue(ret_value); + this->template MoveToNextInst(); + } else if (method->HasCompiledCode()) { + // AOT, JIT + EcmascriptEnvironment *prev_env = js_thread->GetEcmascriptEnv(); + this->template CallCompiledCode(method); + // Restore EcmascriptEnvironment if epilogue was not executed + if (UNLIKELY(js_thread->HasPendingException())) { + js_thread->SetEcmascriptEnv(prev_env); + } + } else { + // Interpreter + JSFunction *js_function = JSFunction::Cast(this_func); + if (UNLIKELY(js_function->IsClassConstructor())) { + JSHandle error = + GetFactory()->GetJSError(ErrorType::TYPE_ERROR, "class constructor cannot called without 'new'"); + js_thread->SetException(error.GetTaggedValue()); + this->MoveToExceptionHandler(); + return; + } + + EcmascriptEnvironment *prev_env = js_thread->GetEcmascriptEnv(); + + // Call stackless interpreter + this->template CallInterpreterStackless, format, true, false, false, false>(method); + + ConstantPool *constant_pool = ConstantPool::Cast(js_function->GetConstantPool().GetHeapObject()); + JSTaggedValue lexical_env = js_function->GetLexicalEnv(); + + // Init EcmascriptEnvironment + EcmascriptEnvironment *new_env = JSFrame::GetJSEnv(this->GetFrame()); + new (new_env) EcmascriptEnvironment(constant_pool, lexical_env.GetHeapObject(), this_func, prev_env); + + // Update EcmascriptEnvironment + js_thread->SetEcmascriptEnv(new_env); + } + } + + template + ALWAYS_INLINE void HandleEcmaCall0dyn() + { + auto v0 = this->GetInst().template GetVReg(); + LOG_INST() << "call0.dyn " + << "v" << v0; + + this->template DoEcmaCallDyn(); + } + + template + ALWAYS_INLINE void HandleEcmaCall1dyn() + { + auto v0 = this->GetInst().template GetVReg(); + auto v1 = this->GetInst().template GetVReg(); + + LOG_INST() << "call1.dyn " + << "v" << v0 << ", v" << v1; + + this->template DoEcmaCallDyn(); + } + + template + ALWAYS_INLINE void HandleEcmaCall2dyn() + { + auto v0 = this->GetInst().template GetVReg(); + auto v1 = this->GetInst().template GetVReg(); + auto v2 = this->GetInst().template GetVReg(); + LOG_INST() << "call2.dyn " + << "v" << v0 << ", v" << v1 << ", v" << v2; + + this->template DoEcmaCallDyn(); + } + + template + ALWAYS_INLINE void HandleEcmaCall3dyn() + { + auto v0 = this->GetInst().template GetVReg(); + auto v1 = this->GetInst().template GetVReg(); + auto v2 = this->GetInst().template GetVReg(); + auto v3 = this->GetInst().template GetVReg(); + LOG_INST() << "call3.dyn " + << "v" << v0 << ", v" << v1 << ", v" << v2 << ", v" << v3; + + this->template DoEcmaCallDyn(); + } + + template + ALWAYS_INLINE void HandleEcmaCallirangedyn() + { + auto v0 = this->GetInst().template GetVReg(); + auto num_args = this->GetInst().template GetImm(); + + auto func = GetRegAsTaggedValue(v0); + + LOG_INST() << "calli.rangedyn " << num_args + 3 << ", v" << v0 << " , func:" << func.GetRawData(); + + this->template DoEcmaCallDyn(); + } + + template + ALWAYS_INLINE void HandleEcmaCallithisrangedyn() + { + auto v0 = this->GetInst().template GetVReg(); + auto num_args = this->GetInst().template GetImm(); + + LOG_INST() << "calli.dyn.this.range " << num_args + 2 << ", v" << v0; + + this->template DoEcmaCallDyn(); + } + + template + ALWAYS_INLINE void HandleEcmaCallspreaddyn() + { + auto v0 = this->GetInst().template GetVReg(); + auto v1 = this->GetInst().template GetVReg(); + auto v2 = this->GetInst().template GetVReg(); + + LOG_INST() << "callspreaddyn" + << " v" << v0 << " v" << v1 << " v" << v2; + + uint64_t func = GetRegAsTaggedValue(v0).GetRawData(); + uint64_t obj = GetRegAsTaggedValue(v1).GetRawData(); + uint64_t array = GetRegAsTaggedValue(v2).GetRawData(); + + INTRINSIC_CALL_CHECK_SETACC(intrinsics::CallspreadDyn(this->GetJSThread(), func, obj, array)); + this->template MoveToNextInst(); + } + + template + ALWAYS_INLINE void HandleEcmaJfalse() + { + auto imm = this->GetInst().template GetImm(); + LOG_INST() << "jfalse" << imm; + + auto acc = GetAccAsTaggedValue(); + + if (acc == TaggedValue::False()) { + this->template UpdateBranchStatistics(); + this->template JumpToInst(imm); + } else { + this->template UpdateBranchStatistics(); + this->template MoveToNextInst(); + } + } + + template + ALWAYS_INLINE void HandleEcmaJtrue() + { + auto imm = this->GetInst().template GetImm(); + LOG_INST() << "jtrue" << imm; + + auto acc = GetAccAsTaggedValue(); + + if (acc == TaggedValue::True()) { + this->template UpdateBranchStatistics(); + this->template JumpToInst(imm); + } else { + this->template UpdateBranchStatistics(); + this->template MoveToNextInst(); + } + } + + template + ALWAYS_INLINE void HandleEcmaLdinfinity() + { + LOG_INST() << "ldinfinity"; + + INTRINSIC_CALL_SETACC(intrinsics::Ldinfinity()); + + this->template MoveToNextInst(); + } + + template + ALWAYS_INLINE void HandleEcmaLdglobalthis() + { + LOG_INST() << "ldglobalthis"; + INTRINSIC_CALL_SETACC(intrinsics::Ldglobalthis(this->GetJSThread())); + this->template MoveToNextInst(); + } + + template + ALWAYS_INLINE void HandleEcmaLdundefined() + { + LOG_INST() << "ldundefined"; + INTRINSIC_CALL_SETACC(intrinsics::Ldundefined()); + this->template MoveToNextInst(); + } + + template + ALWAYS_INLINE void HandleEcmaLdnull() + { + LOG_INST() << "ldnull"; + INTRINSIC_CALL_SETACC(intrinsics::Ldnull()); + this->template MoveToNextInst(); + } + + template + ALWAYS_INLINE void HandleEcmaLdsymbol() + { + LOG_INST() << "ldsymbol"; + INTRINSIC_CALL_SETACC(intrinsics::Ldsymbol(this->GetJSThread())); + this->template MoveToNextInst(); + } + + template + ALWAYS_INLINE void HandleEcmaLdglobal() + { + LOG_INST() << "ldglobal"; + INTRINSIC_CALL_SETACC(intrinsics::Ldglobal(this->GetJSThread())); + this->template MoveToNextInst(); + } + + template + ALWAYS_INLINE void HandleEcmaLdtrue() + { + LOG_INST() << "ldtrue"; + INTRINSIC_CALL_SETACC(intrinsics::Ldtrue()); + this->template MoveToNextInst(); + } + + template + ALWAYS_INLINE void HandleEcmaLdfalse() + { + LOG_INST() << "ldfalse"; + INTRINSIC_CALL_SETACC(intrinsics::Ldfalse()); + this->template MoveToNextInst(); + } + + template + ALWAYS_INLINE void HandleEcmaLdlexenvdyn() + { + LOG_INST() << "ldlexenvDyn "; + INTRINSIC_CALL_SETACC(intrinsics::LdlexenvDyn(this->GetJSThread())); + this->template MoveToNextInst(); + } + + template + ALWAYS_INLINE void HandleEcmaGetunmappedargs() + { + LOG_INST() << "getunmappedargs"; + + uint32_t actualNumArgs = this->GetFrame()->GetNumActualArgs() - NUM_MANDATORY_JSFUNC_ARGS; // not compile-time + uint32_t startIdx = this->GetFrame()->GetMethod()->GetNumVregs() + NUM_MANDATORY_JSFUNC_ARGS; + + auto thread = JSThread::Cast(this->GetThread()); + JSTaggedValue res = SlowRuntimeStub::GetUnmappedArgs(thread, actualNumArgs, GetStkArgs(startIdx)); + + INTERPRETER_RETURN_IF_ABRUPT(res); + SetAccFromTaggedValue(res); + this->template MoveToNextInst(); + } + + template + ALWAYS_INLINE void HandleEcmaAsyncfunctionenter() + { + LOG_INST() << "asyncfunctionenter"; + INTRINSIC_CALL_CHECK_SETACC(intrinsics::AsyncFunctionEnter(this->GetJSThread())); + this->template MoveToNextInst(); + } + + template + ALWAYS_INLINE void HandleEcmaLdboolean() + { + // it38 Unimplement + UNREACHABLE(); + } + + template + ALWAYS_INLINE void HandleEcmaLdnumber() + { + // it38 Unimplement + UNREACHABLE(); + } + + template + ALWAYS_INLINE void HandleEcmaLdstring() + { + // it38 Unimplement + UNREACHABLE(); + } + + template + ALWAYS_INLINE void HandleEcmaLdbigint() + { + // it38 Unimplement + UNREACHABLE(); + } + + template + ALWAYS_INLINE void HandleEcmaTonumber() + { + auto v0 = this->GetInst().template GetVReg(); + LOG_INST() << "intrinsics::tonumber" + << " v" << v0; + uint64_t value = GetRegAsTaggedValue(v0).GetRawData(); + + INTRINSIC_CALL_CHECK_SETACC(intrinsics::Tonumber(this->GetJSThread(), value)); + this->template MoveToNextInst(); + } + + template + ALWAYS_INLINE void HandleEcmaNegdyn() + { + auto v0 = this->GetInst().template GetVReg(); + LOG_INST() << "negdyn" + << " v" << v0; + + uint64_t value = GetRegAsTaggedValue(v0).GetRawData(); + + INTRINSIC_CALL_CHECK_SETACC(intrinsics::NegDyn(this->GetJSThread(), value)); + this->template MoveToNextInst(); + } + + template + ALWAYS_INLINE void HandleEcmaNotdyn() + { + auto v0 = this->GetInst().template GetVReg(); + LOG_INST() << "notdyn" + << " v" << v0; + + uint64_t value = GetRegAsTaggedValue(v0).GetRawData(); + + INTRINSIC_CALL_CHECK_SETACC(intrinsics::NotDyn(this->GetJSThread(), value)); + this->template MoveToNextInst(); + } + + template + ALWAYS_INLINE void HandleEcmaIncdyn() + { + auto v0 = this->GetInst().template GetVReg(); + LOG_INST() << "incdyn" + << " v" << v0; + + uint64_t value = GetRegAsTaggedValue(v0).GetRawData(); + + INTRINSIC_CALL_CHECK_SETACC(intrinsics::IncDyn(this->GetJSThread(), value)); + this->template MoveToNextInst(); + } + + template + ALWAYS_INLINE void HandleEcmaDecdyn() + { + auto v0 = this->GetInst().template GetVReg(); + LOG_INST() << "decdyn" + << " v" << v0; + + uint64_t value = GetRegAsTaggedValue(v0).GetRawData(); + + INTRINSIC_CALL_CHECK_SETACC(intrinsics::DecDyn(this->GetJSThread(), value)); + this->template MoveToNextInst(); + } + + template + ALWAYS_INLINE void HandleEcmaRethrowdyn() + { + LOG_INST() << "rethrowdyn"; + auto thread = JSThread::Cast(this->GetThread()); + auto acc = GetAccAsTaggedValue(); + + if (acc.IsHole()) { + this->template MoveToNextInst(); + return; + } + + SlowRuntimeStub::ThrowDyn(thread, acc); + this->MoveToExceptionHandler(); + return; + } + + template + ALWAYS_INLINE void HandleEcmaThrowdyn() + { + LOG_INST() << "throwdyn"; + auto thread = JSThread::Cast(this->GetThread()); + auto acc = GetAccAsTaggedValue(); + SlowRuntimeStub::ThrowDyn(thread, acc); + this->MoveToExceptionHandler(); + } + + template + ALWAYS_INLINE void HandleEcmaTypeofdyn() + { + LOG_INST() << "typeofdyn"; + uint64_t acc = GetAccAsTaggedValue().GetRawData(); + + INTRINSIC_CALL_SETACC(intrinsics::TypeofDyn(this->GetJSThread(), acc)); + this->template MoveToNextInst(); + } + + template + ALWAYS_INLINE void HandleEcmaToboolean() + { + LOG_INST() << "toboolean"; + + INTRINSIC_CALL_SETACC(intrinsics::Toboolean(GetAccAsTaggedValue().GetRawData())); + this->template MoveToNextInst(); + } + + template + ALWAYS_INLINE void HandleEcmaNegate() + { + LOG_INST() << "negate"; + uint64_t value = GetAccAsTaggedValue().GetRawData(); + + INTRINSIC_CALL_SETACC(intrinsics::Negate(value)); + this->template MoveToNextInst(); + } + + template + ALWAYS_INLINE void HandleEcmaIsundefined() + { + LOG_INST() << "isundefined"; + uint64_t value = GetAccAsTaggedValue().GetRawData(); + + INTRINSIC_CALL_SETACC(intrinsics::IsUndefined(value)); + this->template MoveToNextInst(); + } + + template + ALWAYS_INLINE void HandleEcmaIstrue() + { + LOG_INST() << "istrue"; + uint64_t value = GetAccAsTaggedValue().GetRawData(); + + INTRINSIC_CALL_SETACC(intrinsics::IsTrue(value)); + this->template MoveToNextInst(); + } + + template + ALWAYS_INLINE void HandleEcmaIsfalse() + { + LOG_INST() << "isfalse"; + uint64_t value = GetAccAsTaggedValue().GetRawData(); + + INTRINSIC_CALL_SETACC(intrinsics::IsFalse(value)); + this->template MoveToNextInst(); + } + + template + ALWAYS_INLINE void HandleEcmaIscoercible() + { + LOG_INST() << "iscoercible"; + uint64_t value = GetAccAsTaggedValue().GetRawData(); + + INTRINSIC_CALL_SETACC(intrinsics::IsCoercible(value)); + this->template MoveToNextInst(); + } + + template + ALWAYS_INLINE void HandleEcmaGetpropiterator() + { + LOG_INST() << "getpropiterator"; + + uint64_t acc = GetAccAsTaggedValue().GetRawData(); + + INTRINSIC_CALL_CHECK_SETACC(intrinsics::GetPropIterator(this->GetJSThread(), acc)); + this->template MoveToNextInst(); + } + + template + ALWAYS_INLINE void HandleEcmaResumegenerator() + { + auto v0 = this->GetInst().template GetVReg(); + LOG_INST() << "resumegenerator" + << " v" << v0; + + uint64_t value = GetRegAsTaggedValue(v0).GetRawData(); + + INTRINSIC_CALL_SETACC(intrinsics::ResumeGenerator(value)); + this->template MoveToNextInst(); + } + + template + ALWAYS_INLINE void HandleEcmaGetresumemode() + { + auto v0 = this->GetInst().template GetVReg(); + LOG_INST() << "getresumemode" + << " v" << v0; + + uint64_t value = GetRegAsTaggedValue(v0).GetRawData(); + + INTRINSIC_CALL_SETACC(intrinsics::GetResumeMode(value)); + this->template MoveToNextInst(); + } + + template + ALWAYS_INLINE void HandleEcmaGetiterator() + { + LOG_INST() << "getiterator"; + + uint64_t obj = GetAccAsTaggedValue().GetRawData(); + + INTRINSIC_CALL_CHECK_SETACC(intrinsics::GetIterator(this->GetJSThread(), obj)); + this->template MoveToNextInst(); + } + + template + ALWAYS_INLINE void HandleEcmaGetasynciterator() + { + LOG_INST() << "getasynciterator"; + + uint64_t obj = GetAccAsTaggedValue().GetRawData(); + + INTRINSIC_CALL_CHECK_SETACC(intrinsics::GetAsyncIterator(this->GetJSThread(), obj)); + this->template MoveToNextInst(); + } + + template + ALWAYS_INLINE void HandleEcmaThrowundefined() + { + // the instrunction has beed retired + LOG_INST() << "-------------"; + UNREACHABLE(); + } + + template + ALWAYS_INLINE void HandleEcmaThrowconstassignment() + { + auto string_id = this->GetInst().template GetId(); + auto prop = GetConstantPool(this->GetJSThread())->GetObjectFromCache(string_id.AsIndex()); + LOG_INST() << "throwconstassignment " + << "stringId:" << ConvertToString(EcmaString::Cast(prop.GetHeapObject())); + + auto thread = JSThread::Cast(this->GetThread()); + SlowRuntimeStub::ThrowConstAssignment(thread, prop); + this->MoveToExceptionHandler(); + } + + template + ALWAYS_INLINE void HandleEcmaThrowthrownotexists() + { + LOG_INST() << "throwthrownotexists"; + auto thread = JSThread::Cast(this->GetThread()); + SlowRuntimeStub::ThrowThrowNotExists(thread); + this->MoveToExceptionHandler(); + } + + template + ALWAYS_INLINE void HandleEcmaThrowpatternnoncoercible() + { + LOG_INST() << "throwpatternnoncoercible"; + auto thread = JSThread::Cast(this->GetThread()); + SlowRuntimeStub::ThrowPatternNonCoercible(thread); + this->MoveToExceptionHandler(); + } + + template + ALWAYS_INLINE void HandleEcmaThrowifnotobject() + { + LOG_INST() << "throwifnotobject"; + + if (GetAccAsTaggedValue().IsECMAObject()) { + this->template MoveToNextInst(); + return; + } + auto thread = JSThread::Cast(this->GetThread()); + SlowRuntimeStub::ThrowIfNotObject(thread); + this->MoveToExceptionHandler(); + } + + template + ALWAYS_INLINE void HandleEcmaCloseiterator() + { + auto v0 = this->GetInst().template GetVReg(); + LOG_INST() << "closeiterator" + << " v" << v0; + + uint64_t iter = GetRegAsTaggedValue(v0).GetRawData(); + uint64_t acc = GetAccAsTaggedValue().GetRawData(); + + INTRINSIC_CALL_CHECK_SETACC(intrinsics::CloseIterator(this->GetJSThread(), iter, acc)); + this->template MoveToNextInst(); + } + + template + ALWAYS_INLINE void HandleEcmaLdobject() + { + LOG_INST() << "-------------"; + UNREACHABLE(); + } + + template + ALWAYS_INLINE void HandleEcmaLdfunction() + { + LOG_INST() << "-------------"; + UNREACHABLE(); + } + + template + ALWAYS_INLINE void HandleEcmaAdd2dyn() + { + auto v0 = this->GetInst().template GetVReg(); + LOG_INST() << "intrinsics::add2dyn" + << " v" << v0; + uint64_t left = GetRegAsTaggedValue(v0).GetRawData(); + uint64_t right = GetAccAsTaggedValue().GetRawData(); + + INTRINSIC_CALL_CHECK_SETACC(intrinsics::Add2Dyn(this->GetJSThread(), left, right)); + this->template MoveToNextInst(); + } + + template + ALWAYS_INLINE void HandleEcmaSub2dyn() + { + auto v0 = this->GetInst().template GetVReg(); + LOG_INST() << "intrinsics::sub2dyn" + << " v" << v0; + uint64_t left = GetRegAsTaggedValue(v0).GetRawData(); + uint64_t right = GetAccAsTaggedValue().GetRawData(); + + INTRINSIC_CALL_CHECK_SETACC(intrinsics::Sub2Dyn(this->GetJSThread(), left, right)); + this->template MoveToNextInst(); + } + + template + ALWAYS_INLINE void HandleEcmaMul2dyn() + { + auto v0 = this->GetInst().template GetVReg(); + LOG_INST() << "intrinsics::mul2dyn" + << " v" << v0; + uint64_t left = GetRegAsTaggedValue(v0).GetRawData(); + uint64_t right = GetAccAsTaggedValue().GetRawData(); + + INTRINSIC_CALL_CHECK_SETACC(intrinsics::Mul2Dyn(this->GetJSThread(), left, right)); + this->template MoveToNextInst(); + } + + template + ALWAYS_INLINE void HandleEcmaDiv2dyn() + { + auto v0 = this->GetInst().template GetVReg(); + LOG_INST() << "intrinsics::div2dyn" + << " v" << v0; + uint64_t left = GetRegAsTaggedValue(v0).GetRawData(); + uint64_t right = GetAccAsTaggedValue().GetRawData(); + + INTRINSIC_CALL_CHECK_SETACC(intrinsics::Div2Dyn(this->GetJSThread(), left, right)); + this->template MoveToNextInst(); + } + + template + ALWAYS_INLINE void HandleEcmaMod2dyn() + { + auto v0 = this->GetInst().template GetVReg(); + LOG_INST() << "intrinsics::mod2dyn" + << " v" << v0; + uint64_t left = GetRegAsTaggedValue(v0).GetRawData(); + uint64_t right = GetAccAsTaggedValue().GetRawData(); + + INTRINSIC_CALL_CHECK_SETACC(intrinsics::Mod2Dyn(this->GetJSThread(), left, right)); + this->template MoveToNextInst(); + } + + template + ALWAYS_INLINE void HandleEcmaEqdyn() + { + auto v0 = this->GetInst().template GetVReg(); + LOG_INST() << "intrinsics::eqdyn" + << " v" << v0; + uint64_t left = GetRegAsTaggedValue(v0).GetRawData(); + uint64_t right = GetAccAsTaggedValue().GetRawData(); + + INTRINSIC_CALL_CHECK_SETACC(intrinsics::EqDyn(this->GetJSThread(), left, right)); + this->template MoveToNextInst(); + } + + template + ALWAYS_INLINE void HandleEcmaNoteqdyn() + { + auto v0 = this->GetInst().template GetVReg(); + LOG_INST() << "noteqdyn" + << " v" << v0; + + uint64_t lhs = GetRegAsTaggedValue(v0).GetRawData(); + uint64_t rhs = GetAccAsTaggedValue().GetRawData(); + + INTRINSIC_CALL_CHECK_SETACC(intrinsics::NotEqDyn(this->GetJSThread(), lhs, rhs)); + this->template MoveToNextInst(); + } + + template + ALWAYS_INLINE void HandleEcmaLessdyn() + { + auto v0 = this->GetInst().template GetVReg(); + LOG_INST() << "intrinsics::lessdyn" + << " v" << v0; + uint64_t left = GetRegAsTaggedValue(v0).GetRawData(); + uint64_t right = GetAccAsTaggedValue().GetRawData(); + + INTRINSIC_CALL_CHECK_SETACC(intrinsics::LessDyn(this->GetJSThread(), left, right)); + this->template MoveToNextInst(); + } + + template + ALWAYS_INLINE void HandleEcmaLesseqdyn() + { + auto v0 = this->GetInst().template GetVReg(); + LOG_INST() << "intrinsics::lesseqdyn" + << " v" << v0; + uint64_t left = GetRegAsTaggedValue(v0).GetRawData(); + uint64_t right = GetAccAsTaggedValue().GetRawData(); + + INTRINSIC_CALL_CHECK_SETACC(intrinsics::LessEqDyn(this->GetJSThread(), left, right)); + this->template MoveToNextInst(); + } + + template + ALWAYS_INLINE void HandleEcmaGreaterdyn() + { + auto v0 = this->GetInst().template GetVReg(); + LOG_INST() << "intrinsics::greaterdyn" + << " v" << v0; + uint64_t left = GetRegAsTaggedValue(v0).GetRawData(); + uint64_t right = GetAccAsTaggedValue().GetRawData(); + + INTRINSIC_CALL_CHECK_SETACC(intrinsics::GreaterDyn(this->GetJSThread(), left, right)); + this->template MoveToNextInst(); + } + + template + ALWAYS_INLINE void HandleEcmaGreatereqdyn() + { + auto v0 = this->GetInst().template GetVReg(); + LOG_INST() << "intrinsics::greatereqdyn" + << " v" << v0; + uint64_t left = GetRegAsTaggedValue(v0).GetRawData(); + uint64_t right = GetAccAsTaggedValue().GetRawData(); + + INTRINSIC_CALL_CHECK_SETACC(intrinsics::GreaterEqDyn(this->GetJSThread(), left, right)); + this->template MoveToNextInst(); + } + + template + ALWAYS_INLINE void HandleEcmaShl2dyn() + { + auto v0 = this->GetInst().template GetVReg(); + LOG_INST() << "shl2dyn" + << " v" << v0; + + uint64_t lhs = GetRegAsTaggedValue(v0).GetRawData(); + uint64_t rhs = GetAccAsTaggedValue().GetRawData(); + + INTRINSIC_CALL_CHECK_SETACC(intrinsics::Shl2Dyn(this->GetJSThread(), lhs, rhs)); + this->template MoveToNextInst(); + } + + template + ALWAYS_INLINE void HandleEcmaShr2dyn() + { + auto v0 = this->GetInst().template GetVReg(); + LOG_INST() << "shr2dyn" + << " v" << v0; + + uint64_t lhs = GetRegAsTaggedValue(v0).GetRawData(); + uint64_t rhs = GetAccAsTaggedValue().GetRawData(); + + INTRINSIC_CALL_CHECK_SETACC(intrinsics::Shr2Dyn(this->GetJSThread(), lhs, rhs)); + this->template MoveToNextInst(); + } + + template + ALWAYS_INLINE void HandleEcmaAshr2dyn() + { + auto v0 = this->GetInst().template GetVReg(); + LOG_INST() << "ashr2dyn" + << " v" << v0; + + uint64_t lhs = GetRegAsTaggedValue(v0).GetRawData(); + uint64_t rhs = GetAccAsTaggedValue().GetRawData(); + + INTRINSIC_CALL_CHECK_SETACC(intrinsics::Ashr2Dyn(this->GetJSThread(), lhs, rhs)); + this->template MoveToNextInst(); + } + + template + ALWAYS_INLINE void HandleEcmaAnd2dyn() + { + auto v0 = this->GetInst().template GetVReg(); + LOG_INST() << "and2dyn" + << " v" << v0; + + uint64_t lhs = GetRegAsTaggedValue(v0).GetRawData(); + uint64_t rhs = GetAccAsTaggedValue().GetRawData(); + + INTRINSIC_CALL_CHECK_SETACC(intrinsics::And2Dyn(this->GetJSThread(), lhs, rhs)); + this->template MoveToNextInst(); + } + + template + ALWAYS_INLINE void HandleEcmaOr2dyn() + { + auto v0 = this->GetInst().template GetVReg(); + LOG_INST() << "or2dyn" + << " v" << v0; + + uint64_t lhs = GetRegAsTaggedValue(v0).GetRawData(); + uint64_t rhs = GetAccAsTaggedValue().GetRawData(); + + INTRINSIC_CALL_CHECK_SETACC(intrinsics::Or2Dyn(this->GetJSThread(), lhs, rhs)); + this->template MoveToNextInst(); + } + + template + ALWAYS_INLINE void HandleEcmaXor2dyn() + { + auto v0 = this->GetInst().template GetVReg(); + LOG_INST() << "xor2dyn" + << " v" << v0; + + uint64_t lhs = GetRegAsTaggedValue(v0).GetRawData(); + uint64_t rhs = GetAccAsTaggedValue().GetRawData(); + + INTRINSIC_CALL_CHECK_SETACC(intrinsics::Xor2Dyn(this->GetJSThread(), lhs, rhs)); + this->template MoveToNextInst(); + } + + template + ALWAYS_INLINE void HandleEcmaDelobjprop() + { + auto v0 = this->GetInst().template GetVReg(); + auto v1 = this->GetInst().template GetVReg(); + LOG_INST() << "delobjprop" + << " v0" << v0 << " v1" << v1; + + uint64_t obj = GetRegAsTaggedValue(v0).GetRawData(); + uint64_t prop = GetRegAsTaggedValue(v1).GetRawData(); + + INTRINSIC_CALL_CHECK_SETACC(intrinsics::Delobjprop(this->GetJSThread(), obj, prop)); + this->template MoveToNextInst(); + } + + template + ALWAYS_INLINE void HandleEcmaDefineglobalvar() + { + // it38 Unimplement + LOG_INST() << "-------------"; + UNREACHABLE(); + } + + template + ALWAYS_INLINE void HandleEcmaDefinelocalvar() + { + // it38 Unimplement + LOG_INST() << "-------------"; + UNREACHABLE(); + } + + template + ALWAYS_INLINE void HandleEcmaDefinefuncexpr() + { + // it38 Unimplement + LOG_INST() << "-------------"; + UNREACHABLE(); + } + + template + ALWAYS_INLINE void HandleEcmaDefinefuncdyn() + { + auto method_id = this->GetInst().template GetId(); + auto v0 = this->GetInst().template GetVReg(); + LOG_INST() << "definefuncDyn" + << " v" << v0 << std::hex << method_id; + + uint64_t env = GetRegAsTaggedValue(v0).GetRawData(); + INTRINSIC_CALL_CHECK_SETACC(intrinsics::DefinefuncDyn(this->GetJSThread(), method_id.AsIndex(), env)); + this->template MoveToNextInst(); + } + + template + ALWAYS_INLINE void HandleEcmaDefinencfuncdyn() + { + auto method_id = this->GetInst().template GetId(); + auto v0 = this->GetInst().template GetVReg(); + LOG_INST() << "definencfuncDyn" + << " v" << v0 << ", method_id: " << std::hex << method_id; + + uint64_t env = GetRegAsTaggedValue(v0).GetRawData(); + uint64_t home_object = GetAccAsTaggedValue().GetRawData(); + + INTRINSIC_CALL_CHECK_SETACC( + intrinsics::DefineNCFuncDyn(this->GetJSThread(), method_id.AsIndex(), env, home_object)); + this->template MoveToNextInst(); + } + + template + ALWAYS_INLINE void HandleEcmaDefinemethod() + { + auto method_id = this->GetInst().template GetId(); + auto v0 = this->GetInst().template GetVReg(); + LOG_INST() << "definemethod" + << " v" << v0 << std::hex << method_id; + + uint64_t tagged_cur_env = GetRegAsTaggedValue(v0).GetRawData(); + uint64_t home_object = GetAccAsTaggedValue().GetRawData(); + + SaveAccToFrame(); + INTRINSIC_CALL_CHECK_SETACC( + intrinsics::DefineMethod(this->GetJSThread(), method_id.AsIndex(), tagged_cur_env, home_object)); + this->template MoveToNextInst(); + } + + template + ALWAYS_INLINE void HandleEcmaNewobjdynrange() + { + auto first_arg_reg_idx = this->GetInst().template GetVReg(); + auto num_args = this->GetInst().template GetImm(); + + LOG_INST() << "newobjDynrange " << num_args << " v" << first_arg_reg_idx; + + auto thread = JSThread::Cast(this->GetThread()); + JSTaggedValue res = SlowRuntimeStub::NewObjDynRange(thread, num_args, GetStkArgs(first_arg_reg_idx)); + INTERPRETER_RETURN_IF_ABRUPT(res); + + SetAccFromTaggedValue(res); + this->template MoveToNextInst(); + } + + template + ALWAYS_INLINE void HandleEcmaRefeqdyn() + { + LOG_INST() << "-------------"; + UNREACHABLE(); + } + + template + ALWAYS_INLINE void HandleEcmaExpdyn() + { + auto v0 = this->GetInst().template GetVReg(); + LOG_INST() << "intrinsics::expdyn" + << " v" << v0; + uint64_t base = GetRegAsTaggedValue(v0).GetRawData(); + uint64_t exponent = GetAccAsTaggedValue().GetRawData(); + + SaveAccToFrame(); + INTRINSIC_CALL_CHECK_SETACC(intrinsics::ExpDyn(this->GetJSThread(), base, exponent)); + this->template MoveToNextInst(); + } + + template + ALWAYS_INLINE void HandleEcmaCallruntimerange() + { + // it38 Unimplement + LOG_INST() << "-------------"; + UNREACHABLE(); + } + + template + ALWAYS_INLINE void HandleEcmaIsindyn() + { + auto v0 = this->GetInst().template GetVReg(); + LOG_INST() << "isindyn" + << " v" << v0; + + uint64_t prop = GetRegAsTaggedValue(v0).GetRawData(); + uint64_t obj = GetAccAsTaggedValue().GetRawData(); + + INTRINSIC_CALL_CHECK_SETACC(intrinsics::IsinDyn(this->GetJSThread(), prop, obj)); + this->template MoveToNextInst(); + } + + template + ALWAYS_INLINE void HandleEcmaInstanceofdyn() + { + auto v0 = this->GetInst().template GetVReg(); + LOG_INST() << "instanceofdyn" + << " v" << v0; + + uint64_t obj = GetRegAsTaggedValue(v0).GetRawData(); + uint64_t target = GetAccAsTaggedValue().GetRawData(); + + INTRINSIC_CALL_CHECK_SETACC(intrinsics::InstanceofDyn(this->GetJSThread(), obj, target)); + this->template MoveToNextInst(); + } + + template + ALWAYS_INLINE void HandleEcmaStrictnoteqdyn() + { + auto v0 = this->GetInst().template GetVReg(); + LOG_INST() << "strictnoteq" + << " v" << v0; + + uint64_t left = GetRegAsTaggedValue(v0).GetRawData(); + uint64_t right = GetAccAsTaggedValue().GetRawData(); + + INTRINSIC_CALL_SETACC(intrinsics::StrictNotEqDyn(left, right)); + this->template MoveToNextInst(); + } + + template + ALWAYS_INLINE void HandleEcmaStricteqdyn() + { + auto v0 = this->GetInst().template GetVReg(); + LOG_INST() << "intrinsics::stricteqdyn" + << " v" << v0; + uint64_t left = GetRegAsTaggedValue(v0).GetRawData(); + uint64_t right = GetAccAsTaggedValue().GetRawData(); + + INTRINSIC_CALL_CHECK_SETACC(intrinsics::StrictEqDyn(left, right)); + this->template MoveToNextInst(); + } + + template + ALWAYS_INLINE void HandleEcmaLdlexvardyn() + { + auto level = this->GetInst().template GetImm(); + auto slot = this->GetInst().template GetImm(); + LOG_INST() << "ldlexvardyn" + << " level:" << level << " slot:" << slot; + + INTRINSIC_CALL_SETACC(intrinsics::LdLexVarDyn(this->GetJSThread(), level, slot)); + this->template MoveToNextInst(); + } + + template + ALWAYS_INLINE void HandleEcmaStlexvardyn() + { + auto level = this->GetInst().template GetImm(); + auto slot = this->GetInst().template GetImm(); + + LOG_INST() << "stlexvardyn" + << " level:" << level << " slot:" << slot; + + uint64_t value = GetAccAsTaggedValue().GetRawData(); + intrinsics::StLexVarDyn(this->GetJSThread(), level, slot, value); + + this->template MoveToNextInst(); + } + + template + ALWAYS_INLINE void HandleEcmaNewlexenvdyn() + { + auto numVars = this->GetInst().template GetImm(); + + LOG_INST() << "newlexenvdyn" + << " imm " << numVars; + + INTRINSIC_CALL_CHECK_SETACC(intrinsics::NewlexenvDyn(this->GetJSThread(), numVars)); + this->template MoveToNextInst(); + } + + template + ALWAYS_INLINE void HandleEcmaCopylexenvdyn() + { + LOG_INST() << "copylexenvdyn"; + + INTRINSIC_CALL_CHECK_SETACC(intrinsics::CopylexenvDyn()); + this->template MoveToNextInst(); + } + + template + ALWAYS_INLINE void HandleEcmaCreateiterresultobj() + { + auto done = this->GetInst().template GetImm(); + LOG_INST() << "createiterresultobj" + << " done " << std::boolalpha << done; + + uint64_t value = GetAccAsTaggedValue().GetRawData(); + + INTRINSIC_CALL_CHECK_SETACC(intrinsics::CreateIterResultObj(this->GetJSThread(), value, done)); + this->template MoveToNextInst(); + } + + template + ALWAYS_INLINE void HandleEcmaSuspendgenerator() + { + auto v0 = this->GetInst().template GetVReg(); + LOG_INST() << "suspendgenerator" + << " v" << v0; + + auto gen_obj = GetRegAsTaggedValue(v0); + auto value = GetAccAsTaggedValue(); + // SuspendGenerator preserves BCOffset and acc + this->GetFrame()->SetBytecodeOffset(this->GetBytecodeOffset()); + SaveAccToFrame(); + + auto thread = JSThread::Cast(this->GetThread()); + auto res = SlowRuntimeStub::SuspendGenerator(thread, gen_obj, value); + + LOG(DEBUG, INTERPRETER) << "Exit: SuspendGenerator: res - " << res.GetRawData(); + + INTERPRETER_RETURN_IF_ABRUPT(res); + SetAccFromTaggedValue(res); + SaveAccToFrame(); + // return to caller frame + } + + template + ALWAYS_INLINE void HandleEcmaSuspendasyncgenerator() + { + auto v0 = this->GetInst().template GetVReg(); + LOG_INST() << "suspendasyncgenerator" + << " v" << v0; + + auto gen_obj = GetRegAsTaggedValue(v0); + auto value = GetAccAsTaggedValue(); + // SuspendAsyncGenerator preserves BCOffset and acc + this->GetFrame()->SetBytecodeOffset(this->GetBytecodeOffset()); + SaveAccToFrame(); + + auto thread = JSThread::Cast(this->GetThread()); + auto res = SlowRuntimeStub::SuspendAsyncGenerator(thread, gen_obj, value); + + LOG(DEBUG, INTERPRETER) << "Exit: SuspendAsyncGenerator: res - " << res.GetRawData(); + + INTERPRETER_RETURN_IF_ABRUPT(res); + SetAccFromTaggedValue(res); + SaveAccToFrame(); + ASSERT(!this->GetFrame()->IsStackless()); + // return to caller frame + } + + template + ALWAYS_INLINE void HandleEcmaAsyncfunctionawait() + { + auto v0 = this->GetInst().template GetVReg(); + + LOG_INST() << "asyncfunctionawait" + << " v" << v0; + + uint64_t async_func_obj = GetRegAsTaggedValue(v0).GetRawData(); + uint64_t value = GetAccAsTaggedValue().GetRawData(); + + INTRINSIC_CALL_CHECK_SETACC(intrinsics::AsyncFunctionAwait(this->GetJSThread(), async_func_obj, value)); + this->template MoveToNextInst(); + } + + template + ALWAYS_INLINE void HandleEcmaAsyncfunctionresolve() + { + auto v0 = this->GetInst().template GetVReg(); + LOG_INST() << "asyncfunctionresolve" + << " v" << v0; + + uint64_t async_func_obj = GetRegAsTaggedValue(v0).GetRawData(); + uint64_t value = GetAccAsTaggedValue().GetRawData(); + + INTRINSIC_CALL_CHECK_SETACC(intrinsics::AsyncFunctionResolve(this->GetJSThread(), async_func_obj, value)); + this->template MoveToNextInst(); + } + + template + ALWAYS_INLINE void HandleEcmaAsyncfunctionreject() + { + auto v0 = this->GetInst().template GetVReg(); + LOG_INST() << "asyncfunctionreject" + << " v" << v0; + + uint64_t async_func_obj = GetRegAsTaggedValue(v0).GetRawData(); + uint64_t value = GetAccAsTaggedValue().GetRawData(); + + INTRINSIC_CALL_CHECK_SETACC(intrinsics::AsyncFunctionReject(this->GetJSThread(), async_func_obj, value)); + this->template MoveToNextInst(); + } + + template + ALWAYS_INLINE void HandleEcmaAsyncgeneratorresolve() + { + auto v0 = this->GetInst().template GetVReg(); + LOG_INST() << "asyncgeneratorresolve" + << " v" << v0; + + uint64_t async_func_obj = GetRegAsTaggedValue(v0).GetRawData(); + uint64_t value = GetAccAsTaggedValue().GetRawData(); + + INTRINSIC_CALL_CHECK_SETACC(intrinsics::AsyncGeneratorResolve(this->GetJSThread(), async_func_obj, value)); + this->template MoveToNextInst(); + } + + template + ALWAYS_INLINE void HandleEcmaAsyncgeneratorreject() + { + auto v0 = this->GetInst().template GetVReg(); + LOG_INST() << "asyncgeneratorreject" + << " v" << v0; + + uint64_t async_func_obj = GetRegAsTaggedValue(v0).GetRawData(); + uint64_t value = GetAccAsTaggedValue().GetRawData(); + + INTRINSIC_CALL_CHECK_SETACC(intrinsics::AsyncGeneratorReject(this->GetJSThread(), async_func_obj, value)); + this->template MoveToNextInst(); + } + + template + ALWAYS_INLINE void HandleEcmaNewobjspreaddyn() + { + auto v0 = this->GetInst().template GetVReg(); + auto v1 = this->GetInst().template GetVReg(); + LOG_INST() << "intrinsic::newobjspearddyn" + << " v" << v0 << " v" << v1; + + uint64_t func = GetRegAsTaggedValue(v0).GetRawData(); + uint64_t new_target = GetRegAsTaggedValue(v1).GetRawData(); + uint64_t array = GetAccAsTaggedValue().GetRawData(); + + INTRINSIC_CALL_CHECK_SETACC(intrinsics::NewobjspreadDyn(this->GetJSThread(), func, new_target, array)); + this->template MoveToNextInst(); + } + + template + ALWAYS_INLINE void HandleEcmaThrowundefinedifhole() + { + auto string_id = this->GetInst().template GetId(); + auto prop = GetConstantPool(this->GetJSThread())->GetObjectFromCache(string_id.AsIndex()); + LOG_INST() << "intrinsic::throwundefinedifhole " + << "stringId:" << ConvertToString(EcmaString::Cast(prop.GetHeapObject())); + + if (!GetAccAsTaggedValue().IsHole()) { + this->template MoveToNextInst(); + return; + } + + auto thread = JSThread::Cast(this->GetThread()); + SlowRuntimeStub::ThrowUndefinedIfHole(thread, prop); + this->MoveToExceptionHandler(); + } + + template + ALWAYS_INLINE void HandleEcmaStownbyname() + { + auto v0 = this->GetInst().template GetVReg(); + auto id = this->GetInst().template GetId(); + + LOG_INST() << "stownbyname " + << "v" << v0 << " stringId:" << id.AsIndex(); + + uint64_t obj = GetRegAsTaggedValue(v0).GetRawData(); + uint64_t value = GetAccAsTaggedValue().GetRawData(); + + SaveAccToFrame(); + INTRINSIC_CALL_CHECK(intrinsics::StOwnByName(this->GetJSThread(), id.AsIndex(), obj, value)); + RestoreAccFromFrame(); + this->template MoveToNextInst(); + } + + template + ALWAYS_INLINE void HandleEcmaCreateemptyarray() + { + LOG_INST() << "createemptyarray"; + + INTRINSIC_CALL_SETACC(intrinsics::CreateEmptyArray(this->GetJSThread())); + this->template MoveToNextInst(); + } + + template + ALWAYS_INLINE void HandleEcmaCreateemptyobject() + { + LOG_INST() << "createemptyobject"; + + INTRINSIC_CALL_SETACC(intrinsics::CreateEmptyObject(this->GetJSThread())); + this->template MoveToNextInst(); + } + + template + ALWAYS_INLINE void HandleEcmaCreateobjectwithbuffer() + { + auto imm = this->GetInst().template GetImm(); + LOG_INST() << "createobjectwithbuffer" + << " imm:" << imm; + + INTRINSIC_CALL_CHECK_SETACC(intrinsics::CreateObjectWithBuffer(this->GetJSThread(), imm)); + this->template MoveToNextInst(); + } + + template + ALWAYS_INLINE void HandleEcmaCreatearraywithbuffer() + { + auto imm = this->GetInst().template GetImm(); + LOG_INST() << "createarraywithbuffer" + << " imm:" << imm; + + INTRINSIC_CALL_CHECK_SETACC(intrinsics::CreateArrayWithBuffer(this->GetJSThread(), imm)); + this->template MoveToNextInst(); + } + + template + ALWAYS_INLINE void HandleEcmaImportmodule() + { + auto string_id = this->GetInst().template GetId(); + LOG_INST() << "importmodule " + << "stringId:" << std::hex << string_id.AsIndex(); + + INTRINSIC_CALL_SETACC(intrinsics::ImportModule(this->GetJSThread(), string_id.AsIndex())); + this->template MoveToNextInst(); + } + + template + ALWAYS_INLINE void HandleEcmaStmodulevar() + { + auto string_id = this->GetInst().template GetId(); + LOG_INST() << "stmodulevar " + << "stringId:" << string_id; + + uint64_t value = GetAccAsTaggedValue().GetRawData(); + + SaveAccToFrame(); + intrinsics::StModuleVar(this->GetJSThread(), string_id.AsIndex(), value); + RestoreAccFromFrame(); + this->template MoveToNextInst(); + } + + template + ALWAYS_INLINE void HandleEcmaCopymodule() + { + auto v0 = this->GetInst().template GetVReg(); + LOG_INST() << "copymodule " + << " v" << v0; + + uint64_t src_module = GetRegAsTaggedValue(v0).GetRawData(); + SaveAccToFrame(); + intrinsics::CopyModule(this->GetJSThread(), src_module); + RestoreAccFromFrame(); + this->template MoveToNextInst(); + } + + template + ALWAYS_INLINE void HandleEcmaLdmodvarbyname() + { + auto v0 = this->GetInst().template GetVReg(); + auto string_id = this->GetInst().template GetId(); + + LOG_INST() << "ldmodvarbyname " + << "string_id:" << string_id.AsIndex(); + + uint64_t module_obj = GetRegAsTaggedValue(v0).GetRawData(); + + INTRINSIC_CALL_SETACC(intrinsics::LdModvarByName(this->GetJSThread(), string_id.AsIndex(), module_obj)); + this->template MoveToNextInst(); + } + + template + ALWAYS_INLINE void HandleEcmaGetmethod() + { + auto v0 = this->GetInst().template GetVReg(); + auto string_id = this->GetInst().template GetId(); + + LOG_INST() << "getmethod v" << v0 << " stringId:" << string_id; + uint64_t obj = GetRegAsTaggedValue(v0).GetRawData(); + + INTRINSIC_CALL_CHECK_SETACC(intrinsics::GetMethod(this->GetJSThread(), string_id.AsIndex(), obj)); + this->template MoveToNextInst(); + } + + template + ALWAYS_INLINE void HandleEcmaGettemplateobject() + { + auto v0 = this->GetInst().template GetVReg(); + LOG_INST() << "intrinsic::gettemplateobject" + << " v" << v0; + + uint64_t literal = GetRegAsTaggedValue(v0).GetRawData(); + + INTRINSIC_CALL_CHECK_SETACC(intrinsics::GetTemplateObject(this->GetJSThread(), literal)); + this->template MoveToNextInst(); + } + + template + ALWAYS_INLINE void HandleEcmaGetnextpropname() + { + auto v0 = this->GetInst().template GetVReg(); + LOG_INST() << "intrinsic::getnextpropname" + << " v" << v0; + + uint64_t iter = GetRegAsTaggedValue(v0).GetRawData(); + + INTRINSIC_CALL_CHECK_SETACC(intrinsics::GetNextPropName(this->GetJSThread(), iter)); + this->template MoveToNextInst(); + } + + template + ALWAYS_INLINE void HandleEcmaCopydataproperties() + { + auto v0 = this->GetInst().template GetVReg(); + auto v1 = this->GetInst().template GetVReg(); + LOG_INST() << "intrinsic::copydataproperties" + << " v" << v0 << " v" << v1; + + uint64_t dst = GetRegAsTaggedValue(v0).GetRawData(); + uint64_t src = GetRegAsTaggedValue(v1).GetRawData(); + + INTRINSIC_CALL_CHECK_SETACC(intrinsics::CopyDataProperties(this->GetJSThread(), dst, src)); + this->template MoveToNextInst(); + } + + template + ALWAYS_INLINE void HandleEcmaStownbyindex() + { + uint32_t idx = this->GetInst().template GetImm(); + auto v0 = this->GetInst().template GetVReg(); + LOG_INST() << "stownbyindex" + << " v" << v0 << " imm" << idx; + + uint64_t obj = GetRegAsTaggedValue(v0).GetRawData(); + uint64_t value = GetAccAsTaggedValue().GetRawData(); + + SaveAccToFrame(); + INTRINSIC_CALL_CHECK(intrinsics::StOwnByIndex(this->GetJSThread(), idx, obj, value)); + RestoreAccFromFrame(); + this->template MoveToNextInst(); + } + + template + ALWAYS_INLINE void HandleEcmaStownbyvalue() + { + auto v0 = this->GetInst().template GetVReg(); + auto v1 = this->GetInst().template GetVReg(); + LOG_INST() << "stownbyvalue" + << " v" << v0 << " v" << v1; + + uint64_t receiver = GetRegAsTaggedValue(v0).GetRawData(); + uint64_t prop_key = GetRegAsTaggedValue(v1).GetRawData(); + uint64_t value = GetAccAsTaggedValue().GetRawData(); + + SaveAccToFrame(); + INTRINSIC_CALL_CHECK(intrinsics::StOwnByValue(this->GetJSThread(), receiver, prop_key, value)); + RestoreAccFromFrame(); + this->template MoveToNextInst(); + } + + template + ALWAYS_INLINE void HandleEcmaCreateobjectwithexcludedkeys() + { + auto num_keys = this->GetInst().template GetImm(); + auto v0 = this->GetInst().template GetVReg(); + uint16_t first_arg_reg_idx = this->GetInst().template GetVReg(); + LOG_INST() << "createobjectwithexcludedkeys " << num_keys << " v" << v0 << std::hex << first_arg_reg_idx; + + auto obj = GetRegAsTaggedValue(v0); + auto thread = JSThread::Cast(this->GetThread()); + + auto res = SlowRuntimeStub::CreateObjectWithExcludedKeys(thread, num_keys, obj, GetStkArgs(first_arg_reg_idx)); + INTERPRETER_RETURN_IF_ABRUPT(res); + SetAccFromTaggedValue(res); + + this->template MoveToNextInst(); + } + + template + ALWAYS_INLINE void HandleEcmaDefinegeneratorfunc() + { + auto v0 = this->GetInst().template GetVReg(); + auto method_id = this->GetInst().template GetId(); + LOG_INST() << "define generator function" + << " v" << v0; + + uint64_t env = GetRegAsTaggedValue(v0).GetRawData(); + INTRINSIC_CALL_CHECK_SETACC(intrinsics::DefineGeneratorFunc(this->GetJSThread(), method_id.AsIndex(), env)); + this->template MoveToNextInst(); + } + + template + ALWAYS_INLINE void HandleEcmaDefineasyncfunc() + { + auto v0 = this->GetInst().template GetVReg(); + auto method_id = this->GetInst().template GetId(); + LOG_INST() << "define async function" + << " v" << v0; + + uint64_t env = GetRegAsTaggedValue(v0).GetRawData(); + INTRINSIC_CALL_CHECK_SETACC(intrinsics::DefineAsyncFunc(this->GetJSThread(), method_id.AsIndex(), env)); + this->template MoveToNextInst(); + } + + template + ALWAYS_INLINE void HandleEcmaDefineasyncgeneratorfunc() + { + auto v0 = this->GetInst().template GetVReg(); + auto method_id = this->GetInst().template GetId(); + LOG_INST() << "define async generator function" + << " v" << v0; + + uint64_t env = GetRegAsTaggedValue(v0).GetRawData(); + INTRINSIC_CALL_CHECK_SETACC( + intrinsics::DefineAsyncGeneratorFunc(this->GetJSThread(), method_id.AsIndex(), env)); + this->template MoveToNextInst(); + } + + template + ALWAYS_INLINE void HandleEcmaLdhole() + { + LOG_INST() << "intrinsic::ldhole"; + INTRINSIC_CALL_SETACC(intrinsics::Ldhole()); + + this->template MoveToNextInst(); + } + + template + ALWAYS_INLINE void HandleEcmaCopyrestargs() + { + auto rest_idx = this->GetInst().template GetImm(); + LOG_INST() << "copyrestargs" + << " index: " << rest_idx; + + auto *state = this->GetFrame(); + uint32_t num_vregs = state->GetMethod()->GetNumVregs(); + // Exclude func, newTarget and "this" + int32_t actual_num_args = state->GetNumActualArgs() - NUM_MANDATORY_JSFUNC_ARGS; + int32_t tmp = actual_num_args - rest_idx; + uint32_t rest_num_args = (tmp > 0) ? tmp : 0; + uint32_t start_idx = num_vregs + NUM_MANDATORY_JSFUNC_ARGS + rest_idx; + + auto thread = JSThread::Cast(this->GetThread()); + JSTaggedValue res = SlowRuntimeStub::CopyRestArgs(thread, rest_num_args, GetStkArgs(start_idx)); + INTERPRETER_RETURN_IF_ABRUPT(res); + SetAccFromTaggedValue(res); + this->template MoveToNextInst(); + } + + template + ALWAYS_INLINE void HandleEcmaDefinegettersetterbyvalue() + { + auto v0 = this->GetInst().template GetVReg(); + auto v1 = this->GetInst().template GetVReg(); + auto v2 = this->GetInst().template GetVReg(); + auto v3 = this->GetInst().template GetVReg(); + LOG_INST() << "definegettersetterbyvalue" + << " v" << v0 << " v" << v1 << " v" << v2 << " v" << v3; + + uint64_t obj = GetRegAsTaggedValue(v0).GetRawData(); + uint64_t prop = GetRegAsTaggedValue(v1).GetRawData(); + uint64_t getter = GetRegAsTaggedValue(v2).GetRawData(); + uint64_t setter = GetRegAsTaggedValue(v3).GetRawData(); + uint64_t flag = GetAccAsTaggedValue().GetRawData(); + + INTRINSIC_CALL_CHECK_SETACC( + intrinsics::DefineGetterSetterByValue(this->GetJSThread(), obj, prop, getter, setter, flag)); + this->template MoveToNextInst(); + } + + template + ALWAYS_INLINE void HandleEcmaLdobjbyindex() + { + uint32_t idx = this->GetInst().template GetImm(); + auto v0 = this->GetInst().template GetVReg(); + LOG_INST() << "ldobjbyindex" + << " v" << v0 << " imm" << idx; + + uint64_t obj = GetRegAsTaggedValue(v0).GetRawData(); + + INTRINSIC_CALL_CHECK_SETACC(intrinsics::LdObjByIndex(this->GetJSThread(), idx, obj)); + this->template MoveToNextInst(); + } + + template + ALWAYS_INLINE void HandleEcmaStobjbyindex() + { + uint32_t idx = this->GetInst().template GetImm(); + auto v0 = this->GetInst().template GetVReg(); + LOG_INST() << "stobjbyindex" + << " v" << v0 << " imm" << idx; + + uint64_t obj = GetRegAsTaggedValue(v0).GetRawData(); + uint64_t value = GetAccAsTaggedValue().GetRawData(); + + SaveAccToFrame(); + INTRINSIC_CALL_CHECK(intrinsics::StObjByIndex(this->GetJSThread(), idx, obj, value)); + RestoreAccFromFrame(); + this->template MoveToNextInst(); + } + + template + ALWAYS_INLINE void HandleEcmaLdobjbyvalue() + { + auto v0 = this->GetInst().template GetVReg(); + auto v1 = this->GetInst().template GetVReg(); + LOG_INST() << "Ldobjbyvalue" + << " v" << v0 << " v" << v1; + + uint64_t receiver = GetRegAsTaggedValue(v0).GetRawData(); + uint64_t prop_key = GetRegAsTaggedValue(v1).GetRawData(); + + INTRINSIC_CALL_CHECK_SETACC( + intrinsics::LdObjByValue(this->GetJSThread(), receiver, prop_key, this->GetBytecodeOffset())); + this->template MoveToNextInst(); + } + + template + ALWAYS_INLINE void HandleEcmaStobjbyvalue() + { + auto v0 = this->GetInst().template GetVReg(); + auto v1 = this->GetInst().template GetVReg(); + LOG_INST() << "stobjbyvalue" + << " v" << v0 << " v" << v1; + + uint64_t receiver = GetRegAsTaggedValue(v0).GetRawData(); + uint64_t prop_key = GetRegAsTaggedValue(v1).GetRawData(); + uint64_t value = GetAccAsTaggedValue().GetRawData(); + + SaveAccToFrame(); + INTRINSIC_CALL_CHECK( + intrinsics::StObjByValue(this->GetJSThread(), receiver, prop_key, value, this->GetBytecodeOffset())); + RestoreAccFromFrame(); + this->template MoveToNextInst(); + } + + template + ALWAYS_INLINE void HandleEcmaLdsuperbyvalue() + { + auto v0 = this->GetInst().template GetVReg(); + auto v1 = this->GetInst().template GetVReg(); + LOG_INST() << "Ldsuperbyvalue" + << " v" << v0 << " v" << v1; + + uint64_t receiver = GetRegAsTaggedValue(v0).GetRawData(); + uint64_t prop_key = GetRegAsTaggedValue(v1).GetRawData(); + + INTRINSIC_CALL_CHECK_SETACC(intrinsics::LdSuperByValue(this->GetJSThread(), receiver, prop_key)); + this->template MoveToNextInst(); + } + + template + ALWAYS_INLINE void HandleEcmaStsuperbyvalue() + { + auto v0 = this->GetInst().template GetVReg(); + auto v1 = this->GetInst().template GetVReg(); + LOG_INST() << "stsuperbyvalue" + << " v" << v0 << " v" << v1; + + uint64_t receiver = GetRegAsTaggedValue(v0).GetRawData(); + uint64_t prop_key = GetRegAsTaggedValue(v1).GetRawData(); + uint64_t value = GetAccAsTaggedValue().GetRawData(); + + SaveAccToFrame(); + INTRINSIC_CALL_CHECK(intrinsics::StSuperByValue(this->GetJSThread(), receiver, prop_key, value)); + RestoreAccFromFrame(); + this->template MoveToNextInst(); + } + + template + ALWAYS_INLINE void HandleEcmaTryldglobalbyname() + { + auto string_id = this->GetInst().template GetId(); + + INTRINSIC_CALL_CHECK_SETACC( + intrinsics::TryLdGlobalByName(this->GetJSThread(), string_id.AsIndex(), this->GetBytecodeOffset())); + this->template MoveToNextInst(); + } + + template + ALWAYS_INLINE void HandleEcmaTrystglobalbyname() + { + auto string_id = this->GetInst().template GetId(); + LOG_INST() << "trystglobalbyname" + << " stringId:" << string_id; + + uint64_t value = GetAccAsTaggedValue().GetRawData(); + + SaveAccToFrame(); + INTRINSIC_CALL_CHECK( + intrinsics::TryStGlobalByName(this->GetJSThread(), string_id.AsIndex(), value, this->GetBytecodeOffset())); + RestoreAccFromFrame(); + this->template MoveToNextInst(); + } + + template + ALWAYS_INLINE void HandleEcmaLdglobalvar() + { + auto string_id = this->GetInst().template GetId(); + + LOG_INST() << "ldglobalvar " + << " stringId:" << string_id; + + INTRINSIC_CALL_CHECK_SETACC( + intrinsics::LdGlobalVar(this->GetJSThread(), string_id.AsIndex(), this->GetBytecodeOffset())); + this->template MoveToNextInst(); + } + + template + ALWAYS_INLINE void HandleEcmaLdobjbyname() + { + auto v0 = this->GetInst().template GetVReg(); + auto string_id = this->GetInst().template GetId(); + + LOG_INST() << "ldobjbyname v" << v0 << " stringId:" << string_id; + uint64_t obj = GetRegAsTaggedValue(v0).GetRawData(); + + INTRINSIC_CALL_CHECK_SETACC( + intrinsics::LdObjByName(this->GetJSThread(), string_id.AsIndex(), obj, this->GetBytecodeOffset())); + this->template MoveToNextInst(); + } + + template + ALWAYS_INLINE void HandleEcmaStobjbyname() + { + auto v0 = this->GetInst().template GetVReg(); + auto string_id = this->GetInst().template GetId(); + + LOG_INST() << "stobjbyname " + << "v" << v0 << " stringId:" << string_id.AsIndex(); + + uint64_t obj = GetRegAsTaggedValue(v0).GetRawData(); + uint64_t value = GetAccAsTaggedValue().GetRawData(); + + SaveAccToFrame(); + INTRINSIC_CALL_CHECK( + intrinsics::StObjByName(this->GetJSThread(), string_id.AsIndex(), obj, value, this->GetBytecodeOffset())); + RestoreAccFromFrame(); + this->template MoveToNextInst(); + } + + template + ALWAYS_INLINE void HandleEcmaLdsuperbyname() + { + auto v0 = this->GetInst().template GetVReg(); + auto string_id = this->GetInst().template GetId(); + + LOG_INST() << "ldsuperbyname" + << "v" << v0 << " stringId:" << string_id.AsIndex(); + + uint64_t obj = GetRegAsTaggedValue(v0).GetRawData(); + + INTRINSIC_CALL_CHECK_SETACC(intrinsics::LdSuperByName(this->GetJSThread(), string_id.AsIndex(), obj)); + this->template MoveToNextInst(); + } + + template + ALWAYS_INLINE void HandleEcmaStsuperbyname() + { + auto string_id = this->GetInst().template GetId(); + auto v0 = this->GetInst().template GetVReg(); + + LOG_INST() << "stsuperbyname" + << "v" << v0 << " stringId:" << string_id.AsIndex(); + + uint64_t obj = GetRegAsTaggedValue(v0).GetRawData(); + uint64_t value = GetAccAsTaggedValue().GetRawData(); + + SaveAccToFrame(); + INTRINSIC_CALL_CHECK(intrinsics::StSuperByName(this->GetJSThread(), string_id.AsIndex(), obj, value)); + RestoreAccFromFrame(); + this->template MoveToNextInst(); + } + + template + ALWAYS_INLINE void HandleEcmaStglobalvar() + { + auto string_id = this->GetInst().template GetId(); + + LOG_INST() << "stglobalvar " + << "stringId:" << string_id.AsIndex() << ", "; + + uint64_t value = GetAccAsTaggedValue().GetRawData(); + + SaveAccToFrame(); + INTRINSIC_CALL_CHECK( + intrinsics::StGlobalVar(this->GetJSThread(), string_id.AsIndex(), value, this->GetBytecodeOffset())); + RestoreAccFromFrame(); + this->template MoveToNextInst(); + } + + template + ALWAYS_INLINE void HandleEcmaStgloballet() + { + auto string_id = this->GetInst().template GetId(); + + LOG_INST() << "stgloballet " + << "stringId:" << string_id.AsIndex(); + + uint64_t value = GetAccAsTaggedValue().GetRawData(); + + SaveAccToFrame(); + INTRINSIC_CALL_CHECK(intrinsics::StGlobalLet(this->GetJSThread(), string_id.AsIndex(), value)); + RestoreAccFromFrame(); + this->template MoveToNextInst(); + } + + template + ALWAYS_INLINE void HandleEcmaCreategeneratorobj() + { + auto v0 = this->GetInst().template GetVReg(); + LOG_INST() << "creategeneratorobj" + << " v" << v0; + + uint64_t gen_func = GetRegAsTaggedValue(v0).GetRawData(); + + INTRINSIC_CALL_CHECK_SETACC(intrinsics::CreateGeneratorObj(this->GetJSThread(), gen_func)); + this->template MoveToNextInst(); + } + + template + ALWAYS_INLINE void HandleEcmaSetgeneratorstate() + { + auto v0 = this->GetInst().template GetVReg(); + auto state = this->GetInst().template GetImm(); + LOG_INST() << "setgeneratorstate" + << " v" << v0 << " state" << state; + + uint64_t gen_func = GetRegAsTaggedValue(v0).GetRawData(); + + intrinsics::SetGeneratorState(this->GetJSThread(), gen_func, state); + this->template MoveToNextInst(); + } + + template + ALWAYS_INLINE void HandleEcmaCreateasyncgeneratorobj() + { + auto v0 = this->GetInst().template GetVReg(); + LOG_INST() << "createasyncgeneratorobj" + << " v" << v0; + + uint64_t gen_func = GetRegAsTaggedValue(v0).GetRawData(); + + INTRINSIC_CALL_CHECK_SETACC(intrinsics::CreateAsyncGeneratorObj(this->GetJSThread(), gen_func)); + this->template MoveToNextInst(); + } + + template + ALWAYS_INLINE void HandleEcmaStarrayspread() + { + auto v0 = this->GetInst().template GetVReg(); + auto v1 = this->GetInst().template GetVReg(); + LOG_INST() << "ecmascript::starrayspread" + << " v" << v0 << " v" << v1 << "acc"; + + uint64_t dst = GetRegAsTaggedValue(v0).GetRawData(); + uint64_t index = GetRegAsTaggedValue(v1).GetRawData(); + uint64_t src = GetAccAsTaggedValue().GetRawData(); + + SaveAccToFrame(); + INTRINSIC_CALL_CHECK(intrinsics::StArraySpread(this->GetJSThread(), dst, index, src)); + RestoreAccFromFrame(); + + this->template MoveToNextInst(); + } + + template + ALWAYS_INLINE void HandleEcmaDefineclasswithbuffer() + { + auto methodId = this->GetInst().template GetId().AsIndex(); + auto imm = this->GetInst().template GetImm(); + auto v0 = this->GetInst().template GetVReg(); + auto v1 = this->GetInst().template GetVReg(); + LOG_INST() << "defineclasswithbuffer" + << " method id:" << methodId << " literal id:" << imm << " lexenv: v" << v0 << " parent: v" << v1; + + uint64_t lexenv = GetRegAsTaggedValue(v0).GetRawData(); + uint64_t proto = GetRegAsTaggedValue(v1).GetRawData(); + + INTRINSIC_CALL_CHECK_SETACC( + intrinsics::DefineClassWithBuffer(this->GetJSThread(), methodId, imm, lexenv, proto)); + this->template MoveToNextInst(); + } + + template + ALWAYS_INLINE void HandleEcmaSupercall() + { + auto v0 = this->GetInst().template GetVReg(); + auto range = this->GetInst().template GetImm(); + LOG_INST() << "supercall" + << "range: " << range << " v" << v0; + + auto num_vregs = this->GetFrame()->GetMethod()->GetNumVregs(); + + auto this_func = GetAccAsTaggedValue(); + auto new_target = GetRegAsTaggedValue(num_vregs + 1); + + auto thread = JSThread::Cast(this->GetThread()); + auto res = SlowRuntimeStub::SuperCall(thread, this_func, new_target, range, GetStkArgs(v0)); + INTERPRETER_RETURN_IF_ABRUPT(res); + SetAccFromTaggedValue(res); + + this->template MoveToNextInst(); + } + + template + ALWAYS_INLINE void HandleEcmaSupercallspread() + { + auto v0 = this->GetInst().template GetVReg(); + LOG_INST() << "intrinsic::supercallspread" + << " array: v" << v0; + + uint64_t array = GetRegAsTaggedValue(v0).GetRawData(); + uint64_t this_func = GetAccAsTaggedValue().GetRawData(); + + auto num_vregs = this->GetFrame()->GetMethod()->GetNumVregs(); + uint64_t new_target = GetRegAsTaggedValue(num_vregs + 1).GetRawData(); + + INTRINSIC_CALL_CHECK_SETACC(intrinsics::SuperCallSpread(this->GetJSThread(), array, new_target, this_func)); + this->template MoveToNextInst(); + } + + template + ALWAYS_INLINE void HandleEcmaCreateobjecthavingmethod() + { + auto imm = this->GetInst().template GetImm(); + LOG_INST() << "createobjecthavingmethod" + << " imm:" << imm; + + uint64_t env = GetAccAsTaggedValue().GetRawData(); + + INTRINSIC_CALL_CHECK_SETACC(intrinsics::CreateObjectHavingMethod(this->GetJSThread(), imm, env)); + this->template MoveToNextInst(); + } + + template + ALWAYS_INLINE void HandleEcmaReturnundefined() + { + LOG_INST() << "return.undefined"; + SetAccFromTaggedValue(JSTaggedValue::Undefined()); + this->GetFrame()->GetAcc().Set(JSTaggedValue::Undefined().GetRawData()); + this->template MoveToNextInst(); + } + + template + ALWAYS_INLINE void HandleEcmaLdnan() + { + LOG_INST() << "ldnan"; + + INTRINSIC_CALL_SETACC(intrinsics::Ldnan()); + + this->template MoveToNextInst(); + } + + template + ALWAYS_INLINE void HandleEcmaThrowifsupernotcorrectcall() + { + uint16_t imm = this->GetInst().template GetImm(); + JSTaggedValue thisValue = GetAccAsTaggedValue(); + LOG_INST() << "intrinsic::throwifsupernotcorrectcall" + << " imm:" << imm; + auto thread = JSThread::Cast(this->GetThread()); + JSTaggedValue res = SlowRuntimeStub::ThrowIfSuperNotCorrectCall(thread, imm, thisValue); + INTERPRETER_RETURN_IF_ABRUPT(res); + this->template MoveToNextInst(); + } + + template + ALWAYS_INLINE void HandleEcmaThrowdeletesuperproperty() + { + LOG_INST() << "throwdeletesuperproperty"; + auto thread = JSThread::Cast(this->GetThread()); + SlowRuntimeStub::ThrowDeleteSuperProperty(thread); + this->MoveToExceptionHandler(); + } + + template + ALWAYS_INLINE void HandleEcmaLdhomeobject() + { + LOG_INST() << "ldhomeobject"; + + INTRINSIC_CALL_SETACC(intrinsics::LdHomeObject(this->GetJSThread())); + this->template MoveToNextInst(); + } + + template + ALWAYS_INLINE void HandleEcmaPoplexenvdyn() + { + intrinsics::popLexenvDyn(this->GetJSThread()); + this->template MoveToNextInst(); + } + + template + ALWAYS_INLINE void HandleEcmaSetobjectwithproto() + { + auto v0 = this->GetInst().template GetVReg(); + auto v1 = this->GetInst().template GetVReg(); + + LOG_INST() << "setobjectwithproto" + << " v" << v0 << " v" << v1; + + uint64_t proto = GetRegAsTaggedValue(v0).GetRawData(); + uint64_t obj = GetRegAsTaggedValue(v1).GetRawData(); + + SaveAccToFrame(); + INTRINSIC_CALL_CHECK(intrinsics::SetObjectWithProto(this->GetJSThread(), proto, obj)); + RestoreAccFromFrame(); + this->template MoveToNextInst(); + } + + template + ALWAYS_INLINE void HandleEcmaDebugger() + { + LOG_INST() << "debugger"; + this->template MoveToNextInst(); + } + + ALWAYS_INLINE JSThread *GetJSThread() + { + return JSThread::Cast(this->GetThread()); + } + +}; // InstructionHandler + +} // namespace panda::ecmascript + +#endif // PANDA_ECMASCRIPT_INTERPRETER_INL_H diff --git a/runtime/interpreter/ecma-interpreter.h b/runtime/interpreter/ecma-interpreter.h new file mode 100644 index 000000000..402ba5bd0 --- /dev/null +++ b/runtime/interpreter/ecma-interpreter.h @@ -0,0 +1,29 @@ +/** + * Copyright (c) Huawei Technologies Co., Ltd. 2019-2021. All rights reserved. + */ + +#ifndef PANDA_ECMASCRIPT_INTERPRETER_H +#define PANDA_ECMASCRIPT_INTERPRETER_H + +#include "plugins/ecmascript/runtime/js_tagged_value.h" +#include "runtime/interpreter/frame.h" + +namespace panda::ecmascript { + +constexpr size_t ECMA_CALL_PREALLOC_SIZE = 32; + +template +inline JSTaggedType VRegAsTaggedType(VReg const &vreg) +{ + return JSTaggedType(vreg.GetValue()); +} + +template +inline JSTaggedValue VRegAsTaggedValue(VReg const &vreg) +{ + return JSTaggedValue(VRegAsTaggedType(vreg)); +} + +} // namespace panda::ecmascript + +#endif // PANDA_ECMASCRIPT_INTERPRETER_H diff --git a/runtime/interpreter/fast_runtime_stub-inl.h b/runtime/interpreter/fast_runtime_stub-inl.h new file mode 100644 index 000000000..a747d40ff --- /dev/null +++ b/runtime/interpreter/fast_runtime_stub-inl.h @@ -0,0 +1,1364 @@ +/* + * Copyright (c) 2021-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_INTERPRETER_FAST_RUNTIME_STUB_INL_H +#define ECMASCRIPT_INTERPRETER_FAST_RUNTIME_STUB_INL_H + +#include "plugins/ecmascript/runtime/interpreter/fast_runtime_stub.h" + +#include "plugins/ecmascript/runtime/global_dictionary-inl.h" +#include "plugins/ecmascript/runtime/global_env.h" +#include "plugins/ecmascript/runtime/internal_call_params.h" +#include "plugins/ecmascript/runtime/js_arraylist.h" +#include "plugins/ecmascript/runtime/js_function.h" +#include "plugins/ecmascript/runtime/js_hclass-inl.h" +#include "plugins/ecmascript/runtime/js_proxy.h" +#include "plugins/ecmascript/runtime/js_tagged_value-inl.h" +#include "plugins/ecmascript/runtime/js_typed_array.h" +#include "plugins/ecmascript/runtime/object_factory-inl.h" +#include "plugins/ecmascript/runtime/runtime_call_id.h" +#include "plugins/ecmascript/runtime/tagged_dictionary.h" +#include "plugins/ecmascript/runtime/vmstat/runtime_stat.h" + +namespace panda::ecmascript { +JSTaggedValue FastRuntimeStub::FastAdd(JSTaggedValue left, JSTaggedValue right) +{ + if (left.IsNumber() && right.IsNumber()) { + return JSTaggedValue(left.GetNumber() + right.GetNumber()); + } + + return JSTaggedValue::Hole(); +} + +JSTaggedValue FastRuntimeStub::FastSub(JSTaggedValue left, JSTaggedValue right) +{ + if (left.IsNumber() && right.IsNumber()) { + return JSTaggedValue(left.GetNumber() - right.GetNumber()); + } + + return JSTaggedValue::Hole(); +} + +JSTaggedValue FastRuntimeStub::FastMul(JSTaggedValue left, JSTaggedValue right) +{ + if (left.IsNumber() && right.IsNumber()) { + return JSTaggedValue(left.GetNumber() * right.GetNumber()); + } + + return JSTaggedValue::Hole(); +} + +JSTaggedValue FastRuntimeStub::FastDiv(JSTaggedValue left, JSTaggedValue right) +{ + if (left.IsNumber() && right.IsNumber()) { + double dLeft = left.IsInt() ? left.GetInt() : left.GetDouble(); + double dRight = right.IsInt() ? right.GetInt() : right.GetDouble(); + if (UNLIKELY(dRight == 0.0)) { + if (dLeft == 0.0 || std::isnan(dLeft)) { + return JSTaggedValue(base::NAN_VALUE); + } + uint64_t flagBit = ((bit_cast(dLeft)) ^ (bit_cast(dRight))) & base::DOUBLE_SIGN_MASK; + return JSTaggedValue(bit_cast(flagBit ^ (bit_cast(base::POSITIVE_INFINITY)))); + } + return JSTaggedValue(dLeft / dRight); + } + return JSTaggedValue::Hole(); +} + +JSTaggedValue FastRuntimeStub::FastMod(JSTaggedValue left, JSTaggedValue right) +{ + if (right.IsInt() && left.IsInt()) { + int iRight = right.GetInt(); + int iLeft = left.GetInt(); + if (iRight > 0 && iLeft > 0) { + return JSTaggedValue(iLeft % iRight); + } + } + if (left.IsNumber() && right.IsNumber()) { + double dLeft = left.IsInt() ? left.GetInt() : left.GetDouble(); + double dRight = right.IsInt() ? right.GetInt() : right.GetDouble(); + if (dRight == 0.0 || std::isnan(dRight) || std::isnan(dLeft) || std::isinf(dLeft)) { + return JSTaggedValue(base::NAN_VALUE); + } + if (dLeft == 0.0 || std::isinf(dRight)) { + return JSTaggedValue(dLeft); + } + return JSTaggedValue(std::fmod(dLeft, dRight)); + } + return JSTaggedValue::Hole(); +} + +JSTaggedValue FastRuntimeStub::FastEqual(JSTaggedValue left, JSTaggedValue right) +{ + if (left == right) { + if (UNLIKELY(left.IsDouble())) { + return JSTaggedValue(!std::isnan(left.GetDouble())); + } + return JSTaggedValue::True(); + } + if (left.IsNumber()) { + if (left.IsInt() && right.IsInt()) { + return JSTaggedValue::False(); + } + } + if (right.IsUndefinedOrNull()) { + if (left.IsHeapObject()) { + return JSTaggedValue::False(); + } + if (left.IsUndefinedOrNull()) { + return JSTaggedValue::True(); + } + } + if (left.IsBoolean()) { + if (right.IsSpecial()) { + return JSTaggedValue::False(); + } + } + return JSTaggedValue::Hole(); +} + +bool FastRuntimeStub::FastStrictEqual(JSTaggedValue left, JSTaggedValue right) +{ + if (left.IsNumber()) { + if (right.IsNumber()) { + double dLeft = left.IsInt() ? left.GetInt() : left.GetDouble(); + double dRight = right.IsInt() ? right.GetInt() : right.GetDouble(); + return JSTaggedValue::StrictNumberEquals(dLeft, dRight); + } + return false; + } + if (right.IsNumber()) { + return false; + } + if (left == right) { + return true; + } + if (left.IsString() && right.IsString()) { + return EcmaString::StringsAreEqual(static_cast(left.GetTaggedObject()), + static_cast(right.GetTaggedObject())); + } + return false; +} + +bool FastRuntimeStub::IsSpecialIndexedObj(JSType jsType) +{ + return jsType > JSType::JS_ARRAY; +} + +bool FastRuntimeStub::IsSpecialReceiverObj(JSType jsType) +{ + return jsType > JSType::JS_PRIMITIVE_REF; +} + +bool FastRuntimeStub::IsSpecialContainer(JSType jsType) +{ + return jsType >= JSType::JS_ARRAY_LIST && jsType <= JSType::JS_QUEUE; +} + +uint32_t FastRuntimeStub::TryToElementsIndex(JSTaggedValue key) +{ + if (LIKELY(key.IsInt())) { + return key.GetInt(); + } + if (key.IsString()) { + uint32_t index = 0; + if (JSTaggedValue::StringToElementIndex(key, &index)) { + return index; + } + } else if (key.IsDouble()) { + double number = key.GetDouble(); + auto integer = base::NumberHelper::DoubleToInt(number, base::INT32_BITS); + if (number == integer) { + return integer; + } + } + return JSObject::MAX_ELEMENT_INDEX; +} + +JSTaggedValue FastRuntimeStub::CallGetter(JSThread *thread, JSTaggedValue receiver, JSTaggedValue holder, + JSTaggedValue value) +{ + INTERPRETER_TRACE(thread, CallGetter); + // Accessor + [[maybe_unused]] EcmaHandleScope handleScope(thread); + AccessorData *accessor = AccessorData::Cast(value.GetTaggedObject()); + if (UNLIKELY(accessor->IsInternal())) { + JSHandle objHandle(thread, holder); + return accessor->CallInternalGet(thread, objHandle); + } + JSHandle objHandle(thread, receiver); + return JSObject::CallGetter(thread, accessor, objHandle); +} + +JSTaggedValue FastRuntimeStub::CallSetter(JSThread *thread, JSTaggedValue receiver, JSTaggedValue value, + JSTaggedValue accessorValue) +{ + INTERPRETER_TRACE(thread, CallSetter); + // Accessor + [[maybe_unused]] EcmaHandleScope handleScope(thread); + JSHandle objHandle(thread, receiver); + JSHandle valueHandle(thread, value); + + auto accessor = AccessorData::Cast(accessorValue.GetTaggedObject()); + bool success = JSObject::CallSetter(thread, *accessor, objHandle, valueHandle, true); + return success ? JSTaggedValue::Undefined() : JSTaggedValue::Exception(); +} + +bool FastRuntimeStub::ShouldCallSetter(JSTaggedValue receiver, JSTaggedValue holder, JSTaggedValue accessorValue, + PropertyAttributes attr) +{ + if (!AccessorData::Cast(accessorValue.GetTaggedObject())->IsInternal()) { + return true; + } + if (receiver != holder) { + return false; + } + return attr.IsWritable(); +} + +PropertyAttributes FastRuntimeStub::AddPropertyByName(JSThread *thread, JSHandle objHandle, + JSHandle keyHandle, + JSHandle valueHandle, PropertyAttributes attr) +{ + INTERPRETER_TRACE(thread, AddPropertyByName); + + if (objHandle->IsJSArray() && keyHandle.GetTaggedValue() == thread->GlobalConstants()->GetConstructorString()) { + objHandle->GetJSHClass()->SetHasConstructor(true); + } + int32_t nextInlinedPropsIndex = objHandle->GetJSHClass()->GetNextInlinedPropsIndex(); + if (nextInlinedPropsIndex >= 0) { + objHandle->SetPropertyInlinedProps(thread, nextInlinedPropsIndex, valueHandle.GetTaggedValue()); + attr.SetOffset(nextInlinedPropsIndex); + attr.SetIsInlinedProps(true); + JSHClass::AddProperty(thread, objHandle, keyHandle, attr); + return attr; + } + + JSMutableHandle array(thread, objHandle->GetProperties()); + uint32_t length = array->GetLength(); + if (length == 0) { + length = JSObject::MIN_PROPERTIES_LENGTH; + ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); + array.Update(factory->NewTaggedArray(length).GetTaggedValue()); + objHandle->SetProperties(thread, array.GetTaggedValue()); + } + + if (!array->IsDictionaryMode()) { + attr.SetIsInlinedProps(false); + + uint32_t nonInlinedProps = 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)) { + // change to dictionary and add one. + JSHandle dict(JSObject::TransitionToDictionary(thread, objHandle)); + JSHandle newDict = + NameDictionary::PutIfAbsent(thread, dict, keyHandle, valueHandle, attr); + objHandle->SetProperties(thread, newDict); + // index is not essential when fastMode is false; + return attr; + } + // Grow properties array size + uint32_t capacity = JSObject::ComputePropertyCapacity(length); + ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); + array.Update(factory->CopyArray(array, length, capacity).GetTaggedValue()); + objHandle->SetProperties(thread, array.GetTaggedValue()); + } + + attr.SetOffset(nonInlinedProps + objHandle->GetJSHClass()->GetInlinedProperties()); + JSHClass::AddProperty(thread, objHandle, keyHandle, attr); + array->Set(thread, nonInlinedProps, valueHandle.GetTaggedValue()); + } else { + JSHandle dictHandle(array); + JSHandle newDict = + NameDictionary::PutIfAbsent(thread, dictHandle, keyHandle, valueHandle, attr); + objHandle->SetProperties(thread, newDict); + } + return attr; +} + +JSTaggedValue FastRuntimeStub::AddPropertyByIndex(JSThread *thread, JSTaggedValue receiver, uint32_t index, + JSTaggedValue value) +{ + INTERPRETER_TRACE(thread, AddPropertyByIndex); + [[maybe_unused]] EcmaHandleScope handleScope(thread); + if (UNLIKELY(!JSObject::Cast(receiver)->IsExtensible())) { + THROW_TYPE_ERROR_AND_RETURN(thread, "Cannot add property in prevent extensions ", JSTaggedValue::Exception()); + } + + bool success = JSObject::AddElementInternal(thread, JSHandle(thread, receiver), index, + JSHandle(thread, value), PropertyAttributes::Default()); + return success ? JSTaggedValue::Undefined() : JSTaggedValue::Exception(); +} + +template +JSTaggedValue FastRuntimeStub::GetPropertyByIndex(JSThread *thread, JSTaggedValue receiver, uint32_t index) +{ + INTERPRETER_TRACE(thread, GetPropertyByIndex); + [[maybe_unused]] EcmaHandleScope handleScope(thread); + JSTaggedValue holder = receiver; + do { + auto *hclass = holder.GetTaggedObject()->GetClass(); + JSType jsType = hclass->GetObjectType(); + if (IsSpecialIndexedObj(jsType)) { + if (IsSpecialContainer(jsType)) { + return GetContainerProperty(thread, holder, index, jsType); + } + return JSTaggedValue::Hole(); + } + TaggedArray *elements = TaggedArray::Cast(JSObject::Cast(holder)->GetElements().GetTaggedObject()); + + if (!hclass->IsDictionaryElement()) { + ASSERT(!elements->IsDictionaryMode()); + if (index < elements->GetLength()) { + JSTaggedValue value = elements->Get(index); + if (!value.IsHole()) { + return value; + } + } else { + return JSTaggedValue::Hole(); + } + } else { + NumberDictionary *dict = NumberDictionary::Cast(elements); + int entry = dict->FindEntry(JSTaggedValue(static_cast(index))); + if (entry != -1) { + auto attr = dict->GetAttributes(entry); + auto value = dict->GetValue(entry); + if (UNLIKELY(attr.IsAccessor())) { + return CallGetter(thread, receiver, holder, value); + } + ASSERT(!value.IsAccessor()); + return value; + } + } + if (UseOwn) { + break; + } + holder = JSObject::Cast(holder)->GetJSHClass()->GetPrototype(); + } while (holder.IsHeapObject()); + + // not found + return JSTaggedValue::Undefined(); +} + +template +JSTaggedValue FastRuntimeStub::GetPropertyByValue(JSThread *thread, JSTaggedValue receiver, JSTaggedValue key) +{ + INTERPRETER_TRACE(thread, GetPropertyByValue); + if (UNLIKELY(!key.IsNumber() && !key.IsStringOrSymbol())) { + return JSTaggedValue::Hole(); + } + // fast path + auto index = TryToElementsIndex(key); + if (LIKELY(index < JSObject::MAX_ELEMENT_INDEX)) { + return GetPropertyByIndex(thread, receiver, index); + } + if (!key.IsNumber()) { + if (key.IsString() && !EcmaString::Cast(key.GetTaggedObject())->IsInternString()) { + // update string stable + [[maybe_unused]] EcmaHandleScope handleScope(thread); + JSHandle receiverHandler(thread, receiver); + key = JSTaggedValue(thread->GetEcmaVM()->GetFactory()->InternString(JSHandle(thread, key))); + // Maybe moved by GC + receiver = receiverHandler.GetTaggedValue(); + } + return FastRuntimeStub::GetPropertyByName(thread, receiver, key); + } + return JSTaggedValue::Hole(); +} + +template +JSTaggedValue FastRuntimeStub::GetPropertyByName(JSThread *thread, JSTaggedValue receiver, JSTaggedValue key) +{ + INTERPRETER_TRACE(thread, GetPropertyByName); + // no gc when return hole + ASSERT(key.IsStringOrSymbol()); + JSTaggedValue holder = receiver; + do { + auto *hclass = holder.GetTaggedObject()->GetClass(); + JSType jsType = hclass->GetObjectType(); + if (IsSpecialIndexedObj(jsType)) { + return JSTaggedValue::Hole(); + } + + if (LIKELY(!hclass->IsDictionaryMode())) { + ASSERT(!TaggedArray::Cast(JSObject::Cast(holder)->GetProperties().GetTaggedObject())->IsDictionaryMode()); + + LayoutInfo *layoutInfo = LayoutInfo::Cast(hclass->GetLayout().GetTaggedObject()); + int propsNumber = hclass->NumberOfProps(); + int entry = layoutInfo->FindElementWithCache(thread, hclass, key, propsNumber); + if (entry != -1) { + PropertyAttributes attr(layoutInfo->GetAttr(entry)); + ASSERT(static_cast(attr.GetOffset()) == entry); + auto value = JSObject::Cast(holder)->GetProperty(hclass, attr); + if (UNLIKELY(attr.IsAccessor())) { + return CallGetter(thread, receiver, holder, value); + } + ASSERT(!value.IsAccessor()); + return value; + } + } else { + TaggedArray *array = TaggedArray::Cast(JSObject::Cast(holder)->GetProperties().GetTaggedObject()); + ASSERT(array->IsDictionaryMode()); + NameDictionary *dict = NameDictionary::Cast(array); + int entry = dict->FindEntry(key); + if (entry != -1) { + auto value = dict->GetValue(entry); + auto attr = dict->GetAttributes(entry); + if (UNLIKELY(attr.IsAccessor())) { + return CallGetter(thread, receiver, holder, value); + } + ASSERT(!value.IsAccessor()); + return value; + } + } + if (UseOwn) { + break; + } + holder = hclass->GetPrototype(); + } while (holder.IsHeapObject()); + // not found + return JSTaggedValue::Undefined(); +} + +template +JSTaggedValue FastRuntimeStub::SetPropertyByName(JSThread *thread, JSTaggedValue receiver, JSTaggedValue key, + JSTaggedValue value) +{ + INTERPRETER_TRACE(thread, SetPropertyByName); + // property + JSTaggedValue holder = receiver; + do { + auto *hclass = holder.GetTaggedObject()->GetClass(); + JSType jsType = hclass->GetObjectType(); + if (IsSpecialIndexedObj(jsType)) { + if (IsSpecialContainer(jsType)) { + THROW_TYPE_ERROR_AND_RETURN(thread, "Cannot set property on Container", JSTaggedValue::Exception()); + } + return JSTaggedValue::Hole(); + } + // UpdateRepresentation + if (LIKELY(!hclass->IsDictionaryMode())) { + ASSERT(!TaggedArray::Cast(JSObject::Cast(holder)->GetProperties().GetTaggedObject())->IsDictionaryMode()); + + LayoutInfo *layoutInfo = LayoutInfo::Cast(hclass->GetLayout().GetTaggedObject()); + + int propsNumber = hclass->NumberOfProps(); + int entry = layoutInfo->FindElementWithCache(thread, hclass, key, propsNumber); + if (entry != -1) { + PropertyAttributes attr(layoutInfo->GetAttr(entry)); + ASSERT(static_cast(attr.GetOffset()) == entry); + if (UNLIKELY(attr.IsAccessor())) { + auto accessor = JSObject::Cast(holder)->GetProperty(hclass, attr); + if (ShouldCallSetter(receiver, holder, accessor, attr)) { + return CallSetter(thread, receiver, value, accessor); + } + } + if (UNLIKELY(!attr.IsWritable())) { + [[maybe_unused]] EcmaHandleScope handleScope(thread); + THROW_TYPE_ERROR_AND_RETURN(thread, "Cannot set readonly property", JSTaggedValue::Exception()); + } + if (UNLIKELY(holder != receiver)) { + break; + } + JSObject::Cast(holder)->SetProperty(thread, hclass, attr, value); + return JSTaggedValue::Undefined(); + } + } else { + TaggedArray *properties = TaggedArray::Cast(JSObject::Cast(holder)->GetProperties().GetTaggedObject()); + ASSERT(properties->IsDictionaryMode()); + NameDictionary *dict = NameDictionary::Cast(properties); + int entry = dict->FindEntry(key); + if (entry != -1) { + auto attr = dict->GetAttributes(entry); + if (UNLIKELY(attr.IsAccessor())) { + auto accessor = dict->GetValue(entry); + if (ShouldCallSetter(receiver, holder, accessor, attr)) { + return CallSetter(thread, receiver, value, accessor); + } + } + if (UNLIKELY(!attr.IsWritable())) { + [[maybe_unused]] EcmaHandleScope handleScope(thread); + THROW_TYPE_ERROR_AND_RETURN(thread, "Cannot set readonly property", JSTaggedValue::Exception()); + } + if (UNLIKELY(holder != receiver)) { + break; + } + dict->UpdateValue(thread, entry, value); + return JSTaggedValue::Undefined(); + } + } + if (UseOwn) { + break; + } + holder = hclass->GetPrototype(); + } while (holder.IsHeapObject()); + + [[maybe_unused]] EcmaHandleScope handleScope(thread); + JSHandle objHandle(thread, receiver); + JSHandle keyHandle(thread, key); + JSHandle valueHandle(thread, value); + + if (UNLIKELY(!JSObject::Cast(receiver)->IsExtensible())) { + THROW_TYPE_ERROR_AND_RETURN(thread, "Cannot add property in prevent extensions ", JSTaggedValue::Exception()); + } + AddPropertyByName(thread, objHandle, keyHandle, valueHandle, PropertyAttributes::Default()); + return JSTaggedValue::Undefined(); +} + +template +JSTaggedValue FastRuntimeStub::SetPropertyByIndex(JSThread *thread, JSTaggedValue receiver, uint32_t index, + JSTaggedValue value) +{ + INTERPRETER_TRACE(thread, SetPropertyByIndex); + JSTaggedValue holder = receiver; + do { + auto *hclass = holder.GetTaggedObject()->GetClass(); + JSType jsType = hclass->GetObjectType(); + if (IsSpecialIndexedObj(jsType)) { + if (IsSpecialContainer(jsType)) { + return SetContainerProperty(thread, holder, index, value, jsType); + } + return JSTaggedValue::Hole(); + } + TaggedArray *elements = TaggedArray::Cast(JSObject::Cast(holder)->GetElements().GetTaggedObject()); + if (!hclass->IsDictionaryElement()) { + ASSERT(!elements->IsDictionaryMode()); + if (UNLIKELY(holder != receiver)) { + break; + } + if (index < elements->GetLength()) { + if (!elements->Get(index).IsHole()) { + elements->Set(thread, index, value); + return JSTaggedValue::Undefined(); + } + } + } else { + return JSTaggedValue::Hole(); + } + if (UseOwn) { + break; + } + holder = JSObject::Cast(holder)->GetJSHClass()->GetPrototype(); + } while (holder.IsHeapObject()); + + return AddPropertyByIndex(thread, receiver, index, value); +} + +template +JSTaggedValue FastRuntimeStub::SetPropertyByValue(JSThread *thread, JSTaggedValue receiver, JSTaggedValue key, + JSTaggedValue value) +{ + INTERPRETER_TRACE(thread, SetPropertyByValue); + if (UNLIKELY(!key.IsNumber() && !key.IsStringOrSymbol())) { + return JSTaggedValue::Hole(); + } + // fast path + auto index = TryToElementsIndex(key); + if (LIKELY(index < JSObject::MAX_ELEMENT_INDEX)) { + return SetPropertyByIndex(thread, receiver, index, value); + } + if (!key.IsNumber()) { + if (key.IsString() && !EcmaString::Cast(key.GetTaggedObject())->IsInternString()) { + // update string stable + [[maybe_unused]] EcmaHandleScope handleScope(thread); + JSHandle receiverHandler(thread, receiver); + JSHandle valueHandler(thread, value); + key = JSTaggedValue(thread->GetEcmaVM()->GetFactory()->InternString(JSHandle(thread, key))); + // Maybe moved by GC + receiver = receiverHandler.GetTaggedValue(); + value = valueHandler.GetTaggedValue(); + } + return FastRuntimeStub::SetPropertyByName(thread, receiver, key, value); + } + return JSTaggedValue::Hole(); +} + +JSTaggedValue FastRuntimeStub::GetGlobalOwnProperty(JSTaggedValue receiver, JSTaggedValue key, bool *found) +{ + JSObject *obj = JSObject::Cast(receiver); + TaggedArray *properties = TaggedArray::Cast(obj->GetProperties().GetTaggedObject()); + GlobalDictionary *dict = GlobalDictionary::Cast(properties); + int entry = dict->FindEntry(key); + if (entry != -1) { + *found = true; + return dict->GetValue(entry); + } + + return JSTaggedValue::Undefined(); +} + +JSTaggedValue FastRuntimeStub::FastTypeOf(JSThread *thread, JSTaggedValue obj) +{ + INTERPRETER_TRACE(thread, FastTypeOf); + const GlobalEnvConstants *globalConst = thread->GlobalConstants(); + switch (obj.GetRawData()) { + case JSTaggedValue::VALUE_TRUE: + case JSTaggedValue::VALUE_FALSE: + return globalConst->GetBooleanString(); + case JSTaggedValue::VALUE_NULL: + return globalConst->GetObjectString(); + case JSTaggedValue::VALUE_UNDEFINED: + return globalConst->GetUndefinedString(); + default: + if (obj.IsHeapObject()) { + if (obj.IsString()) { + return globalConst->GetStringString(); + } + if (obj.IsSymbol()) { + return globalConst->GetSymbolString(); + } + if (obj.IsCallable()) { + return globalConst->GetFunctionString(); + } + return globalConst->GetObjectString(); + } + if (obj.IsNumber()) { + return globalConst->GetNumberString(); + } + } + return globalConst->GetUndefinedString(); +} + +bool FastRuntimeStub::FastSetPropertyByIndex(JSThread *thread, JSTaggedValue receiver, uint32_t index, + JSTaggedValue value) +{ + INTERPRETER_TRACE(thread, FastSetPropertyByIndex); +#ifdef ECMASCRIPT_ENABLE_STUB_AOT1 + auto stubAddr = thread->GetFastStubEntry(FAST_STUB_ID(SetPropertyByIndex)); + typedef JSTaggedValue (*PFSetPropertyByIndex)(uintptr_t, JSTaggedValue, uint32_t, JSTaggedValue); + auto setPropertyByIndex = reinterpret_cast(stubAddr); + JSTaggedValue result = setPropertyByIndex(thread->GetGlueAddr(), receiver, index, value); +#else + JSTaggedValue result = FastRuntimeStub::SetPropertyByIndex(thread, receiver, index, value); +#endif + if (!result.IsHole()) { + return result != JSTaggedValue::Exception(); + } + return JSTaggedValue::SetProperty(thread, JSHandle(thread, receiver), index, + JSHandle(thread, value), true); +} + +bool FastRuntimeStub::FastSetPropertyByValue(JSThread *thread, JSTaggedValue receiver, JSTaggedValue key, + JSTaggedValue value) +{ + INTERPRETER_TRACE(thread, FastSetPropertyByValue); + JSTaggedValue result = FastRuntimeStub::SetPropertyByValue(thread, receiver, key, value); + if (!result.IsHole()) { + return result != JSTaggedValue::Exception(); + } + return JSTaggedValue::SetProperty(thread, JSHandle(thread, receiver), + JSHandle(thread, key), JSHandle(thread, value), + true); +} + +// must not use for interpreter +JSTaggedValue FastRuntimeStub::FastGetPropertyByName(JSThread *thread, JSTaggedValue receiver, JSTaggedValue key) +{ + INTERPRETER_TRACE(thread, FastGetPropertyByName); + ASSERT(key.IsStringOrSymbol()); + if (key.IsString() && !EcmaString::Cast(key.GetTaggedObject())->IsInternString()) { + JSHandle receiverHandler(thread, receiver); + key = JSTaggedValue(thread->GetEcmaVM()->GetFactory()->InternString(JSHandle(thread, key))); + // Maybe moved by GC + receiver = receiverHandler.GetTaggedValue(); + } +#ifdef ECMASCRIPT_ENABLE_STUB_AOT1 + auto stubAddr = thread->GetFastStubEntry(FAST_STUB_ID(GetPropertyByName)); + typedef JSTaggedValue (*PFGetPropertyByName)(uintptr_t, JSTaggedValue, JSTaggedValue); + auto getPropertyByNamePtr = reinterpret_cast(stubAddr); + JSTaggedValue result = getPropertyByNamePtr(thread->GetGlueAddr(), receiver, key); +#else + JSTaggedValue result = FastRuntimeStub::GetPropertyByName(thread, receiver, key); +#endif + if (result.IsHole()) { + return JSTaggedValue::GetProperty(thread, JSHandle(thread, receiver), + JSHandle(thread, key)) + .GetValue() + .GetTaggedValue(); + } + return result; +} + +JSTaggedValue FastRuntimeStub::FastGetPropertyByValue(JSThread *thread, JSTaggedValue receiver, JSTaggedValue key) +{ + INTERPRETER_TRACE(thread, FastGetPropertyByValue); +#ifdef ECMASCRIPT_ENABLE_STUB_AOT1 + auto stubAddr = thread->GetFastStubEntry(FAST_STUB_ID(GetPropertyByValue)); + typedef JSTaggedValue (*PFGetPropertyByValue)(uintptr_t, JSTaggedValue, JSTaggedValue); + auto getPropertyByValuePtr = reinterpret_cast(stubAddr); + JSTaggedValue result = getPropertyByValuePtr(thread->GetGlueAddr(), receiver, key); +#else + JSTaggedValue result = FastRuntimeStub::GetPropertyByValue(thread, receiver, key); +#endif + if (result.IsHole()) { + return JSTaggedValue::GetProperty(thread, JSHandle(thread, receiver), + JSHandle(thread, key)) + .GetValue() + .GetTaggedValue(); + } + return result; +} + +template // UseHole is only for Array::Sort() which requires Hole order +JSTaggedValue FastRuntimeStub::FastGetPropertyByIndex(JSThread *thread, JSTaggedValue receiver, uint32_t index) +{ + INTERPRETER_TRACE(thread, FastGetPropertyByIndex); +#ifdef ECMASCRIPT_ENABLE_STUB_AOT1 + auto stubAddr = thread->GetFastStubEntry(FAST_STUB_ID(GetPropertyByIndex)); + typedef JSTaggedValue (*PFGetPropertyByIndex)(uintptr_t, JSTaggedValue, uint32_t); + auto getPropertyByIndex = reinterpret_cast(stubAddr); + JSTaggedValue result = getPropertyByIndex(thread->GetGlueAddr(), receiver, index); +#else + JSTaggedValue result = FastRuntimeStub::GetPropertyByIndex(thread, receiver, index); +#endif + if (result.IsHole() && !UseHole) { + return JSTaggedValue::GetProperty(thread, JSHandle(thread, receiver), index) + .GetValue() + .GetTaggedValue(); + } + return result; +} + +JSTaggedValue FastRuntimeStub::NewLexicalEnvDyn(JSThread *thread, ObjectFactory *factory, uint16_t numVars) +{ + INTERPRETER_TRACE(thread, NewLexicalEnvDyn); + LexicalEnv *newEnv = factory->InlineNewLexicalEnv(numVars); + if (UNLIKELY(newEnv == nullptr)) { + return JSTaggedValue::Hole(); + } + return JSTaggedValue(newEnv); +} + +// Those interface below is discarded +bool FastRuntimeStub::IsSpecialIndexedObjForGet(JSTaggedValue obj) +{ + JSType jsType = obj.GetTaggedObject()->GetClass()->GetObjectType(); + return jsType > JSType::JS_ARRAY && jsType <= JSType::JS_PRIMITIVE_REF; +} + +bool FastRuntimeStub::IsSpecialIndexedObjForSet(JSTaggedValue obj) +{ + JSType jsType = obj.GetTaggedObject()->GetClass()->GetObjectType(); + return jsType >= JSType::JS_ARRAY && jsType <= JSType::JS_PRIMITIVE_REF; +} + +JSTaggedValue FastRuntimeStub::GetElement(JSTaggedValue receiver, uint32_t index) +{ + JSTaggedValue holder = receiver; + while (true) { + JSTaggedValue val = FindOwnElement(JSObject::Cast(holder), index); + if (!val.IsHole()) { + return val; + } + + holder = JSObject::Cast(holder)->GetJSHClass()->GetPrototype(); + if (!holder.IsHeapObject()) { + return JSTaggedValue::Undefined(); + } + + if (UNLIKELY(holder.IsJSProxy())) { + return JSTaggedValue::Hole(); + } + } +} + +JSTaggedValue FastRuntimeStub::GetElementWithArray(JSTaggedValue receiver, uint32_t index) +{ + DISALLOW_GARBAGE_COLLECTION; + JSTaggedValue holder = receiver; + while (true) { + JSTaggedValue val = FindOwnElement(JSObject::Cast(holder), index); + if (!val.IsHole()) { + return val; + } + + holder = JSObject::Cast(holder)->GetJSHClass()->GetPrototype(); + if (!holder.IsHeapObject()) { + return val; + } + } +} + +bool FastRuntimeStub::SetElement(JSThread *thread, JSTaggedValue receiver, uint32_t index, JSTaggedValue value, + bool mayThrow) +{ + INTERPRETER_TRACE(thread, SetElement); + JSTaggedValue holder = receiver; + bool onPrototype = false; + + while (true) { + PropertyAttributes attr; + uint32_t indexOrEntry = 0; + TaggedArray *elements = TaggedArray::Cast(JSObject::Cast(holder)->GetElements().GetHeapObject()); + bool isDict = elements->IsDictionaryMode(); + JSTaggedValue val = FindOwnElement(elements, index, isDict, &attr, &indexOrEntry); + if (!val.IsHole()) { + if (UNLIKELY(onPrototype)) { + if (UNLIKELY(!JSObject::Cast(receiver)->IsExtensible())) { + if (mayThrow) { + THROW_TYPE_ERROR_AND_RETURN(thread, "Cannot add property in prevent extensions ", false); + } + return false; + } + + return JSObject::AddElementInternal(thread, JSHandle(thread, receiver), index, + JSHandle(thread, value), + PropertyAttributes::Default()); + } + if (!attr.IsAccessor()) { + if (attr.IsWritable()) { + elements = TaggedArray::Cast(JSObject::Cast(receiver)->GetElements().GetHeapObject()); + if (!isDict) { + elements->Set(thread, indexOrEntry, value); + JSObject::Cast(receiver)->GetJSHClass()->UpdateRepresentation(value); + return true; + } + NumberDictionary::Cast(elements)->UpdateValueAndAttributes(thread, indexOrEntry, value, attr); + return true; + } + + if (mayThrow) { + THROW_TYPE_ERROR_AND_RETURN(thread, "Cannot set readonly property", false); + } + return false; + } + + // Accessor + [[maybe_unused]] EcmaHandleScope handleScope(thread); + JSHandle objHandle(thread, receiver); + JSHandle valueHandle(thread, value); + AccessorData *access = AccessorData::Cast(val.GetHeapObject()); + return JSObject::CallSetter(thread, *access, objHandle, valueHandle, mayThrow); + } + + holder = JSObject::Cast(holder)->GetJSHClass()->GetPrototype(); + if (!holder.IsHeapObject()) { + if (UNLIKELY(!JSObject::Cast(receiver)->IsExtensible())) { + if (mayThrow) { + THROW_TYPE_ERROR_AND_RETURN(thread, "Cannot add property in prevent extensions ", false); + } + return false; + } + + return JSObject::AddElementInternal(thread, JSHandle(thread, receiver), index, + JSHandle(thread, value), PropertyAttributes::Default()); + } + if (holder.IsJSProxy()) { + return JSProxy::SetProperty( + thread, JSHandle(thread, holder), JSHandle(thread, JSTaggedValue(index)), + JSHandle(thread, value), JSHandle(thread, receiver), mayThrow); + } + onPrototype = true; + } +} + +bool FastRuntimeStub::SetPropertyByName(JSThread *thread, JSTaggedValue receiver, JSTaggedValue key, + JSTaggedValue value, bool mayThrow) +{ + INTERPRETER_TRACE(thread, SetPropertyByName); + // property + JSTaggedValue holder = receiver; + bool onPrototype = false; + + while (true) { + TaggedArray *properties = TaggedArray::Cast(JSObject::Cast(holder)->GetProperties().GetHeapObject()); + PropertyAttributes attr; + uint32_t indexOrEntry = 0; + JSTaggedValue val = FindOwnProperty(thread, JSObject::Cast(holder), properties, key, &attr, &indexOrEntry); + if (!val.IsHole()) { + if (!attr.IsAccessor()) { + if (UNLIKELY(onPrototype)) { + if (UNLIKELY(!JSObject::Cast(receiver)->IsExtensible() || !attr.IsWritable())) { + if (mayThrow) { + THROW_TYPE_ERROR_AND_RETURN(thread, "Cannot add property in prevent extensions ", false); + } + return false; + } + [[maybe_unused]] EcmaHandleScope handleScope(thread); + ObjectOperator::FastAdd(thread, receiver, key, JSHandle(thread, value), + PropertyAttributes::Default()); + + return true; + } + + if (attr.IsWritable()) { + properties = TaggedArray::Cast(JSObject::Cast(receiver)->GetProperties().GetHeapObject()); + if (!properties->IsDictionaryMode()) { + Representation representation = + PropertyAttributes::UpdateRepresentation(attr.GetRepresentation(), value); + if (attr.GetRepresentation() != representation) { + attr.SetRepresentation(representation); + } + + JSObject::Cast(receiver)->GetJSHClass()->UpdatePropertyMetaData(thread, key, attr); + if (UNLIKELY(val.IsInternalAccessor())) { + [[maybe_unused]] EcmaHandleScope handleScope(thread); + AccessorData::Cast(val.GetHeapObject()) + ->CallInternalSet(thread, JSHandle(thread, receiver), + JSHandle(thread, value), mayThrow); + RETURN_VALUE_IF_ABRUPT_COMPLETION(thread, false); + return true; + } + + if (attr.IsInlinedProps()) { + JSObject::Cast(receiver)->SetPropertyInlinedProps(thread, indexOrEntry, value); + } else { + properties->Set(thread, indexOrEntry, value); + } + return true; + } + + if (receiver.IsJSGlobalObject()) { + [[maybe_unused]] EcmaHandleScope handleScope(thread); + JSHandle dictHandle(thread, properties); + // globalobj have no internal accessor + GlobalDictionary::InvalidatePropertyBox(thread, dictHandle, indexOrEntry, attr); + return true; + } + + if (UNLIKELY(val.IsInternalAccessor())) { + [[maybe_unused]] EcmaHandleScope handleScope(thread); + AccessorData::Cast(val.GetHeapObject()) + ->CallInternalSet(thread, JSHandle(thread, receiver), + JSHandle(thread, value), mayThrow); + RETURN_VALUE_IF_ABRUPT_COMPLETION(thread, false); + return true; + } + + NameDictionary::Cast(properties)->UpdateValueAndAttributes(thread, indexOrEntry, value, attr); + return true; + } + + if (mayThrow) { + THROW_TYPE_ERROR_AND_RETURN(thread, "Cannot set readonly property", false); + } + return false; + } + + // Accessor + [[maybe_unused]] EcmaHandleScope handleScope(thread); + JSHandle objHandle(thread, receiver); + JSHandle valueHandle(thread, value); + AccessorData *access = AccessorData::Cast(val.GetHeapObject()); + return JSObject::CallSetter(thread, *access, objHandle, valueHandle, mayThrow); + } + + if (holder.IsTypedArray()) { + [[maybe_unused]] EcmaHandleScope handleScope(thread); + return JSTypedArray::SetProperty(thread, JSHandle(thread, holder), + JSTypedArray::ToPropKey(thread, JSHandle(thread, key)), + JSHandle(thread, value), + JSHandle(thread, receiver), mayThrow); + } + + holder = JSObject::Cast(holder)->GetJSHClass()->GetPrototype(); + if (!holder.IsHeapObject()) { + if (UNLIKELY(!JSObject::Cast(receiver)->IsExtensible())) { + if (mayThrow) { + THROW_TYPE_ERROR_AND_RETURN(thread, "Cannot add property in prevent extensions ", false); + } + return false; + } + [[maybe_unused]] EcmaHandleScope handleScope(thread); + ObjectOperator::FastAdd(thread, receiver, key, JSHandle(thread, value), + PropertyAttributes::Default()); + + return true; + } + if (holder.IsJSProxy()) { + [[maybe_unused]] EcmaHandleScope handleScope(thread); + return JSProxy::SetProperty(thread, JSHandle(thread, holder), JSHandle(thread, key), + JSHandle(thread, value), + JSHandle(thread, receiver), mayThrow); + } + onPrototype = true; + } +} + +bool FastRuntimeStub::SetGlobalOwnProperty(JSThread *thread, JSTaggedValue receiver, JSTaggedValue key, + JSTaggedValue value, bool mayThrow) +{ + INTERPRETER_TRACE(thread, SetGlobalOwnProperty); + uint32_t index = 0; + if (JSTaggedValue::ToElementIndex(key, &index)) { + return SetElement(thread, receiver, index, value, mayThrow); + } + + JSObject *obj = JSObject::Cast(receiver); + GlobalDictionary *dict = GlobalDictionary::Cast(obj->GetProperties().GetTaggedObject()); + PropertyAttributes attr = PropertyAttributes::Default(); + if (UNLIKELY(dict->GetLength() == 0)) { + JSHandle keyHandle(thread, key); + JSHandle valHandle(thread, value); + JSHandle objHandle(thread, obj); + JSHandle dictHandle(GlobalDictionary::Create(thread)); + + // Add PropertyBox to global dictionary + ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); + JSHandle boxHandle = factory->NewPropertyBox(valHandle); + boxHandle->SetValue(thread, valHandle.GetTaggedValue()); + PropertyBoxType boxType = valHandle->IsUndefined() ? PropertyBoxType::UNDEFINED : PropertyBoxType::CONSTANT; + attr.SetBoxType(boxType); + + JSHandle properties = + GlobalDictionary::PutIfAbsent(thread, dictHandle, keyHandle, JSHandle(boxHandle), attr); + objHandle->SetProperties(thread, properties); + return true; + } + + int entry = dict->FindEntry(key); + if (entry != -1) { + attr = dict->GetAttributes(entry); + JSTaggedValue val = dict->GetValue(entry); + if (!attr.IsAccessor()) { + if (attr.IsWritable()) { + // globalobj have no internal accessor + JSHandle dictHandle(thread, dict); + GlobalDictionary::InvalidatePropertyBox(thread, dictHandle, entry, attr); + return true; + } + } + + // Accessor + JSTaggedValue setter = AccessorData::Cast(val.GetHeapObject())->GetSetter(); + if (setter.IsUndefined()) { + if (mayThrow) { + THROW_TYPE_ERROR_AND_RETURN(thread, "Cannot set property when setter is undefined", false); + } + return false; + } + + JSHandle objHandle(thread, receiver); + JSHandle setFunc(thread, setter); + InternalCallParams *arguments = thread->GetInternalCallParams(); + arguments->MakeArgv(value); + JSFunction::Call(thread, setFunc, objHandle, 1, arguments->GetArgv()); + // 10. ReturnIfAbrupt(setterResult). + RETURN_VALUE_IF_ABRUPT_COMPLETION(thread, false); + return true; + } + + JSHandle keyHandle(thread, key); + JSHandle valHandle(thread, value); + JSHandle objHandle(thread, obj); + JSHandle dictHandle(thread, dict); + + // Add PropertyBox to global dictionary + ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); + JSHandle boxHandle = factory->NewPropertyBox(keyHandle); + boxHandle->SetValue(thread, valHandle.GetTaggedValue()); + PropertyBoxType boxType = valHandle->IsUndefined() ? PropertyBoxType::UNDEFINED : PropertyBoxType::CONSTANT; + attr.SetBoxType(boxType); + + JSHandle properties = + GlobalDictionary::PutIfAbsent(thread, dictHandle, keyHandle, JSHandle(boxHandle), attr); + objHandle->SetProperties(thread, properties); + return true; +} + +// set property that is not accessor and is writable +void FastRuntimeStub::SetOwnPropertyByName(JSThread *thread, JSTaggedValue receiver, JSTaggedValue key, + JSTaggedValue value) +{ + INTERPRETER_TRACE(thread, SetOwnPropertyByName); + TaggedArray *properties = TaggedArray::Cast(JSObject::Cast(receiver)->GetProperties().GetHeapObject()); + PropertyAttributes attr; + uint32_t indexOrEntry; + JSTaggedValue val = FindOwnProperty(thread, JSObject::Cast(receiver), properties, key, &attr, &indexOrEntry); + if (!val.IsHole()) { + ASSERT(!attr.IsAccessor() && attr.IsWritable()); + if (!properties->IsDictionaryMode()) { + Representation representation = PropertyAttributes::UpdateRepresentation(attr.GetRepresentation(), value); + if (attr.GetRepresentation() != representation) { + attr.SetRepresentation(representation); + } + + JSObject::Cast(receiver)->GetJSHClass()->UpdatePropertyMetaData(thread, key, attr); + + if (attr.IsInlinedProps()) { + JSObject::Cast(receiver)->SetPropertyInlinedProps(thread, indexOrEntry, value); + } else { + properties->Set(thread, indexOrEntry, value); + } + return; + } + + NameDictionary::Cast(properties)->UpdateValueAndAttributes(thread, indexOrEntry, value, attr); + return; + } + [[maybe_unused]] EcmaHandleScope handleScope(thread); + + ObjectOperator::FastAdd(thread, receiver, key, JSHandle(thread, value), + PropertyAttributes::Default()); +} + +// set element that is not accessor and is writable +bool FastRuntimeStub::SetOwnElement(JSThread *thread, JSTaggedValue receiver, uint32_t index, JSTaggedValue value) +{ + INTERPRETER_TRACE(thread, SetOwnElement); + PropertyAttributes attr; + uint32_t indexOrEntry; + TaggedArray *elements = TaggedArray::Cast(JSObject::Cast(receiver)->GetElements().GetHeapObject()); + bool isDict = elements->IsDictionaryMode(); + [[maybe_unused]] JSTaggedValue val = FindOwnElement(elements, index, isDict, &attr, &indexOrEntry); + if (!val.IsHole()) { + ASSERT(!attr.IsAccessor() && attr.IsWritable()); + if (!isDict) { + elements->Set(thread, indexOrEntry, value); + JSObject::Cast(receiver)->GetJSHClass()->UpdateRepresentation(value); + return true; + } + NumberDictionary::Cast(elements)->UpdateValueAndAttributes(thread, indexOrEntry, value, attr); + return true; + } + + return JSObject::AddElementInternal(thread, JSHandle(thread, receiver), index, + JSHandle(thread, value), PropertyAttributes::Default()); +} + +bool FastRuntimeStub::FastSetProperty(JSThread *thread, JSTaggedValue receiver, JSTaggedValue key, JSTaggedValue value, + bool mayThrow) +{ + INTERPRETER_TRACE(thread, FastSetProperty); + if (receiver.IsJSObject() && !receiver.IsTypedArray() && (key.IsStringOrSymbol())) { + uint32_t index = 0; + if (UNLIKELY(JSTaggedValue::ToElementIndex(key, &index))) { + if (!FastRuntimeStub::IsSpecialIndexedObjForSet(receiver)) { + return FastRuntimeStub::SetElement(thread, receiver, index, value, true); + } + return JSTaggedValue::SetProperty(thread, JSHandle(thread, receiver), + JSHandle(thread, key), + JSHandle(thread, value), mayThrow); + } + if (key.IsString()) { + key = JSTaggedValue(thread->GetEcmaVM()->GetFactory()->InternString(JSHandle(thread, key))); + } + return FastRuntimeStub::SetPropertyByName(thread, receiver, key, value, mayThrow); + } + return JSTaggedValue::SetProperty(thread, JSHandle(thread, receiver), + JSHandle(thread, key), JSHandle(thread, value), + mayThrow); +} + +JSTaggedValue FastRuntimeStub::FastGetProperty(JSThread *thread, JSTaggedValue receiver, JSTaggedValue key) +{ + INTERPRETER_TRACE(thread, FastGetProperty); + JSTaggedValue result; + if (receiver.IsJSObject() && !receiver.IsTypedArray() && (key.IsStringOrSymbol())) { + uint32_t index = 0; + if (UNLIKELY(JSTaggedValue::ToElementIndex(key, &index))) { + if (FastRuntimeStub::IsSpecialIndexedObjForSet(receiver)) { + result = JSTaggedValue::Hole(); + } else { + result = FastRuntimeStub::GetElement(receiver, index); + } + } else { + if (key.IsString()) { + key = JSTaggedValue( + thread->GetEcmaVM()->GetFactory()->InternString(JSHandle(thread, key))); + } + result = FastRuntimeStub::GetPropertyByName(thread, receiver, key); + } + } + if (!result.IsHole()) { + if (UNLIKELY(result.IsAccessor())) { + return JSObject::CallGetter(thread, AccessorData::Cast(result.GetHeapObject()), + JSHandle(thread, receiver)); + } + return result; + } + return JSTaggedValue::GetProperty(thread, JSHandle(thread, receiver), + JSHandle(thread, key)) + .GetValue() + .GetTaggedValue(); +} + +JSTaggedValue FastRuntimeStub::FindOwnProperty(JSThread *thread, JSObject *obj, TaggedArray *properties, + JSTaggedValue key, PropertyAttributes *attr, uint32_t *indexOrEntry) +{ + INTERPRETER_TRACE(thread, FindOwnProperty); + if (!properties->IsDictionaryMode()) { + JSHClass *cls = obj->GetJSHClass(); + JSTaggedValue attrs = cls->GetLayout(); + if (!attrs.IsNull()) { + LayoutInfo *layoutInfo = LayoutInfo::Cast(attrs.GetHeapObject()); + int propNumber = cls->NumberOfProps(); + int entry = layoutInfo->FindElementWithCache(thread, cls, key, propNumber); + if (entry != -1) { + *attr = layoutInfo->GetAttr(entry); + ASSERT(entry == static_cast(attr->GetOffset())); + *indexOrEntry = entry; + if (attr->IsInlinedProps()) { + return obj->GetPropertyInlinedProps(entry); + } + *indexOrEntry -= cls->GetInlinedProperties(); + return properties->Get(*indexOrEntry); + } + } + return JSTaggedValue::Hole(); // properties == empty properties will return here. + } + + if (obj->IsJSGlobalObject()) { + GlobalDictionary *dict = GlobalDictionary::Cast(properties); + int entry = dict->FindEntry(key); + if (entry != -1) { + *indexOrEntry = entry; + *attr = dict->GetAttributes(entry); + return dict->GetValue(entry); + } + return JSTaggedValue::Hole(); + } + + NameDictionary *dict = NameDictionary::Cast(properties); + int entry = dict->FindEntry(key); + if (entry != -1) { + *indexOrEntry = entry; + *attr = dict->GetAttributes(entry); + return dict->GetValue(entry); + } + + return JSTaggedValue::Hole(); +} + +JSTaggedValue FastRuntimeStub::FindOwnElement(TaggedArray *elements, uint32_t index, bool isDict, + PropertyAttributes *attr, uint32_t *indexOrEntry) +{ + if (!isDict) { + if (elements->GetLength() <= index) { + return JSTaggedValue::Hole(); + } + + JSTaggedValue value = elements->Get(index); + if (!value.IsHole()) { + *attr = PropertyAttributes::Default(); + *indexOrEntry = index; + return value; + } + } else { + NumberDictionary *dict = NumberDictionary::Cast(elements); + int entry = dict->FindEntry(JSTaggedValue(static_cast(index))); + if (entry != -1) { + *indexOrEntry = entry; + *attr = dict->GetAttributes(entry); + return dict->GetValue(entry); + } + } + return JSTaggedValue::Hole(); +} + +JSTaggedValue FastRuntimeStub::FindOwnProperty(JSThread *thread, JSObject *obj, JSTaggedValue key) +{ + INTERPRETER_TRACE(thread, FindOwnProperty); + TaggedArray *array = TaggedArray::Cast(obj->GetProperties().GetHeapObject()); + if (!array->IsDictionaryMode()) { + JSHClass *cls = obj->GetJSHClass(); + JSTaggedValue attrs = cls->GetLayout(); + if (!attrs.IsNull()) { + LayoutInfo *layoutInfo = LayoutInfo::Cast(attrs.GetHeapObject()); + int propsNumber = cls->NumberOfProps(); + int entry = layoutInfo->FindElementWithCache(thread, cls, key, propsNumber); + if (entry != -1) { + PropertyAttributes attr(layoutInfo->GetAttr(entry)); + ASSERT(static_cast(attr.GetOffset()) == entry); + return attr.IsInlinedProps() ? obj->GetPropertyInlinedProps(entry) + : array->Get(entry - cls->GetInlinedProperties()); + } + } + return JSTaggedValue::Hole(); // array == empty array will return here. + } + + if (obj->IsJSGlobalObject()) { + GlobalDictionary *dict = GlobalDictionary::Cast(array); + int entry = dict->FindEntry(key); + if (entry != -1) { + return dict->GetValue(entry); + } + return JSTaggedValue::Hole(); + } + + NameDictionary *dict = NameDictionary::Cast(array); + int entry = dict->FindEntry(key); + if (entry != -1) { + return dict->GetValue(entry); + } + + return JSTaggedValue::Hole(); +} + +JSTaggedValue FastRuntimeStub::FindOwnElement(JSObject *obj, uint32_t index) +{ + TaggedArray *elements = TaggedArray::Cast(JSObject::Cast(obj)->GetElements().GetHeapObject()); + + if (!elements->IsDictionaryMode()) { + if (elements->GetLength() <= index) { + return JSTaggedValue::Hole(); + } + + JSTaggedValue value = elements->Get(index); + if (!value.IsHole()) { + return value; + } + } else { + NumberDictionary *dict = NumberDictionary::Cast(elements); + int entry = dict->FindEntry(JSTaggedValue(static_cast(index))); + if (entry != -1) { + return dict->GetValue(entry); + } + } + return JSTaggedValue::Hole(); +} + +JSTaggedValue FastRuntimeStub::HasOwnProperty(JSThread *thread, JSObject *obj, JSTaggedValue key) +{ + INTERPRETER_TRACE(thread, HasOwnProperty); + uint32_t index = 0; + if (UNLIKELY(JSTaggedValue::ToElementIndex(key, &index))) { + return FastRuntimeStub::FindOwnElement(obj, index); + } + + return FastRuntimeStub::FindOwnProperty(thread, obj, key); +} + +JSTaggedValue FastRuntimeStub::GetContainerProperty(JSThread *thread, JSTaggedValue receiver, uint32_t index, + JSType jsType) +{ + JSTaggedValue res = JSTaggedValue::Undefined(); + switch (jsType) { + case JSType::JS_ARRAY_LIST: + res = JSArrayList::Cast(receiver.GetTaggedObject())->Get(thread, index); + break; + default: + break; + } + return res; +} + +JSTaggedValue FastRuntimeStub::SetContainerProperty(JSThread *thread, JSTaggedValue receiver, uint32_t index, + JSTaggedValue value, JSType jsType) +{ + JSTaggedValue res = JSTaggedValue::Undefined(); + switch (jsType) { + case JSType::JS_ARRAY_LIST: + res = JSArrayList::Cast(receiver.GetTaggedObject())->Set(thread, index, value); + break; + default: + break; + } + return res; +} +} // namespace panda::ecmascript +#endif // ECMASCRIPT_INTERPRETER_FAST_RUNTIME_STUB_INL_H diff --git a/runtime/interpreter/fast_runtime_stub.h b/runtime/interpreter/fast_runtime_stub.h new file mode 100644 index 000000000..1151775f8 --- /dev/null +++ b/runtime/interpreter/fast_runtime_stub.h @@ -0,0 +1,122 @@ +/* + * Copyright (c) 2021-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_INTERPRETER_FAST_RUNTIME_STUB_H +#define ECMASCRIPT_INTERPRETER_FAST_RUNTIME_STUB_H + +#include +#include "plugins/ecmascript/runtime/global_env_constants.h" +#include "plugins/ecmascript/runtime/js_tagged_value.h" + +namespace panda::ecmascript { +class GlobalEnv; +class PropertyAttributes; + +class FastRuntimeStub { +public: + /* -------------- Common API Begin, Don't change those interface!!! ----------------- */ + static inline JSTaggedValue FastAdd(JSTaggedValue left, JSTaggedValue right); + static inline JSTaggedValue FastSub(JSTaggedValue left, JSTaggedValue right); + static inline JSTaggedValue FastMul(JSTaggedValue left, JSTaggedValue right); + static inline JSTaggedValue FastDiv(JSTaggedValue left, JSTaggedValue right); + static inline JSTaggedValue FastMod(JSTaggedValue left, JSTaggedValue right); + static inline JSTaggedValue FastEqual(JSTaggedValue left, JSTaggedValue right); + static inline JSTaggedValue FastTypeOf(JSThread *thread, JSTaggedValue obj); + static inline bool FastStrictEqual(JSTaggedValue left, JSTaggedValue right); + static inline JSTaggedValue NewLexicalEnvDyn(JSThread *thread, ObjectFactory *factory, uint16_t numVars); + static inline JSTaggedValue GetGlobalOwnProperty(JSTaggedValue receiver, JSTaggedValue key, bool *found); + /* -------------- Special API For Multi-Language VM Begin ----------------- */ + static inline bool IsSpecialIndexedObjForGet(JSTaggedValue obj); + static inline bool IsSpecialIndexedObjForSet(JSTaggedValue obj); + static inline JSTaggedValue GetElement(JSTaggedValue receiver, uint32_t index); + static inline JSTaggedValue GetElementWithArray(JSTaggedValue receiver, uint32_t index); + static inline bool SetElement(JSThread *thread, JSTaggedValue receiver, uint32_t index, JSTaggedValue value, + bool mayThrow); + static inline bool SetPropertyByName(JSThread *thread, JSTaggedValue receiver, JSTaggedValue key, + JSTaggedValue value, bool mayThrow); + static inline bool SetGlobalOwnProperty(JSThread *thread, JSTaggedValue receiver, JSTaggedValue key, + JSTaggedValue value, bool mayThrow); + + // set property that is not accessor and is writable + static inline void SetOwnPropertyByName(JSThread *thread, JSTaggedValue receiver, JSTaggedValue key, + JSTaggedValue value); + // set element that is not accessor and is writable + static inline bool SetOwnElement(JSThread *thread, JSTaggedValue receiver, uint32_t index, JSTaggedValue value); + static inline bool FastSetProperty(JSThread *thread, JSTaggedValue receiver, JSTaggedValue key, JSTaggedValue value, + bool mayThrow); + static inline JSTaggedValue FastGetProperty(JSThread *thread, JSTaggedValue receiver, JSTaggedValue key); + static inline JSTaggedValue FindOwnProperty(JSThread *thread, JSObject *obj, TaggedArray *properties, + JSTaggedValue key, PropertyAttributes *attr, uint32_t *indexOrEntry); + static inline JSTaggedValue FindOwnElement(TaggedArray *elements, uint32_t index, bool isDict, + PropertyAttributes *attr, uint32_t *indexOrEntry); + static inline JSTaggedValue FindOwnProperty(JSThread *thread, JSObject *obj, JSTaggedValue key); + + static inline JSTaggedValue FindOwnElement(JSObject *obj, uint32_t index); + + static inline JSTaggedValue HasOwnProperty(JSThread *thread, JSObject *obj, JSTaggedValue key); + /* -------------- Special API For Multi-Language VM End ----------------- */ + /* -------------- Common API End, Don't change those interface!!! ----------------- */ + + template + static inline JSTaggedValue GetPropertyByName(JSThread *thread, JSTaggedValue receiver, JSTaggedValue key); + template + static inline JSTaggedValue GetPropertyByValue(JSThread *thread, JSTaggedValue receiver, JSTaggedValue key); + template + static inline JSTaggedValue GetPropertyByIndex(JSThread *thread, JSTaggedValue receiver, uint32_t index); + template + static inline JSTaggedValue SetPropertyByName(JSThread *thread, JSTaggedValue receiver, JSTaggedValue key, + JSTaggedValue value); + template + static inline JSTaggedValue SetPropertyByValue(JSThread *thread, JSTaggedValue receiver, JSTaggedValue key, + JSTaggedValue value); + template + static inline JSTaggedValue SetPropertyByIndex(JSThread *thread, JSTaggedValue receiver, uint32_t index, + JSTaggedValue value); + + static inline bool FastSetPropertyByValue(JSThread *thread, JSTaggedValue receiver, JSTaggedValue key, + JSTaggedValue value); + static inline bool FastSetPropertyByIndex(JSThread *thread, JSTaggedValue receiver, uint32_t index, + JSTaggedValue value); + static inline JSTaggedValue FastGetPropertyByName(JSThread *thread, JSTaggedValue receiver, JSTaggedValue key); + static inline JSTaggedValue FastGetPropertyByValue(JSThread *thread, JSTaggedValue receiver, JSTaggedValue key); + template + static inline JSTaggedValue FastGetPropertyByIndex(JSThread *thread, JSTaggedValue receiver, uint32_t index); + static inline PropertyAttributes AddPropertyByName(JSThread *thread, JSHandle objHandle, + JSHandle keyHandle, + JSHandle valueHandle, + PropertyAttributes attr); + +private: + friend class ICRuntimeStub; + static inline bool IsSpecialIndexedObj(JSType jsType); + static inline bool IsSpecialReceiverObj(JSType jsType); + static inline bool IsSpecialContainer(JSType jsType); + static inline uint32_t TryToElementsIndex(JSTaggedValue key); + static inline JSTaggedValue CallGetter(JSThread *thread, JSTaggedValue receiver, JSTaggedValue holder, + JSTaggedValue value); + static inline JSTaggedValue CallSetter(JSThread *thread, JSTaggedValue receiver, JSTaggedValue value, + JSTaggedValue accessorValue); + static inline bool ShouldCallSetter(JSTaggedValue receiver, JSTaggedValue holder, JSTaggedValue accessorValue, + PropertyAttributes attr); + static inline JSTaggedValue AddPropertyByIndex(JSThread *thread, JSTaggedValue receiver, uint32_t index, + JSTaggedValue value); + static inline JSTaggedValue GetContainerProperty(JSThread *thread, JSTaggedValue receiver, uint32_t index, + JSType jsType); + static inline JSTaggedValue SetContainerProperty(JSThread *thread, JSTaggedValue receiver, uint32_t index, + JSTaggedValue value, JSType jsType); +}; +} // namespace panda::ecmascript + +#endif // ECMASCRIPT_INTERPRETER_OBJECT_OPERATOR_INL_H diff --git a/runtime/interpreter/interpreter-inl.h b/runtime/interpreter/interpreter-inl.h new file mode 100644 index 000000000..eaa0815fe --- /dev/null +++ b/runtime/interpreter/interpreter-inl.h @@ -0,0 +1,210 @@ +/** + * Copyright (c) Huawei Technologies Co., Ltd. 2019-2021. All rights reserved. + */ + +#ifndef PANDA_RUNTIME_ECMASCRIPT_INTERPRETER_INL_H +#define PANDA_RUNTIME_ECMASCRIPT_INTERPRETER_INL_H + +#include "plugins/ecmascript/runtime/class_linker/program_object-inl.h" +#include "plugins/ecmascript/runtime/class_linker/program_object-inl.h" +#include "plugins/ecmascript/runtime/ecma_string.h" +#include "plugins/ecmascript/runtime/ecma_vm.h" +#include "plugins/ecmascript/runtime/global_env.h" +#include "plugins/ecmascript/runtime/interpreter/ecma-interpreter.h" +#include "plugins/ecmascript/runtime/interpreter/interpreter.h" +#include "plugins/ecmascript/runtime/interpreter/js_frame-inl.h" +#include "plugins/ecmascript/runtime/interpreter/slow_runtime_stub.h" +#include "plugins/ecmascript/runtime/js_generator_object.h" +#include "plugins/ecmascript/runtime/js_tagged_value.h" +#include "plugins/ecmascript/runtime/literal_data_extractor.h" +#include "plugins/ecmascript/runtime/template_string.h" +#include "plugins/ecmascript/compiler/ecmascript_extensions/thread_environment_api.h" +#include "include/method-inl.h" +#include "include/runtime_notification.h" +#include "libpandafile/code_data_accessor.h" +#include "libpandafile/file.h" +#include "libpandafile/method_data_accessor.h" + +namespace panda::ecmascript { +#if defined(__clang__) +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wvoid-ptr-dereference" +#pragma clang diagnostic ignored "-Wgnu-label-as-value" +#elif defined(__GNUC__) +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wpedantic" +#endif + +class JSInvokeHelper : public InvokeHelperDynamic { +public: + ALWAYS_INLINE inline static void InterpreterExecute(ManagedThread *thread, const uint8_t *pc, Frame *frame) + { + JSThread *js_thread = JSThread::Cast(thread); + + // Get current functional object + JSTaggedValue func = js_thread->GetFunctionalObject(); + ObjectHeader *lexical_env = js_thread->GetInvocationLexicalEnv().GetHeapObject(); + + JSFunction *js_function = JSFunction::Cast(func.GetHeapObject()); + ObjectHeader *constant_pool = js_function->GetConstantPool().GetHeapObject(); + EcmascriptEnvironment *prev_env = js_thread->GetEcmascriptEnv(); + + // Init EcmascriptEnvironment + EcmascriptEnvironment *new_env = JSFrame::GetJSEnv(frame); + new (new_env) EcmascriptEnvironment(constant_pool, lexical_env, func.GetHeapObject(), prev_env); + + // Change EcmascriptEnvironment + js_thread->SetEcmascriptEnv(new_env); + + InvokeHelperDynamic::InterpreterExecute(thread, pc, frame); + + // Restore EcmascriptEnvironment + js_thread->SetEcmascriptEnv(prev_env); + } + + ALWAYS_INLINE inline static coretypes::TaggedValue CompiledCodeExecute(ManagedThread *thread, Method *method, + uint32_t num_args, + coretypes::TaggedValue *args) + { + JSThread *js_thread = JSThread::Cast(thread); + EcmascriptEnvironment *prev_env = js_thread->GetEcmascriptEnv(); + + auto ret = InvokeHelperDynamic::CompiledCodeExecute(thread, method, num_args, args); + + // Restore EcmascriptEnvironment if epilogue was not executed + if (UNLIKELY(js_thread->HasPendingException())) { + js_thread->SetEcmascriptEnv(prev_env); + } + return ret; + } + + ALWAYS_INLINE static Frame *CreateFrame(ManagedThread *thread, uint32_t nregs_size, Method *method, + Frame *prev, uint32_t nregs, uint32_t num_actual_args) + { + return ::panda::CreateFrame(thread->GetStackFrameAllocator(), nregs_size, method, prev, + nregs, num_actual_args); + } +}; + +JSTaggedValue EcmaInterpreter::ExecuteNative(JSThread *js_thread, JSHandle call_target, uint32_t num_args, + const JSTaggedType *args) +{ + return ExecuteNative(js_thread, call_target.GetTaggedValue(), num_args, + reinterpret_cast(args)); +} + +JSTaggedValue EcmaInterpreter::ExecuteNative(JSThread *js_thread, JSTaggedValue fn_object, uint32_t num_args, + const JSTaggedValue *args) +{ + ASSERT(!js_thread->HasPendingException()); + + Method *method = ECMAObject::Cast(fn_object.GetHeapObject())->GetCallTarget(); + ASSERT(method->GetNumVregs() == 0); + + uint32_t num_declared_args = method->GetNumArgs(); + uint32_t nregs = std::max(num_declared_args, num_args); + + Frame *frame = JSFrame::CreateNativeFrame(js_thread, method, js_thread->GetCurrentFrame(), nregs, num_args); + JSFrame::InitNativeFrameArgs(frame, num_args, args); + JSTaggedValue ret_value = JSFrame::ExecuteNativeMethod(js_thread, frame, method, num_args); + JSFrame::DestroyNativeFrame(js_thread, frame); + + return ret_value; +} + +JSTaggedValue EcmaInterpreter::ExecuteInvoke(JSThread *js_thread, JSHandle call_target, uint32_t num_args, + JSTaggedValue *args) +{ + return ExecuteInvoke(js_thread, call_target.GetTaggedValue(), num_args, args); +} + +JSTaggedValue EcmaInterpreter::ExecuteInvoke(JSThread *thread, JSTaggedValue fn_object, uint32_t num_args, + JSTaggedValue *args) +{ + ASSERT(!thread->HasPendingException()); + ASSERT(fn_object.IsJSFunction()); + +#ifndef NDEBUG + EcmascriptEnvironment *prev_env = thread->GetEcmascriptEnv(); +#endif // !NDEBUG + + JSFunction *js_function = JSFunction::Cast(fn_object.GetHeapObject()); + Method *method = js_function->GetCallTarget(); + + // Set current functional object to thread so we can get it from InvokeHelper + thread->SetFunctionalObject(fn_object); + thread->SetInvocationLexicalEnv(js_function->GetLexicalEnv()); + TaggedValue ret_value = method->InvokeDyn(thread, num_args, args); + ASSERT(thread->GetEcmascriptEnv() == prev_env); + + return JSTaggedValue(ret_value); +} + +JSTaggedValue EcmaInterpreter::Execute(JSThread *thread, JSHandle callTarget, uint32_t numActualArgs, + JSTaggedType *args) +{ + ASSERT(JSHandle::Cast(callTarget)->IsJSFunction()); + + if (UNLIKELY(thread->HasPendingException())) { + return JSTaggedValue::Undefined(); + } + + Method *method = callTarget->GetCallTarget(); + if (method->IsNative()) { + return EcmaInterpreter::ExecuteNative(thread, callTarget, numActualArgs, args); + } + + return ExecuteInvoke(thread, callTarget, numActualArgs, reinterpret_cast(args)); +} + +JSTaggedValue EcmaInterpreter::GeneratorReEnterInterpreter(JSThread *thread, JSHandle context) +{ + if (UNLIKELY(thread->HasPendingException())) { + return JSTaggedValue::Exception(); + } + +#ifndef NDEBUG + EcmascriptEnvironment *prev_env = thread->GetEcmascriptEnv(); +#endif // !NDEBUG + + JSHandle func = JSHandle::Cast(JSHandle(thread, context->GetMethod())); + Method *method = func->GetCallTarget(); + + JSTaggedValue pcOffset = context->GetBCOffset(); + // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-pointer-arithmetic) + const uint8_t *resumePc = method->GetInstructions() + static_cast(pcOffset.GetInt()) + + BytecodeInstruction::Size(BytecodeInstruction::Format::PREF_V8); + + TaggedValue acc(context->GetAcc().GetRawData()); + uint32_t nregs = context->GetNRegs().GetInt(); + TaggedArray *regsArray = TaggedArray::Cast(context->GetRegsArray().GetHeapObject()); + auto *regs = reinterpret_cast(regsArray->GetData()); + ASSERT(nregs == regsArray->GetLength()); + + // Set current functional object to thread so we can get it from InvokeHelper + thread->SetFunctionalObject(func.GetTaggedValue()); + thread->SetInvocationLexicalEnv(context->GetLexicalEnv()); + TaggedValue value = method->InvokeContext(thread, resumePc, acc, nregs, regs); + ASSERT(thread->GetEcmascriptEnv() == prev_env); + + return JSTaggedValue(value); +} + +void EcmaInterpreter::ChangeGenContext([[maybe_unused]] JSThread *thread, + [[maybe_unused]] JSHandle context) +{ + LOG(DEBUG, INTERPRETER) << "ChangeGenContext"; +} + +void EcmaInterpreter::ResumeContext([[maybe_unused]] JSThread *thread) +{ + LOG(DEBUG, INTERPRETER) << "ResumeContext"; +} + +#if defined(__clang__) +#pragma clang diagnostic pop +#elif defined(__GNUC__) +#pragma GCC diagnostic pop +#endif +} // namespace panda::ecmascript +#endif // PANDA_RUNTIME_ECMASCRIPT_INTERPRETER_INL_H diff --git a/runtime/interpreter/interpreter.h b/runtime/interpreter/interpreter.h new file mode 100644 index 000000000..691a6e9be --- /dev/null +++ b/runtime/interpreter/interpreter.h @@ -0,0 +1,230 @@ +/* + * Copyright (c) 2021-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_INTERPRETER_INTERPRETER_H +#define ECMASCRIPT_INTERPRETER_INTERPRETER_H + +#include "plugins/ecmascript/runtime/js_method.h" +#include "plugins/ecmascript/runtime/js_tagged_value.h" +#include "plugins/ecmascript/runtime/js_handle.h" +#include "plugins/ecmascript/runtime/js_thread.h" +#include "plugins/ecmascript/runtime/frames.h" + +namespace panda::ecmascript { +class ConstantPool; +class ECMAObject; +class GeneratorContext; + +class EcmaInterpreter { +public: + static inline JSTaggedValue ExecuteNative(JSThread *thread, JSTaggedValue fn_object, uint32_t num_args, + const JSTaggedValue *args); + static inline JSTaggedValue ExecuteInvoke(JSThread *thread, JSTaggedValue fn_object, uint32_t num_args, + JSTaggedValue *args); + static inline JSTaggedValue Execute(JSThread *thread, JSHandle callTarget, uint32_t numActualArgs, + JSTaggedType *args); + static inline JSTaggedValue ExecuteNative(JSThread *js_thread, JSHandle call_target, uint32_t num_args, + const JSTaggedType *args); + static inline JSTaggedValue ExecuteInvoke(JSThread *js_thread, JSHandle call_target, + uint32_t num_args, JSTaggedValue *args); + static inline JSTaggedValue GeneratorReEnterInterpreter(JSThread *thread, JSHandle context); + static inline void ChangeGenContext(JSThread *thread, JSHandle context); + static inline void ResumeContext(JSThread *thread); + static inline void RunInternal(JSThread *thread, ConstantPool *constpool, const uint8_t *pc, JSTaggedType *sp); + static inline uint8_t ReadU8(const uint8_t *pc, uint32_t offset); + static inline void InitStackFrame(JSThread *thread); + static inline uint32_t FindCatchBlock(JSMethod *caller, uint32_t pc); + static inline size_t GetJumpSizeAfterCall(const uint8_t *prevPc); + + static inline JSTaggedValue GetRuntimeProfileTypeInfo(JSTaggedType *sp); + static inline bool UpdateHotnessCounter(JSThread* thread, JSTaggedType *sp, JSTaggedValue acc, int32_t offset); + static inline void InterpreterFrameCopyArgs(JSTaggedType *newSp, uint32_t numVregs, uint32_t numActualArgs, + uint32_t numDeclaredArgs, bool haveExtraArgs = true); + static inline void NotifyBytecodePcChanged(JSThread *thread); + static inline JSTaggedValue GetThisFunction(JSTaggedType *sp); + static inline JSTaggedValue GetNewTarget(JSTaggedType *sp); + static inline uint32_t GetNumArgs(JSTaggedType *sp, uint32_t restIdx, uint32_t &startIdx); +}; + +enum EcmaOpcode { + MOV_DYN_V8_V8, + MOV_DYN_V16_V16, + LDA_STR_ID32, + LDAI_DYN_IMM32, + FLDAI_DYN_IMM64, + LDNAN_IMM8, + LDINFINITY_IMM8, + LDGLOBALTHIS_IMM8, + LDUNDEFINED_IMM8, + LDNULL_IMM8, + LDSYMBOL_IMM8, + LDGLOBAL_IMM8, + LDTRUE_IMM8, + LDFALSE_IMM8, + RETHROWDYN_IMM8, + THROWDYN_IMM8, + TYPEOFDYN_IMM8, + LDLEXENVDYN_IMM8, + POPLEXENVDYN_IMM8, + GETUNMAPPEDARGS_IMM8, + TOBOOLEAN_IMM8, + NEGATE_IMM8, + ISUNDEFINED_IMM8, + ISTRUE_IMM8, + ISFALSE_IMM8, + ISCOERCIBLE_IMM8, + GETPROPITERATOR_IMM8, + ASYNCFUNCTIONENTER_IMM8, + LDHOLE_IMM8, + RETURNUNDEFINED_IMM8, + CREATEEMPTYOBJECT_IMM8, + CREATEEMPTYARRAY_IMM8, + GETITERATOR_IMM8, + GETASYNCITERATOR_IMM8, + THROWTHROWNOTEXISTS_IMM8, + THROWPATTERNNONCOERCIBLE_IMM8, + LDHOMEOBJECT_IMM8, + THROWDELETESUPERPROPERTY_IMM8, + DEBUGGER_IMM8, + JMP_IMM8, + JMP_IMM16, + JMP_IMM32, + JEQZ_IMM8, + JEQZ_IMM16, + LDA_DYN_V8, + STA_DYN_V8, + LDBOOLEAN_IMM8_V8, + LDNUMBER_IMM8_V8, + LDSTRING_IMM8_V8, + LDBIGINT_IMM8_V8, + ADD2DYN_IMM8_V8, + SUB2DYN_IMM8_V8, + MUL2DYN_IMM8_V8, + DIV2DYN_IMM8_V8, + MOD2DYN_IMM8_V8, + EQDYN_IMM8_V8, + NOTEQDYN_IMM8_V8, + LESSDYN_IMM8_V8, + LESSEQDYN_IMM8_V8, + GREATERDYN_IMM8_V8, + GREATEREQDYN_IMM8_V8, + SHL2DYN_IMM8_V8, + SHR2DYN_IMM8_V8, + ASHR2DYN_IMM8_V8, + AND2DYN_IMM8_V8, + OR2DYN_IMM8_V8, + XOR2DYN_IMM8_V8, + TONUMBER_IMM8_V8, + NEGDYN_IMM8_V8, + NOTDYN_IMM8_V8, + INCDYN_IMM8_V8, + DECDYN_IMM8_V8, + EXPDYN_IMM8_V8, + ISINDYN_IMM8_V8, + INSTANCEOFDYN_IMM8_V8, + STRICTNOTEQDYN_IMM8_V8, + STRICTEQDYN_IMM8_V8, + RESUMEGENERATOR_IMM8_V8, + GETRESUMEMODE_IMM8_V8, + CREATEGENERATOROBJ_IMM8_V8, + SETGENERATORSTATE_IMM8_V8_IMM8, + CREATEASYNCGENERATOROBJ_IMM8_V8, + THROWUNDEFINED_IMM8_V8, + THROWCONSTASSIGNMENT_IMM8_ID32, + GETMETHOD_IMM8_IMM16_V8, + GETTEMPLATEOBJECT_IMM8_V8, + GETNEXTPROPNAME_IMM8_V8, + CALL0DYN_IMM8_V8, + THROWIFNOTOBJECT_IMM8, + CLOSEITERATOR_IMM8_V8, + COPYMODULE_IMM8_V8, + SUPERCALLSPREAD_IMM8_V8, + LDOBJECT_IMM8_V8_V8, + LDFUNCTION_IMM8_V8_V8, + DELOBJPROP_IMM8_V8_V8, + DEFINEGLOBALVAR_IMM8_V8_V8, + DEFINELOCALVAR_IMM8_V8_V8, + DEFINEFUNCEXPR_IMM8_V8_V8, + REFEQDYN_IMM8_V8_V8, + CALLRUNTIMERANGE_IMM8_V8_V8, + NEWOBJSPREADDYN_IMM8_V8_V8, + CREATEITERRESULTOBJ_IMM8, + SUSPENDGENERATOR_IMM8_V8, + SUSPENDASYNCGENERATOR_IMM8_V8, + ASYNCFUNCTIONAWAIT_IMM8_V8, + THROWUNDEFINEDIFHOLE_IMM8_ID32, + CALL1DYN_IMM8_V8_V8, + COPYDATAPROPERTIES_IMM8_V8_V8, + STARRAYSPREAD_IMM8_V8_V8, + SETOBJECTWITHPROTO_IMM8_V8_V8, + CALLSPREADDYN_IMM8_V8_V8_V8, + ASYNCFUNCTIONRESOLVE_IMM8_V8, + ASYNCFUNCTIONREJECT_IMM8_V8, + ASYNCGENERATORRESOLVE_IMM8_V8, + ASYNCGENERATORREJECT_IMM8_V8, + CALL2DYN_IMM8_V8_V8_V8, + CALL3DYN_IMM8_V8_V8_V8_V8, + DEFINEGETTERSETTERBYVALUE_IMM8_V8_V8_V8_V8, + TRYLDGLOBALBYVALUE_IMM8_IMM16_V8, + NEWOBJDYNRANGE_IMM8_IMM16_V8, + TRYSTGLOBALBYVALUE_IMM8_IMM16_V8, + CALLIRANGEDYN_IMM8_IMM16_V8, + CALLITHISRANGEDYN_IMM8_IMM16_V8, + SUPERCALL_IMM8_IMM16_V8, + LDOBJBYVALUE_IMM8_IMM16_V8_V8, + STOBJBYVALUE_IMM8_IMM16_V8_V8, + LDOBJBYINDEX_IMM8_IMM32_IMM16_V8, + STOBJBYINDEX_IMM8_IMM32_IMM16_V8, + STOWNBYINDEX_IMM8_IMM32_IMM16_V8, + STOWNBYVALUE_IMM8_IMM16_V8_V8, + CREATEOBJECTWITHEXCLUDEDKEYS_IMM8_IMM16_V8_V8, + STSUPERBYVALUE_IMM8_IMM16_V8_V8, + LDSUPERBYVALUE_IMM8_IMM16_V8_V8, + IMPORTMODULE_IMM8_ID32, + STMODULEVAR_IMM8_ID32, + DEFINEFUNCDYN_IMM8_ID16_V8, + DEFINENCFUNCDYN_IMM8_ID16_V8, + DEFINEGENERATORFUNC_IMM8_ID16_V8, + DEFINEASYNCFUNC_IMM8_ID16_V8, + DEFINEASYNCGENERATORFUNC_IMM8_ID16_V8, + DEFINEMETHOD_IMM8_ID16_V8, + TRYLDGLOBALBYNAME_IMM8_ID32_IMM16, + TRYSTGLOBALBYNAME_IMM8_ID32_IMM16, + LDGLOBALVAR_IMM8_ID32_IMM16, + STGLOBALVAR_IMM8_ID32_IMM16, + LDOBJBYNAME_IMM8_ID32_IMM16_V8, + STOBJBYNAME_IMM8_ID32_IMM16_V8, + STOWNBYNAME_IMM8_ID32_IMM16_V8, + LDMODVARBYNAME_IMM8_ID32_IMM16_V8, + LDSUPERBYNAME_IMM8_ID32_IMM16_V8, + STSUPERBYNAME_IMM8_ID32_IMM16_V8, + STLEXVARDYN_IMM8_IMM16_IMM16, + LDLEXVARDYN_IMM8_IMM16_IMM16, + NEWLEXENVDYN_IMM8_IMM16, + COPYLEXENVDYN_IMM8, + COPYRESTARGS_IMM8_IMM16, + CREATEOBJECTWITHBUFFER_IMM8_IMM16, + CREATEARRAYWITHBUFFER_IMM8_IMM16, + CREATEOBJECTHAVINGMETHOD_IMM8_IMM16, + THROWIFSUPERNOTCORRECTCALL_IMM8_IMM16, + DEFINECLASSWITHBUFFER_IMM8_ID16_IMM16_V8_V8, + RETURN_DYN, + MOV_V4_V4, + JNEZ_IMM8, + JNEZ_IMM16, + LAST_OPCODE, +}; +} // namespace panda::ecmascript +#endif // PANDA_RUNTIME_ECMASCRIPT_INTERPRETER_H diff --git a/runtime/interpreter/js_decode_call_instr.h b/runtime/interpreter/js_decode_call_instr.h new file mode 100644 index 000000000..0d20e9dc6 --- /dev/null +++ b/runtime/interpreter/js_decode_call_instr.h @@ -0,0 +1,132 @@ +/** + * Copyright (c) Huawei Technologies Co., Ltd. 2019-2021. All rights reserved. + */ + +#ifndef PANDA_PLUGINS_ECMASCRIPT_RUNTIME_INTERPRETER_JS_DECODE_CALL_INSTR_H +#define PANDA_PLUGINS_ECMASCRIPT_RUNTIME_INTERPRETER_JS_DECODE_CALL_INSTR_H + +#include "plugins/ecmascript/runtime/global_env.h" +#include "plugins/ecmascript/runtime/js_object.h" +#include "plugins/ecmascript/runtime/js_function.h" +#include "plugins/ecmascript/runtime/js_thread.h" + +namespace panda::ecmascript { +template +ALWAYS_INLINE inline uint32_t JSGetNumberActualArgsDyn([[maybe_unused]] BytecodeInstruction inst) +{ + if constexpr (opcode == BytecodeInstruction::Opcode::ECMA_CALL0DYN_PREF_V8) { + static_assert(format == BytecodeInstruction::Format::PREF_V8); + return NUM_MANDATORY_JSFUNC_ARGS + 0; + } else if constexpr (opcode == BytecodeInstruction::Opcode::ECMA_CALL1DYN_PREF_V8_V8) { + static_assert(format == BytecodeInstruction::Format::PREF_V8_V8); + return NUM_MANDATORY_JSFUNC_ARGS + 1; + } else if constexpr (opcode == BytecodeInstruction::Opcode::ECMA_CALL2DYN_PREF_V8_V8_V8) { + static_assert(format == BytecodeInstruction::Format::PREF_V8_V8_V8); + return NUM_MANDATORY_JSFUNC_ARGS + 2; + } else if constexpr (opcode == BytecodeInstruction::Opcode::ECMA_CALL3DYN_PREF_V8_V8_V8_V8) { + static_assert(format == BytecodeInstruction::Format::PREF_V8_V8_V8_V8); + return NUM_MANDATORY_JSFUNC_ARGS + 3; + } else if constexpr (opcode == BytecodeInstruction::Opcode::ECMA_CALLIRANGEDYN_PREF_IMM16_V8) { + static_assert(format == BytecodeInstruction::Format::PREF_IMM16_V8); + auto imm = inst.GetImm(); + return NUM_MANDATORY_JSFUNC_ARGS + imm; + } else if constexpr (opcode == BytecodeInstruction::Opcode::ECMA_CALLITHISRANGEDYN_PREF_IMM16_V8) { + static_assert(format == BytecodeInstruction::Format::PREF_IMM16_V8); + auto imm = inst.GetImm(); + ASSERT(imm >= 1); // 'this' is always passed to the function + return NUM_MANDATORY_JSFUNC_ARGS - 1 + imm; + } else { + enum { IMPOSSIBLE_CASE = false }; + static_assert(IMPOSSIBLE_CASE, "Impossible case"); + } + + UNREACHABLE_CONSTEXPR(); + return 0; +} + +template +ALWAYS_INLINE inline static void JSCopyArgumets(JSThread *thread, Frame *prev_frame, uint16_t prev_v0, + BytecodeInstruction prev_inst, Frame *new_frame, uint32_t num_vregs, + uint32_t num_actual_args) +{ + // ecma.call0dyn + // ecma.call1dyn + // ecma.call2dyn + // ecma.call3dyn + // ecma.callirangedyn: + // dyn_arg[0] - vreg[ 0] [functional object] + // dyn_arg[1] - undefined [ new.target ] + // dyn_arg[2] - undefined | global object + // dyn_arg[3] - vreg[ 1] [ args[ 0] ] + // dyn_arg[4] - vreg[ 2] [ args[ 1] ] + // dyn_arg[5] - vreg[ 3] [ args[ 2] ] + // ... + // dyn_arg[N] - vreg[N-2] [ args[N-3] ] + + // ecma.callithisrangedyn: + // dyn_arg[0] - vreg[ 0] [functional object] + // dyn_arg[1] - undefined [ new.target ] + // dyn_arg[2] - vreg[ 1] [ this ] + // dyn_arg[3] - vreg[ 2] [ args[ 0] ] + // dyn_arg[4] - vreg[ 3] [ args[ 1] ] + // ... + // dyn_arg[N] - vreg[N-1] [ args[N-3] ] + + ASSERT(num_actual_args >= 3); + uint64_t raw_fn_object = prev_frame->GetVReg(prev_v0).GetValue(); + + new_frame->GetVReg(num_vregs + 0).SetValue(raw_fn_object); + new_frame->GetVReg(num_vregs + 1).SetValue(JSTaggedValue::VALUE_UNDEFINED); + + if constexpr (opcode != BytecodeInstruction::Opcode::ECMA_CALLITHISRANGEDYN_PREF_IMM16_V8) { + JSTaggedValue fn_object(raw_fn_object); + uint64_t dyn_arg2 = JSTaggedValue::VALUE_UNDEFINED; + if (fn_object.IsJSFunction() && !JSFunction::Cast(fn_object.GetHeapObject())->IsStrict()) { + dyn_arg2 = thread->GetGlobalObject().GetRawData(); + } + new_frame->GetVReg(num_vregs + 2).SetValue(dyn_arg2); + } + + if constexpr (opcode == BytecodeInstruction::Opcode::ECMA_CALL0DYN_PREF_V8) { + static_assert(format == BytecodeInstruction::Format::PREF_V8); + ASSERT(num_actual_args == 3); + // Do nothing + } else if constexpr (opcode == BytecodeInstruction::Opcode::ECMA_CALL1DYN_PREF_V8_V8) { + static_assert(format == BytecodeInstruction::Format::PREF_V8_V8); + ASSERT(num_actual_args == 3 + 1); + new_frame->GetVReg(num_vregs + 3 + 0) = prev_frame->GetVReg(prev_inst.GetVReg(1)); + } else if constexpr (opcode == BytecodeInstruction::Opcode::ECMA_CALL2DYN_PREF_V8_V8_V8) { + static_assert(format == BytecodeInstruction::Format::PREF_V8_V8_V8); + ASSERT(num_actual_args == 3 + 2); + new_frame->GetVReg(num_vregs + 3 + 0) = prev_frame->GetVReg(prev_inst.GetVReg(1)); + new_frame->GetVReg(num_vregs + 3 + 1) = prev_frame->GetVReg(prev_inst.GetVReg(2)); + } else if constexpr (opcode == BytecodeInstruction::Opcode::ECMA_CALL3DYN_PREF_V8_V8_V8_V8) { + static_assert(format == BytecodeInstruction::Format::PREF_V8_V8_V8_V8); + ASSERT(num_actual_args == 3 + 3); + new_frame->GetVReg(num_vregs + 3 + 0) = prev_frame->GetVReg(prev_inst.GetVReg(1)); + new_frame->GetVReg(num_vregs + 3 + 1) = prev_frame->GetVReg(prev_inst.GetVReg(2)); + new_frame->GetVReg(num_vregs + 3 + 2) = prev_frame->GetVReg(prev_inst.GetVReg(3)); + } else if constexpr (opcode == BytecodeInstruction::Opcode::ECMA_CALLIRANGEDYN_PREF_IMM16_V8) { + static_assert(format == BytecodeInstruction::Format::PREF_IMM16_V8); + for (uint16_t i = 1; i < (num_actual_args - 2); ++i) { + new_frame->GetVReg(num_vregs + 2 + i) = prev_frame->GetVReg(prev_v0 + i); + } + } else if constexpr (opcode == BytecodeInstruction::Opcode::ECMA_CALLITHISRANGEDYN_PREF_IMM16_V8) { + static_assert(format == BytecodeInstruction::Format::PREF_IMM16_V8); + for (uint16_t i = 0; i < (num_actual_args - 2); ++i) { + new_frame->GetVReg(num_vregs + 2 + i) = prev_frame->GetVReg(prev_v0 + 1 + i); + } + // Check 'this' value + ASSERT(new_frame->GetVReg(num_vregs + 2).GetValue() != JSTaggedValue::VALUE_UNDEFINED); + } else { + enum { IMPOSSIBLE_CASE = false }; + static_assert(IMPOSSIBLE_CASE, "Impossible case"); + } + + for (size_t i = num_vregs + num_actual_args; i < new_frame->GetSize(); ++i) { + new_frame->GetVReg(i).SetValue(JSTaggedValue::VALUE_UNDEFINED); + } +} +} // namespace panda::ecmascript + +#endif // PANDA_PLUGINS_ECMASCRIPT_RUNTIME_INTERPRETER_JS_DECODE_CALL_INSTR_H diff --git a/runtime/interpreter/js_frame-inl.h b/runtime/interpreter/js_frame-inl.h new file mode 100644 index 000000000..a2ea30ef8 --- /dev/null +++ b/runtime/interpreter/js_frame-inl.h @@ -0,0 +1,73 @@ +/** + * Copyright (c) Huawei Technologies Co., Ltd. 2019-2021. All rights reserved. + */ + +#ifndef PANDA_PLUGINS_ECMASCRIPT_RUNTIME_NATIVE_FRAME_INL_H +#define PANDA_PLUGINS_ECMASCRIPT_RUNTIME_NATIVE_FRAME_INL_H + +#include "plugins/ecmascript/runtime/interpreter/js_frame.h" +#include "plugins/ecmascript/runtime/js_thread.h" +#include "plugins/ecmascript/runtime/ecma_runtime_call_info.h" +#include "runtime/interpreter/frame.h" + +namespace panda::ecmascript { +/* static */ +inline Frame *JSFrame::CreateNativeFrame(JSThread *js_thread, Method *method, Frame *prev_frame, uint32_t nregs, + uint32_t num_actual_args) +{ + ASSERT(js_thread == JSThread::GetCurrent()); + + Frame *new_frame = CreateFrame(js_thread->GetStackFrameAllocator(), nregs, method, prev_frame, nregs, + num_actual_args); + js_thread->SetCurrentFrame(new_frame); + LOG_IF(new_frame == nullptr, FATAL, ECMASCRIPT) << "Cannot allocate native frame"; + new_frame->SetInvoke(); + new_frame->SetDynamic(); + return new_frame; +} + +/* static */ +inline void JSFrame::DestroyNativeFrame(JSThread *js_thread, Frame *frame) +{ + ASSERT(js_thread == JSThread::GetCurrent()); + + js_thread->SetCurrentFrame(frame->GetPrevFrame()); + DestroyFrame(js_thread->GetStackFrameAllocator(), frame); +} + +/* static */ +inline void JSFrame::InitNativeFrameArgs(Frame *frame, uint32_t num_args, const JSTaggedValue *args) +{ + uint32_t num_actual_args = frame->GetNumActualArgs(); + ASSERT(num_args <= num_actual_args); + + for (uint32_t i = 0; i < num_args; ++i) { + // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-pointer-arithmetic) + frame->GetVReg(i).SetValue(args[i].GetRawData()); + } + for (uint32_t i = num_args; i < num_actual_args; ++i) { + frame->GetVReg(i).SetValue(JSTaggedValue::VALUE_UNDEFINED); + } + frame->SetInvoke(); +} + +/* static */ +inline JSTaggedValue JSFrame::ExecuteNativeMethod(JSThread *js_thread, Frame *frame, Method *method, + uint32_t num_actual_args) +{ + ASSERT(js_thread == JSThread::GetCurrent()); + + bool is_compiled = js_thread->IsCurrentFrameCompiled(); + js_thread->SetCurrentFrameIsCompiled(false); + + EcmaRuntimeCallInfo call_info(js_thread, num_actual_args, &frame->GetVReg(0)); + auto ecma_entry_point = reinterpret_cast(method->GetNativePointer()); + JSTaggedValue ret_value = ecma_entry_point(&call_info); + + js_thread->SetCurrentFrameIsCompiled(is_compiled); + + return ret_value; +} +} // namespace panda::ecmascript + +#endif // PANDA_PLUGINS_ECMASCRIPT_RUNTIME_NATIVE_FRAME_INL_H diff --git a/runtime/interpreter/js_frame.h b/runtime/interpreter/js_frame.h new file mode 100644 index 000000000..ac807d878 --- /dev/null +++ b/runtime/interpreter/js_frame.h @@ -0,0 +1,41 @@ +/** + * Copyright (c) Huawei Technologies Co., Ltd. 2019-2021. All rights reserved. + */ + +#ifndef PANDA_PLUGINS_ECMASCRIPT_RUNTIME_INTERPRETER_JS_FRAME_H +#define PANDA_PLUGINS_ECMASCRIPT_RUNTIME_INTERPRETER_JS_FRAME_H + +#include +#include "plugins/ecmascript/runtime/js_tagged_value.h" +#include "plugins/ecmascript/compiler/ecmascript_extensions/ecmascript_environment.h" + +namespace panda { +class Frame; +class Method; + +namespace ecmascript { +class JSThread; + +using JSExtFrame = ExtFrame; + +using JSEnv = EcmascriptEnvironment; + +class JSFrame { +public: + static Frame *CreateNativeFrame(JSThread *js_thread, Method *method, Frame *prev_frame, uint32_t nregs, + uint32_t num_actual_args); + static void DestroyNativeFrame(JSThread *js_thread, Frame *frame); + + static void InitNativeFrameArgs(Frame *frame, uint32_t num_args, const JSTaggedValue *args); + static JSTaggedValue ExecuteNativeMethod(JSThread *js_thread, Frame *frame, Method *method, + uint32_t num_actual_args); + + static JSEnv *GetJSEnv(Frame *frame) + { + return JSExtFrame::FromFrame(frame)->GetExtData(); + } +}; +} // namespace ecmascript +} // namespace panda + +#endif // PANDA_PLUGINS_ECMASCRIPT_RUNTIME_INTERPRETER_JS_FRAME_H diff --git a/runtime/interpreter/slow_runtime_helper.cpp b/runtime/interpreter/slow_runtime_helper.cpp new file mode 100644 index 000000000..81e4af059 --- /dev/null +++ b/runtime/interpreter/slow_runtime_helper.cpp @@ -0,0 +1,270 @@ +/* + * Copyright (c) 2021-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 "slow_runtime_helper.h" +#include "plugins/ecmascript/runtime/global_env.h" +#include "plugins/ecmascript/runtime/internal_call_params.h" +#include "plugins/ecmascript/runtime/interpreter/ecma-interpreter.h" +#include "plugins/ecmascript/runtime/interpreter/interpreter-inl.h" +#include "plugins/ecmascript/runtime/js_generator_object.h" +#include "plugins/ecmascript/runtime/js_invoker.h" +#include "plugins/ecmascript/runtime/js_tagged_value.h" +#include "plugins/ecmascript/runtime/mem/c_containers.h" +#include "plugins/ecmascript/runtime/tagged_array-inl.h" + +namespace panda::ecmascript { +JSTaggedValue SlowRuntimeHelper::CallBoundFunction(JSThread *thread, JSHandle boundFunc, + JSHandle obj) +{ + JSHandle targetFunc(thread, boundFunc->GetBoundTarget()); + if (targetFunc->IsClassConstructor()) { + THROW_TYPE_ERROR_AND_RETURN(thread, "class constructor cannot called without 'new'", + JSTaggedValue::Exception()); + } + + InternalCallParams *arguments = thread->GetInternalCallParams(); + arguments->MakeBoundArgv(thread, boundFunc); + JSHandle newTarget(thread, JSTaggedValue::Undefined()); + return InvokeJsFunction(thread, targetFunc, obj, newTarget, arguments); +} + +JSTaggedValue SlowRuntimeHelper::NewObject(JSThread *thread, JSHandle func, + JSHandle newTarget, uint32_t argc, + const JSTaggedType argv[] // NOLINT(modernize-avoid-c-arrays) +) +{ + if (!func->IsHeapObject()) { + THROW_TYPE_ERROR_AND_RETURN(thread, "function is nullptr", JSTaggedValue::Exception()); + } + + if (!func->IsJSFunction()) { + if (func->IsBoundFunction()) { + JSTaggedValue result = + JSBoundFunction::ConstructInternal(thread, JSHandle::Cast(func), newTarget); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + return result; + } + + if (func->IsJSProxy()) { + JSTaggedValue jsObj = JSProxy::ConstructInternal(thread, JSHandle(func), argc, argv, newTarget); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + return jsObj; + } + THROW_TYPE_ERROR_AND_RETURN(thread, "Constructed NonConstructable", JSTaggedValue::Exception()); + } + + JSHandle jsFunc = JSHandle::Cast(func); + ASSERT(jsFunc->GetCallTarget() != nullptr); + ASSERT(JSFunction::Cast(newTarget->GetTaggedObject())->GetCallTarget() != nullptr); + + if (jsFunc->GetCallTarget()->IsNative()) { + if (jsFunc->IsBuiltinsConstructor()) { + return InvokeJsFunction(thread, jsFunc, JSHandle(thread, JSTaggedValue::Undefined()), + newTarget, thread->GetInternalCallParams()); + } + THROW_TYPE_ERROR_AND_RETURN(thread, "Constructed NonConstructable", JSTaggedValue::Exception()); + } + + JSTaggedValue result = JSFunction::ConstructInternal(thread, jsFunc, argc, argv, newTarget); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + return result; +} + +void SlowRuntimeHelper::SaveFrameToContext(JSThread *thread, JSHandle context) +{ + auto frame = thread->GetCurrentFrame(); + ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); + uint32_t nregs = frame->GetSize(); + JSHandle regsArray = factory->NewTaggedArray(nregs); + for (uint32_t i = 0; i < nregs; i++) { + regsArray->Set(thread, i, VRegAsTaggedValue(frame->GetVReg(i))); + } + auto num_vregs = frame->GetMethod()->GetNumVregs(); + auto func = VRegAsTaggedValue(frame->GetVReg(num_vregs)); + auto acc = VRegAsTaggedValue(frame->GetAcc()); + + context->SetRegsArray(thread, regsArray.GetTaggedValue()); + context->SetMethod(thread, func); + + context->SetAcc(thread, acc); + context->SetNRegs(thread, JSTaggedValue(nregs)); + context->SetBCOffset(thread, JSTaggedValue(frame->GetBytecodeOffset())); + context->SetLexicalEnv(thread, thread->GetCurrentLexenv()); +} + +JSTaggedValue ConstructGeneric(JSThread *thread, JSHandle ctor, JSHandle newTgt, + JSHandle preArgs, uint32_t argsCount, JSTaggedType *stkargs) +{ + if (!ctor->IsConstructor()) { + THROW_TYPE_ERROR_AND_RETURN(thread, "Constructor is false", JSTaggedValue::Exception()); + } + + JSHandle obj(thread, JSTaggedValue::Undefined()); + if (!ctor->IsBuiltinsConstructor() && ctor->IsBase()) { + ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); + obj = JSHandle(factory->NewJSObjectByConstructor(ctor, newTgt)); + } + uint32_t preArgsSize = preArgs->IsUndefined() ? 0 : JSHandle::Cast(preArgs)->GetLength(); + const uint32_t size = preArgsSize + argsCount; + CVector values; + values.reserve(size); + + JSMethod *method = ctor->GetCallTarget(); + if (method == nullptr) { + THROW_TYPE_ERROR_AND_RETURN(thread, "Undefined target", JSTaggedValue::Exception()); + } + + // add default arg func + values.emplace_back(ctor.GetTaggedType()); + + // add default arg newTarget, this + values.emplace_back(newTgt.GetTaggedType()); + values.emplace_back(obj.GetTaggedType()); + + // add preArgs when boundfunction is encountered + if (preArgsSize > 0) { + JSHandle tgaPreArgs = JSHandle::Cast(preArgs); + for (uint32_t i = 0; i < preArgsSize; ++i) { + values.emplace_back(tgaPreArgs->Get(i).GetRawData()); + } + for (array_size_t i = 0; i < argsCount; ++i) { + // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-pointer-arithmetic) + values.emplace_back(stkargs[i]); + } + } else { + for (array_size_t i = 0; i < argsCount; ++i) { + // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-pointer-arithmetic) + values.emplace_back(stkargs[i]); + } + } + + JSTaggedValue resultValue = + EcmaInterpreter::Execute(thread, JSHandle::Cast(ctor), values.size(), values.data()); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + // 9.3.2 [[Construct]] (argumentsList, newTarget) + if (ctor->IsBuiltinsConstructor() || resultValue.IsECMAObject()) { + return resultValue; + } + + if (ctor->IsBase()) { + return obj.GetTaggedValue(); + } + if (!resultValue.IsUndefined()) { + THROW_TYPE_ERROR_AND_RETURN(thread, "function is non-constructor", JSTaggedValue::Exception()); + } + return obj.GetTaggedValue(); +} + +JSTaggedValue ConstructBoundFunction(JSThread *thread, JSHandle ctor, JSHandle newTgt, + JSHandle preArgs, uint32_t argsCount, JSTaggedType *stkargs) +{ + JSHandle target(thread, ctor->GetBoundTarget()); + ASSERT(target->IsConstructor()); + + JSHandle boundArgs(thread, ctor->GetBoundArguments()); + JSMutableHandle newPreArgs(thread, preArgs.GetTaggedValue()); + if (newPreArgs->IsUndefined()) { + newPreArgs.Update(boundArgs.GetTaggedValue()); + } else { + newPreArgs.Update( + TaggedArray::Append(thread, boundArgs, JSHandle::Cast(preArgs)).GetTaggedValue()); + } + JSMutableHandle newTargetMutable(thread, newTgt.GetTaggedValue()); + if (JSTaggedValue::SameValue(ctor.GetTaggedValue(), newTgt.GetTaggedValue())) { + newTargetMutable.Update(target.GetTaggedValue()); + } + return SlowRuntimeHelper::Construct(thread, target, newTargetMutable, newPreArgs, argsCount, stkargs); +} + +JSTaggedValue ConstructProxy(JSThread *thread, JSHandle ctor, JSHandle newTgt, + JSHandle preArgs, uint32_t argsCount, JSTaggedType *stkargs) +{ + // step 1 ~ 4 get ProxyHandler and ProxyTarget + JSHandle handler(thread, ctor->GetHandler()); + if (handler->IsNull()) { + THROW_TYPE_ERROR_AND_RETURN(thread, "Constructor: handler is null", JSTaggedValue::Exception()); + } + ASSERT(handler->IsJSObject()); + JSHandle target(thread, ctor->GetTarget()); + + // 5.Let trap be GetMethod(handler, "construct"). + JSHandle key(thread->GlobalConstants()->GetHandledProxyConstructString()); + JSHandle method = JSObject::GetMethod(thread, handler, key); + + // 6.ReturnIfAbrupt(trap). + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + // 7.If trap is undefined, then + // a.Assert: target has a [[Construct]] internal method. + // b.Return Construct(target, argumentsList, newTarget). + if (method->IsUndefined()) { + ASSERT(target->IsConstructor()); + return SlowRuntimeHelper::Construct(thread, target, newTgt, preArgs, argsCount, stkargs); + } + + // 8.Let argArray be CreateArrayFromList(argumentsList). + uint32_t preArgsSize = preArgs->IsUndefined() ? 0 : JSHandle::Cast(preArgs)->GetLength(); + const uint32_t size = preArgsSize + argsCount; + JSHandle args = thread->GetEcmaVM()->GetFactory()->NewTaggedArray(size); + if (preArgsSize > 0) { + JSHandle tgaPreArgs = JSHandle::Cast(preArgs); + for (uint32_t i = 0; i < preArgsSize; ++i) { + JSTaggedValue value = tgaPreArgs->Get(i); + args->Set(thread, i, value); + } + } + for (array_size_t i = 0; i < argsCount; ++i) { + // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-pointer-arithmetic) + args->Set(thread, i + preArgsSize, JSTaggedValue(stkargs[i])); + } + + // step 8 ~ 9 Call(trap, handler, «target, argArray, newTarget »). + InternalCallParams *arguments = thread->GetInternalCallParams(); + arguments->MakeArgv(target, JSHandle(args), newTgt); + JSTaggedValue newObjValue = + JSFunction::Call(thread, method, handler, 3, arguments->GetArgv()); // 3: «target, argArray, newTarget » + // 10.ReturnIfAbrupt(newObj). + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + // 11.If Type(newObj) is not Object, throw a TypeError exception. + if (!newObjValue.IsECMAObject()) { + THROW_TYPE_ERROR_AND_RETURN(thread, "new object is not object", JSTaggedValue::Exception()); + } + // 12.Return newObj. + return newObjValue; +} + +JSTaggedValue SlowRuntimeHelper::Construct(JSThread *thread, JSHandle ctor, + JSHandle newTarget, JSHandle preArgs, + uint32_t argsCount, JSTaggedType *stkargs) +{ + if (newTarget->IsUndefined()) { + newTarget = ctor; + } + + if (!(newTarget->IsConstructor() && ctor->IsConstructor())) { + THROW_TYPE_ERROR_AND_RETURN(thread, "Constructor is false", JSTaggedValue::Exception()); + } + if (ctor->IsJSFunction()) { + return ConstructGeneric(thread, JSHandle::Cast(ctor), newTarget, preArgs, argsCount, stkargs); + } + if (ctor->IsBoundFunction()) { + return ConstructBoundFunction(thread, JSHandle::Cast(ctor), newTarget, preArgs, argsCount, + stkargs); + } + if (ctor->IsJSProxy()) { + return ConstructProxy(thread, JSHandle::Cast(ctor), newTarget, preArgs, argsCount, stkargs); + } + THROW_TYPE_ERROR_AND_RETURN(thread, "Constructor NonConstructor", JSTaggedValue::Exception()); +} +} // namespace panda::ecmascript diff --git a/runtime/interpreter/slow_runtime_helper.h b/runtime/interpreter/slow_runtime_helper.h new file mode 100644 index 000000000..73dc07d16 --- /dev/null +++ b/runtime/interpreter/slow_runtime_helper.h @@ -0,0 +1,40 @@ +/* + * Copyright (c) 2021-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_INTERPRETER_SLOW_RUNTIME_HELPER_H +#define ECMASCRIPT_INTERPRETER_SLOW_RUNTIME_HELPER_H + +#include "plugins/ecmascript/runtime/js_function.h" +#include "plugins/ecmascript/runtime/js_thread.h" +#include "plugins/ecmascript/runtime/object_factory.h" + +namespace panda::ecmascript { +class SlowRuntimeHelper { +public: + static JSTaggedValue NewObject(JSThread *thread, JSHandle func, JSHandle newTarget, + uint32_t argc, const JSTaggedType argv[] // NOLINT(modernize-avoid-c-arrays) + ); + + static JSTaggedValue CallBoundFunction(JSThread *thread, JSHandle boundFunc, + JSHandle obj); + + static void SaveFrameToContext(JSThread *thread, JSHandle context); + + static JSTaggedValue Construct(JSThread *thread, JSHandle ctor, JSHandle newTarget, + JSHandle preArgs, uint32_t argsCount, JSTaggedType *stkargs); +}; +} // namespace panda::ecmascript + +#endif // ECMASCRIPT_INTERPRETER_SLOW_RUNTIME_HELPER_H diff --git a/runtime/interpreter/slow_runtime_stub.cpp b/runtime/interpreter/slow_runtime_stub.cpp new file mode 100644 index 000000000..a587eda32 --- /dev/null +++ b/runtime/interpreter/slow_runtime_stub.cpp @@ -0,0 +1,2033 @@ +/* + * Copyright (c) 2021-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 "plugins/ecmascript/runtime/interpreter/slow_runtime_stub.h" + +#include "plugins/ecmascript/runtime/base/number_helper.h" +#include "plugins/ecmascript/runtime/builtins/builtins_regexp.h" +#include "plugins/ecmascript/runtime/class_linker/program_object-inl.h" +#include "plugins/ecmascript/runtime/ecma_module.h" +#include "plugins/ecmascript/runtime/global_dictionary-inl.h" +#include "plugins/ecmascript/runtime/ic/profile_type_info.h" +#include "plugins/ecmascript/runtime/internal_call_params.h" +#include "plugins/ecmascript/runtime/interpreter/ecma-interpreter.h" +#include "plugins/ecmascript/runtime/interpreter/fast_runtime_stub-inl.h" +#include "plugins/ecmascript/runtime/interpreter/slow_runtime_helper.h" +#include "plugins/ecmascript/runtime/js_arguments.h" +#include "plugins/ecmascript/runtime/js_array.h" +#include "plugins/ecmascript/runtime/js_array_iterator.h" +#include "plugins/ecmascript/runtime/js_async_from_sync_iterator_object.h" +#include "plugins/ecmascript/runtime/js_async_function.h" +#include "plugins/ecmascript/runtime/js_async_generator_object.h" +#include "plugins/ecmascript/runtime/js_for_in_iterator.h" +#include "plugins/ecmascript/runtime/js_function.h" +#include "plugins/ecmascript/runtime/js_generator_object.h" +#include "plugins/ecmascript/runtime/js_hclass-inl.h" +#include "plugins/ecmascript/runtime/js_invoker.h" +#include "plugins/ecmascript/runtime/js_iterator.h" +#include "plugins/ecmascript/runtime/js_promise.h" +#include "plugins/ecmascript/runtime/js_proxy.h" +#include "plugins/ecmascript/runtime/js_tagged_value-inl.h" +#include "plugins/ecmascript/runtime/js_thread.h" +#include "plugins/ecmascript/runtime/tagged_dictionary.h" +#include "plugins/ecmascript/runtime/runtime_call_id.h" +#include "plugins/ecmascript/runtime/template_string.h" +#include "plugins/ecmascript/runtime/vmstat/runtime_stat.h" + +namespace panda::ecmascript { +JSTaggedValue SlowRuntimeStub::CallSpreadDyn(JSThread *thread, JSTaggedValue func, JSTaggedValue obj, + JSTaggedValue array) +{ + INTERPRETER_TRACE(thread, CallSpreadDyn); + if ((!obj.IsUndefined() && !obj.IsECMAObject()) || !func.IsJSFunction() || !array.IsJSArray()) { + THROW_TYPE_ERROR_AND_RETURN(thread, "cannot Callspread", JSTaggedValue::Exception()); + } + [[maybe_unused]] EcmaHandleScope handleScope(thread); + + JSHandle jsFunc(thread, func); + JSHandle jsArray(thread, array); + JSHandle taggedObj(thread, obj); + + JSHandle coretypesArray(thread, GetCallSpreadArgs(thread, jsArray.GetTaggedValue())); + InternalCallParams *params = thread->GetInternalCallParams(); + params->MakeArgList(*coretypesArray); + JSHandle newTarget(thread, JSTaggedValue::Undefined()); + JSTaggedValue res = InvokeJsFunction(thread, jsFunc, taggedObj, newTarget, params); + + return res; +} + +JSTaggedValue SlowRuntimeStub::NegDyn(JSThread *thread, JSTaggedValue value) +{ + INTERPRETER_TRACE(thread, NegDyn); + [[maybe_unused]] EcmaHandleScope handleScope(thread); + + JSHandle input(thread, value); + JSTaggedNumber number = JSTaggedValue::ToNumber(thread, input); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + + if (number.IsInt()) { + int32_t intValue = number.GetInt(); + if (intValue == 0) { + return JSTaggedValue(-0.0); + } + return JSTaggedValue(-intValue); + } + if (number.IsDouble()) { + return JSTaggedValue(-number.GetDouble()); + } + + UNREACHABLE(); +} + +JSTaggedValue SlowRuntimeStub::AsyncFunctionEnter(JSThread *thread) +{ + INTERPRETER_TRACE(thread, AsyncFunctionEnter); + ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); + [[maybe_unused]] EcmaHandleScope handleScope(thread); + // 1. create promise + JSHandle globalEnv = thread->GetEcmaVM()->GetGlobalEnv(); + JSHandle promiseFunc = globalEnv->GetPromiseFunction(); + + JSHandle promiseObject = + JSHandle::Cast(factory->NewJSObjectByConstructor(JSHandle(promiseFunc), promiseFunc)); + promiseObject->SetPromiseState(thread, JSTaggedValue(static_cast(PromiseStatus::PENDING))); + // 2. create asyncfuncobj + JSHandle asyncFuncObj = factory->NewJSAsyncFuncObject(); + asyncFuncObj->SetPromise(thread, promiseObject); + + JSHandle context = factory->NewGeneratorContext(); + context->SetGeneratorObject(thread, asyncFuncObj); + + // change state to EXECUTING + asyncFuncObj->SetState(thread, JSGeneratorState::EXECUTING); + asyncFuncObj->SetGeneratorContext(thread, context); + + // 3. return asyncfuncobj + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + return asyncFuncObj.GetTaggedValue(); +} + +JSTaggedValue SlowRuntimeStub::ToNumber(JSThread *thread, JSTaggedValue value) +{ + INTERPRETER_TRACE(thread, Tonumber); + [[maybe_unused]] EcmaHandleScope handleScope(thread); + + JSHandle number(thread, value); + // may return exception + return JSTaggedValue::ToNumber(thread, number); +} + +JSTaggedValue SlowRuntimeStub::NotDyn(JSThread *thread, JSTaggedValue value) +{ + INTERPRETER_TRACE(thread, NotDyn); + [[maybe_unused]] EcmaHandleScope handleScope(thread); + JSHandle valueHandle(thread, value); + int32_t number = JSTaggedValue::ToInt32(thread, valueHandle); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + return JSTaggedValue(~number); // NOLINT(hicpp-signed-bitwise) +} + +JSTaggedValue SlowRuntimeStub::IncDyn(JSThread *thread, JSTaggedValue value) +{ + INTERPRETER_TRACE(thread, IncDyn); + [[maybe_unused]] EcmaHandleScope handleScope(thread); + + JSHandle valueHandle(thread, value); + JSTaggedNumber number = JSTaggedValue::ToNumber(thread, valueHandle); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + return (++number); +} + +JSTaggedValue SlowRuntimeStub::DecDyn(JSThread *thread, JSTaggedValue value) +{ + INTERPRETER_TRACE(thread, DecDyn); + [[maybe_unused]] EcmaHandleScope handleScope(thread); + + JSHandle valueHandle(thread, value); + JSTaggedNumber number = JSTaggedValue::ToNumber(thread, valueHandle); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + return (--number); +} + +void SlowRuntimeStub::ThrowDyn(JSThread *thread, JSTaggedValue value) +{ + INTERPRETER_TRACE(thread, ThrowDyn); + + [[maybe_unused]] EcmaHandleScope handleScope(thread); + + ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); + JSHandle obj(thread, value); + JSHandle wrapperObject = factory->NewObjectWrapper(obj); + + thread->SetException(wrapperObject.GetTaggedValue()); +} + +JSTaggedValue SlowRuntimeStub::GetPropIterator(JSThread *thread, JSTaggedValue value) +{ + INTERPRETER_TRACE(thread, GetPropIterator); + [[maybe_unused]] EcmaHandleScope handleScope(thread); + + JSHandle objHandle(thread, value); + JSHandle iteratorHandle = JSObject::EnumerateObjectProperties(thread, objHandle); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + iteratorHandle->SetFastRemainingIndex(JSTaggedNumber(0)); + return iteratorHandle.GetTaggedValue(); +} + +void SlowRuntimeStub::ThrowConstAssignment(JSThread *thread, JSTaggedValue value) +{ + INTERPRETER_TRACE(thread, ThrowConstAssignment); + [[maybe_unused]] EcmaHandleScope handleScope(thread); + + ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); + + JSHandle name(thread, value.GetTaggedObject()); + JSHandle info = factory->NewFromCanBeCompressString("Assignment to const variable "); + + JSHandle msg = factory->ConcatFromString(info, name); + THROW_NEW_ERROR_AND_RETURN(thread, factory->NewJSError(base::ErrorType::TYPE_ERROR, msg).GetTaggedValue()); +} + +JSTaggedValue SlowRuntimeStub::Add2Dyn(JSThread *thread, EcmaVM *ecma_vm, JSTaggedValue left, JSTaggedValue right) +{ + INTERPRETER_TRACE(thread, Add2Dyn); + [[maybe_unused]] EcmaHandleScope handleScope(thread); + + JSHandle leftValue(thread, left); + JSHandle rightValue(thread, right); + if (leftValue->IsString() && rightValue->IsString()) { + EcmaString *newString = + EcmaString::Concat(JSHandle(leftValue), JSHandle(rightValue), ecma_vm); + return JSTaggedValue(newString); + } + JSHandle primitiveA0(thread, JSTaggedValue::ToPrimitive(thread, leftValue)); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + JSHandle primitiveA1(thread, JSTaggedValue::ToPrimitive(thread, rightValue)); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + // contain string + if (primitiveA0->IsString() || primitiveA1->IsString()) { + JSHandle stringA0 = JSTaggedValue::ToString(thread, primitiveA0); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + JSHandle stringA1 = JSTaggedValue::ToString(thread, primitiveA1); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + EcmaString *newString = EcmaString::Concat(stringA0, stringA1, ecma_vm); + return JSTaggedValue(newString); + } + JSTaggedNumber taggedValueA0 = JSTaggedValue::ToNumber(thread, primitiveA0); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + JSTaggedNumber taggedValueA1 = JSTaggedValue::ToNumber(thread, primitiveA1); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + double a0Double = taggedValueA0.GetNumber(); + double a1Double = taggedValueA1.GetNumber(); + return JSTaggedValue(a0Double + a1Double); +} + +JSTaggedValue SlowRuntimeStub::Sub2Dyn(JSThread *thread, JSTaggedValue left, JSTaggedValue right) +{ + INTERPRETER_TRACE(thread, Sub2Dyn); + [[maybe_unused]] EcmaHandleScope handleScope(thread); + + JSHandle leftHandle(thread, left); + JSHandle rightHandle(thread, right); + + JSTaggedNumber number0 = JSTaggedValue::ToNumber(thread, leftHandle); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + JSTaggedNumber number1 = JSTaggedValue::ToNumber(thread, rightHandle); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + return number0 - number1; +} + +JSTaggedValue SlowRuntimeStub::Mul2Dyn(JSThread *thread, JSTaggedValue left, JSTaggedValue right) +{ + INTERPRETER_TRACE(thread, Mul2Dyn); + [[maybe_unused]] EcmaHandleScope handleScope(thread); + + JSHandle leftValue(thread, left); + JSHandle rightValue(thread, right); + + // 6. Let lnum be ToNumber(leftValue). + JSTaggedNumber primitiveA = JSTaggedValue::ToNumber(thread, leftValue); + // 7. ReturnIfAbrupt(lnum). + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + // 8. Let rnum be ToNumber(rightValue). + JSTaggedNumber primitiveB = JSTaggedValue::ToNumber(thread, rightValue); + // 9. ReturnIfAbrupt(rnum). + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + + // 12.6.3.1 Applying the * Operator + return primitiveA * primitiveB; +} + +JSTaggedValue SlowRuntimeStub::Div2Dyn(JSThread *thread, JSTaggedValue left, JSTaggedValue right) +{ + INTERPRETER_TRACE(thread, Div2Dyn); + [[maybe_unused]] EcmaHandleScope handleScope(thread); + + JSHandle leftHandle(thread, left); + JSHandle rightHandle(thread, right); + + JSTaggedNumber leftNumber = JSTaggedValue::ToNumber(thread, leftHandle); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + double dLeft = leftNumber.GetNumber(); + JSTaggedNumber rightNumber = JSTaggedValue::ToNumber(thread, rightHandle); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + double dRight = rightNumber.GetNumber(); + if (dRight == 0) { + if (dLeft == 0 || std::isnan(dLeft)) { + return JSTaggedValue(base::NAN_VALUE); + } + bool positive = (((bit_cast(dRight)) & base::DOUBLE_SIGN_MASK) == + ((bit_cast(dLeft)) & base::DOUBLE_SIGN_MASK)); + return JSTaggedValue(positive ? base::POSITIVE_INFINITY : -base::POSITIVE_INFINITY); + } + return JSTaggedValue(dLeft / dRight); +} + +JSTaggedValue SlowRuntimeStub::Mod2Dyn(JSThread *thread, JSTaggedValue left, JSTaggedValue right) +{ + INTERPRETER_TRACE(thread, Mod2Dyn); + [[maybe_unused]] EcmaHandleScope handleScope(thread); + + JSHandle leftHandle(thread, left); + JSHandle rightHandle(thread, right); + + JSTaggedNumber leftNumber = JSTaggedValue::ToNumber(thread, leftHandle); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + double dLeft = leftNumber.GetNumber(); + JSTaggedNumber rightNumber = JSTaggedValue::ToNumber(thread, rightHandle); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + double dRight = rightNumber.GetNumber(); + // 12.6.3.3 Applying the % Operator + if ((dRight == 0.0) || std::isnan(dRight) || std::isnan(dLeft) || std::isinf(dLeft)) { + return JSTaggedValue(base::NAN_VALUE); + } + if ((dLeft == 0.0) || std::isinf(dRight)) { + return JSTaggedValue(dLeft); + } + + return JSTaggedValue(std::fmod(dLeft, dRight)); +} + +JSTaggedValue SlowRuntimeStub::EqDyn(JSThread *thread, JSTaggedValue left, JSTaggedValue right) +{ + INTERPRETER_TRACE(thread, EqDyn); + [[maybe_unused]] EcmaHandleScope handleScope(thread); + + JSHandle leftValue(thread, left); + JSHandle rightValue(thread, right); + bool ret = JSTaggedValue::Equal(thread, leftValue, rightValue); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + return (ret ? JSTaggedValue::True() : JSTaggedValue::False()); +} + +JSTaggedValue SlowRuntimeStub::NotEqDyn(JSThread *thread, JSTaggedValue left, JSTaggedValue right) +{ + INTERPRETER_TRACE(thread, NotEqDyn); + [[maybe_unused]] EcmaHandleScope handleScope(thread); + + JSHandle leftValue(thread, left); + JSHandle rightValue(thread, right); + bool ret = JSTaggedValue::Equal(thread, leftValue, rightValue); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + return (ret ? JSTaggedValue::False() : JSTaggedValue::True()); +} + +JSTaggedValue SlowRuntimeStub::LessDyn(JSThread *thread, JSTaggedValue left, JSTaggedValue right) +{ + INTERPRETER_TRACE(thread, LessDyn); + [[maybe_unused]] EcmaHandleScope handleScope(thread); + + JSHandle leftValue(thread, left); + JSHandle rightValue(thread, right); + bool ret = JSTaggedValue::Compare(thread, leftValue, rightValue) == ComparisonResult::LESS; + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + return (ret ? JSTaggedValue::True() : JSTaggedValue::False()); +} + +JSTaggedValue SlowRuntimeStub::LessEqDyn(JSThread *thread, JSTaggedValue left, JSTaggedValue right) +{ + INTERPRETER_TRACE(thread, LessEqDyn); + [[maybe_unused]] EcmaHandleScope handleScope(thread); + + JSHandle leftValue(thread, left); + JSHandle rightValue(thread, right); + bool ret = JSTaggedValue::Compare(thread, leftValue, rightValue) <= ComparisonResult::EQUAL; + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + return (ret ? JSTaggedValue::True() : JSTaggedValue::False()); +} + +JSTaggedValue SlowRuntimeStub::GreaterDyn(JSThread *thread, JSTaggedValue left, JSTaggedValue right) +{ + INTERPRETER_TRACE(thread, GreaterDyn); + [[maybe_unused]] EcmaHandleScope handleScope(thread); + + JSHandle leftValue(thread, left); + JSHandle rightValue(thread, right); + bool ret = JSTaggedValue::Compare(thread, leftValue, rightValue) == ComparisonResult::GREAT; + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + return (ret ? JSTaggedValue::True() : JSTaggedValue::False()); +} + +JSTaggedValue SlowRuntimeStub::GreaterEqDyn(JSThread *thread, JSTaggedValue left, JSTaggedValue right) +{ + INTERPRETER_TRACE(thread, GreaterEqDyn); + [[maybe_unused]] EcmaHandleScope handleScope(thread); + + JSHandle leftValue(thread, left); + JSHandle rightValue(thread, right); + ComparisonResult comparison = JSTaggedValue::Compare(thread, leftValue, rightValue); + bool ret = (comparison == ComparisonResult::GREAT) || (comparison == ComparisonResult::EQUAL); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + return (ret ? JSTaggedValue::True() : JSTaggedValue::False()); +} + +JSTaggedValue SlowRuntimeStub::ToJSTaggedValueWithInt32(JSThread *thread, JSTaggedValue value) +{ + INTERPRETER_TRACE(thread, ToJSTaggedValueWithInt32); + [[maybe_unused]] EcmaHandleScope handleScope(thread); + JSHandle valueHandle(thread, value); + int32_t res = JSTaggedValue::ToInt32(thread, valueHandle); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + return JSTaggedValue(res); +} + +JSTaggedValue SlowRuntimeStub::ToJSTaggedValueWithUint32(JSThread *thread, JSTaggedValue value) +{ + INTERPRETER_TRACE(thread, ToJSTaggedValueWithUint32); + [[maybe_unused]] EcmaHandleScope handleScope(thread); + JSHandle valueHandle(thread, value); + int32_t res = JSTaggedValue::ToUint32(thread, valueHandle); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + return JSTaggedValue(res); +} + +JSTaggedValue SlowRuntimeStub::DelObjProp(JSThread *thread, JSTaggedValue obj, JSTaggedValue prop) +{ + INTERPRETER_TRACE(thread, Delobjprop); + [[maybe_unused]] EcmaHandleScope handleScope(thread); + + JSHandle objHandle(thread, obj); + JSHandle propHandle(thread, prop); + JSHandle jsObj(JSTaggedValue::ToObject(thread, objHandle)); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + JSHandle propKey = JSTaggedValue::ToPropertyKey(thread, propHandle); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + bool ret = JSTaggedValue::DeletePropertyOrThrow(thread, jsObj, propKey); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + return JSTaggedValue(ret); +} + +JSTaggedValue SlowRuntimeStub::NewObjDynRange(JSThread *thread, uint16_t argsCount, JSTaggedType *stkargs) +{ + INTERPRETER_TRACE(thread, NewobjDynrange); + [[maybe_unused]] EcmaHandleScope handleScope(thread); + + // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-pointer-arithmetic) + JSHandle funcHandle(thread, JSTaggedValue(stkargs[0])); // FUNC_INDEX + // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-pointer-arithmetic) + JSHandle newTargetHandle(thread, JSTaggedValue(stkargs[1])); // NEW_TARGET_INDEX + + argsCount -= 2; + // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-pointer-arithmetic) + stkargs = &stkargs[2]; + + JSHandle preArgs(thread, JSTaggedValue::Undefined()); + auto tagged = SlowRuntimeHelper::Construct(thread, funcHandle, newTargetHandle, preArgs, argsCount, stkargs); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + return tagged; +} + +JSTaggedValue SlowRuntimeStub::CreateObjectWithExcludedKeys(JSThread *thread, uint16_t numKeys, JSTaggedValue objVal, + JSTaggedType *stkargs) +{ + INTERPRETER_TRACE(thread, CreateObjectWithExcludedKeys); + [[maybe_unused]] EcmaHandleScope handleScope(thread); + JSHandle obj_tval(thread, objVal); + ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); + + uint32_t numExcludedKeys = 0; + JSHandle excludedKeys = factory->NewTaggedArray(numKeys + 1); + // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-pointer-arithmetic) + auto excludedKey = JSTaggedValue(stkargs[0]); + if (!excludedKey.IsUndefined()) { + numExcludedKeys = numKeys + 1; + excludedKeys->Set(thread, 0, excludedKey); + for (array_size_t i = 1; i < numExcludedKeys; i++) { + // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-pointer-arithmetic) + excludedKey = JSTaggedValue(stkargs[i]); + excludedKeys->Set(thread, i, excludedKey); + } + } + + JSHandle restObj = factory->NewEmptyJSObject(); + JSHandle allKeys; + + if (obj_tval->IsJSProxy()) { + allKeys = JSProxy::OwnPropertyKeys(thread, JSHandle(obj_tval)); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + } else { + ASSERT(obj_tval->IsJSObject()); + JSHandle obj(obj_tval); + uint32_t numAllKeys = obj->GetNumberOfKeys(); + allKeys = factory->NewTaggedArray(numAllKeys); + JSObject::GetAllKeys(thread, obj, 0, allKeys); + } + + JSMutableHandle key(thread, JSTaggedValue::Undefined()); + + for (uint32_t i = 0; i < allKeys->GetLength(); i++) { + key.Update(allKeys->Get(i)); + bool isExcludedKey = false; + for (uint32_t j = 0; j < numExcludedKeys; j++) { + if (JSTaggedValue::Equal(thread, key, JSHandle(thread, excludedKeys->Get(j)))) { + isExcludedKey = true; + break; + } + } + if (!isExcludedKey) { + PropertyDescriptor desc(thread); + bool success = JSTaggedValue::GetOwnProperty(thread, obj_tval, key, desc); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + if (success && desc.IsEnumerable()) { + JSHandle value = + JSObject::GetProperty(thread, JSHandle(obj_tval), key).GetValue(); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + JSObject::SetProperty(thread, restObj, key, value, true); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + } + } + } + return restObj.GetTaggedValue(); +} + +JSTaggedValue SlowRuntimeStub::ExpDyn(JSThread *thread, JSTaggedValue base, JSTaggedValue exponent) +{ + INTERPRETER_TRACE(thread, ExpDyn); + [[maybe_unused]] EcmaHandleScope handleScope(thread); + + JSTaggedNumber baseNumber = JSTaggedValue::ToNumber(thread, JSHandle(thread, base)); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + double doubleBase = baseNumber.GetNumber(); + JSTaggedNumber exponentNumber = JSTaggedValue::ToNumber(thread, JSHandle(thread, exponent)); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + double doubleExponent = exponentNumber.GetNumber(); + + return base::NumberHelper::Pow(doubleBase, doubleExponent); +} + +JSTaggedValue SlowRuntimeStub::IsInDyn(JSThread *thread, JSTaggedValue prop, JSTaggedValue obj) +{ + INTERPRETER_TRACE(thread, IsInDyn); + [[maybe_unused]] EcmaHandleScope handleScope(thread); + + JSHandle propHandle(thread, prop); + JSHandle objHandle(thread, obj); + if (!objHandle->IsECMAObject()) { + return ThrowTypeError(thread, "Cannot use 'in' operator in Non-Object"); + } + JSHandle propKey = JSTaggedValue::ToPropertyKey(thread, propHandle); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + bool ret = JSTaggedValue::HasProperty(thread, objHandle, propKey); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + return JSTaggedValue(ret); +} + +JSTaggedValue SlowRuntimeStub::InstanceofDyn(JSThread *thread, JSTaggedValue obj, JSTaggedValue target) +{ + INTERPRETER_TRACE(thread, InstanceofDyn); + [[maybe_unused]] EcmaHandleScope handleScope(thread); + + JSHandle objHandle(thread, obj); + JSHandle targetHandle(thread, target); + bool ret = JSObject::InstanceOf(thread, objHandle, targetHandle); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + return JSTaggedValue(ret); +} + +JSTaggedValue SlowRuntimeStub::NewLexicalEnvDyn(JSThread *thread, uint16_t numVars) +{ + INTERPRETER_TRACE(thread, NewlexenvDyn); + [[maybe_unused]] EcmaHandleScope handleScope(thread); + + ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); + JSHandle newEnv = factory->NewLexicalEnv(numVars); + + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + return newEnv.GetTaggedValue(); +} + +JSTaggedValue SlowRuntimeStub::CreateIterResultObj(JSThread *thread, JSTaggedValue value, bool done) +{ + INTERPRETER_TRACE(thread, CreateIterResultObj); + [[maybe_unused]] EcmaHandleScope handleScope(thread); + + JSHandle valueHandle(thread, value); + JSHandle iter = JSIterator::CreateIterResultObject(thread, valueHandle, done); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + return iter.GetTaggedValue(); +} + +JSTaggedValue SlowRuntimeStub::CreateGeneratorObj(JSThread *thread, JSTaggedValue genFunc) +{ + INTERPRETER_TRACE(thread, CreateGeneratorObj); + [[maybe_unused]] EcmaHandleScope handleScope(thread); + + ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); + JSHandle generatorFunction(thread, genFunc); + JSHandle obj = factory->NewJSGeneratorObject(generatorFunction); + JSHandle context = factory->NewGeneratorContext(); + context->SetGeneratorObject(thread, obj.GetTaggedValue()); + + // change state to SUSPENDED_START + obj->SetState(thread, JSGeneratorState::SUSPENDED_START); + obj->SetGeneratorContext(thread, context); + + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + return obj.GetTaggedValue(); +} + +JSTaggedValue SlowRuntimeStub::SetGeneratorState(JSThread *thread, JSTaggedValue genObj, uint8_t state) +{ + [[maybe_unused]] EcmaHandleScope handleScope(thread); + + JSHandle generatorObjectHandle(thread, genObj); + generatorObjectHandle->SetState(thread, static_cast(state)); + + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + return JSTaggedValue::Undefined(); +} + +JSTaggedValue SlowRuntimeStub::CreateAsyncGeneratorObj(JSThread *thread, JSTaggedValue genFunc) +{ + [[maybe_unused]] EcmaHandleScope handleScope(thread); + + ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); + JSHandle generatorFunction(thread, genFunc); + JSHandle obj = factory->NewJSAsyncGeneratorObject(generatorFunction); + JSHandle context = factory->NewGeneratorContext(); + JSHandle asyncQueue = factory->NewTaggedQueue(0); + context->SetGeneratorObject(thread, obj.GetTaggedValue()); + + // change state to SUSPENDED_START + obj->SetState(thread, JSGeneratorState::SUSPENDED_START); + obj->SetGeneratorContext(thread, context); + obj->SetAsyncGeneratorQueue(thread, asyncQueue); + + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + return obj.GetTaggedValue(); +} + +JSTaggedValue SlowRuntimeStub::SuspendGenerator(JSThread *thread, JSTaggedValue genObj, JSTaggedValue value) +{ + INTERPRETER_TRACE(thread, SuspendGenerator); + [[maybe_unused]] EcmaHandleScope handleScope(thread); + + JSHandle generatorObjectHandle(thread, genObj); + JSHandle genContextHandle(thread, generatorObjectHandle->GetGeneratorContext()); + JSHandle valueHandle(thread, value); + // save stack, should copy cur_frame, function execute over will free cur_frame + SlowRuntimeHelper::SaveFrameToContext(thread, genContextHandle); + + return valueHandle.GetTaggedValue(); +} + +JSTaggedValue SlowRuntimeStub::SuspendAsyncGenerator(JSThread *thread, JSTaggedValue asyncGenObj, JSTaggedValue value) +{ + [[maybe_unused]] EcmaHandleScope handleScope(thread); + + JSHandle asyncGeneratorObjectHandle(thread, asyncGenObj); + JSHandle asyncGeneratorHandle(thread, asyncGenObj); + JSHandle genContextHandle(thread, asyncGeneratorObjectHandle->GetGeneratorContext()); + JSHandle valueHandle(thread, value); + // save stack, should copy cur_frame, function execute over will free cur_frame + SlowRuntimeHelper::SaveFrameToContext(thread, genContextHandle); + + return JSAsyncGeneratorObject::AsyncGeneratorResolve(thread, asyncGeneratorHandle, valueHandle, false) + .GetTaggedValue(); +} + +JSTaggedValue SlowRuntimeStub::AsyncFunctionAwait(JSThread *thread, JSTaggedValue asyncFuncObj, JSTaggedValue value) +{ + INTERPRETER_TRACE(thread, AsyncFunctionAwait); + [[maybe_unused]] EcmaHandleScope handleScope(thread); + + JSHandle asyncFuncObjHandle(thread, asyncFuncObj); + JSHandle valueHandle(thread, value); + JSAsyncFuncObject::AsyncFunctionAwait(thread, asyncFuncObjHandle, valueHandle); + JSHandle promise(thread, asyncFuncObjHandle->GetPromise()); + + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + return promise.GetTaggedValue(); +} + +JSTaggedValue SlowRuntimeStub::AsyncFunctionResolveOrReject(JSThread *thread, JSTaggedValue asyncFuncObj, + JSTaggedValue value, bool is_resolve) +{ + if (is_resolve) { + INTERPRETER_TRACE(thread, AsyncFunctionResolve); + } else { + INTERPRETER_TRACE(thread, AsyncFunctionReject); + } + + [[maybe_unused]] EcmaHandleScope handleScope(thread); + + JSHandle asyncFuncObjHandle(thread, asyncFuncObj); + JSHandle promise(thread, asyncFuncObjHandle->GetPromise()); + JSHandle valueHandle(thread, value); + + // ActivePromise + JSHandle reactions = JSPromise::CreateResolvingFunctions(thread, promise); + const GlobalEnvConstants *globalConst = thread->GlobalConstants(); + JSHandle thisArg = globalConst->GetHandledUndefined(); + JSHandle activeFunc; + if (is_resolve) { + activeFunc = JSHandle(thread, reactions->GetResolveFunction()); + } else { + activeFunc = JSHandle(thread, reactions->GetRejectFunction()); + } + InternalCallParams *arguments = thread->GetInternalCallParams(); + arguments->MakeArgv(valueHandle); + [[maybe_unused]] JSTaggedValue res = JSFunction::Call(thread, activeFunc, thisArg, 1, arguments->GetArgv()); + + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + return promise.GetTaggedValue(); +} + +// 27.6.3.2 AsyncGeneratorStart (generator, generatorBody) +JSTaggedValue SlowRuntimeStub::AsyncGeneratorResolve(JSThread *thread, JSTaggedValue asyncGenObj, JSTaggedValue value) +{ + [[maybe_unused]] EcmaHandleScope handleScope(thread); + + JSHandle asyncGeneratorHandle(thread, asyncGenObj); + JSHandle valueHandle(thread, value); + + // e. Set generator.[[AsyncGeneratorState]] to completed. + JSAsyncGeneratorObject::Cast(asyncGenObj.GetHeapObject())->SetState(thread, JSGeneratorState::COMPLETED); + + // f. If result is a normal completion, let resultValue be undefined. + // i. Let resultValue be result.[[Value]]. + // h. Return ! AsyncGeneratorResolve(generator, resultValue, true). + return JSAsyncGeneratorObject::AsyncGeneratorResolve(thread, asyncGeneratorHandle, valueHandle, true) + .GetTaggedValue(); +} + +// 27.6.3.2 AsyncGeneratorStart (generator, generatorBody) +JSTaggedValue SlowRuntimeStub::AsyncGeneratorReject(JSThread *thread, JSTaggedValue asyncGenObj, JSTaggedValue value) +{ + [[maybe_unused]] EcmaHandleScope handleScope(thread); + + JSHandle asyncGeneratorHandle(thread, asyncGenObj); + JSHandle valueHandle(thread, value); + + // e. Set generator.[[AsyncGeneratorState]] to completed. + JSAsyncGeneratorObject::Cast(asyncGenObj.GetHeapObject())->SetState(thread, JSGeneratorState::COMPLETED); + + // ii. ii. If result.[[Type]] is not return, then + // 1. Return ! AsyncGeneratorReject(generator, resultValue). + // 1. 1. Return ! AsyncGeneratorReject(generator, resultValue). + return JSAsyncGeneratorObject::AsyncGeneratorReject(thread, asyncGeneratorHandle, valueHandle).GetTaggedValue(); +} + +JSTaggedValue SlowRuntimeStub::NewObjSpreadDyn(JSThread *thread, JSTaggedValue func, JSTaggedValue newTarget, + JSTaggedValue array) +{ + INTERPRETER_TRACE(thread, NewobjspreadDyn); + [[maybe_unused]] EcmaHandleScope handleScope(thread); + + ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); + JSHandle funcHandle(thread, func); + JSHandle newTargetHandle(thread, newTarget); + JSHandle jsArray(thread, array); + if (!jsArray->IsJSArray()) { + return ThrowTypeError(thread, "Cannot Newobjspread"); + } + + uint32_t length = JSHandle::Cast(jsArray)->GetArrayLength(); + JSHandle argsArray = factory->NewTaggedArray(length); + for (uint32_t i = 0; i < length; ++i) { + auto prop = JSTaggedValue::GetProperty(thread, jsArray, i).GetValue(); + argsArray->Set(thread, i, prop); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + } + InternalCallParams *arguments = thread->GetInternalCallParams(); + arguments->MakeArgList(*argsArray); + auto tagged = SlowRuntimeHelper::NewObject(thread, funcHandle, newTargetHandle, length, arguments->GetArgv()); + return tagged; +} + +void SlowRuntimeStub::ThrowUndefinedIfHole(JSThread *thread, JSTaggedValue obj) +{ + INTERPRETER_TRACE(thread, ThrowUndefinedIfHole); + [[maybe_unused]] EcmaHandleScope handleScope(thread); + + ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); + JSHandle name(thread, obj); + JSHandle info = factory->NewFromCanBeCompressString(" is not initialized"); + + JSHandle msg = factory->ConcatFromString(name, info); + THROW_NEW_ERROR_AND_RETURN(thread, factory->NewJSError(base::ErrorType::REFERENCE_ERROR, msg).GetTaggedValue()); +} + +JSTaggedValue SlowRuntimeStub::ThrowIfSuperNotCorrectCall(JSThread *thread, uint16_t index, JSTaggedValue thisValue) +{ + INTERPRETER_TRACE(thread, ThrowIfSuperNotCorrectCall); + [[maybe_unused]] EcmaHandleScope handleScope(thread); + + if (index == 0 && (thisValue.IsUndefined() || thisValue.IsHole())) { + return ThrowReferenceError(thread, JSTaggedValue::Undefined(), "sub-class must call super before use 'this'"); + } + if (index == 1 && !thisValue.IsUndefined() && !thisValue.IsHole()) { + return ThrowReferenceError(thread, JSTaggedValue::Undefined(), "super() forbidden re-bind 'this'"); + } + return JSTaggedValue::True(); +} + +void SlowRuntimeStub::ThrowIfNotObject(JSThread *thread) +{ + INTERPRETER_TRACE(thread, ThrowIfNotObject); + [[maybe_unused]] EcmaHandleScope handleScope(thread); + + THROW_TYPE_ERROR(thread, "Inner return result is not object"); +} + +void SlowRuntimeStub::ThrowThrowNotExists(JSThread *thread) +{ + INTERPRETER_TRACE(thread, ThrowThrowNotExists); + [[maybe_unused]] EcmaHandleScope handleScope(thread); + + THROW_TYPE_ERROR(thread, "Throw method is not defined"); +} + +void SlowRuntimeStub::ThrowPatternNonCoercible(JSThread *thread) +{ + INTERPRETER_TRACE(thread, ThrowPatternNonCoercible); + [[maybe_unused]] EcmaHandleScope handleScope(thread); + + JSHandle msg(thread->GlobalConstants()->GetHandledObjNotCoercibleString()); + ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); + THROW_NEW_ERROR_AND_RETURN(thread, factory->NewJSError(base::ErrorType::TYPE_ERROR, msg).GetTaggedValue()); +} + +JSTaggedValue SlowRuntimeStub::StOwnByName(JSThread *thread, JSTaggedValue obj, JSTaggedValue prop, JSTaggedValue value) +{ + INTERPRETER_TRACE(thread, StOwnByNameDyn); + [[maybe_unused]] EcmaHandleScope handleScope(thread); + + const GlobalEnvConstants *globalConst = thread->GlobalConstants(); + JSHandle objHandle(thread, obj); + JSHandle propHandle(thread, prop); + JSHandle valueHandle(thread, value); + ASSERT(propHandle->IsStringOrSymbol()); + + if (objHandle->IsClassConstructor() && + JSTaggedValue::SameValue(propHandle, globalConst->GetHandledPrototypeString())) { + return ThrowTypeError(thread, "In a class, static property named 'prototype' throw a TypeError"); + } + + // property in class is non-enumerable + bool enumerable = !(objHandle->IsClassPrototype() || objHandle->IsClassConstructor()); + + PropertyDescriptor desc(thread, valueHandle, true, enumerable, true); + bool ret = JSTaggedValue::DefineOwnProperty(thread, objHandle, propHandle, desc); + if (!ret) { + return ThrowTypeError(thread, "SetOwnByName failed"); + } + return JSTaggedValue::True(); +} + +JSTaggedValue SlowRuntimeStub::StOwnByNameWithNameSet(JSThread *thread, JSTaggedValue obj, JSTaggedValue prop, + JSTaggedValue value) +{ + INTERPRETER_TRACE(thread, StOwnByNameDyn); + [[maybe_unused]] EcmaHandleScope handleScope(thread); + + JSHandle objHandle(thread, obj); + JSHandle propHandle(thread, prop); + JSHandle valueHandle(thread, value); + ASSERT(propHandle->IsStringOrSymbol()); + + JSHandle propKey = JSTaggedValue::ToPropertyKey(thread, propHandle); + + // property in class is non-enumerable + bool enumerable = !(objHandle->IsClassPrototype() || objHandle->IsClassConstructor()); + + PropertyDescriptor desc(thread, valueHandle, true, enumerable, true); + bool ret = JSTaggedValue::DefineOwnProperty(thread, objHandle, propHandle, desc); + if (!ret) { + return ThrowTypeError(thread, "SetOwnByNameWithNameSet failed"); + } + JSFunctionBase::SetFunctionName(thread, JSHandle::Cast(valueHandle), propKey, + JSHandle(thread, JSTaggedValue::Undefined())); + return JSTaggedValue::True(); +} + +JSTaggedValue SlowRuntimeStub::StOwnByIndex(JSThread *thread, JSTaggedValue obj, uint32_t idx, JSTaggedValue value) +{ + INTERPRETER_TRACE(thread, StOwnByIdDyn); + [[maybe_unused]] EcmaHandleScope handleScope(thread); + + JSHandle objHandle(thread, obj); + JSHandle idxHandle(thread, JSTaggedValue(idx)); + JSHandle valueHandle(thread, value); + + // property in class is non-enumerable + bool enumerable = !(objHandle->IsClassPrototype() || objHandle->IsClassConstructor()); + + PropertyDescriptor desc(thread, valueHandle, true, enumerable, true); + bool ret = JSTaggedValue::DefineOwnProperty(thread, objHandle, idxHandle, desc); + if (!ret) { + return ThrowTypeError(thread, "SetOwnByIndex failed"); + } + return JSTaggedValue::True(); +} + +JSTaggedValue SlowRuntimeStub::StOwnByValue(JSThread *thread, const JSHandle &obj, + const JSHandle &key, const JSHandle &value) +{ + [[maybe_unused]] EcmaHandleScope handleScope(thread); + + INTERPRETER_TRACE(thread, StOwnByValueDyn); + const GlobalEnvConstants *globalConst = thread->GlobalConstants(); + + if (obj->IsClassConstructor() && JSTaggedValue::SameValue(key, globalConst->GetHandledPrototypeString())) { + return ThrowTypeError(thread, "In a class, static property named 'prototype' throw a TypeError"); + } + + // propery in class is non-enumerable + bool enumerable = !(obj->IsClassPrototype() || obj->IsClassConstructor()); + + PropertyDescriptor desc(thread, value, true, enumerable, true); + JSMutableHandle propKey(JSTaggedValue::ToPropertyKey(thread, key)); + bool ret = JSTaggedValue::DefineOwnProperty(thread, obj, propKey, desc); + if (!ret) { + return ThrowTypeError(thread, "StOwnByValue failed"); + } + if (value->IsJSFunction()) { + if (propKey->IsNumber()) { + propKey.Update(base::NumberHelper::NumberToString(thread, propKey.GetTaggedValue()).GetTaggedValue()); + } + JSFunctionBase::SetFunctionName(thread, JSHandle::Cast(value), propKey, + JSHandle(thread, JSTaggedValue::Undefined())); + } + return JSTaggedValue::True(); +} + +JSTaggedValue SlowRuntimeStub::StOwnByValueWithNameSet(JSThread *thread, JSHandle obj, + JSHandle key, JSHandle value) +{ + [[maybe_unused]] EcmaHandleScope handleScope(thread); + INTERPRETER_TRACE(thread, StOwnByValueDyn); + const GlobalEnvConstants *globalConst = thread->GlobalConstants(); + + if (obj->IsClassConstructor() && JSTaggedValue::SameValue(key, globalConst->GetHandledPrototypeString())) { + return ThrowTypeError(thread, "In a class, static property named 'prototype' throw a TypeError"); + } + + // property in class is non-enumerable + bool enumerable = !(obj->IsClassPrototype() || obj->IsClassConstructor()); + + PropertyDescriptor desc(thread, value, true, enumerable, true); + JSMutableHandle propKey(JSTaggedValue::ToPropertyKey(thread, key)); + bool ret = JSTaggedValue::DefineOwnProperty(thread, obj, propKey, desc); + if (!ret) { + return ThrowTypeError(thread, "StOwnByValueWithNameSet failed"); + } + if (value->IsJSFunction()) { + if (propKey->IsNumber()) { + propKey.Update(base::NumberHelper::NumberToString(thread, propKey.GetTaggedValue()).GetTaggedValue()); + } + JSFunctionBase::SetFunctionName(thread, JSHandle::Cast(value), propKey, + JSHandle(thread, JSTaggedValue::Undefined())); + } + return JSTaggedValue::True(); +} + +JSTaggedValue SlowRuntimeStub::CreateEmptyArray(JSThread *thread, ObjectFactory *factory, JSHandle globalEnv) +{ + INTERPRETER_TRACE(thread, CreateEmptyArray); + [[maybe_unused]] EcmaHandleScope handleScope(thread); + + JSHandle builtinObj(globalEnv->GetArrayFunction()); + JSHandle arr = factory->NewJSObjectByConstructor(builtinObj, JSHandle(builtinObj)); + return arr.GetTaggedValue(); +} + +JSTaggedValue SlowRuntimeStub::CreateEmptyObject(JSThread *thread, ObjectFactory *factory, + JSHandle globalEnv) +{ + INTERPRETER_TRACE(thread, CreateEmptyObject); + [[maybe_unused]] EcmaHandleScope handleScope(thread); + + JSHandle builtinObj(globalEnv->GetObjectFunction()); + JSHandle obj = factory->NewJSObjectByConstructor(builtinObj, JSHandle(builtinObj)); + return obj.GetTaggedValue(); +} + +JSTaggedValue SlowRuntimeStub::CreateObjectWithBuffer(JSThread *thread, ObjectFactory *factory, JSObject *literal) +{ + INTERPRETER_TRACE(thread, CreateObjectWithBuffer); + [[maybe_unused]] EcmaHandleScope handleScope(thread); + + JSHandle obj(thread, literal); + JSHandle objLiteral = factory->CloneObjectLiteral(obj); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + + return objLiteral.GetTaggedValue(); +} + +JSTaggedValue SlowRuntimeStub::CreateObjectHavingMethod(JSThread *thread, ObjectFactory *factory, JSObject *literal, + JSTaggedValue env, ConstantPool *constpool) +{ + INTERPRETER_TRACE(thread, CreateObjectHavingMethod); + [[maybe_unused]] EcmaHandleScope handleScope(thread); + + JSHandle obj(thread, literal); + JSHandle objLiteral = factory->CloneObjectLiteral( + obj, JSHandle(thread, env), JSHandle(thread, JSTaggedValue(constpool))); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + + return objLiteral.GetTaggedValue(); +} + +JSTaggedValue SlowRuntimeStub::SetObjectWithProto(JSThread *thread, JSTaggedValue proto, JSTaggedValue obj) +{ + INTERPRETER_TRACE(thread, SetObjectWithProto); + [[maybe_unused]] EcmaHandleScope handleScope(thread); + + if (!proto.IsECMAObject() && !proto.IsNull()) { + return JSTaggedValue::False(); + } + JSHandle protoHandle(thread, proto); + JSHandle objHandle(thread, obj); + JSObject::SetPrototype(thread, objHandle, protoHandle); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + return JSTaggedValue::True(); +} + +JSTaggedValue SlowRuntimeStub::CloseIterator(JSThread *thread, JSTaggedValue iter) +{ + INTERPRETER_TRACE(thread, CloseIterator); + [[maybe_unused]] EcmaHandleScope handleScope(thread); + + ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); + const GlobalEnvConstants *globalConst = thread->GlobalConstants(); + + JSHandle iterHandle(thread, iter); + JSHandle record; + if (thread->HasPendingException()) { + JSHandle exception(thread, + ObjectWrapper::Cast(thread->GetException().GetTaggedObject())->GetValue()); + record = JSHandle(factory->NewCompletionRecord(CompletionRecord::THROW, exception)); + } else { + JSHandle undefinedVal = globalConst->GetHandledUndefined(); + record = JSHandle(factory->NewCompletionRecord(CompletionRecord::NORMAL, undefinedVal)); + } + JSHandle result = JSIterator::IteratorClose(thread, iterHandle, record); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + return result.GetTaggedValue(); +} + +JSTaggedValue SlowRuntimeStub::ImportModule([[maybe_unused]] JSThread *thread, + [[maybe_unused]] JSTaggedValue moduleName) +{ + INTERPRETER_TRACE(thread, ImportModule); + [[maybe_unused]] EcmaHandleScope scope(thread); + JSHandle name(thread, moduleName); + JSHandle module = thread->GetEcmaVM()->GetModuleByName(name); + return module.GetTaggedValue(); // return moduleRef +} + +void SlowRuntimeStub::StModuleVar([[maybe_unused]] JSThread *thread, [[maybe_unused]] JSTaggedValue exportName, + [[maybe_unused]] JSTaggedValue exportObj) +{ + INTERPRETER_TRACE(thread, StModuleVar); + [[maybe_unused]] EcmaHandleScope scope(thread); + JSHandle name(thread, exportName); + JSHandle value(thread, exportObj); + thread->GetEcmaVM()->GetModuleManager()->AddModuleItem(thread, name, value); +} + +void SlowRuntimeStub::CopyModule(JSThread *thread, JSTaggedValue srcModule) +{ + INTERPRETER_TRACE(thread, CopyModule); + [[maybe_unused]] EcmaHandleScope scope(thread); + JSHandle srcModuleObj(thread, srcModule); + thread->GetEcmaVM()->GetModuleManager()->CopyModule(thread, srcModuleObj); +} + +JSTaggedValue SlowRuntimeStub::LdModvarByName([[maybe_unused]] JSThread *thread, + [[maybe_unused]] JSTaggedValue moduleObj, + [[maybe_unused]] JSTaggedValue itemName) +{ + INTERPRETER_TRACE(thread, LdModvarByName); + [[maybe_unused]] EcmaHandleScope scope(thread); + JSHandle module(thread, moduleObj); + JSHandle item(thread, itemName); + JSHandle moduleVar = thread->GetEcmaVM()->GetModuleManager()->GetModuleItem(thread, module, item); + return moduleVar.GetTaggedValue(); +} + +JSTaggedValue SlowRuntimeStub::CreateRegExpWithLiteral(JSThread *thread, JSTaggedValue pattern, uint8_t flags) +{ + INTERPRETER_TRACE(thread, CreateRegExpWithLiteral); + [[maybe_unused]] EcmaHandleScope handleScope(thread); + + JSHandle patternHandle(thread, pattern); + JSHandle flagsHandle(thread, JSTaggedValue(flags)); + + return builtins::BuiltinsRegExp::RegExpCreate(thread, patternHandle, flagsHandle); +} + +JSTaggedValue SlowRuntimeStub::CreateArrayWithBuffer(JSThread *thread, ObjectFactory *factory, JSArray *literal) +{ + INTERPRETER_TRACE(thread, CreateArrayWithBuffer); + [[maybe_unused]] EcmaHandleScope handleScope(thread); + + JSHandle array(thread, literal); + JSHandle arrLiteral = factory->CloneArrayLiteral(array); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + + return arrLiteral.GetTaggedValue(); +} + +JSTaggedValue SlowRuntimeStub::GetMethod(JSThread *thread, JSTaggedValue object, JSTaggedValue prop_key) +{ + [[maybe_unused]] EcmaHandleScope handleScope(thread); + + JSHandle obj(thread, object); + JSHandle key(thread, prop_key); + + JSHandle method = JSObject::GetMethod(thread, obj, key); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + return method.GetTaggedValue(); +} + +JSTaggedValue SlowRuntimeStub::GetTemplateObject(JSThread *thread, JSTaggedValue literal) +{ + INTERPRETER_TRACE(thread, GetTemplateObject); + [[maybe_unused]] EcmaHandleScope handleScope(thread); + + JSHandle templateLiteral(thread, literal); + JSHandle templateObj = TemplateString::GetTemplateObject(thread, templateLiteral); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + return templateObj.GetTaggedValue(); +} + +JSTaggedValue SlowRuntimeStub::GetNextPropName(JSThread *thread, JSTaggedValue iter) +{ + INTERPRETER_TRACE(thread, GetNextPropName); + [[maybe_unused]] EcmaHandleScope handleScope(thread); + + JSHandle iterator(thread, iter); + ASSERT(iterator->IsForinIterator()); + std::pair res = + JSForInIterator::NextInternal(thread, JSHandle::Cast(iterator)); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + return res.first; +} + +JSTaggedValue SlowRuntimeStub::CopyDataProperties(JSThread *thread, JSTaggedValue dst, JSTaggedValue src) +{ + INTERPRETER_TRACE(thread, CopyDataProperties); + [[maybe_unused]] EcmaHandleScope handleScope(thread); + + JSHandle dstHandle(thread, dst); + JSHandle srcHandle(thread, src); + if (!srcHandle->IsNull() && !srcHandle->IsUndefined()) { + JSHandle keys = JSTaggedValue::GetOwnPropertyKeys(thread, srcHandle); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + + JSMutableHandle key(thread, JSTaggedValue::Undefined()); + uint32_t keysLen = keys->GetLength(); + for (uint32_t i = 0; i < keysLen; i++) { + PropertyDescriptor desc(thread); + key.Update(keys->Get(i)); + bool success = JSTaggedValue::GetOwnProperty(thread, srcHandle, key, desc); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + + if (success && desc.IsEnumerable()) { + JSTaggedValue::DefineOwnProperty(thread, dstHandle, key, desc); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + } + } + } + return dstHandle.GetTaggedValue(); +} + +JSTaggedValue SlowRuntimeStub::GetUnmappedArgs(JSThread *thread, uint32_t actualNumArgs, JSTaggedType *stkargs) +{ + INTERPRETER_TRACE(thread, GetUnmapedArgs); + [[maybe_unused]] EcmaHandleScope handleScope(thread); + + ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); + JSHandle globalEnv = thread->GetEcmaVM()->GetGlobalEnv(); + JSHandle argumentsList = factory->NewTaggedArray(actualNumArgs); + for (uint32_t i = 0; i < actualNumArgs; ++i) { + argumentsList->Set(thread, i, + JSTaggedValue(stkargs[i])); // NOLINT(cppcoreguidelines-pro-bounds-pointer-arithmetic) + } + // 1. Let len be the number of elements in argumentsList + int32_t len = argumentsList->GetLength(); + // 2. Let obj be ObjectCreate(%ObjectPrototype%, «[[ParameterMap]]»). + // 3. Set obj’s [[ParameterMap]] internal slot to undefined. + JSHandle obj = factory->NewJSArguments(); + // 4. Perform DefinePropertyOrThrow(obj, "length", PropertyDescriptor{[[Value]]: len, [[Writable]]: true, + // [[Enumerable]]: false, [[Configurable]]: true}). + obj->SetPropertyInlinedProps(thread, JSArguments::LENGTH_INLINE_PROPERTY_INDEX, JSTaggedValue(len)); + // 5. Let index be 0. + // 6. Repeat while index < len, + // a. Let val be argumentsList[index]. + // b. Perform CreateDataProperty(obj, ToString(index), val). + // c. Let index be index + 1 + obj->SetElements(thread, argumentsList.GetTaggedValue()); + // 7. Perform DefinePropertyOrThrow(obj, @@iterator, PropertyDescriptor + // {[[Value]]:%ArrayProto_values%, + // [[Writable]]: true, [[Enumerable]]: false, [[Configurable]]: true}). + obj->SetPropertyInlinedProps(thread, JSArguments::ITERATOR_INLINE_PROPERTY_INDEX, + globalEnv->GetArrayProtoValuesFunction().GetTaggedValue()); + // 9. Perform DefinePropertyOrThrow(obj, "callee", PropertyDescriptor {[[Get]]: %ThrowTypeError%, + // [[Set]]: %ThrowTypeError%, [[Enumerable]]: false, [[Configurable]]: false}). + JSHandle throwFunction = globalEnv->GetThrowTypeError(); + JSHandle accessor = factory->NewAccessorData(); + accessor->SetGetter(thread, throwFunction); + accessor->SetSetter(thread, throwFunction); + obj->SetPropertyInlinedProps(thread, JSArguments::CALLEE_INLINE_PROPERTY_INDEX, accessor.GetTaggedValue()); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + // 11. Return obj + return obj.GetTaggedValue(); +} + +JSTaggedValue SlowRuntimeStub::CopyRestArgs(JSThread *thread, uint32_t restNumArgs, JSTaggedType *stkargs) +{ + INTERPRETER_TRACE(thread, Copyrestargs); + [[maybe_unused]] EcmaHandleScope handleScope(thread); + + JSHandle restArray = JSArray::ArrayCreate(thread, JSTaggedNumber(restNumArgs)); + + JSMutableHandle element(thread, JSTaggedValue::Undefined()); + for (uint32_t i = 0; i < restNumArgs; ++i) { + // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-pointer-arithmetic) + element.Update(JSTaggedValue(stkargs[i])); + JSObject::SetProperty(thread, restArray, i, element, true); + } + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + return restArray.GetTaggedValue(); +} + +// 7.4.1 GetIterator +JSTaggedValue SlowRuntimeStub::GetIterator(JSThread *thread, JSTaggedValue obj, bool async, JSTaggedValue method) +{ + INTERPRETER_TRACE(thread, GetIterator); + [[maybe_unused]] EcmaHandleScope handleScope(thread); + EcmaVM *vm = thread->GetEcmaVM(); + auto globalConst = thread->GlobalConstants(); + JSHandle env = vm->GetGlobalEnv(); + JSHandle objHandle(thread, obj); + JSMutableHandle methodHandle(thread, globalConst->GetHandledUndefined()); + + // 3. If method is not present, then + if (method.IsHole()) { + // a. If hint is async, then + if (async) { + // i. Set method to ? GetMethod(obj, @@asyncIterator). + methodHandle.Update(JSObject::GetMethod(thread, objHandle, env->GetAsyncIteratorSymbol())); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + + if (methodHandle->IsUndefined()) { + // 1. Let syncMethod be ? GetMethod(obj, @@iterator). + JSHandle syncMethod = JSObject::GetMethod(thread, objHandle, env->GetIteratorSymbol()); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + + // 2. Let syncIteratorRecord be ? GetIterator(obj, sync, syncMethod). + JSHandle syncIterator( + thread, SlowRuntimeStub::GetIterator(thread, obj, false, syncMethod.GetTaggedValue())); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + + // 6. Let nextMethod be ? GetV(iterator, "next"). + JSHandle syncNextMethod = + JSTaggedValue::GetProperty(thread, syncIterator, globalConst->GetHandledNextString()).GetValue(); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + + // 3. Return ! CreateAsyncFromSyncIterator(syncIteratorRecord). + return JSAsyncFromSyncIteratorObject::CreateAsyncFromSyncIterator(thread, syncIterator, syncNextMethod); + } + } else { + methodHandle.Update(JSTaggedValue::GetProperty(thread, objHandle, env->GetIteratorSymbol()).GetValue()); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + } + } else { + methodHandle.Update(method); + } + + // 4. Let iterator be ? Call(method, obj). + InternalCallParams *params = thread->GetInternalCallParams(); + params->MakeEmptyArgv(); + JSTaggedValue iterator = JSFunction::Call(thread, methodHandle, objHandle, 0, params->GetArgv()); + + // 5. If Type(iterator) is not Object, throw a TypeError exception. + if (!iterator.IsECMAObject()) { + THROW_TYPE_ERROR_AND_RETURN(thread, "Iterator is not object", JSTaggedValue::Exception()); + } + + return iterator; +} + +JSTaggedValue SlowRuntimeStub::DefineGetterSetterByValue(JSThread *thread, JSTaggedValue obj, JSTaggedValue prop, + JSTaggedValue getter, JSTaggedValue setter, bool flag) +{ + INTERPRETER_TRACE(thread, DefineGetterSetterByValue); + [[maybe_unused]] EcmaHandleScope handleScope(thread); + JSHandle objHandle(thread, obj); + JSHandle propHandle(thread, prop); + + JSHandle getterHandle(thread, getter); + JSHandle setterHandle(thread, setter); + JSHandle propKey = JSTaggedValue::ToPropertyKey(thread, propHandle); + + auto globalConst = thread->GlobalConstants(); + if (objHandle.GetTaggedValue().IsClassConstructor() && + JSTaggedValue::SameValue(propKey, globalConst->GetHandledPrototypeString())) { + return ThrowTypeError( + thread, + "In a class, computed property names for static getter that are named 'prototype' throw a TypeError"); + } + + if (flag) { + if (!getterHandle->IsUndefined()) { + if (propKey->IsNumber()) { + propKey = + JSHandle::Cast(base::NumberHelper::NumberToString(thread, propKey.GetTaggedValue())); + } + JSFunctionBase::SetFunctionName(thread, JSHandle::Cast(getterHandle), propKey, + JSHandle(thread, globalConst->GetGetString())); + } + + if (!setterHandle->IsUndefined()) { + if (propKey->IsNumber()) { + propKey = + JSHandle::Cast(base::NumberHelper::NumberToString(thread, propKey.GetTaggedValue())); + } + JSFunctionBase::SetFunctionName(thread, JSHandle::Cast(setterHandle), propKey, + JSHandle(thread, globalConst->GetSetString())); + } + } + + // set accessor + bool enumerable = + !(objHandle.GetTaggedValue().IsClassPrototype() || objHandle.GetTaggedValue().IsClassConstructor()); + PropertyDescriptor desc(thread, true, enumerable, true); + if (!getterHandle->IsUndefined()) { + JSHandle::Cast(getterHandle)->SetFunctionKind(thread, FunctionKind::GETTER_FUNCTION); + desc.SetGetter(getterHandle); + } + if (!setterHandle->IsUndefined()) { + JSHandle::Cast(setterHandle)->SetFunctionKind(thread, FunctionKind::SETTER_FUNCTION); + desc.SetSetter(setterHandle); + } + JSObject::DefineOwnProperty(thread, objHandle, propKey, desc); + + return objHandle.GetTaggedValue(); +} + +JSTaggedValue SlowRuntimeStub::LdObjByIndex(JSThread *thread, JSTaggedValue obj, uint32_t idx, bool callGetter, + JSTaggedValue receiver) +{ + INTERPRETER_TRACE(thread, LdObjByIndexDyn); + [[maybe_unused]] EcmaHandleScope handleScope(thread); + + JSTaggedValue res; + JSHandle objHandle(thread, obj); + if (callGetter) { + res = JSObject::CallGetter(thread, AccessorData::Cast(receiver.GetTaggedObject()), objHandle); + } else { + res = JSTaggedValue::GetProperty(thread, objHandle, idx).GetValue().GetTaggedValue(); + } + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + return res; +} + +JSTaggedValue SlowRuntimeStub::StObjByIndex(JSThread *thread, JSTaggedValue obj, uint32_t idx, JSTaggedValue value) +{ + INTERPRETER_TRACE(thread, StObjByIndexDyn); + [[maybe_unused]] EcmaHandleScope handleScope(thread); + + JSTaggedValue::SetProperty(thread, JSHandle(thread, obj), idx, + JSHandle(thread, value), true); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + return JSTaggedValue::True(); +} + +JSTaggedValue SlowRuntimeStub::LdObjByName(JSThread *thread, JSTaggedValue obj, JSTaggedValue prop, bool callGetter, + JSTaggedValue receiver) +{ + INTERPRETER_TRACE(thread, LdObjByNameDyn); + [[maybe_unused]] EcmaHandleScope handleScope(thread); + + JSHandle objHandle(thread, obj); + JSTaggedValue res; + if (callGetter) { + res = JSObject::CallGetter(thread, AccessorData::Cast(receiver.GetTaggedObject()), objHandle); + } else { + JSHandle propHandle(thread, prop); + res = JSTaggedValue::GetProperty(thread, objHandle, propHandle).GetValue().GetTaggedValue(); + } + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + return res; +} + +JSTaggedValue SlowRuntimeStub::StObjByName(JSThread *thread, JSTaggedValue obj, JSTaggedValue prop, JSTaggedValue value) +{ + INTERPRETER_TRACE(thread, StObjByNameDyn); + [[maybe_unused]] EcmaHandleScope handleScope(thread); + + JSHandle objHandle(thread, obj); + JSHandle propHandle(thread, prop); + JSHandle valueHandle(thread, value); + JSTaggedValue::SetProperty(thread, objHandle, propHandle, valueHandle, true); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + return JSTaggedValue::True(); +} + +JSTaggedValue SlowRuntimeStub::LdObjByValue(JSThread *thread, JSTaggedValue obj, JSTaggedValue prop, bool callGetter, + JSTaggedValue receiver) +{ + INTERPRETER_TRACE(thread, LdObjByValueDyn); + [[maybe_unused]] EcmaHandleScope handleScope(thread); + + JSHandle objHandle(thread, obj); + JSTaggedValue res; + if (callGetter) { + res = JSObject::CallGetter(thread, AccessorData::Cast(receiver.GetTaggedObject()), objHandle); + } else { + JSHandle propKey = JSTaggedValue::ToPropertyKey(thread, JSHandle(thread, prop)); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + res = JSTaggedValue::GetProperty(thread, objHandle, propKey).GetValue().GetTaggedValue(); + } + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + return res; +} + +JSTaggedValue SlowRuntimeStub::StObjByValue(JSThread *thread, JSTaggedValue obj, JSTaggedValue prop, + JSTaggedValue value) +{ + INTERPRETER_TRACE(thread, StObjByValueDyn); + [[maybe_unused]] EcmaHandleScope handleScope(thread); + + JSHandle objHandle(thread, obj); + JSHandle propHandle(thread, prop); + JSHandle valueHandle(thread, value); + JSHandle propKey(JSTaggedValue::ToPropertyKey(thread, propHandle)); + + // strict mode is true + JSTaggedValue::SetProperty(thread, objHandle, propKey, valueHandle, true); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + return JSTaggedValue::True(); +} + +JSTaggedValue SlowRuntimeStub::TryLdGlobalByName(JSThread *thread, JSTaggedValue global, JSTaggedValue prop) +{ + INTERPRETER_TRACE(thread, Trygetobjprop); + [[maybe_unused]] EcmaHandleScope handleScope(thread); + + JSHandle obj(thread, global.GetTaggedObject()->GetClass()->GetPrototype()); + JSHandle propHandle(thread, prop); + OperationResult res = JSTaggedValue::GetProperty(thread, obj, propHandle); + if (!res.GetPropertyMetaData().IsFound()) { + return ThrowReferenceError(thread, prop, " is not defined"); + } + return res.GetValue().GetTaggedValue(); +} + +JSTaggedValue SlowRuntimeStub::LdGlobalVar(JSThread *thread, JSTaggedValue global, JSTaggedValue prop) +{ + INTERPRETER_TRACE(thread, LdGlobalVar); + [[maybe_unused]] EcmaHandleScope handleScope(thread); + + JSHandle objHandle(thread, global.GetTaggedObject()->GetClass()->GetPrototype()); + JSHandle propHandle(thread, prop); + OperationResult res = JSTaggedValue::GetProperty(thread, objHandle, propHandle); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + return res.GetValue().GetTaggedValue(); +} + +JSTaggedValue SlowRuntimeStub::StGlobalVar(JSThread *thread, JSTaggedValue prop, JSTaggedValue value) +{ + INTERPRETER_TRACE(thread, StGlobalVar); + [[maybe_unused]] EcmaHandleScope handleScope(thread); + + JSHandle global(thread, thread->GetEcmaVM()->GetGlobalEnv()->GetGlobalObject()); + JSHandle propHandle(thread, prop); + JSHandle valueHandle(thread, value); + + JSObject::GlobalSetProperty(thread, propHandle, valueHandle, true); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + return JSTaggedValue::True(); +} + +JSTaggedValue SlowRuntimeStub::TryUpdateGlobalRecord(JSThread *thread, JSTaggedValue prop, JSTaggedValue value) +{ + INTERPRETER_TRACE(thread, TryUpdateGlobalRecord); + [[maybe_unused]] EcmaHandleScope handleScope(thread); + + EcmaVM *vm = thread->GetEcmaVM(); + JSHandle env = vm->GetGlobalEnv(); + GlobalDictionary *dict = GlobalDictionary::Cast(env->GetGlobalRecord()->GetTaggedObject()); + int entry = dict->FindEntry(prop); + ASSERT(entry != -1); + + if (dict->GetAttributes(entry).IsConstProps()) { + return ThrowSyntaxError(thread, "const variable can not be modified"); + } + + PropertyBox *box = dict->GetBox(entry); + box->SetValue(thread, value); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + return JSTaggedValue::True(); +} + +// return box +JSTaggedValue SlowRuntimeStub::LdGlobalRecord(JSThread *thread, JSTaggedValue key, bool *found) +{ + INTERPRETER_TRACE(thread, LdGlobalRecord); + [[maybe_unused]] EcmaHandleScope handleScope(thread); + + EcmaVM *vm = thread->GetEcmaVM(); + JSHandle env = vm->GetGlobalEnv(); + GlobalDictionary *dict = GlobalDictionary::Cast(env->GetGlobalRecord()->GetTaggedObject()); + int entry = dict->FindEntry(key); + if (entry != -1) { + *found = true; + return JSTaggedValue(dict->GetBox(entry)); + } + return JSTaggedValue::Undefined(); +} + +JSTaggedValue SlowRuntimeStub::StGlobalRecord(JSThread *thread, JSTaggedValue prop, JSTaggedValue value, bool isConst) +{ + INTERPRETER_TRACE(thread, StGlobalRecord); + [[maybe_unused]] EcmaHandleScope handleScope(thread); + + EcmaVM *vm = thread->GetEcmaVM(); + JSHandle env = vm->GetGlobalEnv(); + GlobalDictionary *dict = GlobalDictionary::Cast(env->GetGlobalRecord()->GetTaggedObject()); + + // cross files global record name binding judgment + int entry = dict->FindEntry(prop); + if (entry != -1) { + return ThrowSyntaxError(thread, "Duplicate identifier"); + } + + PropertyAttributes attributes; + if (isConst) { + attributes.SetIsConstProps(true); + } + JSHandle propHandle(thread, prop); + JSHandle valueHandle(thread, value); + JSHandle dictHandle(thread, dict); + + ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); + JSHandle box = factory->NewPropertyBox(valueHandle); + PropertyBoxType boxType = valueHandle->IsUndefined() ? PropertyBoxType::UNDEFINED : PropertyBoxType::CONSTANT; + attributes.SetBoxType(boxType); + + dict = *GlobalDictionary::PutIfAbsent(thread, dictHandle, propHandle, JSHandle(box), attributes); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + env->SetGlobalRecord(thread, JSTaggedValue(dict)); + return JSTaggedValue::True(); +} + +JSTaggedValue SlowRuntimeStub::ThrowReferenceError(JSThread *thread, JSTaggedValue prop, const char *desc) +{ + INTERPRETER_TRACE(thread, ThrowReferenceError); + [[maybe_unused]] EcmaHandleScope handleScope(thread); + ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); + JSHandle propName = JSTaggedValue::ToString(thread, JSHandle(thread, prop)); + ASSERT_NO_ABRUPT_COMPLETION(thread); + JSHandle info = factory->NewFromString(desc); + JSHandle msg = factory->ConcatFromString(propName, info); + THROW_NEW_ERROR_AND_RETURN_VALUE(thread, + factory->NewJSError(base::ErrorType::REFERENCE_ERROR, msg).GetTaggedValue(), + JSTaggedValue::Exception()); +} + +JSTaggedValue SlowRuntimeStub::ThrowTypeError(JSThread *thread, const char *message) +{ + INTERPRETER_TRACE(thread, ThrowTypeError); + [[maybe_unused]] EcmaHandleScope handleScope(thread); + ASSERT_NO_ABRUPT_COMPLETION(thread); + THROW_TYPE_ERROR_AND_RETURN(thread, message, JSTaggedValue::Exception()); +} + +JSTaggedValue SlowRuntimeStub::ThrowSyntaxError(JSThread *thread, const char *message) +{ + INTERPRETER_TRACE(thread, ThrowSyntaxError); + [[maybe_unused]] EcmaHandleScope handleScope(thread); + ASSERT_NO_ABRUPT_COMPLETION(thread); + THROW_SYNTAX_ERROR_AND_RETURN(thread, message, JSTaggedValue::Exception()); +} + +JSTaggedValue SlowRuntimeStub::StArraySpread(JSThread *thread, JSTaggedValue dst, JSTaggedValue index, + JSTaggedValue src) +{ + INTERPRETER_TRACE(thread, StArraySpread); + [[maybe_unused]] EcmaHandleScope handleScope(thread); + + JSHandle dstHandle(thread, dst); + JSHandle srcHandle(thread, src); + ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); + ASSERT(dstHandle->IsJSArray() && !srcHandle->IsNull() && !srcHandle->IsUndefined()); + if (srcHandle->IsString()) { + JSHandle srcString = JSTaggedValue::ToString(thread, srcHandle); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + uint32_t dstLen = index.GetInt(); + uint32_t strLen = srcString->GetLength(); + for (uint32_t i = 0; i < strLen; i++) { + uint16_t res = srcString->At(i); + JSHandle strValue(factory->NewFromUtf16Literal(&res, 1)); + JSTaggedValue::SetProperty(thread, dstHandle, dstLen + i, strValue, true); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + } + return JSTaggedValue(dstLen + strLen); + } + + JSHandle iter; + auto globalConst = thread->GlobalConstants(); + if (srcHandle->IsJSArrayIterator() || srcHandle->IsJSMapIterator() || srcHandle->IsJSSetIterator() || + srcHandle->IsIterator()) { + iter = srcHandle; + } else if (srcHandle->IsJSArray() || srcHandle->IsJSMap() || srcHandle->IsTypedArray() || srcHandle->IsJSSet()) { + JSHandle valuesStr = globalConst->GetHandledValuesString(); + JSHandle valuesMethod = JSObject::GetMethod(thread, srcHandle, valuesStr); + iter = JSIterator::GetIterator(thread, srcHandle, valuesMethod); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + } else { + iter = JSIterator::GetIterator(thread, srcHandle); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + } + + JSMutableHandle indexHandle(thread, index); + JSHandle valueStr = globalConst->GetHandledValueString(); + PropertyDescriptor desc(thread); + JSHandle iterResult; + do { + iterResult = JSIterator::IteratorStep(thread, iter); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + if (iterResult->IsFalse()) { + break; + } + bool success = JSTaggedValue::GetOwnProperty(thread, iterResult, valueStr, desc); + if (success && desc.IsEnumerable()) { + JSTaggedValue::DefineOwnProperty(thread, dstHandle, indexHandle, desc); + int tmp = indexHandle->GetInt(); + indexHandle.Update(JSTaggedValue(tmp + 1)); + } + } while (true); + + return indexHandle.GetTaggedValue(); +} + +JSTaggedValue SlowRuntimeStub::DefineGeneratorFunc(JSThread *thread, JSMethod *method) +{ + INTERPRETER_TRACE(thread, DefineGeneratorFunc); + [[maybe_unused]] EcmaHandleScope handleScope(thread); + + JSHandle env = thread->GetEcmaVM()->GetGlobalEnv(); + ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); + JSHandle jsFunc = factory->NewJSGeneratorFunction(method); + ASSERT_NO_ABRUPT_COMPLETION(thread); + + // 26.3.4.3 prototype + // Whenever a GeneratorFunction instance is created another ordinary object is also created and + // is the initial value of the generator function's "prototype" property. + JSHandle objFun = env->GetObjectFunction(); + JSHandle initialGeneratorFuncPrototype = + factory->NewJSObjectByConstructor(JSHandle(objFun), objFun); + JSObject::SetPrototype(thread, initialGeneratorFuncPrototype, env->GetGeneratorPrototype()); + ASSERT_NO_ABRUPT_COMPLETION(thread); + jsFunc->SetProtoOrDynClass(thread, initialGeneratorFuncPrototype); + jsFunc->SetupFunctionLength(thread); + + return jsFunc.GetTaggedValue(); +} + +JSTaggedValue SlowRuntimeStub::DefineAsyncGeneratorFunc(JSThread *thread, JSMethod *method) +{ + [[maybe_unused]] EcmaHandleScope handleScope(thread); + + JSHandle env = thread->GetEcmaVM()->GetGlobalEnv(); + ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); + JSHandle jsFunc = factory->NewJSAsyncGeneratorFunction(method); + ASSERT_NO_ABRUPT_COMPLETION(thread); + + // 27.4.4.3 prototype + // Whenever a AsyncGeneratorFunction instance is created another ordinary object is also created and + // is the initial value of the generator function's "prototype" property. + JSHandle objFun = env->GetObjectFunction(); + JSHandle initialAsyncGeneratorFuncPrototype = + factory->NewJSObjectByConstructor(JSHandle(objFun), objFun); + JSObject::SetPrototype(thread, initialAsyncGeneratorFuncPrototype, env->GetAsyncGeneratorPrototype()); + ASSERT_NO_ABRUPT_COMPLETION(thread); + jsFunc->SetProtoOrDynClass(thread, initialAsyncGeneratorFuncPrototype); + jsFunc->SetupFunctionLength(thread); + + return jsFunc.GetTaggedValue(); +} + +JSTaggedValue SlowRuntimeStub::DefineAsyncFunc(JSThread *thread, JSMethod *method) +{ + INTERPRETER_TRACE(thread, DefineAsyncFunc); + [[maybe_unused]] EcmaHandleScope handleScope(thread); + + JSHandle env = thread->GetEcmaVM()->GetGlobalEnv(); + ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); + JSHandle dynclass = JSHandle::Cast(env->GetAsyncFunctionClass()); + JSHandle jsFunc = factory->NewJSFunctionByDynClass(method, dynclass, FunctionKind::ASYNC_FUNCTION); + ASSERT_NO_ABRUPT_COMPLETION(thread); + return jsFunc.GetTaggedValue(); +} + +JSTaggedValue SlowRuntimeStub::DefineNCFuncDyn(JSThread *thread, JSMethod *method) +{ + INTERPRETER_TRACE(thread, DefineNCFuncDyn); + [[maybe_unused]] EcmaHandleScope handleScope(thread); + + JSHandle env = thread->GetEcmaVM()->GetGlobalEnv(); + ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); + JSHandle dynclass = JSHandle::Cast(env->GetFunctionClassWithoutProto()); + JSHandle jsFunc = factory->NewJSFunctionByDynClass(method, dynclass, FunctionKind::ARROW_FUNCTION); + jsFunc->SetupFunctionLength(thread); + ASSERT_NO_ABRUPT_COMPLETION(thread); + return jsFunc.GetTaggedValue(); +} + +JSTaggedValue SlowRuntimeStub::DefinefuncDyn(JSThread *thread, JSMethod *method) +{ + INTERPRETER_TRACE(thread, DefinefuncDyn); + [[maybe_unused]] EcmaHandleScope handleScope(thread); + + JSHandle env = thread->GetEcmaVM()->GetGlobalEnv(); + ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); + JSHandle dynclass = JSHandle::Cast(env->GetFunctionClassWithProto()); + JSHandle jsFunc = factory->NewJSFunctionByDynClass(method, dynclass, FunctionKind::BASE_CONSTRUCTOR); + jsFunc->SetupFunctionLength(thread); + ASSERT_NO_ABRUPT_COMPLETION(thread); + return jsFunc.GetTaggedValue(); +} + +JSTaggedValue SlowRuntimeStub::SuperCall(JSThread *thread, JSTaggedValue func, JSTaggedValue newTarget, + uint16_t argsCount, JSTaggedType *stkargs) +{ + INTERPRETER_TRACE(thread, SuperCall); + [[maybe_unused]] EcmaHandleScope handleScope(thread); + + JSHandle funcHandle(thread, func); + JSHandle newTargetHandle(thread, newTarget); + + JSHandle superFunc(thread, JSHandle::Cast(funcHandle)->GetPrototype(thread)); + ASSERT(superFunc->IsJSFunction()); + JSTaggedValue result = JSFunction::Construct(thread, superFunc, argsCount, stkargs, newTargetHandle); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + + return result; +} + +JSTaggedValue SlowRuntimeStub::SuperCallSpread(JSThread *thread, JSTaggedValue func, JSTaggedValue newTarget, + JSTaggedValue array) +{ + INTERPRETER_TRACE(thread, SuperCallSpread); + [[maybe_unused]] EcmaHandleScope handleScope(thread); + + JSHandle funcHandle(thread, func); + JSHandle newTargetHandle(thread, newTarget); + JSHandle jsArray(thread, array); + + JSHandle superFunc(thread, JSHandle::Cast(funcHandle)->GetPrototype(thread)); + ASSERT(superFunc->IsJSFunction()); + + JSHandle argv(thread, GetCallSpreadArgs(thread, jsArray.GetTaggedValue())); + InternalCallParams *arguments = thread->GetInternalCallParams(); + arguments->MakeArgList(*argv); + JSTaggedValue result = + JSFunction::Construct(thread, superFunc, argv->GetLength(), arguments->GetArgv(), newTargetHandle); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + + return result; +} + +JSTaggedValue SlowRuntimeStub::DefineMethod(JSThread *thread, JSMethod *method, + const JSHandle &homeObject) +{ + INTERPRETER_TRACE(thread, DefineMethod); + [[maybe_unused]] EcmaHandleScope handleScope(thread); + ASSERT(homeObject->IsECMAObject()); + + JSHandle env = thread->GetEcmaVM()->GetGlobalEnv(); + ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); + JSHandle dynclass = JSHandle::Cast(env->GetFunctionClassWithoutProto()); + JSHandle jsFunc = factory->NewJSFunctionByDynClass(method, dynclass, FunctionKind::NORMAL_FUNCTION); + jsFunc->SetHomeObject(thread, homeObject); + jsFunc->SetupFunctionLength(thread); + ASSERT_NO_ABRUPT_COMPLETION(thread); + return jsFunc.GetTaggedValue(); +} + +JSTaggedValue SlowRuntimeStub::LdSuperByValue(JSThread *thread, JSTaggedValue obj, JSTaggedValue key, + JSTaggedValue thisFunc) +{ + INTERPRETER_TRACE(thread, LdSuperByValue); + [[maybe_unused]] EcmaHandleScope handleScope(thread); + ASSERT(thisFunc.IsJSFunction()); + // get Homeobject form function + JSHandle homeObject(thread, JSFunction::Cast(thisFunc.GetTaggedObject())->GetHomeObject()); + + if (obj.IsUndefined()) { + return ThrowReferenceError(thread, obj, "this is uninitialized."); + } + JSHandle objHandle(thread, obj); + JSHandle propHandle(thread, key); + + JSHandle propKey(JSTaggedValue::ToPropertyKey(thread, propHandle)); + JSHandle superBase(thread, JSTaggedValue::GetSuperBase(thread, homeObject)); + JSTaggedValue::RequireObjectCoercible(thread, superBase); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + + JSTaggedValue res = JSTaggedValue::GetProperty(thread, superBase, propKey, objHandle).GetValue().GetTaggedValue(); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + return res; +} + +JSTaggedValue SlowRuntimeStub::StSuperByValue(JSThread *thread, JSTaggedValue obj, JSTaggedValue key, + JSTaggedValue value, JSTaggedValue thisFunc) +{ + INTERPRETER_TRACE(thread, StSuperByValue); + [[maybe_unused]] EcmaHandleScope handleScope(thread); + ASSERT(thisFunc.IsJSFunction()); + // get Homeobject form function + JSHandle homeObject(thread, JSFunction::Cast(thisFunc.GetTaggedObject())->GetHomeObject()); + + if (obj.IsUndefined()) { + return ThrowReferenceError(thread, obj, "this is uninitialized."); + } + JSHandle objHandle(thread, obj); + JSHandle propHandle(thread, key); + JSHandle valueHandle(thread, value); + + JSHandle propKey(JSTaggedValue::ToPropertyKey(thread, propHandle)); + JSHandle superBase(thread, JSTaggedValue::GetSuperBase(thread, homeObject)); + JSTaggedValue::RequireObjectCoercible(thread, superBase); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + + // check may_throw is false? + JSTaggedValue::SetProperty(thread, superBase, propKey, valueHandle, objHandle, true); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + return JSTaggedValue::True(); +} + +JSTaggedValue SlowRuntimeStub::GetCallSpreadArgs(JSThread *thread, JSTaggedValue array) +{ + INTERPRETER_TRACE(thread, GetCallSpreadArgs); + [[maybe_unused]] EcmaHandleScope handleScope(thread); + ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); + + JSHandle jsArray(thread, array); + uint32_t argvMayMaxLength = JSHandle::Cast(jsArray)->GetArrayLength(); + JSHandle argv = factory->NewTaggedArray(argvMayMaxLength); + JSHandle itor = JSIterator::GetIterator(thread, jsArray); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + JSMutableHandle next(thread, JSTaggedValue::Undefined()); + JSMutableHandle nextArg(thread, JSTaggedValue::Undefined()); + size_t argvIndex = 0; + while (true) { + next.Update(JSIterator::IteratorStep(thread, itor).GetTaggedValue()); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + if (JSTaggedValue::SameValue(next.GetTaggedValue(), JSTaggedValue::False())) { + break; + } + nextArg.Update(JSIterator::IteratorValue(thread, next).GetTaggedValue()); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + argv->Set(thread, argvIndex++, nextArg); + } + + argv = factory->CopyArray(argv, argvMayMaxLength, argvIndex); + return argv.GetTaggedValue(); +} + +void SlowRuntimeStub::ThrowDeleteSuperProperty(JSThread *thread) +{ + INTERPRETER_TRACE(thread, ThrowDeleteSuperProperty); + [[maybe_unused]] EcmaHandleScope handleScope(thread); + + ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); + JSHandle info = factory->NewFromCanBeCompressString("Can not delete super property"); + JSHandle errorObj = factory->NewJSError(base::ErrorType::REFERENCE_ERROR, info); + THROW_NEW_ERROR_AND_RETURN(thread, errorObj.GetTaggedValue()); +} + +JSTaggedValue SlowRuntimeStub::NotifyInlineCache(JSThread *thread, JSFunction *func, JSMethod *method) +{ + INTERPRETER_TRACE(thread, NotifyInlineCache); + uint32_t icSlotSize = method->GetSlotSize(); + if (icSlotSize > 0 && icSlotSize < ProfileTypeInfo::INVALID_SLOT_INDEX) { + ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); + [[maybe_unused]] EcmaHandleScope handleScope(thread); + + JSHandle funcHandle(thread, func); + JSHandle profileTypeInfo = factory->NewProfileTypeInfo(icSlotSize); + funcHandle->SetProfileTypeInfo(thread, profileTypeInfo.GetTaggedValue()); + return profileTypeInfo.GetTaggedValue(); + } + return JSTaggedValue::Undefined(); +} + +JSTaggedValue SlowRuntimeStub::ResolveClass(JSThread *thread, JSTaggedValue ctor, TaggedArray *literal, + JSTaggedValue base, JSTaggedValue lexenv, ConstantPool *constpool) +{ + ASSERT(ctor.IsClassConstructor()); + JSHandle cls(thread, ctor); + JSHandle literalBuffer(thread, literal); + JSHandle lexicalEnv(thread, lexenv); + JSHandle constpoolHandle(thread, constpool); + + SetClassInheritanceRelationship(thread, ctor, base); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + + uint32_t literalBufferLength = literalBuffer->GetLength(); + + // only traverse the value of key-value pair + for (uint32_t index = 1; index < literalBufferLength - 1; index += 2) { // 2: key-value pair + JSTaggedValue value = literalBuffer->Get(index); + if (LIKELY(value.IsJSFunction())) { + JSFunction::Cast(value.GetTaggedObject())->SetLexicalEnv(thread, lexicalEnv.GetTaggedValue()); + JSFunction::Cast(value.GetTaggedObject())->SetConstantPool(thread, constpoolHandle.GetTaggedValue()); + } + } + + cls->SetResolved(thread); + return cls.GetTaggedValue(); +} + +// clone class may need re-set inheritance relationship due to extends may be a variable. +JSTaggedValue SlowRuntimeStub::CloneClassFromTemplate(JSThread *thread, JSTaggedValue ctor, JSTaggedValue base, + JSTaggedValue lexenv, ConstantPool *constpool) +{ + [[maybe_unused]] EcmaHandleScope handleScope(thread); + ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); + const GlobalEnvConstants *globalConst = thread->GlobalConstants(); + + ASSERT(ctor.IsClassConstructor()); + JSHandle lexenvHandle(thread, lexenv); + JSHandle constpoolHandle(thread, JSTaggedValue(constpool)); + JSHandle baseHandle(thread, base); + + JSHandle cls(thread, ctor); + + JSHandle clsPrototype(thread, cls->GetFunctionPrototype()); + + bool canShareHClass = false; + if (cls->GetClass()->GetProto() == baseHandle.GetTaggedValue()) { + canShareHClass = true; + } + + JSHandle cloneClass = factory->CloneClassCtor(cls, lexenvHandle, canShareHClass); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + JSHandle cloneClassPrototype = + factory->CloneObjectLiteral(JSHandle(clsPrototype), lexenvHandle, constpoolHandle, canShareHClass); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + + // After clone both, reset "constructor" and "prototype" properties. + cloneClass->SetFunctionPrototype(thread, cloneClassPrototype.GetTaggedValue()); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + + PropertyDescriptor ctorDesc(thread, JSHandle(cloneClass), true, false, true); + JSTaggedValue::DefinePropertyOrThrow(thread, JSHandle(cloneClassPrototype), + globalConst->GetHandledConstructorString(), ctorDesc); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + + cloneClass->SetHomeObject(thread, cloneClassPrototype); + + if (!canShareHClass) { + SetClassInheritanceRelationship(thread, cloneClass.GetTaggedValue(), baseHandle.GetTaggedValue()); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + } + + return cloneClass.GetTaggedValue(); +} + +JSTaggedValue SlowRuntimeStub::SetClassInheritanceRelationship(JSThread *thread, JSTaggedValue ctor, JSTaggedValue base) +{ + [[maybe_unused]] EcmaHandleScope handleScope(thread); + JSHandle env = thread->GetEcmaVM()->GetGlobalEnv(); + const GlobalEnvConstants *globalConst = thread->GlobalConstants(); + + JSHandle cls(thread, ctor); + ASSERT(cls->IsJSFunction()); + JSMutableHandle parent(thread, base); + + /* + * class A / class A extends null class A extends B + * a a + * | | + * | __proto__ | __proto__ + * | | + * A ----> A.prototype A ----> A.prototype + * | | | | + * | __proto__ | __proto__ | __proto__ | __proto__ + * | | | | + * Function.prototype Object.prototype / null B ----> B.prototype + */ + + JSHandle parentPrototype; + // hole means parent is not present + if (parent->IsHole()) { + JSHandle::Cast(cls)->SetFunctionKind(thread, FunctionKind::CLASS_CONSTRUCTOR); + parentPrototype = env->GetObjectFunctionPrototype(); + parent.Update(env->GetFunctionPrototype().GetTaggedValue()); + } else if (parent->IsNull()) { + JSHandle::Cast(cls)->SetFunctionKind(thread, FunctionKind::DERIVED_CONSTRUCTOR); + parentPrototype = JSHandle(thread, JSTaggedValue::Null()); + parent.Update(env->GetFunctionPrototype().GetTaggedValue()); + } else if (!parent->IsConstructor()) { + return ThrowTypeError(thread, "parent class is not constructor"); + } else { + JSHandle::Cast(cls)->SetFunctionKind(thread, FunctionKind::DERIVED_CONSTRUCTOR); + parentPrototype = + JSTaggedValue::GetProperty(thread, parent, globalConst->GetHandledPrototypeString()).GetValue(); + if (!parentPrototype->IsECMAObject() && !parentPrototype->IsNull()) { + return ThrowTypeError(thread, "parent class have no valid prototype"); + } + } + cls->GetTaggedObject()->GetClass()->SetPrototype(thread, parent); + + JSHandle clsPrototype(thread, JSHandle(cls)->GetFunctionPrototype()); + clsPrototype->GetClass()->SetPrototype(thread, parentPrototype); + + return JSTaggedValue::Undefined(); +} + +JSTaggedValue SlowRuntimeStub::SetClassConstructorLength(JSThread *thread, JSTaggedValue ctor, JSTaggedValue length) +{ + ASSERT(ctor.IsClassConstructor()); + + JSFunction *cls = JSFunction::Cast(ctor.GetTaggedObject()); + if (LIKELY(!cls->GetClass()->IsDictionaryMode())) { + cls->SetPropertyInlinedProps(thread, JSFunction::LENGTH_INLINE_PROPERTY_INDEX, length); + } else { + const GlobalEnvConstants *globalConst = thread->GlobalConstants(); + cls->UpdatePropertyInDictionary(thread, globalConst->GetLengthString(), length); + } + return JSTaggedValue::Undefined(); +} +} // namespace panda::ecmascript diff --git a/runtime/interpreter/slow_runtime_stub.h b/runtime/interpreter/slow_runtime_stub.h new file mode 100644 index 000000000..e57d74d61 --- /dev/null +++ b/runtime/interpreter/slow_runtime_stub.h @@ -0,0 +1,171 @@ +/* + * Copyright (c) 2021-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_INTERPRETER_SLOW_RUNTIME_STUB_H +#define ECMASCRIPT_INTERPRETER_SLOW_RUNTIME_STUB_H + +#include "plugins/ecmascript/runtime/class_linker/program_object.h" +#include "plugins/ecmascript/runtime/js_tagged_value.h" +#include "plugins/ecmascript/runtime/js_thread.h" + +namespace panda::ecmascript { +class GlobalEnv; +class JSArray; + +class SlowRuntimeStub { +public: + /* -------------- Common API Begin, Don't change those interface!!! ----------------- */ + static JSTaggedValue CallSpreadDyn(JSThread *thread, JSTaggedValue func, JSTaggedValue obj, JSTaggedValue array); + static JSTaggedValue NegDyn(JSThread *thread, JSTaggedValue value); + static JSTaggedValue AsyncFunctionEnter(JSThread *thread); + static JSTaggedValue ToNumber(JSThread *thread, JSTaggedValue value); + static JSTaggedValue NotDyn(JSThread *thread, JSTaggedValue value); + static JSTaggedValue IncDyn(JSThread *thread, JSTaggedValue value); + static JSTaggedValue DecDyn(JSThread *thread, JSTaggedValue value); + static void ThrowDyn(JSThread *thread, JSTaggedValue value); + static JSTaggedValue GetPropIterator(JSThread *thread, JSTaggedValue value); + static void ThrowConstAssignment(JSThread *thread, JSTaggedValue value); + static JSTaggedValue Add2Dyn(JSThread *thread, EcmaVM *ecma_vm, JSTaggedValue left, JSTaggedValue right); + static JSTaggedValue Sub2Dyn(JSThread *thread, JSTaggedValue left, JSTaggedValue right); + static JSTaggedValue Mul2Dyn(JSThread *thread, JSTaggedValue left, JSTaggedValue right); + static JSTaggedValue Div2Dyn(JSThread *thread, JSTaggedValue left, JSTaggedValue right); + static JSTaggedValue Mod2Dyn(JSThread *thread, JSTaggedValue left, JSTaggedValue right); + static JSTaggedValue EqDyn(JSThread *thread, JSTaggedValue left, JSTaggedValue right); + static JSTaggedValue NotEqDyn(JSThread *thread, JSTaggedValue left, JSTaggedValue right); + static JSTaggedValue LessDyn(JSThread *thread, JSTaggedValue left, JSTaggedValue right); + static JSTaggedValue LessEqDyn(JSThread *thread, JSTaggedValue left, JSTaggedValue right); + static JSTaggedValue GreaterDyn(JSThread *thread, JSTaggedValue left, JSTaggedValue right); + static JSTaggedValue GreaterEqDyn(JSThread *thread, JSTaggedValue left, JSTaggedValue right); + + static JSTaggedValue ToJSTaggedValueWithInt32(JSThread *thread, JSTaggedValue value); + static JSTaggedValue ToJSTaggedValueWithUint32(JSThread *thread, JSTaggedValue value); + + static JSTaggedValue DelObjProp(JSThread *thread, JSTaggedValue obj, JSTaggedValue prop); + static JSTaggedValue NewObjDynRange(JSThread *thread, uint16_t argsCount, JSTaggedType *stkargs); + static JSTaggedValue CreateObjectWithExcludedKeys(JSThread *thread, uint16_t numKeys, JSTaggedValue objVal, + JSTaggedType *stkargs); + static JSTaggedValue ExpDyn(JSThread *thread, JSTaggedValue base, JSTaggedValue exponent); + static JSTaggedValue IsInDyn(JSThread *thread, JSTaggedValue prop, JSTaggedValue obj); + static JSTaggedValue InstanceofDyn(JSThread *thread, JSTaggedValue obj, JSTaggedValue target); + + static JSTaggedValue NewLexicalEnvDyn(JSThread *thread, uint16_t numVars); + static JSTaggedValue CreateIterResultObj(JSThread *thread, JSTaggedValue value, bool done); + + static JSTaggedValue CreateGeneratorObj(JSThread *thread, JSTaggedValue genFunc); + static JSTaggedValue SetGeneratorState(JSThread *thread, JSTaggedValue genObj, uint8_t state); + static JSTaggedValue CreateAsyncGeneratorObj(JSThread *thread, JSTaggedValue genFunc); + static JSTaggedValue SuspendGenerator(JSThread *thread, JSTaggedValue genObj, JSTaggedValue value); + static JSTaggedValue SuspendAsyncGenerator(JSThread *thread, JSTaggedValue asyncGenObj, JSTaggedValue value); + static JSTaggedValue AsyncFunctionAwait(JSThread *thread, JSTaggedValue asyncFuncObj, JSTaggedValue value); + static JSTaggedValue AsyncFunctionResolveOrReject(JSThread *thread, JSTaggedValue asyncFuncObj, JSTaggedValue value, + bool is_resolve); + static JSTaggedValue AsyncGeneratorResolve(JSThread *thread, JSTaggedValue asyncGenObj, JSTaggedValue value); + static JSTaggedValue AsyncGeneratorReject(JSThread *thread, JSTaggedValue asyncGenObj, JSTaggedValue value); + static JSTaggedValue NewObjSpreadDyn(JSThread *thread, JSTaggedValue func, JSTaggedValue newTarget, + JSTaggedValue array); + static void ThrowUndefinedIfHole(JSThread *thread, JSTaggedValue obj); + static void ThrowIfNotObject(JSThread *thread); + static void ThrowThrowNotExists(JSThread *thread); + static void ThrowPatternNonCoercible(JSThread *thread); + static JSTaggedValue ThrowIfSuperNotCorrectCall(JSThread *thread, uint16_t index, JSTaggedValue thisValue); + static void ThrowDeleteSuperProperty(JSThread *thread); + + static JSTaggedValue StOwnByName(JSThread *thread, JSTaggedValue obj, JSTaggedValue prop, JSTaggedValue value); + static JSTaggedValue StOwnByNameWithNameSet(JSThread *thread, JSTaggedValue obj, JSTaggedValue prop, + JSTaggedValue value); + static JSTaggedValue StOwnByIndex(JSThread *thread, JSTaggedValue obj, uint32_t idx, JSTaggedValue value); + static JSTaggedValue StOwnByValue(JSThread *thread, const JSHandle &obj, + const JSHandle &key, const JSHandle &value); + static JSTaggedValue StOwnByValueWithNameSet(JSThread *thread, JSHandle obj, + JSHandle key, JSHandle value); + static JSTaggedValue CreateEmptyArray(JSThread *thread, ObjectFactory *factory, JSHandle globalEnv); + static JSTaggedValue CreateEmptyObject(JSThread *thread, ObjectFactory *factory, JSHandle globalEnv); + static JSTaggedValue CreateObjectWithBuffer(JSThread *thread, ObjectFactory *factory, JSObject *literal); + static JSTaggedValue CreateObjectHavingMethod(JSThread *thread, ObjectFactory *factory, JSObject *literal, + JSTaggedValue env, ConstantPool *constpool); + static JSTaggedValue SetObjectWithProto(JSThread *thread, JSTaggedValue proto, JSTaggedValue obj); + static JSTaggedValue CreateArrayWithBuffer(JSThread *thread, ObjectFactory *factory, JSArray *literal); + + static JSTaggedValue GetMethod(JSThread *thread, JSTaggedValue object, JSTaggedValue prop_key); + static JSTaggedValue GetTemplateObject(JSThread *thread, JSTaggedValue literal); + static JSTaggedValue GetNextPropName(JSThread *thread, JSTaggedValue iter); + static JSTaggedValue CopyDataProperties(JSThread *thread, JSTaggedValue dst, JSTaggedValue src); + + static JSTaggedValue GetUnmappedArgs(JSThread *thread, uint32_t actualNumArgs, JSTaggedType *stkargs); + static JSTaggedValue CopyRestArgs(JSThread *thread, uint32_t restNumArgs, JSTaggedType *stkargs); + static JSTaggedValue GetIterator(JSThread *thread, JSTaggedValue obj, bool async = false, + JSTaggedValue method = JSTaggedValue::Hole()); + static JSTaggedValue CloseIterator(JSThread *thread, JSTaggedValue iter); + static JSTaggedValue ImportModule(JSThread *thread, JSTaggedValue moduleName); + static void StModuleVar(JSThread *thread, JSTaggedValue exportName, JSTaggedValue exportObj); + static void CopyModule(JSThread *thread, JSTaggedValue srcModule); + static JSTaggedValue LdModvarByName(JSThread *thread, JSTaggedValue moduleObj, JSTaggedValue itemName); + static JSTaggedValue CreateRegExpWithLiteral(JSThread *thread, JSTaggedValue pattern, uint8_t flags); + + static JSTaggedValue DefineGetterSetterByValue(JSThread *thread, JSTaggedValue obj, JSTaggedValue prop, + JSTaggedValue getter, JSTaggedValue setter, bool flag); + + static JSTaggedValue LdObjByIndex(JSThread *thread, JSTaggedValue obj, uint32_t idx, bool callGetter, + JSTaggedValue receiver); + static JSTaggedValue StObjByIndex(JSThread *thread, JSTaggedValue obj, uint32_t idx, JSTaggedValue value); + static JSTaggedValue LdObjByName(JSThread *thread, JSTaggedValue obj, JSTaggedValue prop, bool callGetter, + JSTaggedValue receiver); + static JSTaggedValue StObjByName(JSThread *thread, JSTaggedValue obj, JSTaggedValue prop, JSTaggedValue value); + static JSTaggedValue LdObjByValue(JSThread *thread, JSTaggedValue obj, JSTaggedValue prop, bool callGetter, + JSTaggedValue receiver); + static JSTaggedValue StObjByValue(JSThread *thread, JSTaggedValue obj, JSTaggedValue prop, JSTaggedValue value); + static JSTaggedValue TryLdGlobalByName(JSThread *thread, JSTaggedValue global, JSTaggedValue prop); + static JSTaggedValue LdGlobalVar(JSThread *thread, JSTaggedValue global, JSTaggedValue prop); + static JSTaggedValue StGlobalVar(JSThread *thread, JSTaggedValue prop, JSTaggedValue value); + static JSTaggedValue StGlobalRecord(JSThread *thread, JSTaggedValue prop, JSTaggedValue value, bool isConst); + static JSTaggedValue LdGlobalRecord(JSThread *thread, JSTaggedValue key, bool *found); + static JSTaggedValue TryUpdateGlobalRecord(JSThread *thread, JSTaggedValue prop, JSTaggedValue value); + static JSTaggedValue StArraySpread(JSThread *thread, JSTaggedValue dst, JSTaggedValue index, JSTaggedValue src); + + static JSTaggedValue DefineGeneratorFunc(JSThread *thread, JSMethod *method); + static JSTaggedValue DefineAsyncFunc(JSThread *thread, JSMethod *method); + static JSTaggedValue DefineAsyncGeneratorFunc(JSThread *thread, JSMethod *method); + static JSTaggedValue DefineNCFuncDyn(JSThread *thread, JSMethod *method); + static JSTaggedValue DefinefuncDyn(JSThread *thread, JSMethod *method); + static JSTaggedValue NewClassFunc(JSThread *thread, JSMethod *method); + + static JSTaggedValue DefineClass(JSThread *thread, JSFunction *func, TaggedArray *literal, JSTaggedValue proto, + JSTaggedValue lexenv, ConstantPool *constpool); + static JSTaggedValue SuperCall(JSThread *thread, JSTaggedValue func, JSTaggedValue newTarget, uint16_t argsCount, + JSTaggedType *stkargs); + static JSTaggedValue SuperCallSpread(JSThread *thread, JSTaggedValue func, JSTaggedValue newTarget, + JSTaggedValue array); + static JSTaggedValue DefineMethod(JSThread *thread, JSMethod *method, const JSHandle &homeObject); + static JSTaggedValue LdSuperByValue(JSThread *thread, JSTaggedValue obj, JSTaggedValue key, JSTaggedValue thisFunc); + static JSTaggedValue StSuperByValue(JSThread *thread, JSTaggedValue obj, JSTaggedValue key, JSTaggedValue value, + JSTaggedValue thisFunc); + static JSTaggedValue NotifyInlineCache(JSThread *thread, JSFunction *func, JSMethod *method); + static JSTaggedValue ThrowReferenceError(JSThread *thread, JSTaggedValue prop, const char *desc); + + static JSTaggedValue ResolveClass(JSThread *thread, JSTaggedValue ctor, TaggedArray *literal, JSTaggedValue base, + JSTaggedValue lexenv, ConstantPool *constpool); + static JSTaggedValue CloneClassFromTemplate(JSThread *thread, JSTaggedValue ctor, JSTaggedValue base, + JSTaggedValue lexenv, ConstantPool *constpool); + static JSTaggedValue SetClassConstructorLength(JSThread *thread, JSTaggedValue ctor, JSTaggedValue length); + /* -------------- Common API End, Don't change those interface!!! ----------------- */ + +private: + static JSTaggedValue ThrowTypeError(JSThread *thread, const char *message); + static JSTaggedValue ThrowSyntaxError(JSThread *thread, const char *message); + static JSTaggedValue GetCallSpreadArgs(JSThread *thread, JSTaggedValue array); + static JSTaggedValue SetClassInheritanceRelationship(JSThread *thread, JSTaggedValue ctor, JSTaggedValue base); +}; +} // namespace panda::ecmascript +#endif // ECMASCRIPT_INTERPRETER_SLOW_RUNTIME_STUB_H diff --git a/runtime/interpreter/templates/debugger_instruction_dispatch.inl b/runtime/interpreter/templates/debugger_instruction_dispatch.inl new file mode 100644 index 000000000..e45c0bae5 --- /dev/null +++ b/runtime/interpreter/templates/debugger_instruction_dispatch.inl @@ -0,0 +1,272 @@ +/* + * Copyright (c) 2021-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. + */ + + &&DEBUG_HANDLE_LDNAN_PREF, + &&DEBUG_HANDLE_LDINFINITY_PREF, + &&DEBUG_HANDLE_LDGLOBALTHIS_PREF, + &&DEBUG_HANDLE_LDUNDEFINED_PREF, + &&DEBUG_HANDLE_LDNULL_PREF, + &&DEBUG_HANDLE_LDSYMBOL_PREF, + &&DEBUG_HANDLE_LDGLOBAL_PREF, + &&DEBUG_HANDLE_LDTRUE_PREF, + &&DEBUG_HANDLE_LDFALSE_PREF, + &&DEBUG_HANDLE_THROWDYN_PREF, + &&DEBUG_HANDLE_TYPEOFDYN_PREF, + &&DEBUG_HANDLE_LDLEXENVDYN_PREF, + &&DEBUG_HANDLE_POPLEXENVDYN_PREF, + &&DEBUG_HANDLE_BUILTIN_COPYLEXENVDYN_IMM8, + &&DEBUG_HANDLE_GETUNMAPPEDARGS_PREF, + &&DEBUG_HANDLE_GETPROPITERATOR_PREF, + &&DEBUG_HANDLE_ASYNCFUNCTIONENTER_PREF, + &&DEBUG_HANDLE_LDHOLE_PREF, + &&DEBUG_HANDLE_RETURNUNDEFINED_PREF, + &&DEBUG_HANDLE_CREATEEMPTYOBJECT_PREF, + &&DEBUG_HANDLE_CREATEEMPTYARRAY_PREF, + &&DEBUG_HANDLE_GETITERATOR_PREF, + &&DEBUG_HANDLE_THROWTHROWNOTEXISTS_PREF, + &&DEBUG_HANDLE_THROWPATTERNNONCOERCIBLE_PREF, + &&DEBUG_HANDLE_LDHOMEOBJECT_PREF, + &&DEBUG_HANDLE_THROWDELETESUPERPROPERTY_PREF, + &&DEBUG_HANDLE_DEBUGGER_PREF, + &&DEBUG_HANDLE_ADD2DYN_PREF_V8, + &&DEBUG_HANDLE_SUB2DYN_PREF_V8, + &&DEBUG_HANDLE_MUL2DYN_PREF_V8, + &&DEBUG_HANDLE_DIV2DYN_PREF_V8, + &&DEBUG_HANDLE_MOD2DYN_PREF_V8, + &&DEBUG_HANDLE_EQDYN_PREF_V8, + &&DEBUG_HANDLE_NOTEQDYN_PREF_V8, + &&DEBUG_HANDLE_LESSDYN_PREF_V8, + &&DEBUG_HANDLE_LESSEQDYN_PREF_V8, + &&DEBUG_HANDLE_GREATERDYN_PREF_V8, + &&DEBUG_HANDLE_GREATEREQDYN_PREF_V8, + &&DEBUG_HANDLE_SHL2DYN_PREF_V8, + &&DEBUG_HANDLE_SHR2DYN_PREF_V8, + &&DEBUG_HANDLE_ASHR2DYN_PREF_V8, + &&DEBUG_HANDLE_AND2DYN_PREF_V8, + &&DEBUG_HANDLE_OR2DYN_PREF_V8, + &&DEBUG_HANDLE_XOR2DYN_PREF_V8, + &&DEBUG_HANDLE_TONUMBER_PREF_V8, + &&DEBUG_HANDLE_NEGDYN_PREF_V8, + &&DEBUG_HANDLE_NOTDYN_PREF_V8, + &&DEBUG_HANDLE_INCDYN_PREF_V8, + &&DEBUG_HANDLE_DECDYN_PREF_V8, + &&DEBUG_HANDLE_EXPDYN_PREF_V8, + &&DEBUG_HANDLE_ISINDYN_PREF_V8, + &&DEBUG_HANDLE_INSTANCEOFDYN_PREF_V8, + &&DEBUG_HANDLE_STRICTNOTEQDYN_PREF_V8, + &&DEBUG_HANDLE_STRICTEQDYN_PREF_V8, + &&DEBUG_HANDLE_RESUMEGENERATOR_PREF_V8, + &&DEBUG_HANDLE_GETRESUMEMODE_PREF_V8, + &&DEBUG_HANDLE_CREATEGENERATOROBJ_PREF_V8, + &&DEBUG_HANDLE_THROWCONSTASSIGNMENT_PREF_V8, + &&DEBUG_HANDLE_GETTEMPLATEOBJECT_PREF_V8, + &&DEBUG_HANDLE_GETNEXTPROPNAME_PREF_V8, + &&DEBUG_HANDLE_CALLARG0DYN_PREF_V8, + &&DEBUG_HANDLE_THROWIFNOTOBJECT_PREF_V8, + &&DEBUG_HANDLE_ITERNEXT_PREF_V8, + &&DEBUG_HANDLE_CLOSEITERATOR_PREF_V8, + &&DEBUG_HANDLE_COPYMODULE_PREF_V8, + &&DEBUG_HANDLE_SUPERCALLSPREAD_PREF_V8, + &&DEBUG_HANDLE_DELOBJPROP_PREF_V8_V8, + &&DEBUG_HANDLE_NEWOBJSPREADDYN_PREF_V8_V8, + &&DEBUG_HANDLE_CREATEITERRESULTOBJ_PREF_V8_V8, + &&DEBUG_HANDLE_SUSPENDGENERATOR_PREF_V8_V8, + &&DEBUG_HANDLE_ASYNCFUNCTIONAWAITUNCAUGHT_PREF_V8_V8, + &&DEBUG_HANDLE_THROWUNDEFINEDIFHOLE_PREF_V8_V8, + &&DEBUG_HANDLE_CALLARG1DYN_PREF_V8_V8, + &&DEBUG_HANDLE_COPYDATAPROPERTIES_PREF_V8_V8, + &&DEBUG_HANDLE_STARRAYSPREAD_PREF_V8_V8, + &&DEBUG_HANDLE_GETITERATORNEXT_PREF_V8_V8, + &&DEBUG_HANDLE_SETOBJECTWITHPROTO_PREF_V8_V8, + &&DEBUG_HANDLE_LDOBJBYVALUE_PREF_V8_V8, + &&DEBUG_HANDLE_STOBJBYVALUE_PREF_V8_V8, + &&DEBUG_HANDLE_STOWNBYVALUE_PREF_V8_V8, + &&DEBUG_HANDLE_LDSUPERBYVALUE_PREF_V8_V8, + &&DEBUG_HANDLE_STSUPERBYVALUE_PREF_V8_V8, + &&DEBUG_HANDLE_LDOBJBYINDEX_PREF_V8_IMM32, + &&DEBUG_HANDLE_STOBJBYINDEX_PREF_V8_IMM32, + &&DEBUG_HANDLE_STOWNBYINDEX_PREF_V8_IMM32, + &&DEBUG_HANDLE_CALLSPREADDYN_PREF_V8_V8_V8, + &&DEBUG_HANDLE_ASYNCFUNCTIONRESOLVE_PREF_V8_V8_V8, + &&DEBUG_HANDLE_ASYNCFUNCTIONREJECT_PREF_V8_V8_V8, + &&DEBUG_HANDLE_CALLARGS2DYN_PREF_V8_V8_V8, + &&DEBUG_HANDLE_CALLARGS3DYN_PREF_V8_V8_V8_V8, + &&DEBUG_HANDLE_DEFINEGETTERSETTERBYVALUE_PREF_V8_V8_V8_V8, + &&DEBUG_HANDLE_NEWOBJDYNRANGE_PREF_IMM16_V8, + &&DEBUG_HANDLE_CALLIRANGEDYN_PREF_IMM16_V8, + &&DEBUG_HANDLE_CALLITHISRANGEDYN_PREF_IMM16_V8, + &&DEBUG_HANDLE_SUPERCALL_PREF_IMM16_V8, + &&DEBUG_HANDLE_CREATEOBJECTWITHEXCLUDEDKEYS_PREF_IMM16_V8_V8, + &&DEBUG_HANDLE_DEFINEFUNCDYN_PREF_ID16_IMM16_V8, + &&DEBUG_HANDLE_DEFINENCFUNCDYN_PREF_ID16_IMM16_V8, + &&DEBUG_HANDLE_DEFINEGENERATORFUNC_PREF_ID16_IMM16_V8, + &&DEBUG_HANDLE_DEFINEASYNCFUNC_PREF_ID16_IMM16_V8, + &&DEBUG_HANDLE_DEFINEMETHOD_PREF_ID16_IMM16_V8, + &&DEBUG_HANDLE_NEWLEXENVDYN_PREF_IMM16, + &&DEBUG_HANDLE_COPYRESTARGS_PREF_IMM16, + &&DEBUG_HANDLE_CREATEARRAYWITHBUFFER_PREF_IMM16, + &&DEBUG_HANDLE_CREATEOBJECTHAVINGMETHOD_PREF_IMM16, + &&DEBUG_HANDLE_THROWIFSUPERNOTCORRECTCALL_PREF_IMM16, + &&DEBUG_HANDLE_CREATEOBJECTWITHBUFFER_PREF_IMM16, + &&DEBUG_HANDLE_LDLEXVARDYN_PREF_IMM4_IMM4, + &&DEBUG_HANDLE_LDLEXVARDYN_PREF_IMM8_IMM8, + &&DEBUG_HANDLE_LDLEXVARDYN_PREF_IMM16_IMM16, + &&DEBUG_HANDLE_STLEXVARDYN_PREF_IMM4_IMM4_V8, + &&DEBUG_HANDLE_STLEXVARDYN_PREF_IMM8_IMM8_V8, + &&DEBUG_HANDLE_STLEXVARDYN_PREF_IMM16_IMM16_V8, + &&DEBUG_HANDLE_DEFINECLASSWITHBUFFER_PREF_ID16_IMM16_IMM16_V8_V8, + &&DEBUG_HANDLE_IMPORTMODULE_PREF_ID32, + &&DEBUG_HANDLE_STMODULEVAR_PREF_ID32, + &&DEBUG_HANDLE_TRYLDGLOBALBYNAME_PREF_ID32, + &&DEBUG_HANDLE_TRYSTGLOBALBYNAME_PREF_ID32, + &&DEBUG_HANDLE_LDGLOBALVAR_PREF_ID32, + &&DEBUG_HANDLE_STGLOBALVAR_PREF_ID32, + &&DEBUG_HANDLE_LDOBJBYNAME_PREF_ID32_V8, + &&DEBUG_HANDLE_STOBJBYNAME_PREF_ID32_V8, + &&DEBUG_HANDLE_STOWNBYNAME_PREF_ID32_V8, + &&DEBUG_HANDLE_LDSUPERBYNAME_PREF_ID32_V8, + &&DEBUG_HANDLE_STSUPERBYNAME_PREF_ID32_V8, + &&DEBUG_HANDLE_LDMODVARBYNAME_PREF_ID32_V8, + &&DEBUG_HANDLE_CREATEREGEXPWITHLITERAL_PREF_ID32_IMM8, + &&DEBUG_HANDLE_ISTRUE_PREF, + &&DEBUG_HANDLE_ISFALSE_PREF, + &&DEBUG_HANDLE_STCONSTTOGLOBALRECORD_PREF_ID32, + &&DEBUG_HANDLE_STLETTOGLOBALRECORD_PREF_ID32, + &&DEBUG_HANDLE_STCLASSTOGLOBALRECORD_PREF_ID32, + &&DEBUG_HANDLE_STOWNBYVALUEWITHNAMESET_PREF_V8_V8, + &&DEBUG_HANDLE_STOWNBYNAMEWITHNAMESET_PREF_ID32_V8, + &&DEBUG_HANDLE_LDFUNCTION_PREF, + &&DEBUG_HANDLE_MOV_DYN_V8_V8, + &&DEBUG_HANDLE_MOV_DYN_V16_V16, + &&DEBUG_HANDLE_LDA_STR_ID32, + &&DEBUG_HANDLE_LDAI_DYN_IMM32, + &&DEBUG_HANDLE_FLDAI_DYN_IMM64, + &&DEBUG_HANDLE_JMP_IMM8, + &&DEBUG_HANDLE_JMP_IMM16, + &&DEBUG_HANDLE_JMP_IMM32, + &&DEBUG_HANDLE_JEQZ_IMM8, + &&DEBUG_HANDLE_JEQZ_IMM16, + &&DEBUG_HANDLE_LDA_DYN_V8, + &&DEBUG_HANDLE_STA_DYN_V8, + &&DEBUG_HANDLE_RETURN_DYN, + &&DEBUG_HANDLE_MOV_V4_V4, + &&DEBUG_HANDLE_JNEZ_IMM8, + &&DEBUG_HANDLE_JNEZ_IMM16, + &&DEBUG_EXCEPTION_HANDLER, + &&DEBUG_HANDLE_OVERFLOW, + &&DEBUG_HANDLE_OVERFLOW, + &&DEBUG_HANDLE_OVERFLOW, + &&DEBUG_HANDLE_OVERFLOW, + &&DEBUG_HANDLE_OVERFLOW, + &&DEBUG_HANDLE_OVERFLOW, + &&DEBUG_HANDLE_OVERFLOW, + &&DEBUG_HANDLE_OVERFLOW, + &&DEBUG_HANDLE_OVERFLOW, + &&DEBUG_HANDLE_OVERFLOW, + &&DEBUG_HANDLE_OVERFLOW, + &&DEBUG_HANDLE_OVERFLOW, + &&DEBUG_HANDLE_OVERFLOW, + &&DEBUG_HANDLE_OVERFLOW, + &&DEBUG_HANDLE_OVERFLOW, + &&DEBUG_HANDLE_OVERFLOW, + &&DEBUG_HANDLE_OVERFLOW, + &&DEBUG_HANDLE_OVERFLOW, + &&DEBUG_HANDLE_OVERFLOW, + &&DEBUG_HANDLE_OVERFLOW, + &&DEBUG_HANDLE_OVERFLOW, + &&DEBUG_HANDLE_OVERFLOW, + &&DEBUG_HANDLE_OVERFLOW, + &&DEBUG_HANDLE_OVERFLOW, + &&DEBUG_HANDLE_OVERFLOW, + &&DEBUG_HANDLE_OVERFLOW, + &&DEBUG_HANDLE_OVERFLOW, + &&DEBUG_HANDLE_OVERFLOW, + &&DEBUG_HANDLE_OVERFLOW, + &&DEBUG_HANDLE_OVERFLOW, + &&DEBUG_HANDLE_OVERFLOW, + &&DEBUG_HANDLE_OVERFLOW, + &&DEBUG_HANDLE_OVERFLOW, + &&DEBUG_HANDLE_OVERFLOW, + &&DEBUG_HANDLE_OVERFLOW, + &&DEBUG_HANDLE_OVERFLOW, + &&DEBUG_HANDLE_OVERFLOW, + &&DEBUG_HANDLE_OVERFLOW, + &&DEBUG_HANDLE_OVERFLOW, + &&DEBUG_HANDLE_OVERFLOW, + &&DEBUG_HANDLE_OVERFLOW, + &&DEBUG_HANDLE_OVERFLOW, + &&DEBUG_HANDLE_OVERFLOW, + &&DEBUG_HANDLE_OVERFLOW, + &&DEBUG_HANDLE_OVERFLOW, + &&DEBUG_HANDLE_OVERFLOW, + &&DEBUG_HANDLE_OVERFLOW, + &&DEBUG_HANDLE_OVERFLOW, + &&DEBUG_HANDLE_OVERFLOW, + &&DEBUG_HANDLE_OVERFLOW, + &&DEBUG_HANDLE_OVERFLOW, + &&DEBUG_HANDLE_OVERFLOW, + &&DEBUG_HANDLE_OVERFLOW, + &&DEBUG_HANDLE_OVERFLOW, + &&DEBUG_HANDLE_OVERFLOW, + &&DEBUG_HANDLE_OVERFLOW, + &&DEBUG_HANDLE_OVERFLOW, + &&DEBUG_HANDLE_OVERFLOW, + &&DEBUG_HANDLE_OVERFLOW, + &&DEBUG_HANDLE_OVERFLOW, + &&DEBUG_HANDLE_OVERFLOW, + &&DEBUG_HANDLE_OVERFLOW, + &&DEBUG_HANDLE_OVERFLOW, + &&DEBUG_HANDLE_OVERFLOW, + &&DEBUG_HANDLE_OVERFLOW, + &&DEBUG_HANDLE_OVERFLOW, + &&DEBUG_HANDLE_OVERFLOW, + &&DEBUG_HANDLE_OVERFLOW, + &&DEBUG_HANDLE_OVERFLOW, + &&DEBUG_HANDLE_OVERFLOW, + &&DEBUG_HANDLE_OVERFLOW, + &&DEBUG_HANDLE_OVERFLOW, + &&DEBUG_HANDLE_OVERFLOW, + &&DEBUG_HANDLE_OVERFLOW, + &&DEBUG_HANDLE_OVERFLOW, + &&DEBUG_HANDLE_OVERFLOW, + &&DEBUG_HANDLE_OVERFLOW, + &&DEBUG_HANDLE_OVERFLOW, + &&DEBUG_HANDLE_OVERFLOW, + &&DEBUG_HANDLE_OVERFLOW, + &&DEBUG_HANDLE_OVERFLOW, + &&DEBUG_HANDLE_OVERFLOW, + &&DEBUG_HANDLE_OVERFLOW, + &&DEBUG_HANDLE_OVERFLOW, + &&DEBUG_HANDLE_OVERFLOW, + &&DEBUG_HANDLE_OVERFLOW, + &&DEBUG_HANDLE_OVERFLOW, + &&DEBUG_HANDLE_OVERFLOW, + &&DEBUG_HANDLE_OVERFLOW, + &&DEBUG_HANDLE_OVERFLOW, + &&DEBUG_HANDLE_OVERFLOW, + &&DEBUG_HANDLE_OVERFLOW, + &&DEBUG_HANDLE_OVERFLOW, + &&DEBUG_HANDLE_OVERFLOW, + &&DEBUG_HANDLE_OVERFLOW, + &&DEBUG_HANDLE_OVERFLOW, + &&DEBUG_HANDLE_OVERFLOW, + &&DEBUG_HANDLE_OVERFLOW, + &&DEBUG_HANDLE_OVERFLOW, + &&DEBUG_HANDLE_OVERFLOW, + &&DEBUG_HANDLE_OVERFLOW, + &&DEBUG_HANDLE_OVERFLOW, + &&DEBUG_HANDLE_OVERFLOW, + &&DEBUG_HANDLE_OVERFLOW, + &&DEBUG_HANDLE_OVERFLOW, \ No newline at end of file diff --git a/runtime/interpreter/templates/debugger_instruction_handler.inl b/runtime/interpreter/templates/debugger_instruction_handler.inl new file mode 100644 index 000000000..7c816660f --- /dev/null +++ b/runtime/interpreter/templates/debugger_instruction_handler.inl @@ -0,0 +1,780 @@ +/* + * Copyright (c) 2021-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. + */ + + HANDLE_OPCODE(DEBUG_HANDLE_LDNAN_PREF) + { + NOTIFY_DEBUGGER_EVENT(); + REAL_GOTO_DISPATCH_OPCODE(EcmaOpcode::LDNAN_PREF); + } + HANDLE_OPCODE(DEBUG_HANDLE_LDINFINITY_PREF) + { + NOTIFY_DEBUGGER_EVENT(); + REAL_GOTO_DISPATCH_OPCODE(EcmaOpcode::LDINFINITY_PREF); + } + HANDLE_OPCODE(DEBUG_HANDLE_LDGLOBALTHIS_PREF) + { + NOTIFY_DEBUGGER_EVENT(); + REAL_GOTO_DISPATCH_OPCODE(EcmaOpcode::LDGLOBALTHIS_PREF); + } + HANDLE_OPCODE(DEBUG_HANDLE_LDUNDEFINED_PREF) + { + NOTIFY_DEBUGGER_EVENT(); + REAL_GOTO_DISPATCH_OPCODE(EcmaOpcode::LDUNDEFINED_PREF); + } + HANDLE_OPCODE(DEBUG_HANDLE_LDNULL_PREF) + { + NOTIFY_DEBUGGER_EVENT(); + REAL_GOTO_DISPATCH_OPCODE(EcmaOpcode::LDNULL_PREF); + } + HANDLE_OPCODE(DEBUG_HANDLE_LDSYMBOL_PREF) + { + NOTIFY_DEBUGGER_EVENT(); + REAL_GOTO_DISPATCH_OPCODE(EcmaOpcode::LDSYMBOL_PREF); + } + HANDLE_OPCODE(DEBUG_HANDLE_LDGLOBAL_PREF) + { + NOTIFY_DEBUGGER_EVENT(); + REAL_GOTO_DISPATCH_OPCODE(EcmaOpcode::LDGLOBAL_PREF); + } + HANDLE_OPCODE(DEBUG_HANDLE_LDTRUE_PREF) + { + NOTIFY_DEBUGGER_EVENT(); + REAL_GOTO_DISPATCH_OPCODE(EcmaOpcode::LDTRUE_PREF); + } + HANDLE_OPCODE(DEBUG_HANDLE_LDFALSE_PREF) + { + NOTIFY_DEBUGGER_EVENT(); + REAL_GOTO_DISPATCH_OPCODE(EcmaOpcode::LDFALSE_PREF); + } + HANDLE_OPCODE(DEBUG_HANDLE_THROWDYN_PREF) + { + NOTIFY_DEBUGGER_EVENT(); + REAL_GOTO_DISPATCH_OPCODE(EcmaOpcode::THROWDYN_PREF); + } + HANDLE_OPCODE(DEBUG_HANDLE_TYPEOFDYN_PREF) + { + NOTIFY_DEBUGGER_EVENT(); + REAL_GOTO_DISPATCH_OPCODE(EcmaOpcode::TYPEOFDYN_PREF); + } + HANDLE_OPCODE(DEBUG_HANDLE_LDLEXENVDYN_PREF) + { + NOTIFY_DEBUGGER_EVENT(); + REAL_GOTO_DISPATCH_OPCODE(EcmaOpcode::LDLEXENVDYN_PREF); + } + HANDLE_OPCODE(DEBUG_HANDLE_POPLEXENVDYN_PREF) + { + NOTIFY_DEBUGGER_EVENT(); + REAL_GOTO_DISPATCH_OPCODE(EcmaOpcode::POPLEXENVDYN_PREF); + } + HANDLE_OPCODE(DEBUG_HANDLE_GETUNMAPPEDARGS_PREF) + { + NOTIFY_DEBUGGER_EVENT(); + REAL_GOTO_DISPATCH_OPCODE(EcmaOpcode::GETUNMAPPEDARGS_PREF); + } + HANDLE_OPCODE(DEBUG_HANDLE_GETPROPITERATOR_PREF) + { + NOTIFY_DEBUGGER_EVENT(); + REAL_GOTO_DISPATCH_OPCODE(EcmaOpcode::GETPROPITERATOR_PREF); + } + HANDLE_OPCODE(DEBUG_HANDLE_ASYNCFUNCTIONENTER_PREF) + { + NOTIFY_DEBUGGER_EVENT(); + REAL_GOTO_DISPATCH_OPCODE(EcmaOpcode::ASYNCFUNCTIONENTER_PREF); + } + HANDLE_OPCODE(DEBUG_HANDLE_LDHOLE_PREF) + { + NOTIFY_DEBUGGER_EVENT(); + REAL_GOTO_DISPATCH_OPCODE(EcmaOpcode::LDHOLE_PREF); + } + HANDLE_OPCODE(DEBUG_HANDLE_RETURNUNDEFINED_PREF) + { + NOTIFY_DEBUGGER_EVENT(); + REAL_GOTO_DISPATCH_OPCODE(EcmaOpcode::RETURNUNDEFINED_PREF); + } + HANDLE_OPCODE(DEBUG_HANDLE_CREATEEMPTYOBJECT_PREF) + { + NOTIFY_DEBUGGER_EVENT(); + REAL_GOTO_DISPATCH_OPCODE(EcmaOpcode::CREATEEMPTYOBJECT_PREF); + } + HANDLE_OPCODE(DEBUG_HANDLE_CREATEEMPTYARRAY_PREF) + { + NOTIFY_DEBUGGER_EVENT(); + REAL_GOTO_DISPATCH_OPCODE(EcmaOpcode::CREATEEMPTYARRAY_PREF); + } + HANDLE_OPCODE(DEBUG_HANDLE_GETITERATOR_PREF) + { + NOTIFY_DEBUGGER_EVENT(); + REAL_GOTO_DISPATCH_OPCODE(EcmaOpcode::GETITERATOR_PREF); + } + HANDLE_OPCODE(DEBUG_HANDLE_THROWTHROWNOTEXISTS_PREF) + { + NOTIFY_DEBUGGER_EVENT(); + REAL_GOTO_DISPATCH_OPCODE(EcmaOpcode::THROWTHROWNOTEXISTS_PREF); + } + HANDLE_OPCODE(DEBUG_HANDLE_THROWPATTERNNONCOERCIBLE_PREF) + { + NOTIFY_DEBUGGER_EVENT(); + REAL_GOTO_DISPATCH_OPCODE(EcmaOpcode::THROWPATTERNNONCOERCIBLE_PREF); + } + HANDLE_OPCODE(DEBUG_HANDLE_LDHOMEOBJECT_PREF) + { + NOTIFY_DEBUGGER_EVENT(); + REAL_GOTO_DISPATCH_OPCODE(EcmaOpcode::LDHOMEOBJECT_PREF); + } + HANDLE_OPCODE(DEBUG_HANDLE_THROWDELETESUPERPROPERTY_PREF) + { + NOTIFY_DEBUGGER_EVENT(); + REAL_GOTO_DISPATCH_OPCODE(EcmaOpcode::THROWDELETESUPERPROPERTY_PREF); + } + HANDLE_OPCODE(DEBUG_HANDLE_DEBUGGER_PREF) + { + NOTIFY_DEBUGGER_EVENT(); + REAL_GOTO_DISPATCH_OPCODE(EcmaOpcode::DEBUGGER_PREF); + } + HANDLE_OPCODE(DEBUG_HANDLE_ADD2DYN_PREF_V8) + { + NOTIFY_DEBUGGER_EVENT(); + REAL_GOTO_DISPATCH_OPCODE(EcmaOpcode::ADD2DYN_PREF_V8); + } + HANDLE_OPCODE(DEBUG_HANDLE_SUB2DYN_PREF_V8) + { + NOTIFY_DEBUGGER_EVENT(); + REAL_GOTO_DISPATCH_OPCODE(EcmaOpcode::SUB2DYN_PREF_V8); + } + HANDLE_OPCODE(DEBUG_HANDLE_MUL2DYN_PREF_V8) + { + NOTIFY_DEBUGGER_EVENT(); + REAL_GOTO_DISPATCH_OPCODE(EcmaOpcode::MUL2DYN_PREF_V8); + } + HANDLE_OPCODE(DEBUG_HANDLE_DIV2DYN_PREF_V8) + { + NOTIFY_DEBUGGER_EVENT(); + REAL_GOTO_DISPATCH_OPCODE(EcmaOpcode::DIV2DYN_PREF_V8); + } + HANDLE_OPCODE(DEBUG_HANDLE_MOD2DYN_PREF_V8) + { + NOTIFY_DEBUGGER_EVENT(); + REAL_GOTO_DISPATCH_OPCODE(EcmaOpcode::MOD2DYN_PREF_V8); + } + HANDLE_OPCODE(DEBUG_HANDLE_EQDYN_PREF_V8) + { + NOTIFY_DEBUGGER_EVENT(); + REAL_GOTO_DISPATCH_OPCODE(EcmaOpcode::EQDYN_PREF_V8); + } + HANDLE_OPCODE(DEBUG_HANDLE_NOTEQDYN_PREF_V8) + { + NOTIFY_DEBUGGER_EVENT(); + REAL_GOTO_DISPATCH_OPCODE(EcmaOpcode::NOTEQDYN_PREF_V8); + } + HANDLE_OPCODE(DEBUG_HANDLE_LESSDYN_PREF_V8) + { + NOTIFY_DEBUGGER_EVENT(); + REAL_GOTO_DISPATCH_OPCODE(EcmaOpcode::LESSDYN_PREF_V8); + } + HANDLE_OPCODE(DEBUG_HANDLE_LESSEQDYN_PREF_V8) + { + NOTIFY_DEBUGGER_EVENT(); + REAL_GOTO_DISPATCH_OPCODE(EcmaOpcode::LESSEQDYN_PREF_V8); + } + HANDLE_OPCODE(DEBUG_HANDLE_GREATERDYN_PREF_V8) + { + NOTIFY_DEBUGGER_EVENT(); + REAL_GOTO_DISPATCH_OPCODE(EcmaOpcode::GREATERDYN_PREF_V8); + } + HANDLE_OPCODE(DEBUG_HANDLE_GREATEREQDYN_PREF_V8) + { + NOTIFY_DEBUGGER_EVENT(); + REAL_GOTO_DISPATCH_OPCODE(EcmaOpcode::GREATEREQDYN_PREF_V8); + } + HANDLE_OPCODE(DEBUG_HANDLE_SHL2DYN_PREF_V8) + { + NOTIFY_DEBUGGER_EVENT(); + REAL_GOTO_DISPATCH_OPCODE(EcmaOpcode::SHL2DYN_PREF_V8); + } + HANDLE_OPCODE(DEBUG_HANDLE_SHR2DYN_PREF_V8) + { + NOTIFY_DEBUGGER_EVENT(); + REAL_GOTO_DISPATCH_OPCODE(EcmaOpcode::SHR2DYN_PREF_V8); + } + HANDLE_OPCODE(DEBUG_HANDLE_ASHR2DYN_PREF_V8) + { + NOTIFY_DEBUGGER_EVENT(); + REAL_GOTO_DISPATCH_OPCODE(EcmaOpcode::ASHR2DYN_PREF_V8); + } + HANDLE_OPCODE(DEBUG_HANDLE_AND2DYN_PREF_V8) + { + NOTIFY_DEBUGGER_EVENT(); + REAL_GOTO_DISPATCH_OPCODE(EcmaOpcode::AND2DYN_PREF_V8); + } + HANDLE_OPCODE(DEBUG_HANDLE_OR2DYN_PREF_V8) + { + NOTIFY_DEBUGGER_EVENT(); + REAL_GOTO_DISPATCH_OPCODE(EcmaOpcode::OR2DYN_PREF_V8); + } + HANDLE_OPCODE(DEBUG_HANDLE_XOR2DYN_PREF_V8) + { + NOTIFY_DEBUGGER_EVENT(); + REAL_GOTO_DISPATCH_OPCODE(EcmaOpcode::XOR2DYN_PREF_V8); + } + HANDLE_OPCODE(DEBUG_HANDLE_TONUMBER_PREF_V8) + { + NOTIFY_DEBUGGER_EVENT(); + REAL_GOTO_DISPATCH_OPCODE(EcmaOpcode::TONUMBER_PREF_V8); + } + HANDLE_OPCODE(DEBUG_HANDLE_NEGDYN_PREF_V8) + { + NOTIFY_DEBUGGER_EVENT(); + REAL_GOTO_DISPATCH_OPCODE(EcmaOpcode::NEGDYN_PREF_V8); + } + HANDLE_OPCODE(DEBUG_HANDLE_NOTDYN_PREF_V8) + { + NOTIFY_DEBUGGER_EVENT(); + REAL_GOTO_DISPATCH_OPCODE(EcmaOpcode::NOTDYN_PREF_V8); + } + HANDLE_OPCODE(DEBUG_HANDLE_INCDYN_PREF_V8) + { + NOTIFY_DEBUGGER_EVENT(); + REAL_GOTO_DISPATCH_OPCODE(EcmaOpcode::INCDYN_PREF_V8); + } + HANDLE_OPCODE(DEBUG_HANDLE_DECDYN_PREF_V8) + { + NOTIFY_DEBUGGER_EVENT(); + REAL_GOTO_DISPATCH_OPCODE(EcmaOpcode::DECDYN_PREF_V8); + } + HANDLE_OPCODE(DEBUG_HANDLE_EXPDYN_PREF_V8) + { + NOTIFY_DEBUGGER_EVENT(); + REAL_GOTO_DISPATCH_OPCODE(EcmaOpcode::EXPDYN_PREF_V8); + } + HANDLE_OPCODE(DEBUG_HANDLE_ISINDYN_PREF_V8) + { + NOTIFY_DEBUGGER_EVENT(); + REAL_GOTO_DISPATCH_OPCODE(EcmaOpcode::ISINDYN_PREF_V8); + } + HANDLE_OPCODE(DEBUG_HANDLE_INSTANCEOFDYN_PREF_V8) + { + NOTIFY_DEBUGGER_EVENT(); + REAL_GOTO_DISPATCH_OPCODE(EcmaOpcode::INSTANCEOFDYN_PREF_V8); + } + HANDLE_OPCODE(DEBUG_HANDLE_STRICTNOTEQDYN_PREF_V8) + { + NOTIFY_DEBUGGER_EVENT(); + REAL_GOTO_DISPATCH_OPCODE(EcmaOpcode::STRICTNOTEQDYN_PREF_V8); + } + HANDLE_OPCODE(DEBUG_HANDLE_STRICTEQDYN_PREF_V8) + { + NOTIFY_DEBUGGER_EVENT(); + REAL_GOTO_DISPATCH_OPCODE(EcmaOpcode::STRICTEQDYN_PREF_V8); + } + HANDLE_OPCODE(DEBUG_HANDLE_RESUMEGENERATOR_PREF_V8) + { + NOTIFY_DEBUGGER_EVENT(); + REAL_GOTO_DISPATCH_OPCODE(EcmaOpcode::RESUMEGENERATOR_PREF_V8); + } + HANDLE_OPCODE(DEBUG_HANDLE_GETRESUMEMODE_PREF_V8) + { + NOTIFY_DEBUGGER_EVENT(); + REAL_GOTO_DISPATCH_OPCODE(EcmaOpcode::GETRESUMEMODE_PREF_V8); + } + HANDLE_OPCODE(DEBUG_HANDLE_CREATEGENERATOROBJ_PREF_V8) + { + NOTIFY_DEBUGGER_EVENT(); + REAL_GOTO_DISPATCH_OPCODE(EcmaOpcode::CREATEGENERATOROBJ_PREF_V8); + } + HANDLE_OPCODE(DEBUG_HANDLE_THROWCONSTASSIGNMENT_PREF_V8) + { + NOTIFY_DEBUGGER_EVENT(); + REAL_GOTO_DISPATCH_OPCODE(EcmaOpcode::THROWCONSTASSIGNMENT_PREF_V8); + } + HANDLE_OPCODE(DEBUG_HANDLE_GETTEMPLATEOBJECT_PREF_V8) + { + NOTIFY_DEBUGGER_EVENT(); + REAL_GOTO_DISPATCH_OPCODE(EcmaOpcode::GETTEMPLATEOBJECT_PREF_V8); + } + HANDLE_OPCODE(DEBUG_HANDLE_GETNEXTPROPNAME_PREF_V8) + { + NOTIFY_DEBUGGER_EVENT(); + REAL_GOTO_DISPATCH_OPCODE(EcmaOpcode::GETNEXTPROPNAME_PREF_V8); + } + HANDLE_OPCODE(DEBUG_HANDLE_CALLARG0DYN_PREF_V8) + { + NOTIFY_DEBUGGER_EVENT(); + REAL_GOTO_DISPATCH_OPCODE(EcmaOpcode::CALLARG0DYN_PREF_V8); + } + HANDLE_OPCODE(DEBUG_HANDLE_THROWIFNOTOBJECT_PREF_V8) + { + NOTIFY_DEBUGGER_EVENT(); + REAL_GOTO_DISPATCH_OPCODE(EcmaOpcode::THROWIFNOTOBJECT_PREF_V8); + } + HANDLE_OPCODE(DEBUG_HANDLE_ITERNEXT_PREF_V8) + { + NOTIFY_DEBUGGER_EVENT(); + REAL_GOTO_DISPATCH_OPCODE(EcmaOpcode::ITERNEXT_PREF_V8); + } + HANDLE_OPCODE(DEBUG_HANDLE_CLOSEITERATOR_PREF_V8) + { + NOTIFY_DEBUGGER_EVENT(); + REAL_GOTO_DISPATCH_OPCODE(EcmaOpcode::CLOSEITERATOR_PREF_V8); + } + HANDLE_OPCODE(DEBUG_HANDLE_COPYMODULE_PREF_V8) + { + NOTIFY_DEBUGGER_EVENT(); + REAL_GOTO_DISPATCH_OPCODE(EcmaOpcode::COPYMODULE_PREF_V8); + } + HANDLE_OPCODE(DEBUG_HANDLE_SUPERCALLSPREAD_PREF_V8) + { + NOTIFY_DEBUGGER_EVENT(); + REAL_GOTO_DISPATCH_OPCODE(EcmaOpcode::SUPERCALLSPREAD_PREF_V8); + } + HANDLE_OPCODE(DEBUG_HANDLE_DELOBJPROP_PREF_V8_V8) + { + NOTIFY_DEBUGGER_EVENT(); + REAL_GOTO_DISPATCH_OPCODE(EcmaOpcode::DELOBJPROP_PREF_V8_V8); + } + HANDLE_OPCODE(DEBUG_HANDLE_NEWOBJSPREADDYN_PREF_V8_V8) + { + NOTIFY_DEBUGGER_EVENT(); + REAL_GOTO_DISPATCH_OPCODE(EcmaOpcode::NEWOBJSPREADDYN_PREF_V8_V8); + } + HANDLE_OPCODE(DEBUG_HANDLE_CREATEITERRESULTOBJ_PREF_V8_V8) + { + NOTIFY_DEBUGGER_EVENT(); + REAL_GOTO_DISPATCH_OPCODE(EcmaOpcode::CREATEITERRESULTOBJ_PREF_V8_V8); + } + HANDLE_OPCODE(DEBUG_HANDLE_SUSPENDGENERATOR_PREF_V8_V8) + { + NOTIFY_DEBUGGER_EVENT(); + REAL_GOTO_DISPATCH_OPCODE(EcmaOpcode::SUSPENDGENERATOR_PREF_V8_V8); + } + HANDLE_OPCODE(DEBUG_HANDLE_ASYNCFUNCTIONAWAITUNCAUGHT_PREF_V8_V8) + { + NOTIFY_DEBUGGER_EVENT(); + REAL_GOTO_DISPATCH_OPCODE(EcmaOpcode::ASYNCFUNCTIONAWAITUNCAUGHT_PREF_V8_V8); + } + HANDLE_OPCODE(DEBUG_HANDLE_THROWUNDEFINEDIFHOLE_PREF_V8_V8) + { + NOTIFY_DEBUGGER_EVENT(); + REAL_GOTO_DISPATCH_OPCODE(EcmaOpcode::THROWUNDEFINEDIFHOLE_PREF_V8_V8); + } + HANDLE_OPCODE(DEBUG_HANDLE_CALLARG1DYN_PREF_V8_V8) + { + NOTIFY_DEBUGGER_EVENT(); + REAL_GOTO_DISPATCH_OPCODE(EcmaOpcode::CALLARG1DYN_PREF_V8_V8); + } + HANDLE_OPCODE(DEBUG_HANDLE_COPYDATAPROPERTIES_PREF_V8_V8) + { + NOTIFY_DEBUGGER_EVENT(); + REAL_GOTO_DISPATCH_OPCODE(EcmaOpcode::COPYDATAPROPERTIES_PREF_V8_V8); + } + HANDLE_OPCODE(DEBUG_HANDLE_STARRAYSPREAD_PREF_V8_V8) + { + NOTIFY_DEBUGGER_EVENT(); + REAL_GOTO_DISPATCH_OPCODE(EcmaOpcode::STARRAYSPREAD_PREF_V8_V8); + } + HANDLE_OPCODE(DEBUG_HANDLE_GETITERATORNEXT_PREF_V8_V8) + { + NOTIFY_DEBUGGER_EVENT(); + REAL_GOTO_DISPATCH_OPCODE(EcmaOpcode::GETITERATORNEXT_PREF_V8_V8); + } + HANDLE_OPCODE(DEBUG_HANDLE_SETOBJECTWITHPROTO_PREF_V8_V8) + { + NOTIFY_DEBUGGER_EVENT(); + REAL_GOTO_DISPATCH_OPCODE(EcmaOpcode::SETOBJECTWITHPROTO_PREF_V8_V8); + } + HANDLE_OPCODE(DEBUG_HANDLE_LDOBJBYVALUE_PREF_V8_V8) + { + NOTIFY_DEBUGGER_EVENT(); + REAL_GOTO_DISPATCH_OPCODE(EcmaOpcode::LDOBJBYVALUE_PREF_V8_V8); + } + HANDLE_OPCODE(DEBUG_HANDLE_STOBJBYVALUE_PREF_V8_V8) + { + NOTIFY_DEBUGGER_EVENT(); + REAL_GOTO_DISPATCH_OPCODE(EcmaOpcode::STOBJBYVALUE_PREF_V8_V8); + } + HANDLE_OPCODE(DEBUG_HANDLE_STOWNBYVALUE_PREF_V8_V8) + { + NOTIFY_DEBUGGER_EVENT(); + REAL_GOTO_DISPATCH_OPCODE(EcmaOpcode::STOWNBYVALUE_PREF_V8_V8); + } + HANDLE_OPCODE(DEBUG_HANDLE_LDSUPERBYVALUE_PREF_V8_V8) + { + NOTIFY_DEBUGGER_EVENT(); + REAL_GOTO_DISPATCH_OPCODE(EcmaOpcode::LDSUPERBYVALUE_PREF_V8_V8); + } + HANDLE_OPCODE(DEBUG_HANDLE_STSUPERBYVALUE_PREF_V8_V8) + { + NOTIFY_DEBUGGER_EVENT(); + REAL_GOTO_DISPATCH_OPCODE(EcmaOpcode::STSUPERBYVALUE_PREF_V8_V8); + } + HANDLE_OPCODE(DEBUG_HANDLE_LDOBJBYINDEX_PREF_V8_IMM32) + { + NOTIFY_DEBUGGER_EVENT(); + REAL_GOTO_DISPATCH_OPCODE(EcmaOpcode::LDOBJBYINDEX_PREF_V8_IMM32); + } + HANDLE_OPCODE(DEBUG_HANDLE_STOBJBYINDEX_PREF_V8_IMM32) + { + NOTIFY_DEBUGGER_EVENT(); + REAL_GOTO_DISPATCH_OPCODE(EcmaOpcode::STOBJBYINDEX_PREF_V8_IMM32); + } + HANDLE_OPCODE(DEBUG_HANDLE_STOWNBYINDEX_PREF_V8_IMM32) + { + NOTIFY_DEBUGGER_EVENT(); + REAL_GOTO_DISPATCH_OPCODE(EcmaOpcode::STOWNBYINDEX_PREF_V8_IMM32); + } + HANDLE_OPCODE(DEBUG_HANDLE_CALLSPREADDYN_PREF_V8_V8_V8) + { + NOTIFY_DEBUGGER_EVENT(); + REAL_GOTO_DISPATCH_OPCODE(EcmaOpcode::CALLSPREADDYN_PREF_V8_V8_V8); + } + HANDLE_OPCODE(DEBUG_HANDLE_ASYNCFUNCTIONRESOLVE_PREF_V8_V8_V8) + { + NOTIFY_DEBUGGER_EVENT(); + REAL_GOTO_DISPATCH_OPCODE(EcmaOpcode::ASYNCFUNCTIONRESOLVE_PREF_V8_V8_V8); + } + HANDLE_OPCODE(DEBUG_HANDLE_ASYNCFUNCTIONREJECT_PREF_V8_V8_V8) + { + NOTIFY_DEBUGGER_EVENT(); + REAL_GOTO_DISPATCH_OPCODE(EcmaOpcode::ASYNCFUNCTIONREJECT_PREF_V8_V8_V8); + } + HANDLE_OPCODE(DEBUG_HANDLE_CALLARGS2DYN_PREF_V8_V8_V8) + { + NOTIFY_DEBUGGER_EVENT(); + REAL_GOTO_DISPATCH_OPCODE(EcmaOpcode::CALLARGS2DYN_PREF_V8_V8_V8); + } + HANDLE_OPCODE(DEBUG_HANDLE_CALLARGS3DYN_PREF_V8_V8_V8_V8) + { + NOTIFY_DEBUGGER_EVENT(); + REAL_GOTO_DISPATCH_OPCODE(EcmaOpcode::CALLARGS3DYN_PREF_V8_V8_V8_V8); + } + HANDLE_OPCODE(DEBUG_HANDLE_DEFINEGETTERSETTERBYVALUE_PREF_V8_V8_V8_V8) + { + NOTIFY_DEBUGGER_EVENT(); + REAL_GOTO_DISPATCH_OPCODE(EcmaOpcode::DEFINEGETTERSETTERBYVALUE_PREF_V8_V8_V8_V8); + } + HANDLE_OPCODE(DEBUG_HANDLE_NEWOBJDYNRANGE_PREF_IMM16_V8) + { + NOTIFY_DEBUGGER_EVENT(); + REAL_GOTO_DISPATCH_OPCODE(EcmaOpcode::NEWOBJDYNRANGE_PREF_IMM16_V8); + } + HANDLE_OPCODE(DEBUG_HANDLE_CALLIRANGEDYN_PREF_IMM16_V8) + { + NOTIFY_DEBUGGER_EVENT(); + REAL_GOTO_DISPATCH_OPCODE(EcmaOpcode::CALLIRANGEDYN_PREF_IMM16_V8); + } + HANDLE_OPCODE(DEBUG_HANDLE_CALLITHISRANGEDYN_PREF_IMM16_V8) + { + NOTIFY_DEBUGGER_EVENT(); + REAL_GOTO_DISPATCH_OPCODE(EcmaOpcode::CALLITHISRANGEDYN_PREF_IMM16_V8); + } + HANDLE_OPCODE(DEBUG_HANDLE_SUPERCALL_PREF_IMM16_V8) + { + NOTIFY_DEBUGGER_EVENT(); + REAL_GOTO_DISPATCH_OPCODE(EcmaOpcode::SUPERCALL_PREF_IMM16_V8); + } + HANDLE_OPCODE(DEBUG_HANDLE_CREATEOBJECTWITHEXCLUDEDKEYS_PREF_IMM16_V8_V8) + { + NOTIFY_DEBUGGER_EVENT(); + REAL_GOTO_DISPATCH_OPCODE(EcmaOpcode::CREATEOBJECTWITHEXCLUDEDKEYS_PREF_IMM16_V8_V8); + } + HANDLE_OPCODE(DEBUG_HANDLE_DEFINEFUNCDYN_PREF_ID16_IMM16_V8) + { + NOTIFY_DEBUGGER_EVENT(); + REAL_GOTO_DISPATCH_OPCODE(EcmaOpcode::DEFINEFUNCDYN_PREF_ID16_IMM16_V8); + } + HANDLE_OPCODE(DEBUG_HANDLE_DEFINENCFUNCDYN_PREF_ID16_IMM16_V8) + { + NOTIFY_DEBUGGER_EVENT(); + REAL_GOTO_DISPATCH_OPCODE(EcmaOpcode::DEFINENCFUNCDYN_PREF_ID16_IMM16_V8); + } + HANDLE_OPCODE(DEBUG_HANDLE_DEFINEGENERATORFUNC_PREF_ID16_IMM16_V8) + { + NOTIFY_DEBUGGER_EVENT(); + REAL_GOTO_DISPATCH_OPCODE(EcmaOpcode::DEFINEGENERATORFUNC_PREF_ID16_IMM16_V8); + } + HANDLE_OPCODE(DEBUG_HANDLE_DEFINEASYNCFUNC_PREF_ID16_IMM16_V8) + { + NOTIFY_DEBUGGER_EVENT(); + REAL_GOTO_DISPATCH_OPCODE(EcmaOpcode::DEFINEASYNCFUNC_PREF_ID16_IMM16_V8); + } + HANDLE_OPCODE(DEBUG_HANDLE_DEFINEMETHOD_PREF_ID16_IMM16_V8) + { + NOTIFY_DEBUGGER_EVENT(); + REAL_GOTO_DISPATCH_OPCODE(EcmaOpcode::DEFINEMETHOD_PREF_ID16_IMM16_V8); + } + HANDLE_OPCODE(DEBUG_HANDLE_NEWLEXENVDYN_PREF_IMM16) + { + NOTIFY_DEBUGGER_EVENT(); + REAL_GOTO_DISPATCH_OPCODE(EcmaOpcode::NEWLEXENVDYN_PREF_IMM16); + } + HANDLE_OPCODE(DEBUG_HANDLE_BUILTIN_COPYLEXENVDYN_IMM8) + { + NOTIFY_DEBUGGER_EVENT(); + REAL_DISPATCH_OPCODE(EcmaOpcode::COPYLEXENVDYN_IMM8); + } + HANDLE_OPCODE(DEBUG_HANDLE_COPYRESTARGS_PREF_IMM16) + { + NOTIFY_DEBUGGER_EVENT(); + REAL_GOTO_DISPATCH_OPCODE(EcmaOpcode::COPYRESTARGS_PREF_IMM16); + } + HANDLE_OPCODE(DEBUG_HANDLE_CREATEARRAYWITHBUFFER_PREF_IMM16) + { + NOTIFY_DEBUGGER_EVENT(); + REAL_GOTO_DISPATCH_OPCODE(EcmaOpcode::CREATEARRAYWITHBUFFER_PREF_IMM16); + } + HANDLE_OPCODE(DEBUG_HANDLE_CREATEOBJECTHAVINGMETHOD_PREF_IMM16) + { + NOTIFY_DEBUGGER_EVENT(); + REAL_GOTO_DISPATCH_OPCODE(EcmaOpcode::CREATEOBJECTHAVINGMETHOD_PREF_IMM16); + } + HANDLE_OPCODE(DEBUG_HANDLE_THROWIFSUPERNOTCORRECTCALL_PREF_IMM16) + { + NOTIFY_DEBUGGER_EVENT(); + REAL_GOTO_DISPATCH_OPCODE(EcmaOpcode::THROWIFSUPERNOTCORRECTCALL_PREF_IMM16); + } + HANDLE_OPCODE(DEBUG_HANDLE_CREATEOBJECTWITHBUFFER_PREF_IMM16) + { + NOTIFY_DEBUGGER_EVENT(); + REAL_GOTO_DISPATCH_OPCODE(EcmaOpcode::CREATEOBJECTWITHBUFFER_PREF_IMM16); + } + HANDLE_OPCODE(DEBUG_HANDLE_LDLEXVARDYN_PREF_IMM4_IMM4) + { + NOTIFY_DEBUGGER_EVENT(); + REAL_GOTO_DISPATCH_OPCODE(EcmaOpcode::LDLEXVARDYN_PREF_IMM4_IMM4); + } + HANDLE_OPCODE(DEBUG_HANDLE_LDLEXVARDYN_PREF_IMM8_IMM8) + { + NOTIFY_DEBUGGER_EVENT(); + REAL_GOTO_DISPATCH_OPCODE(EcmaOpcode::LDLEXVARDYN_PREF_IMM8_IMM8); + } + HANDLE_OPCODE(DEBUG_HANDLE_LDLEXVARDYN_PREF_IMM16_IMM16) + { + NOTIFY_DEBUGGER_EVENT(); + REAL_GOTO_DISPATCH_OPCODE(EcmaOpcode::LDLEXVARDYN_PREF_IMM16_IMM16); + } + HANDLE_OPCODE(DEBUG_HANDLE_STLEXVARDYN_PREF_IMM4_IMM4_V8) + { + NOTIFY_DEBUGGER_EVENT(); + REAL_GOTO_DISPATCH_OPCODE(EcmaOpcode::STLEXVARDYN_PREF_IMM4_IMM4_V8); + } + HANDLE_OPCODE(DEBUG_HANDLE_STLEXVARDYN_PREF_IMM8_IMM8_V8) + { + NOTIFY_DEBUGGER_EVENT(); + REAL_GOTO_DISPATCH_OPCODE(EcmaOpcode::STLEXVARDYN_PREF_IMM8_IMM8_V8); + } + HANDLE_OPCODE(DEBUG_HANDLE_STLEXVARDYN_PREF_IMM16_IMM16_V8) + { + NOTIFY_DEBUGGER_EVENT(); + REAL_GOTO_DISPATCH_OPCODE(EcmaOpcode::STLEXVARDYN_PREF_IMM16_IMM16_V8); + } + HANDLE_OPCODE(DEBUG_HANDLE_DEFINECLASSWITHBUFFER_PREF_ID16_IMM16_IMM16_V8_V8) + { + NOTIFY_DEBUGGER_EVENT(); + REAL_GOTO_DISPATCH_OPCODE(EcmaOpcode::DEFINECLASSWITHBUFFER_PREF_ID16_IMM16_IMM16_V8_V8); + } + HANDLE_OPCODE(DEBUG_HANDLE_IMPORTMODULE_PREF_ID32) + { + NOTIFY_DEBUGGER_EVENT(); + REAL_GOTO_DISPATCH_OPCODE(EcmaOpcode::IMPORTMODULE_PREF_ID32); + } + HANDLE_OPCODE(DEBUG_HANDLE_STMODULEVAR_PREF_ID32) + { + NOTIFY_DEBUGGER_EVENT(); + REAL_GOTO_DISPATCH_OPCODE(EcmaOpcode::STMODULEVAR_PREF_ID32); + } + HANDLE_OPCODE(DEBUG_HANDLE_TRYLDGLOBALBYNAME_PREF_ID32) + { + NOTIFY_DEBUGGER_EVENT(); + REAL_GOTO_DISPATCH_OPCODE(EcmaOpcode::TRYLDGLOBALBYNAME_PREF_ID32); + } + HANDLE_OPCODE(DEBUG_HANDLE_TRYSTGLOBALBYNAME_PREF_ID32) + { + NOTIFY_DEBUGGER_EVENT(); + REAL_GOTO_DISPATCH_OPCODE(EcmaOpcode::TRYSTGLOBALBYNAME_PREF_ID32); + } + HANDLE_OPCODE(DEBUG_HANDLE_LDGLOBALVAR_PREF_ID32) + { + NOTIFY_DEBUGGER_EVENT(); + REAL_GOTO_DISPATCH_OPCODE(EcmaOpcode::LDGLOBALVAR_PREF_ID32); + } + HANDLE_OPCODE(DEBUG_HANDLE_STGLOBALVAR_PREF_ID32) + { + NOTIFY_DEBUGGER_EVENT(); + REAL_GOTO_DISPATCH_OPCODE(EcmaOpcode::STGLOBALVAR_PREF_ID32); + } + HANDLE_OPCODE(DEBUG_HANDLE_LDOBJBYNAME_PREF_ID32_V8) + { + NOTIFY_DEBUGGER_EVENT(); + REAL_GOTO_DISPATCH_OPCODE(EcmaOpcode::LDOBJBYNAME_PREF_ID32_V8); + } + HANDLE_OPCODE(DEBUG_HANDLE_STOBJBYNAME_PREF_ID32_V8) + { + NOTIFY_DEBUGGER_EVENT(); + REAL_GOTO_DISPATCH_OPCODE(EcmaOpcode::STOBJBYNAME_PREF_ID32_V8); + } + HANDLE_OPCODE(DEBUG_HANDLE_STOWNBYNAME_PREF_ID32_V8) + { + NOTIFY_DEBUGGER_EVENT(); + REAL_GOTO_DISPATCH_OPCODE(EcmaOpcode::STOWNBYNAME_PREF_ID32_V8); + } + HANDLE_OPCODE(DEBUG_HANDLE_LDSUPERBYNAME_PREF_ID32_V8) + { + NOTIFY_DEBUGGER_EVENT(); + REAL_GOTO_DISPATCH_OPCODE(EcmaOpcode::LDSUPERBYNAME_PREF_ID32_V8); + } + HANDLE_OPCODE(DEBUG_HANDLE_STSUPERBYNAME_PREF_ID32_V8) + { + NOTIFY_DEBUGGER_EVENT(); + REAL_GOTO_DISPATCH_OPCODE(EcmaOpcode::STSUPERBYNAME_PREF_ID32_V8); + } + HANDLE_OPCODE(DEBUG_HANDLE_LDMODVARBYNAME_PREF_ID32_V8) + { + NOTIFY_DEBUGGER_EVENT(); + REAL_GOTO_DISPATCH_OPCODE(EcmaOpcode::LDMODVARBYNAME_PREF_ID32_V8); + } + HANDLE_OPCODE(DEBUG_HANDLE_CREATEREGEXPWITHLITERAL_PREF_ID32_IMM8) + { + NOTIFY_DEBUGGER_EVENT(); + REAL_GOTO_DISPATCH_OPCODE(EcmaOpcode::CREATEREGEXPWITHLITERAL_PREF_ID32_IMM8); + } + HANDLE_OPCODE(DEBUG_HANDLE_ISTRUE_PREF) + { + NOTIFY_DEBUGGER_EVENT(); + REAL_GOTO_DISPATCH_OPCODE(EcmaOpcode::ISTRUE_PREF); + } + HANDLE_OPCODE(DEBUG_HANDLE_ISFALSE_PREF) + { + NOTIFY_DEBUGGER_EVENT(); + REAL_GOTO_DISPATCH_OPCODE(EcmaOpcode::ISFALSE_PREF); + } + HANDLE_OPCODE(DEBUG_HANDLE_STCONSTTOGLOBALRECORD_PREF_ID32) + { + NOTIFY_DEBUGGER_EVENT(); + REAL_GOTO_DISPATCH_OPCODE(EcmaOpcode::STCONSTTOGLOBALRECORD_PREF_ID32); + } + HANDLE_OPCODE(DEBUG_HANDLE_STLETTOGLOBALRECORD_PREF_ID32) + { + NOTIFY_DEBUGGER_EVENT(); + REAL_GOTO_DISPATCH_OPCODE(EcmaOpcode::STLETTOGLOBALRECORD_PREF_ID32); + } + HANDLE_OPCODE(DEBUG_HANDLE_STCLASSTOGLOBALRECORD_PREF_ID32) + { + NOTIFY_DEBUGGER_EVENT(); + REAL_GOTO_DISPATCH_OPCODE(EcmaOpcode::STCLASSTOGLOBALRECORD_PREF_ID32); + } + HANDLE_OPCODE(DEBUG_HANDLE_STOWNBYVALUEWITHNAMESET_PREF_V8_V8) + { + NOTIFY_DEBUGGER_EVENT(); + REAL_GOTO_DISPATCH_OPCODE(EcmaOpcode::STOWNBYVALUEWITHNAMESET_PREF_V8_V8); + } + HANDLE_OPCODE(DEBUG_HANDLE_STOWNBYNAMEWITHNAMESET_PREF_ID32_V8) + { + NOTIFY_DEBUGGER_EVENT(); + REAL_GOTO_DISPATCH_OPCODE(EcmaOpcode::STOWNBYNAMEWITHNAMESET_PREF_ID32_V8); + } + HANDLE_OPCODE(DEBUG_HANDLE_LDFUNCTION_PREF) + { + NOTIFY_DEBUGGER_EVENT(); + REAL_GOTO_DISPATCH_OPCODE(EcmaOpcode::LDFUNCTION_PREF); + } + HANDLE_OPCODE(DEBUG_HANDLE_MOV_DYN_V8_V8) + { + NOTIFY_DEBUGGER_EVENT(); + REAL_GOTO_DISPATCH_OPCODE(EcmaOpcode::MOV_DYN_V8_V8); + } + HANDLE_OPCODE(DEBUG_HANDLE_MOV_DYN_V16_V16) + { + NOTIFY_DEBUGGER_EVENT(); + REAL_GOTO_DISPATCH_OPCODE(EcmaOpcode::MOV_DYN_V16_V16); + } + HANDLE_OPCODE(DEBUG_HANDLE_LDA_STR_ID32) + { + NOTIFY_DEBUGGER_EVENT(); + REAL_GOTO_DISPATCH_OPCODE(EcmaOpcode::LDA_STR_ID32); + } + HANDLE_OPCODE(DEBUG_HANDLE_LDAI_DYN_IMM32) + { + NOTIFY_DEBUGGER_EVENT(); + REAL_GOTO_DISPATCH_OPCODE(EcmaOpcode::LDAI_DYN_IMM32); + } + HANDLE_OPCODE(DEBUG_HANDLE_FLDAI_DYN_IMM64) + { + NOTIFY_DEBUGGER_EVENT(); + REAL_GOTO_DISPATCH_OPCODE(EcmaOpcode::FLDAI_DYN_IMM64); + } + HANDLE_OPCODE(DEBUG_HANDLE_JMP_IMM8) + { + NOTIFY_DEBUGGER_EVENT(); + REAL_GOTO_DISPATCH_OPCODE(EcmaOpcode::JMP_IMM8); + } + HANDLE_OPCODE(DEBUG_HANDLE_JMP_IMM16) + { + NOTIFY_DEBUGGER_EVENT(); + REAL_GOTO_DISPATCH_OPCODE(EcmaOpcode::JMP_IMM16); + } + HANDLE_OPCODE(DEBUG_HANDLE_JMP_IMM32) + { + NOTIFY_DEBUGGER_EVENT(); + REAL_GOTO_DISPATCH_OPCODE(EcmaOpcode::JMP_IMM32); + } + HANDLE_OPCODE(DEBUG_HANDLE_JEQZ_IMM8) + { + NOTIFY_DEBUGGER_EVENT(); + REAL_GOTO_DISPATCH_OPCODE(EcmaOpcode::JEQZ_IMM8); + } + HANDLE_OPCODE(DEBUG_HANDLE_JEQZ_IMM16) + { + NOTIFY_DEBUGGER_EVENT(); + REAL_GOTO_DISPATCH_OPCODE(EcmaOpcode::JEQZ_IMM16); + } + HANDLE_OPCODE(DEBUG_HANDLE_LDA_DYN_V8) + { + NOTIFY_DEBUGGER_EVENT(); + REAL_GOTO_DISPATCH_OPCODE(EcmaOpcode::LDA_DYN_V8); + } + HANDLE_OPCODE(DEBUG_HANDLE_STA_DYN_V8) + { + NOTIFY_DEBUGGER_EVENT(); + REAL_GOTO_DISPATCH_OPCODE(EcmaOpcode::STA_DYN_V8); + } + HANDLE_OPCODE(DEBUG_HANDLE_RETURN_DYN) + { + NOTIFY_DEBUGGER_EVENT(); + REAL_GOTO_DISPATCH_OPCODE(EcmaOpcode::RETURN_DYN); + } + HANDLE_OPCODE(DEBUG_HANDLE_MOV_V4_V4) + { + NOTIFY_DEBUGGER_EVENT(); + REAL_GOTO_DISPATCH_OPCODE(EcmaOpcode::MOV_V4_V4); + } + HANDLE_OPCODE(DEBUG_HANDLE_JNEZ_IMM8) + { + NOTIFY_DEBUGGER_EVENT(); + REAL_GOTO_DISPATCH_OPCODE(EcmaOpcode::JNEZ_IMM8); + } + HANDLE_OPCODE(DEBUG_HANDLE_JNEZ_IMM16) + { + NOTIFY_DEBUGGER_EVENT(); + REAL_GOTO_DISPATCH_OPCODE(EcmaOpcode::JNEZ_IMM16); + } + HANDLE_OPCODE(DEBUG_EXCEPTION_HANDLER) + { + NOTIFY_DEBUGGER_EVENT(); + REAL_GOTO_EXCEPTION_HANDLER(); + } + HANDLE_OPCODE(DEBUG_HANDLE_OVERFLOW) + { + NOTIFY_DEBUGGER_EVENT(); + REAL_GOTO_DISPATCH_OPCODE(EcmaOpcode::LAST_OPCODE + 1); + } diff --git a/runtime/interpreter/templates/instruction_dispatch.inl b/runtime/interpreter/templates/instruction_dispatch.inl new file mode 100644 index 000000000..dc4ec7d1c --- /dev/null +++ b/runtime/interpreter/templates/instruction_dispatch.inl @@ -0,0 +1,273 @@ +/* + * Copyright (c) 2021-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. + */ + + &&HANDLE_LDNAN_PREF, + &&HANDLE_LDINFINITY_PREF, + &&HANDLE_LDGLOBALTHIS_PREF, + &&HANDLE_LDUNDEFINED_PREF, + &&HANDLE_LDNULL_PREF, + &&HANDLE_LDSYMBOL_PREF, + &&HANDLE_LDGLOBAL_PREF, + &&HANDLE_LDTRUE_PREF, + &&HANDLE_LDFALSE_PREF, + &&HANDLE_THROWDYN_PREF, + &&HANDLE_TYPEOFDYN_PREF, + &&HANDLE_LDLEXENVDYN_PREF, + &&HANDLE_POPLEXENVDYN_PREF, + &&HANDLE_GETUNMAPPEDARGS_PREF, + &&HANDLE_GETPROPITERATOR_PREF, + &&HANDLE_ASYNCFUNCTIONENTER_PREF, + &&HANDLE_LDHOLE_PREF, + &&HANDLE_RETURNUNDEFINED_PREF, + &&HANDLE_CREATEEMPTYOBJECT_PREF, + &&HANDLE_CREATEEMPTYARRAY_PREF, + &&HANDLE_GETITERATOR_PREF, + &&HANDLE_THROWTHROWNOTEXISTS_PREF, + &&HANDLE_THROWPATTERNNONCOERCIBLE_PREF, + &&HANDLE_LDHOMEOBJECT_PREF, + &&HANDLE_THROWDELETESUPERPROPERTY_PREF, + &&HANDLE_DEBUGGER_PREF, + &&HANDLE_ADD2DYN_PREF_V8, + &&HANDLE_SUB2DYN_PREF_V8, + &&HANDLE_MUL2DYN_PREF_V8, + &&HANDLE_DIV2DYN_PREF_V8, + &&HANDLE_MOD2DYN_PREF_V8, + &&HANDLE_EQDYN_PREF_V8, + &&HANDLE_NOTEQDYN_PREF_V8, + &&HANDLE_LESSDYN_PREF_V8, + &&HANDLE_LESSEQDYN_PREF_V8, + &&HANDLE_GREATERDYN_PREF_V8, + &&HANDLE_GREATEREQDYN_PREF_V8, + &&HANDLE_SHL2DYN_PREF_V8, + &&HANDLE_SHR2DYN_PREF_V8, + &&HANDLE_ASHR2DYN_PREF_V8, + &&HANDLE_AND2DYN_PREF_V8, + &&HANDLE_OR2DYN_PREF_V8, + &&HANDLE_XOR2DYN_PREF_V8, + &&HANDLE_TONUMBER_PREF_V8, + &&HANDLE_NEGDYN_PREF_V8, + &&HANDLE_NOTDYN_PREF_V8, + &&HANDLE_INCDYN_PREF_V8, + &&HANDLE_DECDYN_PREF_V8, + &&HANDLE_EXPDYN_PREF_V8, + &&HANDLE_ISINDYN_PREF_V8, + &&HANDLE_INSTANCEOFDYN_PREF_V8, + &&HANDLE_STRICTNOTEQDYN_PREF_V8, + &&HANDLE_STRICTEQDYN_PREF_V8, + &&HANDLE_RESUMEGENERATOR_PREF_V8, + &&HANDLE_GETRESUMEMODE_PREF_V8, + &&HANDLE_CREATEGENERATOROBJ_PREF_V8, + &&HANDLE_THROWCONSTASSIGNMENT_PREF_V8, + &&HANDLE_GETTEMPLATEOBJECT_PREF_V8, + &&HANDLE_GETNEXTPROPNAME_PREF_V8, + &&HANDLE_CALLARG0DYN_PREF_V8, + &&HANDLE_THROWIFNOTOBJECT_PREF_V8, + &&HANDLE_ITERNEXT_PREF_V8, + &&HANDLE_CLOSEITERATOR_PREF_V8, + &&HANDLE_COPYMODULE_PREF_V8, + &&HANDLE_SUPERCALLSPREAD_PREF_V8, + &&HANDLE_DELOBJPROP_PREF_V8_V8, + &&HANDLE_NEWOBJSPREADDYN_PREF_V8_V8, + &&HANDLE_CREATEITERRESULTOBJ_PREF_V8_V8, + &&HANDLE_SUSPENDGENERATOR_PREF_V8_V8, + &&HANDLE_ASYNCFUNCTIONAWAITUNCAUGHT_PREF_V8_V8, + &&HANDLE_THROWUNDEFINEDIFHOLE_PREF_V8_V8, + &&HANDLE_CALLARG1DYN_PREF_V8_V8, + &&HANDLE_COPYDATAPROPERTIES_PREF_V8_V8, + &&HANDLE_STARRAYSPREAD_PREF_V8_V8, + &&HANDLE_GETITERATORNEXT_PREF_V8_V8, + &&HANDLE_SETOBJECTWITHPROTO_PREF_V8_V8, + &&HANDLE_LDOBJBYVALUE_PREF_V8_V8, + &&HANDLE_STOBJBYVALUE_PREF_V8_V8, + &&HANDLE_STOWNBYVALUE_PREF_V8_V8, + &&HANDLE_LDSUPERBYVALUE_PREF_V8_V8, + &&HANDLE_STSUPERBYVALUE_PREF_V8_V8, + &&HANDLE_LDOBJBYINDEX_PREF_V8_IMM32, + &&HANDLE_STOBJBYINDEX_PREF_V8_IMM32, + &&HANDLE_STOWNBYINDEX_PREF_V8_IMM32, + &&HANDLE_CALLSPREADDYN_PREF_V8_V8_V8, + &&HANDLE_ASYNCFUNCTIONRESOLVE_PREF_V8_V8_V8, + &&HANDLE_ASYNCFUNCTIONREJECT_PREF_V8_V8_V8, + &&HANDLE_CALLARGS2DYN_PREF_V8_V8_V8, + &&HANDLE_CALLARGS3DYN_PREF_V8_V8_V8_V8, + &&HANDLE_DEFINEGETTERSETTERBYVALUE_PREF_V8_V8_V8_V8, + &&HANDLE_NEWOBJDYNRANGE_PREF_IMM16_V8, + &&HANDLE_CALLIRANGEDYN_PREF_IMM16_V8, + &&HANDLE_CALLITHISRANGEDYN_PREF_IMM16_V8, + &&HANDLE_SUPERCALL_PREF_IMM16_V8, + &&HANDLE_CREATEOBJECTWITHEXCLUDEDKEYS_PREF_IMM16_V8_V8, + &&HANDLE_DEFINEFUNCDYN_PREF_ID16_IMM16_V8, + &&HANDLE_DEFINENCFUNCDYN_PREF_ID16_IMM16_V8, + &&HANDLE_DEFINEGENERATORFUNC_PREF_ID16_IMM16_V8, + &&HANDLE_DEFINEASYNCFUNC_PREF_ID16_IMM16_V8, + &&HANDLE_DEFINEMETHOD_PREF_ID16_IMM16_V8, + &&HANDLE_NEWLEXENVDYN_PREF_IMM16, + &&HANDLE_BUILTIN_COPYLEXENVDYN_IMM8, + &&HANDLE_COPYRESTARGS_PREF_IMM16, + &&HANDLE_CREATEARRAYWITHBUFFER_PREF_IMM16, + &&HANDLE_CREATEOBJECTHAVINGMETHOD_PREF_IMM16, + &&HANDLE_THROWIFSUPERNOTCORRECTCALL_PREF_IMM16, + &&HANDLE_CREATEOBJECTWITHBUFFER_PREF_IMM16, + &&HANDLE_LDLEXVARDYN_PREF_IMM4_IMM4, + &&HANDLE_LDLEXVARDYN_PREF_IMM8_IMM8, + &&HANDLE_LDLEXVARDYN_PREF_IMM16_IMM16, + &&HANDLE_STLEXVARDYN_PREF_IMM4_IMM4_V8, + &&HANDLE_STLEXVARDYN_PREF_IMM8_IMM8_V8, + &&HANDLE_STLEXVARDYN_PREF_IMM16_IMM16_V8, + &&HANDLE_DEFINECLASSWITHBUFFER_PREF_ID16_IMM16_IMM16_V8_V8, + &&HANDLE_IMPORTMODULE_PREF_ID32, + &&HANDLE_STMODULEVAR_PREF_ID32, + &&HANDLE_TRYLDGLOBALBYNAME_PREF_ID32, + &&HANDLE_TRYSTGLOBALBYNAME_PREF_ID32, + &&HANDLE_LDGLOBALVAR_PREF_ID32, + &&HANDLE_STGLOBALVAR_PREF_ID32, + &&HANDLE_LDOBJBYNAME_PREF_ID32_V8, + &&HANDLE_STOBJBYNAME_PREF_ID32_V8, + &&HANDLE_STOWNBYNAME_PREF_ID32_V8, + &&HANDLE_LDSUPERBYNAME_PREF_ID32_V8, + &&HANDLE_STSUPERBYNAME_PREF_ID32_V8, + &&HANDLE_LDMODVARBYNAME_PREF_ID32_V8, + &&HANDLE_CREATEREGEXPWITHLITERAL_PREF_ID32_IMM8, + &&HANDLE_ISTRUE_PREF, + &&HANDLE_ISFALSE_PREF, + &&HANDLE_STCONSTTOGLOBALRECORD_PREF_ID32, + &&HANDLE_STLETTOGLOBALRECORD_PREF_ID32, + &&HANDLE_STCLASSTOGLOBALRECORD_PREF_ID32, + &&HANDLE_STOWNBYVALUEWITHNAMESET_PREF_V8_V8, + &&HANDLE_STOWNBYNAMEWITHNAMESET_PREF_ID32_V8, + &&HANDLE_LDFUNCTION_PREF, + &&HANDLE_MOV_DYN_V8_V8, + &&HANDLE_MOV_DYN_V16_V16, + &&HANDLE_LDA_STR_ID32, + &&HANDLE_LDAI_DYN_IMM32, + &&HANDLE_FLDAI_DYN_IMM64, + &&HANDLE_JMP_IMM8, + &&HANDLE_JMP_IMM16, + &&HANDLE_JMP_IMM32, + &&HANDLE_JEQZ_IMM8, + &&HANDLE_JEQZ_IMM16, + &&HANDLE_LDA_DYN_V8, + &&HANDLE_STA_DYN_V8, + &&HANDLE_RETURN_DYN, + &&HANDLE_MOV_V4_V4, + &&HANDLE_JNEZ_IMM8, + &&HANDLE_JNEZ_IMM16, + &&EXCEPTION_HANDLER, + &&HANDLE_OVERFLOW, + &&HANDLE_OVERFLOW, + &&HANDLE_OVERFLOW, + &&HANDLE_OVERFLOW, + &&HANDLE_OVERFLOW, + &&HANDLE_OVERFLOW, + &&HANDLE_OVERFLOW, + &&HANDLE_OVERFLOW, + &&HANDLE_OVERFLOW, + &&HANDLE_OVERFLOW, + &&HANDLE_OVERFLOW, + &&HANDLE_OVERFLOW, + &&HANDLE_OVERFLOW, + &&HANDLE_OVERFLOW, + &&HANDLE_OVERFLOW, + &&HANDLE_OVERFLOW, + &&HANDLE_OVERFLOW, + &&HANDLE_OVERFLOW, + &&HANDLE_OVERFLOW, + &&HANDLE_OVERFLOW, + &&HANDLE_OVERFLOW, + &&HANDLE_OVERFLOW, + &&HANDLE_OVERFLOW, + &&HANDLE_OVERFLOW, + &&HANDLE_OVERFLOW, + &&HANDLE_OVERFLOW, + &&HANDLE_OVERFLOW, + &&HANDLE_OVERFLOW, + &&HANDLE_OVERFLOW, + &&HANDLE_OVERFLOW, + &&HANDLE_OVERFLOW, + &&HANDLE_OVERFLOW, + &&HANDLE_OVERFLOW, + &&HANDLE_OVERFLOW, + &&HANDLE_OVERFLOW, + &&HANDLE_OVERFLOW, + &&HANDLE_OVERFLOW, + &&HANDLE_OVERFLOW, + &&HANDLE_OVERFLOW, + &&HANDLE_OVERFLOW, + &&HANDLE_OVERFLOW, + &&HANDLE_OVERFLOW, + &&HANDLE_OVERFLOW, + &&HANDLE_OVERFLOW, + &&HANDLE_OVERFLOW, + &&HANDLE_OVERFLOW, + &&HANDLE_OVERFLOW, + &&HANDLE_OVERFLOW, + &&HANDLE_OVERFLOW, + &&HANDLE_OVERFLOW, + &&HANDLE_OVERFLOW, + &&HANDLE_OVERFLOW, + &&HANDLE_OVERFLOW, + &&HANDLE_OVERFLOW, + &&HANDLE_OVERFLOW, + &&HANDLE_OVERFLOW, + &&HANDLE_OVERFLOW, + &&HANDLE_OVERFLOW, + &&HANDLE_OVERFLOW, + &&HANDLE_OVERFLOW, + &&HANDLE_OVERFLOW, + &&HANDLE_OVERFLOW, + &&HANDLE_OVERFLOW, + &&HANDLE_OVERFLOW, + &&HANDLE_OVERFLOW, + &&HANDLE_OVERFLOW, + &&HANDLE_OVERFLOW, + &&HANDLE_OVERFLOW, + &&HANDLE_OVERFLOW, + &&HANDLE_OVERFLOW, + &&HANDLE_OVERFLOW, + &&HANDLE_OVERFLOW, + &&HANDLE_OVERFLOW, + &&HANDLE_OVERFLOW, + &&HANDLE_OVERFLOW, + &&HANDLE_OVERFLOW, + &&HANDLE_OVERFLOW, + &&HANDLE_OVERFLOW, + &&HANDLE_OVERFLOW, + &&HANDLE_OVERFLOW, + &&HANDLE_OVERFLOW, + &&HANDLE_OVERFLOW, + &&HANDLE_OVERFLOW, + &&HANDLE_OVERFLOW, + &&HANDLE_OVERFLOW, + &&HANDLE_OVERFLOW, + &&HANDLE_OVERFLOW, + &&HANDLE_OVERFLOW, + &&HANDLE_OVERFLOW, + &&HANDLE_OVERFLOW, + &&HANDLE_OVERFLOW, + &&HANDLE_OVERFLOW, + &&HANDLE_OVERFLOW, + &&HANDLE_OVERFLOW, + &&HANDLE_OVERFLOW, + &&HANDLE_OVERFLOW, + &&HANDLE_OVERFLOW, + &&HANDLE_OVERFLOW, + &&HANDLE_OVERFLOW, + &&HANDLE_OVERFLOW, + &&HANDLE_OVERFLOW, + &&HANDLE_OVERFLOW, + &&HANDLE_OVERFLOW, + &&HANDLE_OVERFLOW, + &&HANDLE_OVERFLOW, + diff --git a/runtime/intrinsics-inl.h b/runtime/intrinsics-inl.h new file mode 100644 index 000000000..dd4fd1ecc --- /dev/null +++ b/runtime/intrinsics-inl.h @@ -0,0 +1,1947 @@ +/* + * Copyright (c) 2021-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 PLUGINS_ECMASCRIPT_RUNTIME_INTRINSICS_INL_H +#define PLUGINS_ECMASCRIPT_RUNTIME_INTRINSICS_INL_H + +#include "intrinsics.h" + +#include "runtime/include/method-inl.h" +#include "plugins/ecmascript/runtime/js_tagged_value-inl.h" +#include "plugins/ecmascript/runtime/interpreter/slow_runtime_stub.h" +#include "plugins/ecmascript/runtime/interpreter/interpreter-inl.h" +#include "plugins/ecmascript/runtime/interpreter/fast_runtime_stub-inl.h" +#include "plugins/ecmascript/compiler/ecmascript_extensions/thread_environment_api.h" +#include "plugins/ecmascript/runtime/ic/ic_runtime_stub-inl.h" +#include "plugins/ecmascript/runtime/ic/profile_type_info.h" +#include "plugins/ecmascript/runtime/js_generator_object.h" +#include "plugins/ecmascript/runtime/js_handle.h" +#include "plugins/ecmascript/runtime/js_array.h" + +namespace panda::ecmascript::intrinsics { +#ifndef INLINE_ECMA_INTRINSICS +#define INLINE_ECMA_INTRINSICS ALWAYS_INLINE inline +#endif + +#if ECMASCRIPT_ENABLE_IC +static inline JSTaggedValue GetRuntimeProfileTypeInfo(JSThread *thread) +{ + auto *func = JSFunction::Cast(GetThisFunc(thread).GetHeapObject()); + return func->GetProfileTypeInfo(); +} +#endif + +template +static inline JSTaggedValue HandlerCall(JSThread *thread, JSTaggedValue fn_object, JSTaggedValue *args, + uint32_t num_args) +{ + ASSERT(num_args >= 3); + + if (UNLIKELY(!fn_object.IsCallable())) { + JSHandle error = GetFactory(thread)->GetJSError(ErrorType::TYPE_ERROR, "is not callable"); + thread->SetException(error.GetTaggedValue()); + return JSTaggedValue(JSTaggedValue::VALUE_EXCEPTION); + } + + ECMAObject *js_object = ECMAObject::Cast(fn_object.GetHeapObject()); + Method *method = js_object->GetCallTarget(); + + if constexpr (!this_call) { + ASSERT(args[2] == JSTaggedValue::Undefined()); // NOLINT(cppcoreguidelines-pro-bounds-pointer-arithmetic) + if (fn_object.IsJSFunction() && !JSFunction::Cast(js_object)->IsStrict()) { + // Set this=GlobalObject for sloppy function + // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-pointer-arithmetic) + args[2] = thread->GetEcmaVM()->GetGlobalEnv()->GetGlobalObject(); + } + } + + if (method->IsNative()) { + ASSERT(method->GetNumVregs() == 0); + + LOG(DEBUG, INTERPRETER) << "Native function"; + + LOG(DEBUG, INTERPRETER) << "Entry: Runtime Call."; + JSTaggedValue ret_value = EcmaInterpreter::ExecuteNative(thread, fn_object, num_args, args); + if (UNLIKELY(thread->HasPendingException())) { + return JSTaggedValue::Exception(); + } + LOG(DEBUG, INTERPRETER) << "Exit: Runtime Call."; + return ret_value; + } + + JSFunction *js_function = JSFunction::Cast(js_object); + if (UNLIKELY(js_function->IsClassConstructor())) { + JSHandle error = thread->GetEcmaVM()->GetFactory()->GetJSError( + ErrorType::TYPE_ERROR, "class constructor cannot called without 'new'"); + thread->SetException(error.GetTaggedValue()); + return JSTaggedValue::Exception(); + } + + LOG(DEBUG, INTERPRETER) << "Method name: " << method->GetName().data; + JSTaggedValue ret_value = EcmaInterpreter::ExecuteInvoke(thread, fn_object, num_args, args); + + if (UNLIKELY(thread->HasPendingException())) { + return JSTaggedValue::Exception(); + } + return ret_value; +} + +// NOLINTNEXTLINE(misc-definitions-in-headers) +INLINE_ECMA_INTRINSICS uint64_t Ldnan() +{ + return JSTaggedValue(base::NAN_VALUE).GetRawData(); +} + +// NOLINTNEXTLINE(misc-definitions-in-headers) +INLINE_ECMA_INTRINSICS uint64_t Ldinfinity() +{ + return JSTaggedValue(base::POSITIVE_INFINITY).GetRawData(); +} + +// NOLINTNEXTLINE(misc-definitions-in-headers) +INLINE_ECMA_INTRINSICS uint64_t Ldglobalthis(JSThread *thread) +{ + return GetGlobalObject(thread).GetRawData(); +} + +// NOLINTNEXTLINE(misc-definitions-in-headers) +INLINE_ECMA_INTRINSICS uint64_t Ldundefined() +{ + return JSTaggedValue::Undefined().GetRawData(); +} + +// NOLINTNEXTLINE(misc-definitions-in-headers) +INLINE_ECMA_INTRINSICS uint64_t Ldboolean([[maybe_unused]] JSThread *thread, [[maybe_unused]] uint64_t a0) +{ + UNREACHABLE(); +} + +// NOLINTNEXTLINE(misc-definitions-in-headers) +INLINE_ECMA_INTRINSICS uint64_t Ldnumber([[maybe_unused]] JSThread *thread, [[maybe_unused]] uint64_t a0) +{ + UNREACHABLE(); +} + +// NOLINTNEXTLINE(misc-definitions-in-headers) +INLINE_ECMA_INTRINSICS uint64_t Ldstring([[maybe_unused]] JSThread *thread, [[maybe_unused]] uint64_t a0) +{ + UNREACHABLE(); +} + +// NOLINTNEXTLINE(misc-definitions-in-headers) +INLINE_ECMA_INTRINSICS uint64_t Ldbigint([[maybe_unused]] JSThread *thread, [[maybe_unused]] uint64_t a0) +{ + UNREACHABLE(); +} + +// NOLINTNEXTLINE(misc-definitions-in-headers) +INLINE_ECMA_INTRINSICS uint64_t Ldnull() +{ + return JSTaggedValue::Null().GetRawData(); +} + +// NOLINTNEXTLINE(misc-definitions-in-headers) +INLINE_ECMA_INTRINSICS uint64_t Ldsymbol(JSThread *thread) +{ + return GetGlobalEnv(thread)->GetSymbolFunction().GetTaggedValue().GetRawData(); +} + +// NOLINTNEXTLINE(misc-definitions-in-headers) +INLINE_ECMA_INTRINSICS uint64_t Ldobject([[maybe_unused]] JSThread *thread, [[maybe_unused]] uint64_t a0, + [[maybe_unused]] uint64_t a1) +{ + UNREACHABLE(); +} + +// NOLINTNEXTLINE(misc-definitions-in-headers) +INLINE_ECMA_INTRINSICS uint64_t Ldfunction([[maybe_unused]] JSThread *thread, [[maybe_unused]] uint64_t a0, + [[maybe_unused]] uint64_t a1) +{ + UNREACHABLE(); +} + +// NOLINTNEXTLINE(misc-definitions-in-headers) +INLINE_ECMA_INTRINSICS uint64_t Ldtrue() +{ + return JSTaggedValue::True().GetRawData(); +} + +// NOLINTNEXTLINE(misc-definitions-in-headers) +INLINE_ECMA_INTRINSICS uint64_t Ldfalse() +{ + return JSTaggedValue::False().GetRawData(); +} + +// NOLINTNEXTLINE(misc-definitions-in-headers) +INLINE_ECMA_INTRINSICS uint64_t Add2Dyn(JSThread *thread, uint64_t lhs, uint64_t rhs) +{ + auto left = JSTaggedValue(lhs); + auto right = JSTaggedValue(rhs); + + if (left.IsInt() && right.IsInt()) { + int32_t a0 = left.GetInt(); + int32_t a1 = right.GetInt(); + if ((a0 > 0 && a1 > INT32_MAX - a0) || (a0 < 0 && a1 < INT32_MIN - a0)) { + auto ret = static_cast(a0) + static_cast(a1); + return JSTaggedValue(ret).GetRawData(); + } + return JSTaggedValue(a0 + a1).GetRawData(); + } + if (left.IsNumber() && right.IsNumber()) { + double a0Double = left.IsInt() ? left.GetInt() : left.GetDouble(); + double a1Double = right.IsInt() ? right.GetInt() : right.GetDouble(); + return JSTaggedValue(a0Double + a1Double).GetRawData(); + } + auto ecmaVm = thread->GetEcmaVM(); + return SlowRuntimeStub::Add2Dyn(thread, ecmaVm, left, right).GetRawData(); +} + +// NOLINTNEXTLINE(misc-definitions-in-headers) +INLINE_ECMA_INTRINSICS uint64_t Sub2Dyn(JSThread *thread, uint64_t lhs, uint64_t rhs) +{ + auto left = JSTaggedValue(lhs); + auto right = JSTaggedValue(rhs); + + if (left.IsInt() && right.IsInt()) { + int32_t a0 = left.GetInt(); + int32_t a1 = -right.GetInt(); + if ((a0 > 0 && a1 > INT32_MAX - a0) || (a0 < 0 && a1 < INT32_MIN - a0)) { + auto ret = static_cast(a0) + static_cast(a1); + return JSTaggedValue(ret).GetRawData(); + } + return JSTaggedValue(a0 + a1).GetRawData(); + } + if (left.IsNumber() && right.IsNumber()) { + double a0Double = left.IsInt() ? left.GetInt() : left.GetDouble(); + double a1Double = right.IsInt() ? right.GetInt() : right.GetDouble(); + return JSTaggedValue(a0Double - a1Double).GetRawData(); + } + return SlowRuntimeStub::Sub2Dyn(thread, left, right).GetRawData(); +} + +// 12.6.3 Runtime Semantics: Evaluation +// NOLINTNEXTLINE(misc-definitions-in-headers) +INLINE_ECMA_INTRINSICS uint64_t Mul2Dyn(JSThread *thread, uint64_t lhs, uint64_t rhs) +{ + auto left = JSTaggedValue(lhs); + auto right = JSTaggedValue(rhs); + + auto res = FastRuntimeStub::FastMul(left, right); + if (!res.IsHole()) { + return res.GetRawData(); + } + return SlowRuntimeStub::Mul2Dyn(thread, left, right).GetRawData(); +} + +// 12.6.3 Runtime Semantics: Evaluation +// NOLINTNEXTLINE(misc-definitions-in-headers) +INLINE_ECMA_INTRINSICS uint64_t Div2Dyn(JSThread *thread, uint64_t lhs, uint64_t rhs) +{ + auto left = JSTaggedValue(lhs); + auto right = JSTaggedValue(rhs); + + auto res = FastRuntimeStub::FastDiv(left, right); + if (!res.IsHole()) { + return res.GetRawData(); + } + return SlowRuntimeStub::Div2Dyn(thread, left, right).GetRawData(); +} + +// 12.6.3 Runtime Semantics: Evaluation +// NOLINTNEXTLINE(misc-definitions-in-headers) +INLINE_ECMA_INTRINSICS uint64_t Mod2Dyn(JSThread *thread, uint64_t lhs, uint64_t rhs) +{ + auto left = JSTaggedValue(lhs); + auto right = JSTaggedValue(rhs); + + auto res = FastRuntimeStub::FastMod(left, right); + if (!res.IsHole()) { + return res.GetRawData(); + } + return SlowRuntimeStub::Mod2Dyn(thread, left, right).GetRawData(); +} + +// NOLINTNEXTLINE(misc-definitions-in-headers) +INLINE_ECMA_INTRINSICS uint64_t ExpDyn(JSThread *thread, uint64_t base_exp, uint64_t exp) +{ + auto base = JSTaggedValue(base_exp); + auto exponent = JSTaggedValue(exp); + + JSTaggedValue res; + + if (base.IsNumber() && exponent.IsNumber()) { + double doubleBase = base.IsInt() ? base.GetInt() : base.GetDouble(); + double doubleExponent = exponent.IsInt() ? exponent.GetInt() : exponent.GetDouble(); + res = base::NumberHelper::Pow(doubleBase, doubleExponent); + } else { + res = SlowRuntimeStub::ExpDyn(thread, base, exponent); + } + return res.GetRawData(); +} + +// NOLINTNEXTLINE(misc-definitions-in-headers) +INLINE_ECMA_INTRINSICS uint64_t EqDyn(JSThread *thread, uint64_t lhs, uint64_t rhs) +{ + auto left = JSTaggedValue(lhs); + auto right = JSTaggedValue(rhs); + + auto res = FastRuntimeStub::FastEqual(left, right); + if (!res.IsHole()) { + return res.GetRawData(); + } + return SlowRuntimeStub::EqDyn(thread, left, right).GetRawData(); +} + +// NOLINTNEXTLINE(misc-definitions-in-headers) +INLINE_ECMA_INTRINSICS uint64_t NotEqDyn(JSThread *thread, uint64_t lhs, uint64_t rhs) +{ + auto left = JSTaggedValue(lhs); + auto right = JSTaggedValue(rhs); + + auto res = FastRuntimeStub::FastEqual(left, right); + if (!res.IsHole()) { + return res.IsTrue() ? JSTaggedValue::False().GetRawData() : JSTaggedValue::True().GetRawData(); + } + return SlowRuntimeStub::NotEqDyn(thread, left, right).GetRawData(); +} + +// NOLINTNEXTLINE(misc-definitions-in-headers) +INLINE_ECMA_INTRINSICS uint64_t StrictEqDyn(uint64_t lhs, uint64_t rhs) +{ + auto left = JSTaggedValue(lhs); + auto right = JSTaggedValue(rhs); + return JSTaggedValue(FastRuntimeStub::FastStrictEqual(left, right)).GetRawData(); +} + +// NOLINTNEXTLINE(misc-definitions-in-headers) +INLINE_ECMA_INTRINSICS uint64_t StrictNotEqDyn(uint64_t lhs, uint64_t rhs) +{ + auto left = JSTaggedValue(lhs); + auto right = JSTaggedValue(rhs); + return JSTaggedValue(!FastRuntimeStub::FastStrictEqual(left, right)).GetRawData(); +} + +// NOLINTNEXTLINE(misc-definitions-in-headers) +INLINE_ECMA_INTRINSICS uint64_t LessDyn(JSThread *thread, uint64_t lhs, uint64_t rhs) +{ + auto left = JSTaggedValue(lhs); + auto right = JSTaggedValue(rhs); + + if (left.IsNumber() && right.IsNumber()) { + double valueA = left.IsInt() ? static_cast(left.GetInt()) : left.GetDouble(); + double valueB = right.IsInt() ? static_cast(right.GetInt()) : right.GetDouble(); + bool ret = JSTaggedValue::StrictNumberCompare(valueA, valueB) == ComparisonResult::LESS; + return ret ? JSTaggedValue::True().GetRawData() : JSTaggedValue::False().GetRawData(); + } + return SlowRuntimeStub::LessDyn(thread, left, right).GetRawData(); +} + +// NOLINTNEXTLINE(misc-definitions-in-headers) +INLINE_ECMA_INTRINSICS uint64_t LessEqDyn(JSThread *thread, uint64_t lhs, uint64_t rhs) +{ + auto left = JSTaggedValue(lhs); + auto right = JSTaggedValue(rhs); + + if (left.IsNumber() && right.IsNumber()) { + double valueA = left.IsInt() ? static_cast(left.GetInt()) : left.GetDouble(); + double valueB = right.IsInt() ? static_cast(right.GetInt()) : right.GetDouble(); + bool ret = JSTaggedValue::StrictNumberCompare(valueA, valueB) <= ComparisonResult::EQUAL; + return ret ? JSTaggedValue::True().GetRawData() : JSTaggedValue::False().GetRawData(); + } + return SlowRuntimeStub::LessEqDyn(thread, left, right).GetRawData(); +} + +// NOLINTNEXTLINE(misc-definitions-in-headers) +INLINE_ECMA_INTRINSICS uint64_t GreaterDyn(JSThread *thread, uint64_t lhs, uint64_t rhs) +{ + auto left = JSTaggedValue(lhs); + auto right = JSTaggedValue(rhs); + + if (left.IsNumber() && right.IsNumber()) { + double valueA = left.IsInt() ? static_cast(left.GetInt()) : left.GetDouble(); + double valueB = right.IsInt() ? static_cast(right.GetInt()) : right.GetDouble(); + bool ret = JSTaggedValue::StrictNumberCompare(valueA, valueB) == ComparisonResult::GREAT; + return ret ? JSTaggedValue::True().GetRawData() : JSTaggedValue::False().GetRawData(); + } + return SlowRuntimeStub::GreaterDyn(thread, left, right).GetRawData(); +} + +// NOLINTNEXTLINE(misc-definitions-in-headers) +INLINE_ECMA_INTRINSICS uint64_t GreaterEqDyn(JSThread *thread, uint64_t lhs, uint64_t rhs) +{ + auto left = JSTaggedValue(lhs); + auto right = JSTaggedValue(rhs); + + if (left.IsNumber() && right.IsNumber()) { + double valueA = left.IsInt() ? static_cast(left.GetInt()) : left.GetDouble(); + double valueB = right.IsInt() ? static_cast(right.GetInt()) : right.GetDouble(); + ComparisonResult comparison = JSTaggedValue::StrictNumberCompare(valueA, valueB); + bool ret = (comparison == ComparisonResult::GREAT) || (comparison == ComparisonResult::EQUAL); + return ret ? JSTaggedValue::True().GetRawData() : JSTaggedValue::False().GetRawData(); + } + return SlowRuntimeStub::GreaterEqDyn(thread, left, right).GetRawData(); +} + +// NOLINTNEXTLINE(misc-definitions-in-headers) +INLINE_ECMA_INTRINSICS uint64_t TryLdGlobalByValue([[maybe_unused]] JSThread *thread, [[maybe_unused]] uint16_t slotId, + [[maybe_unused]] uint64_t propValue) +{ + UNREACHABLE(); +} + +// NOLINTNEXTLINE(misc-definitions-in-headers) +INLINE_ECMA_INTRINSICS void TryStGlobalByValue([[maybe_unused]] JSThread *thread, [[maybe_unused]] uint16_t slotId, + [[maybe_unused]] uint64_t propValue, [[maybe_unused]] uint64_t valValue) +{ + UNREACHABLE(); +} + +// NOLINTNEXTLINE(misc-definitions-in-headers) +INLINE_ECMA_INTRINSICS uint64_t TryLdGlobalByName(JSThread *thread, uint32_t stringId, [[maybe_unused]] uint16_t slotId) +{ + auto constpool = GetConstantPool(thread); + auto global_obj = GetGlobalObject(thread); + auto prop = constpool->GetObjectFromCache(stringId); + +#if ECMASCRIPT_ENABLE_IC + auto profileTypeInfo = GetRuntimeProfileTypeInfo(thread); + if (!profileTypeInfo.IsUndefined()) { + JSTaggedValue res = ICRuntimeStub::LoadGlobalICByName( + thread, ProfileTypeInfo::Cast(profileTypeInfo.GetTaggedObject()), global_obj, prop, slotId, true); + return res.GetRawData(); + } +#endif + + bool found = false; + JSTaggedValue result = FastRuntimeStub::GetGlobalOwnProperty(global_obj, prop, &found); + if (found) { + return result.GetRawData(); + } + return SlowRuntimeStub::TryLdGlobalByName(thread, global_obj, prop).GetRawData(); +} + +// NOLINTNEXTLINE(misc-definitions-in-headers) +INLINE_ECMA_INTRINSICS uint64_t Tonumber(JSThread *thread, uint64_t a0) +{ + auto value = JSTaggedValue(a0); + if (value.IsNumber()) { + return value.GetRawData(); + } + return SlowRuntimeStub::ToNumber(thread, value).GetRawData(); +} + +// NOLINTNEXTLINE(misc-definitions-in-headers) +INLINE_ECMA_INTRINSICS uint64_t NegDyn(JSThread *thread, uint64_t val) +{ + auto value = JSTaggedValue(val); + + if (value.IsInt()) { + if (value.GetInt() == 0) { + return JSTaggedValue(-0.0).GetRawData(); + } + return JSTaggedValue(-value.GetInt()).GetRawData(); + } + if (value.IsDouble()) { + return JSTaggedValue(-value.GetDouble()).GetRawData(); + } + return SlowRuntimeStub::NegDyn(thread, value).GetRawData(); +} + +// NOLINTNEXTLINE(misc-definitions-in-headers) +INLINE_ECMA_INTRINSICS uint64_t NotDyn(JSThread *thread, uint64_t val) +{ + auto value = JSTaggedValue(val); + if (value.IsInt()) { + auto number = static_cast(value.GetInt()); + return JSTaggedValue(~number).GetRawData(); // NOLINT(hicpp-signed-bitwise) + } + if (value.IsDouble()) { + int32_t number = base::NumberHelper::DoubleToInt(value.GetDouble(), base::INT32_BITS); + return JSTaggedValue(~number).GetRawData(); // NOLINT(hicpp-signed-bitwise) + } + return SlowRuntimeStub::NotDyn(thread, value).GetRawData(); +} + +// NOLINTNEXTLINE(misc-definitions-in-headers) +INLINE_ECMA_INTRINSICS uint64_t IncDyn(JSThread *thread, uint64_t val) +{ + auto value = JSTaggedValue(val); + + if (value.IsInt()) { + int32_t a0 = value.GetInt(); + if (UNLIKELY(a0 == INT32_MAX)) { + return JSTaggedValue(static_cast(a0) + 1.0).GetRawData(); + } + return JSTaggedValue(a0 + 1).GetRawData(); + } + if (value.IsDouble()) { + return JSTaggedValue(value.GetDouble() + 1.0).GetRawData(); + } + return SlowRuntimeStub::IncDyn(thread, value).GetRawData(); +} + +// NOLINTNEXTLINE(misc-definitions-in-headers) +INLINE_ECMA_INTRINSICS uint64_t DecDyn(JSThread *thread, uint64_t val) +{ + auto value = JSTaggedValue(val); + + if (value.IsInt()) { + int32_t a0 = value.GetInt(); + if (UNLIKELY(a0 == INT32_MIN)) { + return JSTaggedValue(static_cast(a0) - 1.0).GetRawData(); + } + return JSTaggedValue(a0 - 1).GetRawData(); + } + if (value.IsDouble()) { + return JSTaggedValue(value.GetDouble() - 1.0).GetRawData(); + } + return SlowRuntimeStub::DecDyn(thread, value).GetRawData(); +} + +// NOLINTNEXTLINE(misc-definitions-in-headers) +INLINE_ECMA_INTRINSICS uint64_t Defineglobalvar([[maybe_unused]] JSThread *thread, [[maybe_unused]] uint64_t a0, + [[maybe_unused]] uint64_t a1) +{ + UNREACHABLE(); +} + +// NOLINTNEXTLINE(misc-definitions-in-headers) +INLINE_ECMA_INTRINSICS uint64_t Definelocalvar([[maybe_unused]] JSThread *thread, [[maybe_unused]] uint64_t a0, + [[maybe_unused]] uint64_t a1) +{ + UNREACHABLE(); +} + +// NOLINTNEXTLINE(misc-definitions-in-headers) +INLINE_ECMA_INTRINSICS uint64_t Definefuncexpr([[maybe_unused]] JSThread *thread, [[maybe_unused]] uint64_t a0, + [[maybe_unused]] uint64_t a1) +{ + UNREACHABLE(); +} + +// NOLINTNEXTLINE(misc-definitions-in-headers) +INLINE_ECMA_INTRINSICS uint64_t DefinefuncDyn(JSThread *thread, uint32_t methodId, uint64_t env) +{ + JSHandle constpool(thread, GetConstantPool(thread)); + JSMutableHandle result(thread, constpool->GetObjectFromCache(methodId)); + JSHandle lex_env(thread, JSTaggedValue(env)); + ASSERT(!result.IsEmpty()); + if (!result->GetLexicalEnv().IsUndefined()) { + auto res = SlowRuntimeStub::DefinefuncDyn(thread, result->GetCallTarget()); + result.Update(res); + result->SetConstantPool(thread, constpool.GetTaggedValue()); + } + result->SetLexicalEnv(thread, lex_env.GetTaggedValue()); + return result.GetTaggedValue().GetRawData(); +} + +// a0 is args' length which include ctor and new_target, and a1 is the start index of args +// NOLINTNEXTLINE(misc-definitions-in-headers) +INLINE_ECMA_INTRINSICS uint64_t NewobjDynrange([[maybe_unused]] JSThread *thread, [[maybe_unused]] uint16_t numArgs, + [[maybe_unused]] uint64_t a1) +{ + UNREACHABLE(); +} + +// NOLINTNEXTLINE(misc-definitions-in-headers) +INLINE_ECMA_INTRINSICS uint64_t RefeqDyn([[maybe_unused]] JSThread *thread, [[maybe_unused]] uint64_t a0, + [[maybe_unused]] uint64_t a1) +{ + UNREACHABLE(); +} + +// NOLINTNEXTLINE(misc-definitions-in-headers) +INLINE_ECMA_INTRINSICS uint64_t TypeofDyn(JSThread *thread, uint64_t acc) +{ + return FastRuntimeStub::FastTypeOf(thread, JSTaggedValue(acc)).GetRawData(); +} + +// NOLINTNEXTLINE(misc-definitions-in-headers) +INLINE_ECMA_INTRINSICS uint64_t Callruntimerange([[maybe_unused]] JSThread *thread, [[maybe_unused]] uint64_t a0, + [[maybe_unused]] uint64_t a1) +{ + UNREACHABLE(); +} + +// NOLINTNEXTLINE(misc-definitions-in-headers) +INLINE_ECMA_INTRINSICS uint64_t LdlexenvDyn(JSThread *thread) +{ + return coretypes::TaggedValue(GetEcmascriptEnvironment(thread)->GetLexicalEnv()).GetRawData(); +} + +// NOLINTNEXTLINE(misc-definitions-in-headers) +INLINE_ECMA_INTRINSICS uint64_t GetUnmappedArgs([[maybe_unused]] JSThread *thread) +{ + UNREACHABLE(); +} + +// NOLINTNEXTLINE(misc-definitions-in-headers) +INLINE_ECMA_INTRINSICS uint64_t Negate(uint64_t value) +{ + return JSTaggedValue(value).ToBoolean() ? JSTaggedValue::False().GetRawData() : JSTaggedValue::True().GetRawData(); +} + +// NOLINTNEXTLINE(misc-definitions-in-headers) +INLINE_ECMA_INTRINSICS uint64_t IsUndefined(uint64_t value) +{ + return JSTaggedValue(value).IsUndefined() ? JSTaggedValue::True().GetRawData() + : JSTaggedValue::False().GetRawData(); +} + +// NOLINTNEXTLINE(misc-definitions-in-headers) +INLINE_ECMA_INTRINSICS uint64_t IsTrue(uint64_t value) +{ + return JSTaggedValue(value).IsTrue() ? JSTaggedValue::True().GetRawData() : JSTaggedValue::False().GetRawData(); +} + +// NOLINTNEXTLINE(misc-definitions-in-headers) +INLINE_ECMA_INTRINSICS uint64_t IsFalse(uint64_t value) +{ + return JSTaggedValue(value).IsFalse() ? JSTaggedValue::True().GetRawData() : JSTaggedValue::False().GetRawData(); +} + +// NOLINTNEXTLINE(misc-definitions-in-headers) +INLINE_ECMA_INTRINSICS uint64_t IsCoercible(uint64_t uvalue) +{ + JSTaggedValue value(uvalue); + if (value.IsUndefined() || value.IsNull()) { + return JSTaggedValue::False().GetRawData(); + } + + return JSTaggedValue::True().GetRawData(); +} + +// NOLINTNEXTLINE(misc-definitions-in-headers) +INLINE_ECMA_INTRINSICS uint64_t SuspendGenerator([[maybe_unused]] JSThread *thread, [[maybe_unused]] uint64_t a0, + [[maybe_unused]] uint64_t a1) +{ + UNREACHABLE(); +} + +// NOLINTNEXTLINE(misc-definitions-in-headers) +INLINE_ECMA_INTRINSICS uint64_t SuspendAsyncGenerator([[maybe_unused]] JSThread *thread, [[maybe_unused]] uint64_t a0, + [[maybe_unused]] uint64_t a1) +{ + UNREACHABLE(); +} + +// NOLINTNEXTLINE(misc-definitions-in-headers) +INLINE_ECMA_INTRINSICS void ThrowUndefined([[maybe_unused]] JSThread *thread, [[maybe_unused]] uint64_t objValue) +{ + UNREACHABLE(); +} + +// NOLINTNEXTLINE(misc-definitions-in-headers) +INLINE_ECMA_INTRINSICS uint64_t Copyrestargs([[maybe_unused]] JSThread *thread, [[maybe_unused]] uint16_t index) +{ + UNREACHABLE(); +} + +// NOLINTNEXTLINE(misc-definitions-in-headers) +INLINE_ECMA_INTRINSICS uint64_t Ldhole() +{ + return JSTaggedValue::Hole().GetRawData(); +} + +// Just declared here, not used +// NOLINTNEXTLINE(misc-definitions-in-headers) +INLINE_ECMA_INTRINSICS void ReturnUndefined([[maybe_unused]] JSThread *thread) +{ + UNREACHABLE(); +} + +// NOLINTNEXTLINE(misc-definitions-in-headers) +INLINE_ECMA_INTRINSICS uint64_t Call0Dyn(JSThread *thread, uint64_t raw_fn_object) +{ + std::array args = { + JSTaggedValue(raw_fn_object), + JSTaggedValue::Undefined(), + JSTaggedValue::Undefined(), + }; + return HandlerCall(thread, JSTaggedValue(raw_fn_object), args.data(), args.size()).GetRawData(); +} + +// NOLINTNEXTLINE(misc-definitions-in-headers) +INLINE_ECMA_INTRINSICS uint64_t Call1Dyn(JSThread *thread, uint64_t raw_fn_object, uint64_t raw_a0) +{ + std::array args = { + JSTaggedValue(raw_fn_object), + JSTaggedValue::Undefined(), + JSTaggedValue::Undefined(), + JSTaggedValue(raw_a0), + }; + return HandlerCall(thread, JSTaggedValue(raw_fn_object), args.data(), args.size()).GetRawData(); +} + +// NOLINTNEXTLINE(misc-definitions-in-headers) +INLINE_ECMA_INTRINSICS uint64_t Call2Dyn(JSThread *thread, uint64_t raw_fn_object, uint64_t raw_a0, uint64_t raw_a1) +{ + std::array args = { + JSTaggedValue(raw_fn_object), JSTaggedValue::Undefined(), JSTaggedValue::Undefined(), + JSTaggedValue(raw_a0), JSTaggedValue(raw_a1), + }; + return HandlerCall(thread, JSTaggedValue(raw_fn_object), args.data(), args.size()).GetRawData(); +} + +// NOLINTNEXTLINE(misc-definitions-in-headers) +INLINE_ECMA_INTRINSICS uint64_t Call3Dyn(JSThread *thread, uint64_t raw_fn_object, uint64_t raw_a0, uint64_t raw_a1, + uint64_t raw_a2) +{ + std::array args = { + JSTaggedValue(raw_fn_object), JSTaggedValue::Undefined(), JSTaggedValue::Undefined(), + JSTaggedValue(raw_a0), JSTaggedValue(raw_a1), JSTaggedValue(raw_a2), + }; + return HandlerCall(thread, JSTaggedValue(raw_fn_object), args.data(), args.size()).GetRawData(); +} + +// Just declared here, not used +// NOLINTNEXTLINE(misc-definitions-in-headers) +INLINE_ECMA_INTRINSICS uint64_t CalliRangeDyn([[maybe_unused]] JSThread *thread, [[maybe_unused]] uint16_t num, + [[maybe_unused]] uint64_t objValue) +{ + UNREACHABLE(); +} + +// Just declared here, not used +// NOLINTNEXTLINE(misc-definitions-in-headers) +INLINE_ECMA_INTRINSICS uint64_t CalliThisRangeDyn([[maybe_unused]] JSThread *thread, [[maybe_unused]] uint16_t num, + [[maybe_unused]] uint64_t objValue) +{ + UNREACHABLE(); +} + +// NOLINTNEXTLINE(misc-definitions-in-headers) +INLINE_ECMA_INTRINSICS uint64_t StOwnByIndex(JSThread *thread, uint32_t idx, uint64_t uobj, uint64_t uvalue) +{ + JSTaggedValue obj(uobj); + JSTaggedValue value(uvalue); + // fast path + if (obj.IsECMAObject() && !obj.IsClassConstructor() && !obj.IsClassPrototype() && + !FastRuntimeStub::IsSpecialIndexedObjForSet(obj)) { + if (!FastRuntimeStub::SetOwnElement(thread, obj, idx, value)) { + return JSTaggedValue(JSTaggedValue::VALUE_EXCEPTION).GetRawData(); + } + return JSTaggedValue(JSTaggedValue::VALUE_HOLE).GetRawData(); + } + + return SlowRuntimeStub::StOwnByIndex(thread, obj, idx, value).GetRawData(); +} + +// NOLINTNEXTLINE(misc-definitions-in-headers) +INLINE_ECMA_INTRINSICS uint64_t Ldglobal(JSThread *thread) +{ + return GetGlobalObject(thread).GetRawData(); +} + +// NOLINTNEXTLINE(misc-definitions-in-headers) +INLINE_ECMA_INTRINSICS uint64_t LdObjByValue(JSThread *thread, uint64_t rec, uint64_t pkey, + [[maybe_unused]] uint16_t slotId) +{ + [[maybe_unused]] EcmaHandleScope handleScope(thread); + JSHandle receiverHandle(thread, JSTaggedValue(rec)); + + auto receiver = JSTaggedValue(rec); + auto prop_key = JSTaggedValue(pkey); + +#if ECMASCRIPT_ENABLE_IC + auto profileTypeInfo = GetRuntimeProfileTypeInfo(thread); + if (!profileTypeInfo.IsUndefined()) { + auto profileTypeArray = ProfileTypeInfo::Cast(profileTypeInfo.GetTaggedObject()); + JSTaggedValue firstValue = profileTypeArray->Get(slotId); + JSTaggedValue res = JSTaggedValue::Hole(); + + if (LIKELY(firstValue.IsHeapObject())) { + JSTaggedValue secondValue = profileTypeArray->Get(slotId + 1); + res = ICRuntimeStub::TryLoadICByValue(thread, receiver, prop_key, firstValue, secondValue); + } + // IC miss and not enter the megamorphic state, store as polymorphic + if (res.IsHole() && !firstValue.IsHole()) { + res = ICRuntimeStub::LoadICByValue(thread, profileTypeArray, receiver, prop_key, slotId); + } + + if (LIKELY(!res.IsHole())) { + return res.GetRawData(); + } + } +#endif + + // fast path + if (LIKELY(receiver.IsHeapObject())) { + JSTaggedValue res = FastRuntimeStub::GetPropertyByValue(thread, receiver, prop_key); + if (!res.IsHole()) { + return res.GetRawData(); + } + } + // slow path + return SlowRuntimeStub::LdObjByValue(thread, receiver, prop_key, false, JSTaggedValue::Undefined()).GetRawData(); +} + +// NOLINTNEXTLINE(misc-definitions-in-headers) +INLINE_ECMA_INTRINSICS uint64_t StObjByValue(JSThread *thread, uint64_t rec, uint64_t pkey, uint64_t val, + [[maybe_unused]] uint16_t slotId) +{ + [[maybe_unused]] EcmaHandleScope handleScope(thread); + + auto receiver = JSTaggedValue(rec); + auto prop_key = JSTaggedValue(pkey); + auto value = JSTaggedValue(val); + +#if ECMASCRIPT_ENABLE_IC + auto profileTypeInfo = GetRuntimeProfileTypeInfo(thread); + if (!profileTypeInfo.IsUndefined()) { + auto profileTypeArray = ProfileTypeInfo::Cast(profileTypeInfo.GetTaggedObject()); + JSTaggedValue firstValue = profileTypeArray->Get(slotId); + JSTaggedValue res = JSTaggedValue::Hole(); + + if (LIKELY(firstValue.IsHeapObject())) { + JSTaggedValue secondValue = profileTypeArray->Get(slotId + 1); + res = ICRuntimeStub::TryStoreICByValue(thread, receiver, prop_key, firstValue, secondValue, value); + } + // IC miss and not enter the megamorphic state, store as polymorphic + if (res.IsHole() && !firstValue.IsHole()) { + res = ICRuntimeStub::StoreICByValue(thread, profileTypeArray, receiver, prop_key, value, slotId); + } + + if (LIKELY(!res.IsHole())) { + return res.GetRawData(); + } + } +#endif + + JSHandle receiverHandle(thread, receiver); + JSHandle propKeyHandle(thread, prop_key); + JSHandle valueHandle(thread, value); + + if (receiver.IsHeapObject()) { + JSTaggedValue res = FastRuntimeStub::SetPropertyByValue(thread, receiver, prop_key, value); + if (!res.IsHole()) { + return res.GetRawData(); + } + } + + // slow path + receiver = receiverHandle.GetTaggedValue(); // May be moved by GC + prop_key = propKeyHandle.GetTaggedValue(); // May be moved by GC + value = valueHandle.GetTaggedValue(); // May be moved by GC + + return SlowRuntimeStub::StObjByValue(thread, receiver, prop_key, value).GetRawData(); +} + +// NOLINTNEXTLINE(misc-definitions-in-headers) +INLINE_ECMA_INTRINSICS uint64_t TryStGlobalByName(JSThread *thread, uint32_t string_id, uint64_t value, + [[maybe_unused]] uint16_t slotId) +{ + JSTaggedValue prop_key = GetConstantPool(thread)->GetObjectFromCache(string_id); + auto global_obj = GetGlobalObject(thread); + +#if ECMASCRIPT_ENABLE_IC + auto profileTypeInfo = GetRuntimeProfileTypeInfo(thread); + if (!profileTypeInfo.IsUndefined()) { + JSTaggedValue res = + ICRuntimeStub::StoreGlobalICByName(thread, ProfileTypeInfo::Cast(profileTypeInfo.GetTaggedObject()), + global_obj, prop_key, JSTaggedValue(value), slotId, true); + return res.GetRawData(); + } +#endif + + bool found = false; + SlowRuntimeStub::LdGlobalRecord(thread, prop_key, &found); + // 1. find from global record + if (found) { + return SlowRuntimeStub::TryUpdateGlobalRecord(thread, prop_key, JSTaggedValue(value)).GetRawData(); + } + // 2. find from global object + FastRuntimeStub::GetGlobalOwnProperty(global_obj, prop_key, &found); + if (!found) { + auto result = SlowRuntimeStub::ThrowReferenceError(thread, prop_key, " is not defined"); + if (result.IsException()) { + return result.GetRawData(); + } + } + return SlowRuntimeStub::StGlobalVar(thread, prop_key, JSTaggedValue(value)).GetRawData(); +} + +// NOLINTNEXTLINE(misc-definitions-in-headers) +INLINE_ECMA_INTRINSICS uint64_t LdGlobalVar(JSThread *thread, uint32_t string_id, [[maybe_unused]] uint16_t slotId) +{ + auto global_obj = thread->GetGlobalObject(); + auto prop_key = GetConstantPool(thread)->GetObjectFromCache(string_id); + +#if ECMASCRIPT_ENABLE_IC + auto profileTypeInfo = GetRuntimeProfileTypeInfo(thread); + if (!profileTypeInfo.IsUndefined()) { + JSTaggedValue res = ICRuntimeStub::LoadGlobalICByName( + thread, ProfileTypeInfo::Cast(profileTypeInfo.GetTaggedObject()), global_obj, prop_key, slotId); + return res.GetRawData(); + } +#endif + + bool found = false; + auto result = FastRuntimeStub::GetGlobalOwnProperty(global_obj, prop_key, &found); + if (found) { + return result.GetRawData(); + } + return SlowRuntimeStub::LdGlobalVar(thread, global_obj, prop_key).GetRawData(); +} + +// NOLINTNEXTLINE(misc-definitions-in-headers) +INLINE_ECMA_INTRINSICS uint64_t StGlobalVar(JSThread *thread, uint32_t string_id, uint64_t value, + [[maybe_unused]] uint16_t slotId) +{ + auto global_obj = thread->GetGlobalObject(); + JSTaggedValue prop = GetConstantPool(thread)->GetObjectFromCache(string_id); + +#if ECMASCRIPT_ENABLE_IC + auto profileTypeInfo = GetRuntimeProfileTypeInfo(thread); + if (!profileTypeInfo.IsUndefined()) { + JSTaggedValue res = + ICRuntimeStub::StoreGlobalICByName(thread, ProfileTypeInfo::Cast(profileTypeInfo.GetTaggedObject()), + global_obj, prop, JSTaggedValue(value), slotId); + return res.GetRawData(); + } +#endif + + return SlowRuntimeStub::StGlobalVar(thread, prop, JSTaggedValue(value)).GetRawData(); +} + +// NOLINTNEXTLINE(misc-definitions-in-headers) +INLINE_ECMA_INTRINSICS uint64_t StGlobalLet(JSThread *thread, uint32_t string_id, uint64_t value) +{ + JSTaggedValue prop = GetConstantPool(thread)->GetObjectFromCache(string_id); + return SlowRuntimeStub::StGlobalRecord(thread, prop, JSTaggedValue(value), false).GetRawData(); +} + +// NOLINTNEXTLINE(misc-definitions-in-headers) +INLINE_ECMA_INTRINSICS uint64_t LdObjByName(JSThread *thread, uint32_t stringId, uint64_t object, + [[maybe_unused]] uint16_t slotId) +{ + auto obj = JSTaggedValue(object); + auto prop_key = GetConstantPool(thread)->GetObjectFromCache(stringId); + +#if ECMASCRIPT_ENABLE_IC + auto profileTypeInfo = GetRuntimeProfileTypeInfo(thread); + if (!profileTypeInfo.IsUndefined()) { + auto profileTypeArray = ProfileTypeInfo::Cast(profileTypeInfo.GetTaggedObject()); + JSTaggedValue firstValue = profileTypeArray->Get(slotId); + JSTaggedValue res = JSTaggedValue::Hole(); + + if (LIKELY(firstValue.IsHeapObject())) { + JSTaggedValue secondValue = profileTypeArray->Get(slotId + 1); + res = ICRuntimeStub::TryLoadICByName(thread, obj, firstValue, secondValue); + } + // IC miss and not enter the megamorphic state, store as polymorphic + if (res.IsHole() && !firstValue.IsHole()) { + res = ICRuntimeStub::LoadICByName(thread, profileTypeArray, obj, prop_key, slotId); + } + + if (LIKELY(!res.IsHole())) { + return res.GetRawData(); + } + } +#endif + + if (LIKELY(obj.IsHeapObject())) { + JSTaggedValue res = FastRuntimeStub::GetPropertyByName(thread, obj, prop_key); + if (!res.IsHole()) { + return res.GetRawData(); + } + } + return SlowRuntimeStub::LdObjByName(thread, obj, prop_key, false, JSTaggedValue::Undefined()).GetRawData(); +} + +// NOLINTNEXTLINE(misc-definitions-in-headers) +INLINE_ECMA_INTRINSICS uint64_t StObjByName(JSThread *thread, uint32_t string_id, uint64_t object, uint64_t val, + [[maybe_unused]] uint16_t slotId) +{ + auto obj = JSTaggedValue(object); + auto value = JSTaggedValue(val); + + auto prop_key = GetConstantPool(thread)->GetObjectFromCache(string_id); + +#if ECMASCRIPT_ENABLE_IC + auto profileTypeInfo = GetRuntimeProfileTypeInfo(thread); + if (!profileTypeInfo.IsUndefined()) { + auto profileTypeArray = ProfileTypeInfo::Cast(profileTypeInfo.GetTaggedObject()); + JSTaggedValue firstValue = profileTypeArray->Get(slotId); + JSTaggedValue res = JSTaggedValue::Hole(); + + if (LIKELY(firstValue.IsHeapObject())) { + JSTaggedValue secondValue = profileTypeArray->Get(slotId + 1); + res = ICRuntimeStub::TryStoreICByName(thread, obj, firstValue, secondValue, value); + } + // IC miss and not enter the megamorphic state, store as polymorphic + if (res.IsHole() && !firstValue.IsHole()) { + res = ICRuntimeStub::StoreICByName(thread, profileTypeArray, obj, prop_key, value, slotId); + } + + if (LIKELY(!res.IsHole())) { + return res.GetRawData(); + } + } +#endif + + if (obj.IsHeapObject()) { + JSTaggedValue res = FastRuntimeStub::SetPropertyByName(thread, obj, prop_key, value); + if (!res.IsHole()) { + return res.GetRawData(); + } + } + return SlowRuntimeStub::StObjByName(thread, obj, prop_key, value).GetRawData(); +} + +// NOLINTNEXTLINE(misc-definitions-in-headers) +INLINE_ECMA_INTRINSICS uint64_t LdObjByIndex(JSThread *thread, uint32_t idx, uint64_t uobj) +{ + auto obj = JSTaggedValue(uobj); + + // fast path + if (LIKELY(obj.IsHeapObject())) { + JSTaggedValue res = FastRuntimeStub::GetPropertyByIndex(thread, obj, idx); + if (!res.IsHole()) { + return res.GetRawData(); + } + } + // not meet fast condition or fast path return hole, walk slow path + // slow stub not need receiver + return SlowRuntimeStub::LdObjByIndex(thread, obj, idx, false, JSTaggedValue::Undefined()).GetRawData(); +} + +// NOLINTNEXTLINE(misc-definitions-in-headers) +INLINE_ECMA_INTRINSICS uint64_t StObjByIndex(JSThread *thread, uint32_t idx, uint64_t uobj, uint64_t uval) +{ + auto obj = JSTaggedValue(uobj); + auto val = JSTaggedValue(uval); + + // fast path + if (obj.IsHeapObject()) { + JSTaggedValue res = FastRuntimeStub::SetPropertyByIndex(thread, obj, idx, val); + if (!res.IsHole()) { + return res.GetRawData(); + } + } + // slow path + return SlowRuntimeStub::StObjByIndex(thread, obj, idx, val).GetRawData(); +} + +static inline bool GetLeftRightInt(JSThread *thread, uint64_t lhs, uint64_t rhs, bool isULeft, bool isURight, + int32_t &left, int32_t &right) +{ + auto jleft = JSTaggedValue(lhs); + auto jright = JSTaggedValue(rhs); + + if (jleft.IsInt() && jright.IsInt()) { + left = jleft.GetInt(); + right = jright.GetInt(); + return true; + } + + if (jleft.IsNumber() && jright.IsNumber()) { + left = jleft.IsInt() ? jleft.GetInt() : base::NumberHelper::DoubleToInt(jleft.GetDouble(), base::INT32_BITS); + right = + jright.IsInt() ? jright.GetInt() : base::NumberHelper::DoubleToInt(jright.GetDouble(), base::INT32_BITS); + return true; + } + + [[maybe_unused]] EcmaHandleScope handleScope(thread); + JSHandle rightHandle(thread, jright); + jleft = isULeft ? SlowRuntimeStub::ToJSTaggedValueWithUint32(thread, jleft) + : SlowRuntimeStub::ToJSTaggedValueWithInt32(thread, jleft); + + if (jleft.IsException()) { + return false; + } + + jright = rightHandle.GetTaggedValue(); // Maybe moved by GC + jright = isURight ? SlowRuntimeStub::ToJSTaggedValueWithUint32(thread, jright) + : SlowRuntimeStub::ToJSTaggedValueWithInt32(thread, jright); + + if (jright.IsException()) { + return false; + } + + left = jleft.GetInt(); + right = jright.GetInt(); + return true; +} + +// NOLINTNEXTLINE(misc-definitions-in-headers) +INLINE_ECMA_INTRINSICS uint64_t And2Dyn(JSThread *thread, uint64_t lhs, uint64_t rhs) +{ + int32_t left; + int32_t right; + + if (!GetLeftRightInt(thread, lhs, rhs, false, false, left, right)) { + return TaggedValue::VALUE_EXCEPTION; + } + + // NOLINT(hicpp-signed-bitwise) + auto ret = static_cast(left) & static_cast(right); + return JSTaggedValue(static_cast(ret)).GetRawData(); +} + +// NOLINTNEXTLINE(misc-definitions-in-headers) +INLINE_ECMA_INTRINSICS uint64_t Or2Dyn(JSThread *thread, uint64_t lhs, uint64_t rhs) +{ + int32_t left; + int32_t right; + + if (!GetLeftRightInt(thread, lhs, rhs, false, false, left, right)) { + return TaggedValue::VALUE_EXCEPTION; + } + + auto ret = static_cast(left) | static_cast(right); + return JSTaggedValue(static_cast(ret)).GetRawData(); +} + +// NOLINTNEXTLINE(misc-definitions-in-headers) +INLINE_ECMA_INTRINSICS uint64_t Xor2Dyn(JSThread *thread, uint64_t lhs, uint64_t rhs) +{ + int32_t left; + int32_t right; + + if (!GetLeftRightInt(thread, lhs, rhs, false, false, left, right)) { + return TaggedValue::VALUE_EXCEPTION; + } + + auto ret = static_cast(left) ^ static_cast(right); + return JSTaggedValue(static_cast(ret)).GetRawData(); +} + +// NOLINTNEXTLINE(misc-definitions-in-headers) +INLINE_ECMA_INTRINSICS uint64_t Shl2Dyn(JSThread *thread, uint64_t lhs, uint64_t rhs) +{ + int32_t left; + int32_t right; + + if (!GetLeftRightInt(thread, lhs, rhs, false, true, left, right)) { + return TaggedValue::VALUE_EXCEPTION; + } + + uint32_t shift = static_cast(right) & 0x1f; // NOLINT(hicpp-signed-bitwise, readability-magic-numbers) + using unsigned_type = std::make_unsigned_t; + return JSTaggedValue(static_cast(static_cast(left) << shift)) + .GetRawData(); // NOLINT(hicpp-signed-bitwise) +} + +// NOLINTNEXTLINE(misc-definitions-in-headers) +INLINE_ECMA_INTRINSICS uint64_t Shr2Dyn(JSThread *thread, uint64_t lhs, uint64_t rhs) +{ + int32_t left; + int32_t right; + + if (!GetLeftRightInt(thread, lhs, rhs, false, true, left, right)) { + return TaggedValue::VALUE_EXCEPTION; + } + + uint32_t shift = static_cast(right) & 0x1f; // NOLINT(hicpp-signed-bitwise, readability-magic-numbers) + return JSTaggedValue(static_cast(left >> shift)).GetRawData(); // NOLINT(hicpp-signed-bitwise) +} + +// NOLINTNEXTLINE(misc-definitions-in-headers) +INLINE_ECMA_INTRINSICS uint64_t Ashr2Dyn(JSThread *thread, uint64_t lhs, uint64_t rhs) +{ + int32_t left; + int32_t right; + + if (!GetLeftRightInt(thread, lhs, rhs, true, true, left, right)) { + return TaggedValue::VALUE_EXCEPTION; + } + + uint32_t shift = static_cast(right) & 0x1f; // NOLINT(hicpp-signed-bitwise, readability-magic-numbers) + using unsigned_type = std::make_unsigned_t; + return JSTaggedValue(static_cast(static_cast(left) >> shift)) + .GetRawData(); // NOLINT(hicpp-signed-bitwise) +} + +// NOLINTNEXTLINE(misc-definitions-in-headers) +INLINE_ECMA_INTRINSICS uint64_t RethrowDyn(JSThread *thread, uint64_t obj) +{ + JSTaggedValue objValue(obj); + + if (JSTaggedValue(obj).IsHole()) { + return objValue.GetRawData(); + } + + SlowRuntimeStub::ThrowDyn(thread, objValue); + return JSTaggedValue(JSTaggedValue::VALUE_EXCEPTION).GetRawData(); +} + +// NOLINTNEXTLINE(misc-definitions-in-headers) +INLINE_ECMA_INTRINSICS void ThrowDyn(JSThread *thread, uint64_t obj) +{ + SlowRuntimeStub::ThrowDyn(thread, JSTaggedValue(obj)); +} + +// NOLINTNEXTLINE(misc-definitions-in-headers) +INLINE_ECMA_INTRINSICS uint64_t Delobjprop(JSThread *thread, uint64_t obj, uint64_t prop) +{ + return SlowRuntimeStub::DelObjProp(thread, JSTaggedValue(obj), JSTaggedValue(prop)).GetRawData(); +} + +// define not constructor function +// NOLINTNEXTLINE(misc-definitions-in-headers) +INLINE_ECMA_INTRINSICS uint64_t DefineNCFuncDyn(JSThread *thread, uint32_t methodId, uint64_t env, uint64_t home_obj) +{ + JSHandle home_obj_handle(thread, JSTaggedValue(home_obj)); + JSHandle constpool(thread, GetConstantPool(thread)); + JSMutableHandle result(thread, constpool->GetObjectFromCache(methodId)); + JSHandle lex_env(thread, JSTaggedValue(env)); + ASSERT(!result.IsEmpty()); + if (!result->GetLexicalEnv().IsUndefined()) { + auto res = SlowRuntimeStub::DefineNCFuncDyn(thread, result->GetCallTarget()); + if (res.IsException()) { + return res.GetRawData(); + } + result.Update(res); + result->SetConstantPool(thread, constpool.GetTaggedValue()); + } + result->SetLexicalEnv(thread, lex_env.GetTaggedValue()); + result->SetHomeObject(thread, home_obj_handle.GetTaggedValue()); + return result.GetTaggedValue().GetRawData(); +} + +// NOLINTNEXTLINE(misc-definitions-in-headers) +INLINE_ECMA_INTRINSICS uint64_t LdLexVarDyn(JSThread *thread, uint16_t level, uint16_t slot) +{ + LexicalEnv *env = GetLexicalEnv(thread); + + for (int i = 0; i < level; i++) { + JSTaggedValue taggedParentEnv = env->GetParentEnv(); + ASSERT(!taggedParentEnv.IsUndefined()); + env = LexicalEnv::Cast(taggedParentEnv.GetHeapObject()); + } + return env->GetProperties(slot).GetRawData(); +} + +// NOLINTNEXTLINE(misc-definitions-in-headers) +INLINE_ECMA_INTRINSICS void popLexenvDyn(JSThread *thread) +{ + SetLexicalEnv(thread, GetLexicalEnv(thread)->GetParentEnv()); +} + +// NOLINTNEXTLINE(misc-definitions-in-headers) +INLINE_ECMA_INTRINSICS uint64_t IsinDyn(JSThread *thread, uint64_t prop, uint64_t obj) +{ + return SlowRuntimeStub::IsInDyn(thread, JSTaggedValue(prop), JSTaggedValue(obj)).GetRawData(); +} + +// NOLINTNEXTLINE(misc-definitions-in-headers) +INLINE_ECMA_INTRINSICS uint64_t InstanceofDyn(JSThread *thread, uint64_t obj, uint64_t target) +{ + return SlowRuntimeStub::InstanceofDyn(thread, JSTaggedValue(obj), JSTaggedValue(target)).GetRawData(); +} + +// NOLINTNEXTLINE(misc-definitions-in-headers) +INLINE_ECMA_INTRINSICS uint64_t NewobjspreadDyn(JSThread *thread, uint64_t func, uint64_t target, uint64_t arr) +{ + return SlowRuntimeStub::NewObjSpreadDyn(thread, JSTaggedValue(func), JSTaggedValue(target), JSTaggedValue(arr)) + .GetRawData(); +} + +// NOLINTNEXTLINE(misc-definitions-in-headers) +INLINE_ECMA_INTRINSICS uint64_t CallspreadDyn(JSThread *thread, uint64_t func, uint64_t obj, uint64_t arr) +{ + return SlowRuntimeStub::CallSpreadDyn(thread, JSTaggedValue(func), JSTaggedValue(obj), JSTaggedValue(arr)) + .GetRawData(); +} + +// NOLINTNEXTLINE(misc-definitions-in-headers) +INLINE_ECMA_INTRINSICS void JfalseDyn([[maybe_unused]] JSThread *thread, [[maybe_unused]] uint16_t offs, + [[maybe_unused]] uint64_t acc) +{ + UNREACHABLE(); +} + +// NOLINTNEXTLINE(misc-definitions-in-headers) +INLINE_ECMA_INTRINSICS void JtrueDyn([[maybe_unused]] JSThread *thread, [[maybe_unused]] uint16_t offs, + [[maybe_unused]] uint64_t acc) +{ + UNREACHABLE(); +} + +// NOLINTNEXTLINE(misc-definitions-in-headers) +INLINE_ECMA_INTRINSICS uint64_t ReturnDyn([[maybe_unused]] JSThread *thread, [[maybe_unused]] uint64_t acc) +{ + UNREACHABLE(); +} + +// NOLINTNEXTLINE(misc-definitions-in-headers) +INLINE_ECMA_INTRINSICS uint64_t NewlexenvDyn(JSThread *thread, uint16_t num_slots) +{ + auto factory = thread->GetEcmaVM()->GetFactory(); + JSHandle env(thread, GetEcmascriptEnvironment(thread)->GetLexicalEnv()); + + JSTaggedValue res = FastRuntimeStub::NewLexicalEnvDyn(thread, factory, num_slots); + if (res.IsHole()) { + res = SlowRuntimeStub::NewLexicalEnvDyn(thread, num_slots); + if (res.IsException()) { + return res.GetRawData(); + } + } + LexicalEnv::Cast(res.GetHeapObject())->SetParentEnv(thread, env.GetTaggedValue()); + SetLexicalEnv(thread, res); + return res.GetRawData(); +} + +// NOLINTNEXTLINE(misc-definitions-in-headers) +INLINE_ECMA_INTRINSICS uint64_t CopylexenvDyn(JSThread *thread) +{ + auto factory = thread->GetEcmaVM()->GetFactory(); + LexicalEnv *oldEnv = GetLexicalEnv(thread); + JSTaggedValue parentEnv = oldEnv->GetParentEnv(); + SetLexicalEnv(thread, parentEnv); + array_size_t numOfSlots = oldEnv->GetLength(); + + JSTaggedValue res = FastRuntimeStub::NewLexicalEnvDyn(thread, factory, numOfSlots); + if (res.IsHole()) { + res = SlowRuntimeStub::NewLexicalEnvDyn(thread, numOfSlots); + if (res.IsException()) { + return res.GetRawData(); + } + } + LexicalEnv::Cast(res.GetHeapObject())->SetParentEnv(thread, parentEnv); + SetLexicalEnv(thread, res); + + for (array_size_t i = 0; i < numOfSlots; i++) { + LexicalEnv::Cast(res.GetHeapObject())->Set(thread, i, oldEnv->Get(i)); + } + return res.GetRawData(); +} + +// NOLINTNEXTLINE(misc-definitions-in-headers) +INLINE_ECMA_INTRINSICS void StLexVarDyn(JSThread *thread, uint16_t level, uint16_t slot, uint64_t value) +{ + JSTaggedValue currentLexenv(GetLexicalEnv(thread)); + JSTaggedValue env(currentLexenv); + + for (int i = 0; i < level; i++) { + JSTaggedValue taggedParentEnv = LexicalEnv::Cast(env.GetHeapObject())->GetParentEnv(); + ASSERT(!taggedParentEnv.IsUndefined()); + env = taggedParentEnv; + } + LexicalEnv::Cast(env.GetHeapObject())->SetProperties(thread, slot, JSTaggedValue(value)); +} + +// NOLINTNEXTLINE(misc-definitions-in-headers) +INLINE_ECMA_INTRINSICS uint64_t Toboolean(uint64_t obj) +{ + return JSTaggedValue(obj).ToBoolean() ? JSTaggedValue::True().GetRawData() : JSTaggedValue::False().GetRawData(); +} + +// NOLINTNEXTLINE(misc-definitions-in-headers) +INLINE_ECMA_INTRINSICS uint64_t GetPropIterator(JSThread *thread, uint64_t obj) +{ + return SlowRuntimeStub::GetPropIterator(thread, JSTaggedValue(obj)).GetRawData(); +} + +// NOLINTNEXTLINE(misc-definitions-in-headers) +INLINE_ECMA_INTRINSICS uint64_t DefineGeneratorFunc(JSThread *thread, uint32_t method_id, uint64_t env) +{ + JSMutableHandle result(thread, GetConstantPool(thread)->GetObjectFromCache(method_id)); + JSHandle lex_env(thread, JSTaggedValue(env)); + ASSERT(!result.IsEmpty()); + if (!result->GetLexicalEnv().IsUndefined()) { + auto res = SlowRuntimeStub::DefineGeneratorFunc(thread, result->GetCallTarget()); + if (res.IsException()) { + return res.GetRawData(); + } + result.Update(res); + result->SetConstantPool(thread, JSTaggedValue(GetConstantPool(thread))); + } + result->SetLexicalEnv(thread, lex_env.GetTaggedValue()); + return result.GetTaggedValue().GetRawData(); +} + +// NOLINTNEXTLINE(misc-definitions-in-headers) +INLINE_ECMA_INTRINSICS uint64_t CreateIterResultObj(JSThread *thread, uint64_t value, uint8_t flag) +{ + return SlowRuntimeStub::CreateIterResultObj(thread, JSTaggedValue(value), flag != 0).GetRawData(); +} + +// NOLINTNEXTLINE(misc-definitions-in-headers) +INLINE_ECMA_INTRINSICS uint64_t ResumeGenerator(uint64_t genobj) +{ + JSGeneratorObject *obj = JSGeneratorObject::Cast(JSTaggedValue(genobj).GetHeapObject()); + return obj->GetResumeResult().GetRawData(); +} + +// NOLINTNEXTLINE(misc-definitions-in-headers) +INLINE_ECMA_INTRINSICS uint64_t GetResumeMode(uint64_t genobj) +{ + JSGeneratorObject *obj = JSGeneratorObject::Cast(JSTaggedValue(genobj).GetHeapObject()); + return obj->GetResumeMode().GetRawData(); +} + +// NOLINTNEXTLINE(misc-definitions-in-headers) +INLINE_ECMA_INTRINSICS uint64_t CreateGeneratorObj(JSThread *thread, uint64_t gen_func) +{ + return SlowRuntimeStub::CreateGeneratorObj(thread, JSTaggedValue(gen_func)).GetRawData(); +} + +// NOLINTNEXTLINE(misc-definitions-in-headers) +INLINE_ECMA_INTRINSICS uint64_t SetGeneratorState(JSThread *thread, uint64_t gen_func, uint8_t state) +{ + return SlowRuntimeStub::SetGeneratorState(thread, JSTaggedValue(gen_func), state).GetRawData(); +} + +// NOLINTNEXTLINE(misc-definitions-in-headers) +INLINE_ECMA_INTRINSICS uint64_t CreateAsyncGeneratorObj(JSThread *thread, uint64_t gen_func) +{ + return SlowRuntimeStub::CreateAsyncGeneratorObj(thread, JSTaggedValue(gen_func)).GetRawData(); +} + +// NOLINTNEXTLINE(misc-definitions-in-headers) +INLINE_ECMA_INTRINSICS uint64_t DefineAsyncFunc(JSThread *thread, uint32_t method_id, uint64_t env) +{ + JSMutableHandle result(thread, GetConstantPool(thread)->GetObjectFromCache(method_id)); + JSHandle lex_env(thread, JSTaggedValue(env)); + ASSERT(!result.IsEmpty()); + if (!result->GetLexicalEnv().IsUndefined()) { + auto res = SlowRuntimeStub::DefineAsyncFunc(thread, result->GetCallTarget()); + if (res.IsException()) { + return res.GetRawData(); + } + result.Update(res); + result->SetConstantPool(thread, JSTaggedValue(GetConstantPool(thread))); + } + result->SetLexicalEnv(thread, lex_env.GetTaggedValue()); + return result.GetTaggedValue().GetRawData(); +} + +// NOLINTNEXTLINE(misc-definitions-in-headers) +INLINE_ECMA_INTRINSICS uint64_t DefineAsyncGeneratorFunc(JSThread *thread, uint32_t method_id, uint64_t env) +{ + auto *result = JSFunction::Cast(GetConstantPool(thread)->GetObjectFromCache(method_id).GetHeapObject()); + ASSERT(result != nullptr); + if (!result->GetLexicalEnv().IsUndefined()) { + auto res = SlowRuntimeStub::DefineAsyncGeneratorFunc(thread, result->GetCallTarget()); + if (res.IsException()) { + return res.GetRawData(); + } + result = JSFunction::Cast(res.GetHeapObject()); + result->SetConstantPool(thread, JSTaggedValue(GetConstantPool(thread))); + } + result->SetLexicalEnv(thread, JSTaggedValue(env)); + return JSTaggedValue(result).GetRawData(); +} + +// NOLINTNEXTLINE(misc-definitions-in-headers) +INLINE_ECMA_INTRINSICS uint64_t AsyncFunctionEnter(JSThread *thread) +{ + return SlowRuntimeStub::AsyncFunctionEnter(thread).GetRawData(); +} + +// NOLINTNEXTLINE(misc-definitions-in-headers) +INLINE_ECMA_INTRINSICS uint64_t AsyncFunctionAwait(JSThread *thread, uint64_t async_fn_obj, uint64_t value) +{ + return SlowRuntimeStub::AsyncFunctionAwait(thread, JSTaggedValue(async_fn_obj), JSTaggedValue(value)).GetRawData(); +} + +// NOLINTNEXTLINE(misc-definitions-in-headers) +INLINE_ECMA_INTRINSICS uint64_t AsyncFunctionResolve(JSThread *thread, uint64_t async_fn_obj, uint64_t value) +{ + return SlowRuntimeStub::AsyncFunctionResolveOrReject(thread, JSTaggedValue(async_fn_obj), JSTaggedValue(value), + true) + .GetRawData(); +} + +// NOLINTNEXTLINE(misc-definitions-in-headers) +INLINE_ECMA_INTRINSICS uint64_t AsyncFunctionReject(JSThread *thread, uint64_t async_fn_obj, uint64_t value) +{ + return SlowRuntimeStub::AsyncFunctionResolveOrReject(thread, JSTaggedValue(async_fn_obj), JSTaggedValue(value), + false) + .GetRawData(); +} + +// NOLINTNEXTLINE(misc-definitions-in-headers) +INLINE_ECMA_INTRINSICS uint64_t AsyncGeneratorResolve(JSThread *thread, uint64_t async_fn_obj, uint64_t value) +{ + return SlowRuntimeStub::AsyncGeneratorResolve(thread, JSTaggedValue(async_fn_obj), JSTaggedValue(value)) + .GetRawData(); +} + +// NOLINTNEXTLINE(misc-definitions-in-headers) +INLINE_ECMA_INTRINSICS uint64_t AsyncGeneratorReject(JSThread *thread, uint64_t async_fn_obj, uint64_t value) +{ + return SlowRuntimeStub::AsyncGeneratorReject(thread, JSTaggedValue(async_fn_obj), JSTaggedValue(value)) + .GetRawData(); +} + +// NOLINTNEXTLINE(misc-definitions-in-headers) +INLINE_ECMA_INTRINSICS void ThrowConstAssignment(JSThread *thread, uint32_t string_id) +{ + return SlowRuntimeStub::ThrowConstAssignment(thread, GetConstantPool(thread)->GetObjectFromCache(string_id)); +} + +// NOLINTNEXTLINE(misc-definitions-in-headers) +INLINE_ECMA_INTRINSICS uint64_t ThrowUndefinedIfHole(JSThread *thread, uint32_t string_id, uint64_t hole) +{ + if (!JSTaggedValue(hole).IsHole()) { + return JSTaggedValue(hole).GetRawData(); + } + + auto obj = GetConstantPool(thread)->GetObjectFromCache(string_id); + ASSERT(obj.IsString()); + SlowRuntimeStub::ThrowUndefinedIfHole(thread, obj); + return JSTaggedValue(JSTaggedValue::VALUE_EXCEPTION).GetRawData(); +} + +// NOLINTNEXTLINE(misc-definitions-in-headers) +INLINE_ECMA_INTRINSICS uint64_t GetMethod(JSThread *thread, uint32_t stringId, uint64_t object) +{ + return SlowRuntimeStub::GetMethod(thread, JSTaggedValue(object), + GetConstantPool(thread)->GetObjectFromCache(stringId)) + .GetRawData(); +} + +// NOLINTNEXTLINE(misc-definitions-in-headers) +INLINE_ECMA_INTRINSICS uint64_t GetTemplateObject(JSThread *thread, uint64_t literal) +{ + return SlowRuntimeStub::GetTemplateObject(thread, JSTaggedValue(literal)).GetRawData(); +} + +// NOLINTNEXTLINE(misc-definitions-in-headers) +INLINE_ECMA_INTRINSICS uint64_t GetNextPropName(JSThread *thread, uint64_t iter) +{ + return SlowRuntimeStub::GetNextPropName(thread, JSTaggedValue(iter)).GetRawData(); +} + +// NOLINTNEXTLINE(misc-definitions-in-headers) +INLINE_ECMA_INTRINSICS uint64_t CreateEmptyObject(JSThread *thread) +{ + auto factory = thread->GetEcmaVM()->GetFactory(); + auto global_env = thread->GetEcmaVM()->GetGlobalEnv(); + return SlowRuntimeStub::CreateEmptyObject(thread, factory, global_env).GetRawData(); +} + +// NOLINTNEXTLINE(misc-definitions-in-headers) +INLINE_ECMA_INTRINSICS uint64_t CreateObjectWithBuffer(JSThread *thread, uint16_t index) +{ + JSObject *result = JSObject::Cast(GetConstantPool(thread)->GetObjectFromCache(index).GetHeapObject()); + return SlowRuntimeStub::CreateObjectWithBuffer(thread, thread->GetEcmaVM()->GetFactory(), result).GetRawData(); +} + +// NOLINTNEXTLINE(misc-definitions-in-headers) +INLINE_ECMA_INTRINSICS uint64_t SetObjectWithProto(JSThread *thread, uint64_t proto, uint64_t obj) +{ + return SlowRuntimeStub::SetObjectWithProto(thread, JSTaggedValue(proto), JSTaggedValue(obj)).GetRawData(); +} + +// NOLINTNEXTLINE(misc-definitions-in-headers) +INLINE_ECMA_INTRINSICS uint64_t CopyDataProperties(JSThread *thread, uint64_t dst, uint64_t src) +{ + return SlowRuntimeStub::CopyDataProperties(thread, JSTaggedValue(dst), JSTaggedValue(src)).GetRawData(); +} + +// NOLINTNEXTLINE(misc-definitions-in-headers) +INLINE_ECMA_INTRINSICS uint64_t DefineGetterSetterByValue(JSThread *thread, uint64_t obj, uint64_t prop, + uint64_t getter, uint64_t setter, uint64_t flag) +{ + return SlowRuntimeStub::DefineGetterSetterByValue(thread, JSTaggedValue(obj), JSTaggedValue(prop), + JSTaggedValue(getter), JSTaggedValue(setter), bool(flag)) + .GetRawData(); +} + +// NOLINTNEXTLINE(misc-definitions-in-headers) +INLINE_ECMA_INTRINSICS uint64_t CreateEmptyArray(JSThread *thread) +{ + auto factory = thread->GetEcmaVM()->GetFactory(); + auto global_env = thread->GetEcmaVM()->GetGlobalEnv(); + + return SlowRuntimeStub::CreateEmptyArray(thread, factory, global_env).GetRawData(); +} + +// NOLINTNEXTLINE(misc-definitions-in-headers) +INLINE_ECMA_INTRINSICS uint64_t CreateArrayWithBuffer(JSThread *thread, uint16_t index) +{ + auto factory = thread->GetEcmaVM()->GetFactory(); + JSArray *result = JSArray::Cast(GetConstantPool(thread)->GetObjectFromCache(index).GetHeapObject()); + return SlowRuntimeStub::CreateArrayWithBuffer(thread, factory, result).GetRawData(); +} + +// NOLINTNEXTLINE(misc-definitions-in-headers) +INLINE_ECMA_INTRINSICS uint64_t StOwnByIndex(JSThread *thread, uint64_t object, uint64_t idx, uint64_t val) +{ + [[maybe_unused]] EcmaHandleScope handleScope(thread); + JSHandle objHandle(thread, JSTaggedValue(object)); + JSHandle valueHandle(thread, JSTaggedValue(val)); + + auto obj = JSTaggedValue(object); + auto value = JSTaggedValue(val); + + if (obj.IsHeapObject() && !obj.IsClassConstructor() && !obj.IsClassPrototype()) { + JSTaggedValue res = FastRuntimeStub::SetPropertyByIndex(thread, obj, idx, value); + if (!res.IsHole()) { + return res.GetRawData(); + } + } + + obj = objHandle.GetTaggedValue(); // Maybe moved by GC + value = valueHandle.GetTaggedValue(); // Maybe moved by GC + return SlowRuntimeStub::StOwnByIndex(thread, obj, idx, value).GetRawData(); +} + +// NOLINTNEXTLINE(misc-definitions-in-headers) +INLINE_ECMA_INTRINSICS uint64_t StOwnByName(JSThread *thread, uint32_t string_id, uint64_t object, uint64_t val) +{ + [[maybe_unused]] EcmaHandleScope handleScope(thread); + JSHandle objHandle(thread, JSTaggedValue(object)); + JSHandle valueHandle(thread, JSTaggedValue(val)); + + auto obj = objHandle.GetTaggedValue(); + auto value = valueHandle.GetTaggedValue(); + + JSTaggedValue prop = GetConstantPool(thread)->GetObjectFromCache(string_id); + + if (obj.IsJSObject() && !obj.IsClassConstructor() && !obj.IsClassPrototype()) { + JSTaggedValue res = FastRuntimeStub::SetPropertyByName(thread, obj, prop, value); + if (!res.IsHole()) { + return res.GetRawData(); + } + } + + obj = objHandle.GetTaggedValue(); // Maybe moved by GC + value = valueHandle.GetTaggedValue(); // Maybe moved by GC + GetConstantPool(thread)->GetObjectFromCache(string_id); // Maybe moved by GC + return SlowRuntimeStub::StOwnByName(thread, obj, prop, value).GetRawData(); +} + +// NOLINTNEXTLINE(misc-definitions-in-headers) +INLINE_ECMA_INTRINSICS uint64_t StOwnByValue(JSThread *thread, uint64_t rec, uint64_t pkey, uint64_t val) +{ + [[maybe_unused]] EcmaHandleScope handleScope(thread); + + auto receiver = JSTaggedValue(rec); + auto prop_key = JSTaggedValue(pkey); + auto value = JSTaggedValue(val); + + JSHandle receiverHandle(thread, receiver); + JSHandle propHandle(thread, prop_key); + JSHandle valueHandle(thread, value); + + if (receiver.IsHeapObject() && !receiver.IsClassConstructor() && !receiver.IsClassPrototype()) { + JSTaggedValue res = FastRuntimeStub::SetPropertyByValue(thread, receiver, prop_key, value); + prop_key = propHandle.GetTaggedValue(); // Maybe moved by GC + value = valueHandle.GetTaggedValue(); // Maybe moved by GC + if (!res.IsHole()) { + if (value.IsJSFunction()) { + JSFunction::SetFunctionNameNoPrefix(thread, JSFunction::Cast(value.GetHeapObject()), prop_key); + } + return res.GetRawData(); + } + } + return SlowRuntimeStub::StOwnByValue(thread, receiverHandle, propHandle, valueHandle).GetRawData(); +} + +// NOLINTNEXTLINE(misc-definitions-in-headers) +INLINE_ECMA_INTRINSICS uint64_t StArraySpread(JSThread *thread, uint64_t dst, uint64_t index, uint64_t src) +{ + return SlowRuntimeStub::StArraySpread(thread, JSTaggedValue(dst), JSTaggedValue(index), JSTaggedValue(src)) + .GetRawData(); +} + +// NOLINTNEXTLINE(misc-definitions-in-headers) +INLINE_ECMA_INTRINSICS uint64_t GetIterator(JSThread *thread, uint64_t object) +{ + return SlowRuntimeStub::GetIterator(thread, JSTaggedValue(object)).GetRawData(); +} + +// NOLINTNEXTLINE(misc-definitions-in-headers) +INLINE_ECMA_INTRINSICS uint64_t GetAsyncIterator(JSThread *thread, uint64_t object) +{ + return SlowRuntimeStub::GetIterator(thread, JSTaggedValue(object), true).GetRawData(); +} + +// NOLINTNEXTLINE(misc-definitions-in-headers) +INLINE_ECMA_INTRINSICS uint64_t ThrowIfNotObject(JSThread *thread, uint64_t object) +{ + if (JSTaggedValue(object).IsECMAObject()) { + return JSTaggedValue(JSTaggedValue::VALUE_HOLE).GetRawData(); + } + SlowRuntimeStub::ThrowIfNotObject(thread); + return JSTaggedValue(JSTaggedValue::VALUE_EXCEPTION).GetRawData(); +} + +// NOLINTNEXTLINE(misc-definitions-in-headers) +INLINE_ECMA_INTRINSICS void ThrowThrowNotExists(JSThread *thread) +{ + SlowRuntimeStub::ThrowThrowNotExists(thread); +} + +// NOLINTNEXTLINE(misc-definitions-in-headers) +INLINE_ECMA_INTRINSICS uint64_t CreateObjectWithExcludedKeys([[maybe_unused]] JSThread *thread, + [[maybe_unused]] uint16_t numKeys, + [[maybe_unused]] uint64_t objValue, + [[maybe_unused]] uint64_t a0) +{ + UNREACHABLE(); +} + +// NOLINTNEXTLINE(misc-definitions-in-headers) +INLINE_ECMA_INTRINSICS void ThrowPatternNonCoercible(JSThread *thread) +{ + SlowRuntimeStub::ThrowPatternNonCoercible(thread); +} + +// NOLINTNEXTLINE(misc-definitions-in-headers) +INLINE_ECMA_INTRINSICS uint64_t CloseIterator(JSThread *thread, uint64_t iter, uint64_t completion) +{ + JSTaggedValue completionValue(completion); + + if (!completionValue.IsHole()) { + SlowRuntimeStub::ThrowDyn(thread, completionValue); + } + + return SlowRuntimeStub::CloseIterator(thread, JSTaggedValue(iter)).GetRawData(); +} + +// NOLINTNEXTLINE(misc-definitions-in-headers) +INLINE_ECMA_INTRINSICS uint64_t ImportModule(JSThread *thread, uint32_t string_id) +{ + auto prop = GetConstantPool(thread)->GetObjectFromCache(string_id); + return SlowRuntimeStub::ImportModule(thread, prop).GetRawData(); +} + +// NOLINTNEXTLINE(misc-definitions-in-headers) +INLINE_ECMA_INTRINSICS void StModuleVar(JSThread *thread, uint32_t string_id, uint64_t value) +{ + auto prop = GetConstantPool(thread)->GetObjectFromCache(string_id); + SlowRuntimeStub::StModuleVar(thread, prop, JSTaggedValue(value)); +} + +// NOLINTNEXTLINE(misc-definitions-in-headers) +INLINE_ECMA_INTRINSICS void CopyModule(JSThread *thread, uint64_t module) +{ + SlowRuntimeStub::CopyModule(thread, JSTaggedValue(module)); +} + +// NOLINTNEXTLINE(misc-definitions-in-headers) +INLINE_ECMA_INTRINSICS uint64_t LdModvarByName(JSThread *thread, uint32_t string_id, uint64_t module_obj) +{ + JSTaggedValue item_name = GetConstantPool(thread)->GetObjectFromCache(string_id); + return SlowRuntimeStub::LdModvarByName(thread, JSTaggedValue(module_obj), item_name).GetRawData(); +} + +// NOLINTNEXTLINE(misc-definitions-in-headers) +INLINE_ECMA_INTRINSICS uint64_t DefineClassWithBuffer(JSThread *thread, uint32_t method_id, uint16_t imm, + uint64_t lexenv, uint64_t proto) +{ + auto *constpool = GetConstantPool(thread); + JSFunction *classTemplate = JSFunction::Cast(constpool->GetObjectFromCache(method_id).GetTaggedObject()); + ASSERT(classTemplate != nullptr); + + TaggedArray *literalBuffer = TaggedArray::Cast(constpool->GetObjectFromCache(imm).GetTaggedObject()); + JSTaggedValue res; + if (LIKELY(!classTemplate->IsResolved())) { + res = SlowRuntimeStub::ResolveClass(thread, JSTaggedValue(classTemplate), literalBuffer, JSTaggedValue(proto), + JSTaggedValue(lexenv), constpool); + } else { + res = SlowRuntimeStub::CloneClassFromTemplate(thread, JSTaggedValue(classTemplate), JSTaggedValue(proto), + JSTaggedValue(lexenv), constpool); + } + + if (res.IsException()) { + return res.GetRawData(); + } + + ASSERT(res.IsClassConstructor()); + JSFunction *cls = JSFunction::Cast(res.GetTaggedObject()); + cls->SetLexicalEnv(thread, JSTaggedValue(lexenv)); + SlowRuntimeStub::SetClassConstructorLength(thread, res, cls->GetMethod()->GetLength()); + return res.GetRawData(); +} + +// Just declared here, not used +// NOLINTNEXTLINE(misc-definitions-in-headers) +INLINE_ECMA_INTRINSICS uint64_t SuperCall([[maybe_unused]] JSThread *thread, [[maybe_unused]] uint16_t range, + [[maybe_unused]] uint64_t firstVRegValue, [[maybe_unused]] uint64_t objValue) +{ + UNREACHABLE(); +} + +// NOLINTNEXTLINE(misc-definitions-in-headers) +INLINE_ECMA_INTRINSICS uint64_t SuperCallSpread(JSThread *thread, uint64_t array, uint64_t new_target, + uint64_t this_func) +{ + return SlowRuntimeStub::SuperCallSpread(thread, JSTaggedValue(this_func), JSTaggedValue(new_target), + JSTaggedValue(array)) + .GetRawData(); +} + +// NOLINTNEXTLINE(misc-definitions-in-headers) +INLINE_ECMA_INTRINSICS uint64_t DefineMethod(JSThread *thread, uint32_t methodId, uint64_t tagged_cur_env, + uint64_t home_object) +{ + JSHandle constpool(thread, GetConstantPool(thread)); + JSMutableHandle result(thread, constpool->GetObjectFromCache(methodId)); + JSHandle home(thread, JSTaggedValue(home_object)); + JSHandle env(thread, JSTaggedValue(tagged_cur_env)); + ASSERT(!result.IsEmpty()); + if (!result->GetLexicalEnv().IsUndefined()) { + auto res = SlowRuntimeStub::DefineMethod(thread, result->GetCallTarget(), home); + if (res.IsException()) { + return res.GetRawData(); + } + result.Update(res); + result->SetConstantPool(thread, constpool.GetTaggedValue()); + } else { + result->SetHomeObject(thread, home.GetTaggedValue()); + } + result->SetLexicalEnv(thread, env.GetTaggedValue()); + return result.GetTaggedValue().GetRawData(); +} + +// NOLINTNEXTLINE(misc-definitions-in-headers) +INLINE_ECMA_INTRINSICS uint64_t StSuperByName(JSThread *thread, uint32_t string_id, uint64_t obj, uint64_t value) +{ + JSTaggedValue prop_key = GetConstantPool(thread)->GetObjectFromCache(string_id); + return SlowRuntimeStub::StSuperByValue(thread, JSTaggedValue(obj), prop_key, JSTaggedValue(value), + GetThisFunc(thread)) + .GetRawData(); +} + +// NOLINTNEXTLINE(misc-definitions-in-headers) +INLINE_ECMA_INTRINSICS uint64_t LdSuperByName(JSThread *thread, uint32_t string_id, uint64_t obj) +{ + JSTaggedValue prop_key = GetConstantPool(thread)->GetObjectFromCache(string_id); + + return SlowRuntimeStub::LdSuperByValue(thread, JSTaggedValue(obj), prop_key, GetThisFunc(thread)).GetRawData(); +} + +// NOLINTNEXTLINE(misc-definitions-in-headers) +INLINE_ECMA_INTRINSICS uint64_t StSuperByValue(JSThread *thread, uint64_t receiver, uint64_t prop_key, uint64_t value) +{ + return SlowRuntimeStub::StSuperByValue(thread, JSTaggedValue(receiver), JSTaggedValue(prop_key), + JSTaggedValue(value), GetThisFunc(thread)) + .GetRawData(); +} + +// NOLINTNEXTLINE(misc-definitions-in-headers) +INLINE_ECMA_INTRINSICS uint64_t LdSuperByValue(JSThread *thread, uint64_t receiver, uint64_t prop_key) +{ + return SlowRuntimeStub::LdSuperByValue(thread, JSTaggedValue(receiver), JSTaggedValue(prop_key), + GetThisFunc(thread)) + .GetRawData(); +} + +// NOLINTNEXTLINE(misc-definitions-in-headers) +INLINE_ECMA_INTRINSICS uint64_t CreateObjectHavingMethod(JSThread *thread, uint16_t imm, uint64_t env) +{ + auto constpool = GetConstantPool(thread); + JSObject *result = JSObject::Cast(constpool->GetObjectFromCache(imm).GetHeapObject()); + auto factory = GetFactory(thread); + + return SlowRuntimeStub::CreateObjectHavingMethod(thread, factory, result, JSTaggedValue(env), constpool) + .GetRawData(); +} + +// NOLINTNEXTLINE(misc-definitions-in-headers) +INLINE_ECMA_INTRINSICS uint64_t ThrowIfSuperNotCorrectCall(JSThread *thread, uint16_t index, + [[maybe_unused]] uint64_t thisValue) +{ + return SlowRuntimeStub::ThrowIfSuperNotCorrectCall(thread, index, JSTaggedValue(thisValue)).GetRawData(); +} + +// NOLINTNEXTLINE(misc-definitions-in-headers) +INLINE_ECMA_INTRINSICS uint64_t LdHomeObject(JSThread *thread) +{ + return JSFunction::Cast(GetThisFunc(thread).GetHeapObject())->GetHomeObject().GetRawData(); +} + +// NOLINTNEXTLINE(misc-definitions-in-headers) +INLINE_ECMA_INTRINSICS void ThrowDeleteSuperProperty(JSThread *thread) +{ + SlowRuntimeStub::ThrowDeleteSuperProperty(thread); +} + +// NOLINTNEXTLINE(misc-definitions-in-headers) +INLINE_ECMA_INTRINSICS void Debugger() +{ + // no-op +} + +// NOLINTNEXTLINE(misc-definitions-in-headers) +INLINE_ECMA_INTRINSICS void NativeMethodWrapper([[maybe_unused]] uint64_t arg) +{ + UNREACHABLE(); +} + +// NOLINTNEXTLINE(misc-definitions-in-headers) +INLINE_ECMA_INTRINSICS void GetObjectClassType([[maybe_unused]] uint64_t arg) +{ + UNREACHABLE(); +} + +// NOLINTNEXTLINE(misc-definitions-in-headers) +INLINE_ECMA_INTRINSICS uint64_t IsNan([[maybe_unused]] double arg) +{ + return static_cast(std::isnan(arg)); +} + +// NOLINTNEXTLINE(misc-definitions-in-headers) +INLINE_ECMA_INTRINSICS uint32_t GetStringHashcode(uint64_t arg) +{ + return EcmaString::Cast(JSTaggedValue(arg).GetHeapObject())->GetHashcode(); +} + +// NOLINTNEXTLINE(misc-definitions-in-headers) +INLINE_ECMA_INTRINSICS uint64_t GetEcmaConstantPool(JSThread *thread) +{ + return JSTaggedValue(GetConstantPool(thread)).GetRawData(); +} + +// NOLINTNEXTLINE(misc-definitions-in-headers) +INLINE_ECMA_INTRINSICS uint64_t GetEcmaThisFunc(JSThread *thread) +{ + return JSTaggedValue(GetThisFunc(thread)).GetRawData(); +} + +// NOLINTNEXTLINE(misc-definitions-in-headers) +INLINE_ECMA_INTRINSICS uint64_t GetWeakReferent(uint64_t ref) +{ + return JSTaggedValue(JSTaggedValue(ref).GetWeakReferentUnChecked()).GetRawData(); +} + +// NOLINTNEXTLINE(misc-definitions-in-headers) +INLINE_ECMA_INTRINSICS uint8_t DynClassIsDictionaryElement([[maybe_unused]] uint64_t obj) +{ + JSHClass *cls = JSHClass::Cast(JSTaggedValue(obj).GetTaggedObject()); + return static_cast(cls->IsDictionaryElement()); +} + +// NOLINTNEXTLINE(misc-definitions-in-headers) +INLINE_ECMA_INTRINSICS uint8_t DynClassIsExtensible([[maybe_unused]] uint64_t obj) +{ + JSHClass *cls = JSHClass::Cast(JSTaggedValue(obj).GetTaggedObject()); + return static_cast(cls->IsExtensible()); +} + +// NOLINTNEXTLINE(misc-definitions-in-headers) +INLINE_ECMA_INTRINSICS uint64_t DynObjectGetClass(uint64_t obj) +{ + JSTaggedValue tagged_value(obj); + return JSTaggedValue(tagged_value.GetTaggedObject()->GetClass()).GetRawData(); +} + +// NOLINTNEXTLINE(misc-definitions-in-headers) +INLINE_ECMA_INTRINSICS void DynObjectSetClass(uint64_t obj, uint64_t hclass) +{ + auto *cls = JSHClass::Cast(JSTaggedValue(hclass).GetTaggedObject()); + JSTaggedValue(obj).GetTaggedObject()->SetClass(cls); +} + +} // namespace panda::ecmascript::intrinsics + +#endif // PLUGINS_ECMASCRIPT_RUNTIME_INTRINSICS_INL_H diff --git a/runtime/intrinsics.cpp b/runtime/intrinsics.cpp new file mode 100644 index 000000000..31c12ef85 --- /dev/null +++ b/runtime/intrinsics.cpp @@ -0,0 +1,7 @@ +/** + * Copyright (c) Huawei Technologies Co., Ltd. 2019-2021. All rights reserved. + */ + +#define INLINE_ECMA_INTRINSICS +#include "plugins/ecmascript/runtime/intrinsics-inl.h" +#include "intrinsics_gen.cpp" diff --git a/runtime/jobs/micro_job_queue.cpp b/runtime/jobs/micro_job_queue.cpp new file mode 100644 index 000000000..e0623b5c2 --- /dev/null +++ b/runtime/jobs/micro_job_queue.cpp @@ -0,0 +1,79 @@ +/* + * Copyright (c) 2021-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 "plugins/ecmascript/runtime/jobs/micro_job_queue.h" + +#include "plugins/ecmascript/runtime/global_env.h" +#include "plugins/ecmascript/runtime/jobs/pending_job.h" +#include "plugins/ecmascript/runtime/js_arguments.h" +#include "plugins/ecmascript/runtime/js_handle.h" +#include "plugins/ecmascript/runtime/js_tagged_value.h" +#include "plugins/ecmascript/runtime/js_thread.h" +#include "plugins/ecmascript/runtime/object_factory.h" +#include "plugins/ecmascript/runtime/tagged_queue-inl.h" +#include "plugins/ecmascript/runtime/tagged_queue.h" +#include "utils/expected.h" + +namespace panda::ecmascript::job { +void MicroJobQueue::EnqueueJob(JSThread *thread, JSHandle jobQueue, QueueType queueType, + const JSHandle &job, const JSHandle &argv) +{ + // 1. Assert: Type(queueName) is String and its value is the name of a Job Queue recognized by this implementation. + // 2. Assert: job is the name of a Job. + // 3. Assert: arguments is a List that has the same number of elements as the number of parameters required by job. + // 4. Let callerContext be the running execution context. + // 5. Let callerRealm be callerContext’s Realm. + ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); + [[maybe_unused]] EcmaHandleScope handleScope(thread); + JSHandle pendingJob(factory->NewPendingJob(job, argv)); + if (queueType == QueueType::QUEUE_PROMISE) { + JSHandle promiseQueue(thread, jobQueue->GetPromiseJobQueue()); + LOG_ECMA(DEBUG) << "promiseQueue start length: " << promiseQueue->Size(); + TaggedQueue *newPromiseQueue = TaggedQueue::Push(thread, promiseQueue, JSHandle(pendingJob)); + jobQueue->SetPromiseJobQueue(thread, JSTaggedValue(newPromiseQueue)); + LOG_ECMA(DEBUG) << "promiseQueue end length: " << newPromiseQueue->Size(); + } else if (queueType == QueueType::QUEUE_SCRIPT) { + JSHandle scriptQueue(thread, jobQueue->GetScriptJobQueue()); + TaggedQueue *newScriptQueue = TaggedQueue::Push(thread, scriptQueue, JSHandle(pendingJob)); + jobQueue->SetScriptJobQueue(thread, JSTaggedValue(newScriptQueue)); + } +} + +void MicroJobQueue::ExecutePendingJob(JSThread *thread, JSHandle jobQueue) +{ + [[maybe_unused]] EcmaHandleScope handleScope(thread); + JSMutableHandle promiseQueue(thread, jobQueue->GetPromiseJobQueue()); + JSMutableHandle pendingJob(thread, JSTaggedValue::Undefined()); + while (!promiseQueue->Empty()) { + LOG_ECMA(DEBUG) << "promiseQueue start length: " << promiseQueue->Size(); + pendingJob.Update(promiseQueue->Pop(thread)); + LOG_ECMA(DEBUG) << "promiseQueue end length: " << promiseQueue->Size(); + PendingJob::ExecutePendingJob(pendingJob, thread); + if (thread->HasPendingException()) { + return; + } + promiseQueue.Update(jobQueue->GetPromiseJobQueue()); + } + + JSHandle scriptQueue(thread, jobQueue->GetScriptJobQueue()); + while (!scriptQueue->Empty()) { + pendingJob.Update(scriptQueue->Pop(thread)); + PendingJob::ExecutePendingJob(pendingJob, thread); + if (thread->HasPendingException()) { + return; + } + } +} +} // namespace panda::ecmascript::job diff --git a/runtime/jobs/micro_job_queue.h b/runtime/jobs/micro_job_queue.h new file mode 100644 index 000000000..919494153 --- /dev/null +++ b/runtime/jobs/micro_job_queue.h @@ -0,0 +1,56 @@ +/* + * Copyright (c) 2021-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_JOBS_MICRO_JOB_QUEUE_H +#define ECMASCRIPT_JOBS_MICRO_JOB_QUEUE_H + +#include "plugins/ecmascript/runtime/ecma_runtime_call_info.h" +#include "plugins/ecmascript/runtime/ecma_string.h" +#include "plugins/ecmascript/runtime/jobs/pending_job.h" +#include "plugins/ecmascript/runtime/js_handle.h" +#include "plugins/ecmascript/runtime/js_thread.h" +#include "plugins/ecmascript/runtime/mem/c_containers.h" +#include "plugins/ecmascript/runtime/object_factory.h" +#include "plugins/ecmascript/runtime/record.h" +#include "plugins/ecmascript/runtime/tagged_array.h" + +namespace panda::ecmascript::job { +enum class QueueType : uint8_t { + QUEUE_PROMISE, + QUEUE_SCRIPT, +}; + +class MicroJobQueue final : public Record { +public: + static MicroJobQueue *Cast(ObjectHeader *object) + { + ASSERT(JSTaggedValue(object).IsMicroJobQueue()); + return static_cast(object); + } + + static void EnqueueJob(JSThread *thread, JSHandle jobQueue, QueueType queueType, + const JSHandle &job, const JSHandle &argv); + static void ExecutePendingJob(JSThread *thread, JSHandle jobQueue); + + static constexpr size_t PROMISE_JOB_QUEUE_OFFSET = Record::SIZE; + ACCESSORS(PromiseJobQueue, PROMISE_JOB_QUEUE_OFFSET, SCRIPT_JOB_QUEUE_OFFSET); + ACCESSORS(ScriptJobQueue, SCRIPT_JOB_QUEUE_OFFSET, SIZE); + + DECL_DUMP() + + DECL_VISIT_OBJECT(PROMISE_JOB_QUEUE_OFFSET, SIZE) +}; +} // namespace panda::ecmascript::job +#endif // ECMASCRIPT_JOBS_MICRO_JOB_QUEUE_H diff --git a/runtime/jobs/pending_job.h b/runtime/jobs/pending_job.h new file mode 100644 index 000000000..a29ad55b8 --- /dev/null +++ b/runtime/jobs/pending_job.h @@ -0,0 +1,57 @@ +/* + * Copyright (c) 2021-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_JOBS_PENDING_JOB_H +#define ECMASCRIPT_JOBS_PENDING_JOB_H + +#include "plugins/ecmascript/runtime/ecma_macros.h" +#include "plugins/ecmascript/runtime/internal_call_params.h" +#include "plugins/ecmascript/runtime/js_function.h" +#include "plugins/ecmascript/runtime/record.h" +#include "plugins/ecmascript/runtime/js_handle.h" +#include "plugins/ecmascript/runtime/object_factory.h" +#include "plugins/ecmascript/runtime/tagged_array.h" +#include "plugins/ecmascript/runtime/mem/c_containers.h" + +namespace panda::ecmascript::job { +class PendingJob final : public Record { +public: + static PendingJob *Cast(ObjectHeader *object) + { + ASSERT(JSTaggedValue(object).IsPendingJob()); + return static_cast(object); + } + + static JSTaggedValue ExecutePendingJob(const JSHandle &pendingJob, JSThread *thread) + { + JSHandle job(thread, pendingJob->GetJob()); + ASSERT(job->IsCallable()); + JSHandle thisValue(thread, JSTaggedValue::Undefined()); + JSHandle argv(thread, pendingJob->GetArguments()); + InternalCallParams *args = thread->GetInternalCallParams(); + args->MakeArgList(*argv); + return JSFunction::Call(thread, job, thisValue, argv->GetLength(), args->GetArgv()); + } + + static constexpr size_t JOB_OFFSET = Record::SIZE; + ACCESSORS(Job, JOB_OFFSET, ARGUMENT_OFFSET); + ACCESSORS(Arguments, ARGUMENT_OFFSET, SIZE); + + DECL_DUMP() + + DECL_VISIT_OBJECT(JOB_OFFSET, SIZE) +}; +} // namespace panda::ecmascript::job +#endif // ECMASCRIPT_JOBS_PENDING_JOB_H diff --git a/runtime/js_arguments.cpp b/runtime/js_arguments.cpp new file mode 100644 index 000000000..6e3ec102b --- /dev/null +++ b/runtime/js_arguments.cpp @@ -0,0 +1,190 @@ +/* + * Copyright (c) 2021-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 "plugins/ecmascript/runtime/js_arguments.h" + +#include "plugins/ecmascript/runtime/ecma_vm.h" +#include "plugins/ecmascript/runtime/global_env.h" +#include "plugins/ecmascript/runtime/object_factory.h" +#include "plugins/ecmascript/runtime/tagged_array-inl.h" + +namespace panda::ecmascript { +bool JSArguments::GetOwnProperty(JSThread *thread, const JSHandle &args, + const JSHandle &key, PropertyDescriptor &desc) +{ + // 1 ~ 3 Let desc be OrdinaryGetOwnProperty(args, P). + JSObject::OrdinaryGetOwnProperty(thread, JSHandle(args), key, desc); + if (desc.IsEmpty()) { + return true; + } + + // 4.Let map be the value of the [[ParameterMap]] internal slot of the arguments object. + JSHandle map(thread, args->GetParameterMap()); + + // 5.Let isMapped be HasOwnProperty(map, P). + bool isMapped = JSTaggedValue::HasOwnProperty(thread, map, key); + + // 6.Assert: isMapped is never an abrupt completion. + ASSERT(!thread->HasPendingException()); + + // 7.If the value of isMapped is true, then + // a.Set desc.[[Value]] to Get(map, P). + if (isMapped) { + auto prop = JSObject::GetProperty(thread, map, key).GetValue(); + desc.SetValue(prop); + } + + // 8.If IsDataDescriptor(desc) is true and P is "caller" and desc.[[Value]] is a strict mode Function object, + // throw a TypeError exception. + JSHandle caller = thread->GetEcmaVM()->GetFactory()->NewFromCanBeCompressString("caller"); + if (desc.IsDataDescriptor() && JSTaggedValue::SameValue(key.GetTaggedValue(), caller.GetTaggedValue()) && + desc.GetValue()->IsJSFunction()) { + THROW_TYPE_ERROR_AND_RETURN(thread, "Arguments GetOwnProperty: type error", false); + } + // 9.Return desc. + return true; +} + +bool JSArguments::DefineOwnProperty(JSThread *thread, const JSHandle &args, + const JSHandle &key, const PropertyDescriptor &desc) +{ + // 1 ~ 2 Let args be the arguments object and get map. + JSHandle map(thread, args->GetParameterMap()); + + // 3.Let isMapped be HasOwnProperty(map, P). + bool isMapped = JSTaggedValue::HasOwnProperty(thread, map, key); + + // 4.Let allowed be OrdinaryDefineOwnProperty(args, P, Desc). + bool allowed = JSObject::OrdinaryDefineOwnProperty(thread, JSHandle(args), key, desc); + + // 5.ReturnIfAbrupt(allowed). + RETURN_VALUE_IF_ABRUPT_COMPLETION(thread, allowed); + + // 6.If allowed is false, return false. + if (!allowed) { + return false; + } + + // 7.If the value of isMapped is true, then + // a.If IsAccessorDescriptor(Desc) is true, then + // i.Call map.[[Delete]](P). + // b.Else + // i.If Desc.[[Value]] is present, then + // 1.Let setStatus be Set(map, P, Desc.[[Value]], false). + // 2.Assert: setStatus is true because formal parameters mapped by argument objects are always writable. + // ii.If Desc.[[Writable]] is present and its value is false, then + // 1.Call map.[[Delete]](P). + if (isMapped) { + if (desc.IsAccessorDescriptor()) { + JSTaggedValue::DeleteProperty(thread, map, key); + } else { + if (desc.HasValue()) { + [[maybe_unused]] bool setStatus = JSTaggedValue::SetProperty(thread, map, key, desc.GetValue(), false); + ASSERT(setStatus); + } + if (desc.HasWritable() && !desc.IsWritable()) { + JSTaggedValue::DeleteProperty(thread, map, key); + } + } + } + + // 8.Return true. + return true; +} + +OperationResult JSArguments::GetProperty(JSThread *thread, const JSHandle &args, + const JSHandle &key, const JSHandle &receiver) +{ + // 1 ~ 2 Let args be the arguments object and get map. + JSHandle map(thread, args->GetParameterMap()); + + // 3.Let isMapped be HasOwnProperty(map, P). + bool isMapped = JSTaggedValue::HasOwnProperty(thread, map, key); + + // 4.Assert: isMapped is not an abrupt completion. + ASSERT(!thread->HasPendingException()); + + // 5.If the value of isMapped is false, then + // a.Return the result of calling the default ordinary object [[Get]] internal method (9.1.8) + // on args passing P and Receiver as the arguments. + if (!isMapped) { + return JSTaggedValue::GetProperty(thread, JSHandle::Cast(args), key, receiver); + } + + // 6.Else map contains a formal parameter mapping for P, + // a.Return Get(map, P). + return JSTaggedValue::GetProperty(thread, map, key); +} + +bool JSArguments::SetProperty(JSThread *thread, const JSHandle &args, const JSHandle &key, + const JSHandle &value, const JSHandle &receiver) +{ + // 1.Let args be the arguments object. + JSHandle map(thread, args->GetParameterMap()); + + // 2.If SameValue(args, Receiver) is false, then + // a.Let isMapped be false. + bool isMapped = false; + if (JSTaggedValue::SameValue(args.GetTaggedValue(), receiver.GetTaggedValue())) { + // 3.Else, + // a.Let map be the value of the [[ParameterMap]] internal slot of the arguments object. + // b.Let isMapped be HasOwnProperty(map, P). + // c.Assert: isMapped is not an abrupt completion. + isMapped = JSTaggedValue::HasOwnProperty(thread, map, key); + ASSERT(!thread->HasPendingException()); + } + + // 4.If isMapped is true, then + // a.Let setStatus be Set(map, P, V, false). + // b.Assert: setStatus is true because formal parameters mapped by argument objects are always writable. + if (isMapped) { + [[maybe_unused]] bool setStatus = JSTaggedValue::SetProperty(thread, map, key, value); + ASSERT(setStatus); + } + + // 5.Return the result of calling the default ordinary object [[Set]] internal method (9.1.9) + // on args passing P, V and Receiver as the arguments. + return JSTaggedValue::SetProperty(thread, JSHandle::Cast(args), key, value, receiver); +} + +bool JSArguments::DeleteProperty(JSThread *thread, const JSHandle &args, + const JSHandle &key) +{ + // 1.Let map be the value of the [[ParameterMap]] internal slot of the arguments object. + JSHandle map(thread, args->GetParameterMap()); + + // 2.Let isMapped be HasOwnProperty(map, P). + bool isMapped = JSTaggedValue::HasOwnProperty(thread, map, key); + + // 3.Assert: isMapped is not an abrupt completion. + ASSERT(!thread->HasPendingException()); + + // 4.Let result be the result of calling the default [[Delete]] internal method for ordinary objects (9.1.10) + // on the arguments object passing P as the argument. + bool result = JSTaggedValue::DeleteProperty(thread, JSHandle(args), key); + + // 5.ReturnIfAbrupt(result). + RETURN_VALUE_IF_ABRUPT_COMPLETION(thread, result); + + // 6.If result is true and the value of isMapped is true, then + // a.Call map.[[Delete]](P). + if (result && isMapped) { + JSTaggedValue::DeleteProperty(thread, map, key); + } + + // 7.Return result. + return result; +} +} // namespace panda::ecmascript diff --git a/runtime/js_arguments.h b/runtime/js_arguments.h new file mode 100644 index 000000000..7bf3032ce --- /dev/null +++ b/runtime/js_arguments.h @@ -0,0 +1,69 @@ +/* + * Copyright (c) 2021-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_JSARGUMENTS_H +#define ECMASCRIPT_JSARGUMENTS_H + +#include "plugins/ecmascript/runtime/js_object.h" +#include "plugins/ecmascript/runtime/tagged_array.h" + +namespace panda::ecmascript { +class JSThread; + +class JSArguments : public JSObject { +public: + static constexpr int LENGTH_OF_INLINE_PROPERTIES = 4; + static constexpr int LENGTH_INLINE_PROPERTY_INDEX = 0; + static constexpr int ITERATOR_INLINE_PROPERTY_INDEX = 1; + static constexpr int CALLEE_INLINE_PROPERTY_INDEX = 2; + + CAST_NO_CHECK(JSArguments); + + // 9.4.4.1 [[GetOwnProperty]] (P) + static bool GetOwnProperty(JSThread *thread, const JSHandle &args, const JSHandle &key, + PropertyDescriptor &desc); + // 9.4.4.2 [[DefineOwnProperty]] (P, Desc) + static bool DefineOwnProperty(JSThread *thread, const JSHandle &args, + const JSHandle &key, const PropertyDescriptor &desc); + // 9.4.4.3 [[Get]] (P, Receiver) + static inline OperationResult GetProperty(JSThread *thread, const JSHandle &obj, + const JSHandle &key) + { + return GetProperty(thread, JSHandle::Cast(obj), key, JSHandle::Cast(obj)); + } + static OperationResult GetProperty(JSThread *thread, const JSHandle &args, + const JSHandle &key, const JSHandle &receiver); + // 9.4.4.4 [[Set]] ( P, V, Receiver) + static inline bool SetProperty(JSThread *thread, const JSHandle &obj, const JSHandle &key, + const JSHandle &value) + { + return SetProperty(thread, JSHandle::Cast(obj), key, value, JSHandle::Cast(obj)); + } + static bool SetProperty(JSThread *thread, const JSHandle &args, const JSHandle &key, + const JSHandle &value, const JSHandle &receiver); + // 9.4.4.5 [[Delete]] (P) + static bool DeleteProperty(JSThread *thread, const JSHandle &args, const JSHandle &key); + // 9.4.4.6 CreateUnmappedArgumentsObject(argumentsList) + // 9.4.4.7 CreateMappedArgumentsObject ( func, formals, argumentsList, env ) + + // ACCESSORS(JSTaggedValue, Length, LENGTH_OFFSET) + static constexpr size_t PARAMETER_MAP_OFFSET = JSObject::SIZE; + ACCESSORS(ParameterMap, PARAMETER_MAP_OFFSET, SIZE) + + DECL_VISIT_OBJECT_FOR_JS_OBJECT(JSObject, PARAMETER_MAP_OFFSET, SIZE) +}; +} // namespace panda::ecmascript + +#endif // ECMASCRIPT_JSARGUMENTS_H diff --git a/runtime/js_array.cpp b/runtime/js_array.cpp new file mode 100644 index 000000000..5c493bd5b --- /dev/null +++ b/runtime/js_array.cpp @@ -0,0 +1,379 @@ +/* + * Copyright (c) 2021-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 "plugins/ecmascript/runtime/js_array.h" +#include "plugins/ecmascript/runtime/accessor_data.h" +#include "plugins/ecmascript/runtime/ecma_vm.h" +#include "plugins/ecmascript/runtime/global_env.h" +#include "plugins/ecmascript/runtime/internal_call_params.h" +#include "plugins/ecmascript/runtime/js_invoker.h" +#include "plugins/ecmascript/runtime/js_tagged_value-inl.h" +#include "plugins/ecmascript/runtime/object_factory.h" +#include "interpreter/fast_runtime_stub-inl.h" + +namespace panda::ecmascript { +JSTaggedValue JSArray::LengthGetter([[maybe_unused]] JSThread *thread, const JSHandle &self) +{ + return JSArray::Cast(*self)->GetLength(); +} + +bool JSArray::LengthSetter(JSThread *thread, const JSHandle &self, const JSHandle &value, + bool mayThrow) +{ + uint32_t newLen = 0; + if (!JSTaggedValue::ToArrayLength(thread, value, &newLen) && mayThrow) { + THROW_RANGE_ERROR_AND_RETURN(thread, "array length must less than 2^32 - 1", false); + } + + if (!IsArrayLengthWritable(thread, self)) { + if (mayThrow) { + THROW_TYPE_ERROR_AND_RETURN(thread, "Cannot assign to read only property", false); + } + return false; + } + + uint32_t oldLen = JSArray::Cast(*self)->GetArrayLength(); + JSArray::SetCapacity(thread, self, oldLen, newLen); + return true; +} + +JSHandle JSArray::ArrayCreate(JSThread *thread, JSTaggedNumber length) +{ + JSHandle env = thread->GetEcmaVM()->GetGlobalEnv(); + JSHandle arrayFunction = env->GetArrayFunction(); + return JSArray::ArrayCreate(thread, length, arrayFunction); +} + +// 9.4.2.2 ArrayCreate(length, proto) +JSHandle JSArray::ArrayCreate(JSThread *thread, JSTaggedNumber length, + const JSHandle &newTarget) +{ + ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); + // Assert: length is an integer Number ≥ 0. + ASSERT_PRINT(length.IsInteger() && length.GetNumber() >= 0, "length must be positive integer"); + // 2. If length is −0, let length be +0. + double arrayLength = JSTaggedValue::ToInteger(thread, JSHandle(thread, length)).GetDouble(); + if (arrayLength > MAX_ARRAY_INDEX) { + JSHandle exception(thread, JSTaggedValue::Exception()); + THROW_RANGE_ERROR_AND_RETURN(thread, "array length must less than 2^32 - 1", exception); + } + uint32_t normalArrayLength = length.ToUint32(); + + // 8. Set the [[Prototype]] internal slot of A to proto. + JSHandle env = thread->GetEcmaVM()->GetGlobalEnv(); + JSHandle arrayFunc = env->GetArrayFunction(); + JSHandle obj = factory->NewJSObjectByConstructor(JSHandle(arrayFunc), newTarget); + // 9. Set the [[Extensible]] internal slot of A to true. + obj->GetJSHClass()->SetExtensible(true); + + // 10. Perform OrdinaryDefineOwnProperty(A, "length", PropertyDescriptor{[[Value]]: length, [[Writable]]: + // true, [[Enumerable]]: false, [[Configurable]]: false}). + JSArray::Cast(*obj)->SetArrayLength(thread, normalArrayLength); + + return JSHandle(obj); +} + +// 9.4.2.3 ArraySpeciesCreate(originalArray, length) +JSTaggedValue JSArray::ArraySpeciesCreate(JSThread *thread, const JSHandle &originalArray, + JSTaggedNumber length) +{ + JSHandle env = thread->GetEcmaVM()->GetGlobalEnv(); + const GlobalEnvConstants *globalConst = thread->GlobalConstants(); + // Assert: length is an integer Number ≥ 0. + ASSERT_PRINT(length.IsInteger() && length.GetNumber() >= 0, "length must be positive integer"); + // If length is −0, let length be +0. + double arrayLength = JSTaggedValue::ToInteger(thread, JSHandle(thread, length)).GetDouble(); + if (arrayLength == -0) { + arrayLength = +0; + } + // Let C be undefined. + // Let isArray be IsArray(originalArray). + JSHandle originalValue(originalArray); + bool isArray = originalValue->IsArray(thread); + // ReturnIfAbrupt(isArray). + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + // If isArray is true, then + JSHandle constructor(thread, JSTaggedValue::Undefined()); + if (isArray) { + // Let C be Get(originalArray, "constructor"). + auto *hclass = originalArray->GetJSHClass(); + if (hclass->IsJSArray() && !hclass->HasConstructor()) { + return JSArray::ArrayCreate(thread, length).GetTaggedValue(); + } + JSHandle constructorKey = globalConst->GetHandledConstructorString(); + constructor = JSTaggedValue::GetProperty(thread, originalValue, constructorKey).GetValue(); + // ReturnIfAbrupt(C). + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + // If IsConstructor(C) is true, then + if (constructor->IsConstructor()) { + // Let thisRealm be the running execution context’s Realm. + // Let realmC be GetFunctionRealm(C). + JSHandle realmC = JSObject::GetFunctionRealm(thread, constructor); + // ReturnIfAbrupt(realmC). + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + // If thisRealm and realmC are not the same Realm Record, then + if (*realmC != *env) { + JSTaggedValue realmArrayConstructor = realmC->GetArrayFunction().GetTaggedValue(); + // If SameValue(C, realmC.[[intrinsics]].[[%Array%]]) is true, let C be undefined. + if (JSTaggedValue::SameValue(constructor.GetTaggedValue(), realmArrayConstructor)) { + return JSArray::ArrayCreate(thread, length).GetTaggedValue(); + } + } + } + + // If Type(C) is Object, then + if (constructor->IsECMAObject()) { + // Let C be Get(C, @@species). + JSHandle speciesSymbol = env->GetSpeciesSymbol(); + constructor = JSTaggedValue::GetProperty(thread, constructor, speciesSymbol).GetValue(); + // ReturnIfAbrupt(C). + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + // If C is null, let C be undefined. + if (constructor->IsNull()) { + return JSArray::ArrayCreate(thread, length).GetTaggedValue(); + } + } + } + + // If C is undefined, return ArrayCreate(length). + if (constructor->IsUndefined()) { + return JSArray::ArrayCreate(thread, length).GetTaggedValue(); + } + // If IsConstructor(C) is false, throw a TypeError exception. + if (!constructor->IsConstructor()) { + THROW_TYPE_ERROR_AND_RETURN(thread, "Not a constructor", JSTaggedValue::Exception()); + } + // Return Construct(C, «length»). + JSHandle newTarget(thread, JSTaggedValue::Undefined()); + InternalCallParams *arguments = thread->GetInternalCallParams(); + arguments->MakeArgv(JSTaggedValue(arrayLength)); + JSTaggedValue result = JSFunction::Construct(thread, constructor, 1, arguments->GetArgv(), newTarget); + + // NOTEIf originalArray was created using the standard built-in Array constructor for + // a Realm that is not the Realm of the running execution context, then a new Array is + // created using the Realm of the running execution context. This maintains compatibility + // with Web browsers that have historically had that behaviour for the Array.prototype methods + // that now are defined using ArraySpeciesCreate. + return result; +} + +void JSArray::SetCapacity(JSThread *thread, const JSHandle &array, uint32_t oldLen, uint32_t newLen) +{ + TaggedArray *element = TaggedArray::Cast(array->GetElements().GetTaggedObject()); + + if (element->IsDictionaryMode()) { + ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); + int32_t numOfElements = array->GetNumberOfElements(); + uint32_t newNumOfElements = newLen; + if (newLen < oldLen && numOfElements != 0) { + JSHandle dictHandle(thread, element); + JSHandle newArr = factory->NewTaggedArray(numOfElements); + GetAllElementKeys(thread, array, 0, newArr); + for (uint32_t i = numOfElements - 1; i >= newLen; i--) { + JSTaggedValue value = newArr->Get(i); + uint32_t output = 0; + JSTaggedValue::StringToElementIndex(value, &output); + JSTaggedValue key(static_cast(output)); + int entry = dictHandle->FindEntry(key); + uint32_t attr = dictHandle->GetAttributes(entry).GetValue(); + PropertyAttributes propAttr(attr); + if (propAttr.IsConfigurable()) { + JSHandle newDict = NumberDictionary::Remove(thread, dictHandle, entry); + array->SetElements(thread, newDict); + if (i == 0) { + newNumOfElements = i; + break; + } + } else { + newNumOfElements = i + 1; + break; + } + } + } + JSArray::Cast(*array)->SetArrayLength(thread, newNumOfElements); + return; + } + uint32_t capacity = element->GetLength(); + if (newLen <= capacity) { + // judge if need to cut down the array size, else fill the unused tail with holes + array->FillElementsWithHoles(thread, newLen, oldLen < capacity ? oldLen : capacity); + } + if (JSObject::ShouldTransToDict(oldLen, newLen)) { + JSObject::ElementsToDictionary(thread, array); + } else if (newLen > capacity) { + JSObject::GrowElementsCapacity(thread, array, newLen); + } + JSArray::Cast(*array)->SetArrayLength(thread, newLen); +} + +bool JSArray::ArraySetLength(JSThread *thread, const JSHandle &array, const PropertyDescriptor &desc) +{ + JSHandle lengthKeyHandle(thread->GlobalConstants()->GetHandledLengthString()); + + // 1. If the [[Value]] field of Desc is absent, then + if (!desc.HasValue()) { + // 1a. Return OrdinaryDefineOwnProperty(A, "length", Desc). + return JSObject::OrdinaryDefineOwnProperty(thread, array, lengthKeyHandle, desc); + } + // 2. Let newLenDesc be a copy of Desc. + // (Actual copying is not necessary.) + PropertyDescriptor newLenDesc = desc; + // 3. - 7. Convert Desc.[[Value]] to newLen. + uint32_t newLen = 0; + if (!JSTaggedValue::ToArrayLength(thread, desc.GetValue(), &newLen)) { + THROW_RANGE_ERROR_AND_RETURN(thread, "array length must less than 2^32 - 1", false); + } + // 8. Set newLenDesc.[[Value]] to newLen. + // (Done below, if needed.) + // 9. Let oldLenDesc be OrdinaryGetOwnProperty(A, "length"). + PropertyDescriptor oldLenDesc(thread); + [[maybe_unused]] bool success = GetOwnProperty(thread, array, lengthKeyHandle, oldLenDesc); + // 10. (Assert) + ASSERT(success); + + // 11. Let oldLen be oldLenDesc.[[Value]]. + uint32_t oldLen = 0; + JSTaggedValue::ToArrayLength(thread, oldLenDesc.GetValue(), &oldLen); + // 12. If newLen >= oldLen, then + if (newLen >= oldLen) { + // 8. Set newLenDesc.[[Value]] to newLen. + // 12a. Return OrdinaryDefineOwnProperty(A, "length", newLenDesc). + newLenDesc.SetValue(JSHandle(thread, JSTaggedValue(newLen))); + return JSObject::OrdinaryDefineOwnProperty(thread, array, lengthKeyHandle, newLenDesc); + } + // 13. If oldLenDesc.[[Writable]] is false, return false. + if (!oldLenDesc.IsWritable() || + // Also handle the {configurable: true} case since we later use + // JSArray::SetLength instead of OrdinaryDefineOwnProperty to change + // the length, and it doesn't have access to the descriptor anymore. + newLenDesc.IsConfigurable()) { + return false; + } + // 14. If newLenDesc.[[Writable]] is absent or has the value true, + // let newWritable be true. + bool newWritable = false; + if (!newLenDesc.HasWritable() || newLenDesc.IsWritable()) { + newWritable = true; + } + // 15. Else, + // 15a. Need to defer setting the [[Writable]] attribute to false in case + // any elements cannot be deleted. + // 15b. Let newWritable be false. (It's initialized as "false" anyway.) + // 15c. Set newLenDesc.[[Writable]] to true. + // (Not needed.) + + // Most of steps 16 through 19 is implemented by JSArray::SetCapacity. + JSArray::SetCapacity(thread, array, oldLen, newLen); + // Steps 19d-ii, 20. + if (!newWritable) { + PropertyDescriptor readonly(thread); + readonly.SetWritable(false); + success = JSObject::DefineOwnProperty(thread, array, lengthKeyHandle, readonly); + ASSERT_PRINT(success, "DefineOwnProperty of length must be success here!"); + } + + // Steps 19d-v, 21. Return false if there were non-deletable elements. + uint32_t arrayLength = JSArray::Cast(*array)->GetArrayLength(); + return arrayLength == newLen; +} + +bool JSArray::PropertyKeyToArrayIndex(JSThread *thread, const JSHandle &key, uint32_t *output) +{ + return JSTaggedValue::ToArrayLength(thread, key, output) && *output <= JSArray::MAX_ARRAY_INDEX; +} + +// 9.4.2.1 [[DefineOwnProperty]] ( P, Desc) +bool JSArray::DefineOwnProperty(JSThread *thread, const JSHandle &array, const JSHandle &key, + const PropertyDescriptor &desc) +{ + // 1. Assert: IsPropertyKey(P) is true. + ASSERT_PRINT(JSTaggedValue::IsPropertyKey(key), "Key is not a property key!"); + // 2. If P is "length", then + if (IsLengthString(thread, key)) { + // a. Return ArraySetLength(A, Desc). + return ArraySetLength(thread, array, desc); + } + // 3. Else if P is an array index, then + // already do in step 4. + // 4. Return OrdinaryDefineOwnProperty(A, P, Desc). + bool success = JSObject::OrdinaryDefineOwnProperty(thread, array, key, desc); + if (success) { + JSTaggedValue constructorKey = thread->GlobalConstants()->GetConstructorString(); + if (key.GetTaggedValue() == constructorKey) { + array->GetJSHClass()->SetHasConstructor(true); + return true; + } + } + return success; +} + +bool JSArray::DefineOwnProperty(JSThread *thread, const JSHandle &array, uint32_t index, + const PropertyDescriptor &desc) +{ + return JSObject::OrdinaryDefineOwnProperty(thread, array, index, desc); +} + +bool JSArray::IsLengthString(JSThread *thread, const JSHandle &key) +{ + return key.GetTaggedValue() == thread->GlobalConstants()->GetLengthString(); +} + +// ecma6 7.3 Operations on Objects +JSHandle JSArray::CreateArrayFromList(JSThread *thread, const JSHandle &elements, + array_size_t length) +{ + // Assert: elements is a List whose elements are all ECMAScript language values. + // 2. Let array be ArrayCreate(0) (see 9.4.2.2). + + // 4. For each element e of elements + auto env = thread->GetEcmaVM()->GetGlobalEnv(); + ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); + JSHandle arrayFunc = env->GetArrayFunction(); + JSHandle obj = factory->NewJSObjectByConstructor(JSHandle(arrayFunc), arrayFunc); + obj->GetJSHClass()->SetExtensible(true); + JSArray::Cast(*obj)->SetArrayLength(thread, length); + + obj->SetElements(thread, elements); + + return JSHandle(obj); +} + +JSHandle JSArray::FastGetPropertyByValue(JSThread *thread, const JSHandle &obj, + uint32_t index) +{ + auto result = FastRuntimeStub::FastGetPropertyByIndex(thread, obj.GetTaggedValue(), index); + return JSHandle(thread, result); +} + +JSHandle JSArray::FastGetPropertyByValue(JSThread *thread, const JSHandle &obj, + const JSHandle &key) +{ + auto result = FastRuntimeStub::FastGetPropertyByValue(thread, obj.GetTaggedValue(), key.GetTaggedValue()); + return JSHandle(thread, result); +} + +bool JSArray::FastSetPropertyByValue(JSThread *thread, const JSHandle &obj, uint32_t index, + const JSHandle &value) +{ + return FastRuntimeStub::FastSetPropertyByIndex(thread, obj.GetTaggedValue(), index, value.GetTaggedValue()); +} + +bool JSArray::FastSetPropertyByValue(JSThread *thread, const JSHandle &obj, + const JSHandle &key, const JSHandle &value) +{ + return FastRuntimeStub::FastSetPropertyByValue(thread, obj.GetTaggedValue(), key.GetTaggedValue(), + value.GetTaggedValue()); +} +} // namespace panda::ecmascript diff --git a/runtime/js_array.h b/runtime/js_array.h new file mode 100644 index 000000000..d15ac9ba7 --- /dev/null +++ b/runtime/js_array.h @@ -0,0 +1,101 @@ +/* + * Copyright (c) 2021-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_JSARRAY_H +#define ECMASCRIPT_JSARRAY_H + +#include +#include "plugins/ecmascript/runtime/js_object.h" +#include "plugins/ecmascript/runtime/js_tagged_value-inl.h" +#include "plugins/ecmascript/runtime/js_tagged_value.h" +#include "plugins/ecmascript/runtime/tagged_array.h" + +namespace panda::ecmascript { +// ecma6 9.4.2 Array Exotic Object +class JSArray : public JSObject { +public: + static constexpr int LENGTH_INLINE_PROPERTY_INDEX = 0; + + CAST_CHECK(JSArray, IsJSArray); + + static JSHandle ArrayCreate(JSThread *thread, JSTaggedNumber length); + static JSHandle ArrayCreate(JSThread *thread, JSTaggedNumber length, + const JSHandle &newTarget); + static JSTaggedValue ArraySpeciesCreate(JSThread *thread, const JSHandle &originalArray, + JSTaggedNumber length); + static bool ArraySetLength(JSThread *thread, const JSHandle &array, const PropertyDescriptor &desc); + static bool DefineOwnProperty(JSThread *thread, const JSHandle &array, const JSHandle &key, + const PropertyDescriptor &desc); + static bool DefineOwnProperty(JSThread *thread, const JSHandle &array, uint32_t index, + const PropertyDescriptor &desc); + + static bool IsLengthString(JSThread *thread, const JSHandle &key); + // ecma6 7.3 Operations on Objects + static JSHandle CreateArrayFromList(JSThread *thread, const JSHandle &elements) + { + return CreateArrayFromList(thread, elements, elements->GetLength()); + } + + static JSHandle CreateArrayFromList(JSThread *thread, const JSHandle &elements, + array_size_t length); + // use first inlined property slot for array length + inline uint32_t GetArrayLength() const + { + return GetLength().GetArrayLength(); + } + + inline void SetArrayLength(const JSThread *thread, uint32_t length) + { + SetLength(thread, JSTaggedValue(length)); + } + + static constexpr size_t LENGTH_OFFSET = JSObject::SIZE; + ACCESSORS(Length, LENGTH_OFFSET, SIZE); + + DECL_VISIT_OBJECT_FOR_JS_OBJECT(JSObject, LENGTH_OFFSET, SIZE) + + static const uint32_t MAX_ARRAY_INDEX = MAX_ELEMENT_INDEX; + DECL_DUMP() + + static int32_t GetArrayLengthOffset() + { + return LENGTH_OFFSET; + } + + static bool PropertyKeyToArrayIndex(JSThread *thread, const JSHandle &key, uint32_t *output); + + static JSTaggedValue LengthGetter(JSThread *thread, const JSHandle &self); + + static bool LengthSetter(JSThread *thread, const JSHandle &self, const JSHandle &value, + bool mayThrow = false); + + static JSHandle FastGetPropertyByValue(JSThread *thread, const JSHandle &obj, + uint32_t index); + + static JSHandle FastGetPropertyByValue(JSThread *thread, const JSHandle &obj, + const JSHandle &key); + + static bool FastSetPropertyByValue(JSThread *thread, const JSHandle &obj, uint32_t index, + const JSHandle &value); + + static bool FastSetPropertyByValue(JSThread *thread, const JSHandle &obj, + const JSHandle &key, const JSHandle &value); + +private: + static void SetCapacity(JSThread *thread, const JSHandle &array, uint32_t oldLen, uint32_t newLen); +}; +} // namespace panda::ecmascript + +#endif // ECMASCRIPT_JSARRAY_H diff --git a/runtime/js_array_iterator.cpp b/runtime/js_array_iterator.cpp new file mode 100644 index 000000000..605401a92 --- /dev/null +++ b/runtime/js_array_iterator.cpp @@ -0,0 +1,96 @@ +/* + * Copyright (c) 2021-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 "js_array_iterator.h" +#include "builtins/builtins_errors.h" +#include "plugins/ecmascript/runtime/base/typed_array_helper-inl.h" +#include "plugins/ecmascript/runtime/base/typed_array_helper.h" +#include "global_env.h" +#include "js_array.h" +#include "object_factory.h" + +namespace panda::ecmascript { +using BuiltinsBase = base::BuiltinsBase; +// 22.1.5.2.1 %ArrayIteratorPrototype%.next ( ) +JSTaggedValue JSArrayIterator::Next(EcmaRuntimeCallInfo *argv) +{ + ASSERT(argv); + JSThread *thread = argv->GetThread(); + [[maybe_unused]] EcmaHandleScope handleScope(thread); + // 1.Let O be the this value. + JSHandle input(BuiltinsBase::GetThis(argv)); + + // 2.If Type(O) is not Object, throw a TypeError exception. + // 3.If O does not have all of the internal slots of an TaggedArray Iterator Instance (22.1.5.3), throw a TypeError + // exception. + if (!input->IsJSArrayIterator()) { + THROW_TYPE_ERROR_AND_RETURN(thread, "this value is not an array iterator", JSTaggedValue::Exception()); + } + JSHandle iter(input); + // 4.Let a be O.[[IteratedArrayLike]]. + JSHandle array(thread, iter->GetIteratedArray()); + JSHandle undefinedHandle(thread, JSTaggedValue::Undefined()); + // 5.If a is undefined, return CreateIterResultObject(undefined, true). + if (array->IsUndefined()) { + return JSIterator::CreateIterResultObject(thread, undefinedHandle, true).GetTaggedValue(); + } + // 6.Let index be O.[[ArrayLikeNextIndex]]. + uint32_t index = iter->GetNextIndex().GetInt(); + // 7.Let itemKind be O.[[ArrayLikeIterationKind]]. + IterationKind itemKind = IterationKind(iter->GetIterationKind().GetInt()); + + uint32_t length; + // 8. If a has a [[TypedArrayName]] internal slot, then + // a. Let len be the value of O’s [[ArrayLength]] internal slot. + if (array->IsTypedArray()) { + length = base::TypedArrayHelper::GetArrayLength(thread, JSHandle(array)); + } else { + // 9.Else + JSHandle lengthKey = thread->GlobalConstants()->GetHandledLengthString(); + length = JSTaggedValue::GetProperty(thread, array, lengthKey).GetValue()->GetArrayLength(); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + } + + // 10.If index ≥ len, then + if (index >= length) { + // Set O.[[IteratedArrayLike]] to undefined. + // Return CreateIterResultObject(undefined, true). + iter->SetIteratedArray(thread, undefinedHandle); + return JSIterator::CreateIterResultObject(thread, undefinedHandle, true).GetTaggedValue(); + } + // 11.Set O.[[ArrayLikeNextIndex]] to index + 1. + iter->SetNextIndex(thread, JSTaggedValue(index + 1)); + // 12.If itemKind is key, return CreateIterResultObject(index, false). + JSHandle key(thread, JSTaggedValue(index)); + if (itemKind == IterationKind::KEY) { + return JSIterator::CreateIterResultObject(thread, key, false).GetTaggedValue(); + } + JSHandle sKey(JSTaggedValue::ToString(thread, key)); + JSHandle value = JSTaggedValue::GetProperty(thread, array, sKey).GetValue(); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + // 15.If itemKind is value, let result be elementValue. + if (itemKind == IterationKind::VALUE) { + return JSIterator::CreateIterResultObject(thread, value, false).GetTaggedValue(); + } + // 16. Else + ASSERT_PRINT(itemKind == IterationKind::KEY_AND_VALUE, "itemKind is invalid"); + ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); + JSHandle resultArray(factory->NewTaggedArray(2)); // 2 means the length of array + resultArray->Set(thread, 0, key); + resultArray->Set(thread, 1, value); + JSHandle keyAndValue(JSArray::CreateArrayFromList(thread, resultArray)); + return JSIterator::CreateIterResultObject(thread, keyAndValue, false).GetTaggedValue(); +} +} // namespace panda::ecmascript diff --git a/runtime/js_array_iterator.h b/runtime/js_array_iterator.h new file mode 100644 index 000000000..b13d3b0d1 --- /dev/null +++ b/runtime/js_array_iterator.h @@ -0,0 +1,40 @@ +/* + * Copyright (c) 2021-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_JS_ARRAY_ITERATOR_H +#define ECMASCRIPT_JS_ARRAY_ITERATOR_H + +#include "js_iterator.h" +#include "js_object.h" + +namespace panda::ecmascript { +class JSArrayIterator : public JSObject { +public: + CAST_CHECK(JSArrayIterator, IsJSArrayIterator); + + static JSTaggedValue Next(EcmaRuntimeCallInfo *argv); + + static constexpr size_t ITERATED_ARRAY_OFFSET = JSObject::SIZE; + ACCESSORS(IteratedArray, ITERATED_ARRAY_OFFSET, NEXT_INDEX_OFFSET) + ACCESSORS(NextIndex, NEXT_INDEX_OFFSET, ITERATION_KIND_OFFSET) + ACCESSORS(IterationKind, ITERATION_KIND_OFFSET, SIZE) + + DECL_VISIT_OBJECT_FOR_JS_OBJECT(JSObject, ITERATED_ARRAY_OFFSET, SIZE) + + DECL_DUMP() +}; +} // namespace panda::ecmascript + +#endif // ECMASCRIPT_JS_ARRAY_ITERATOR_H diff --git a/runtime/js_arraybuffer.cpp b/runtime/js_arraybuffer.cpp new file mode 100644 index 000000000..986b366a7 --- /dev/null +++ b/runtime/js_arraybuffer.cpp @@ -0,0 +1,64 @@ +/* + * Copyright (c) 2021-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 "plugins/ecmascript/runtime/js_arraybuffer.h" + +#include "plugins/ecmascript/runtime/base/builtins_base.h" +#include "plugins/ecmascript/runtime/ecma_macros.h" +#include "plugins/ecmascript/runtime/ecma_vm.h" +#include "plugins/ecmascript/runtime/object_factory.h" +#include "plugins/ecmascript/runtime/tagged_array.h" +#include "securec.h" + +namespace panda::ecmascript { +void JSArrayBuffer::CopyDataBlockBytes(JSTaggedValue toBlock, JSTaggedValue fromBlock, int32_t fromIndex, int32_t count) +{ + void *fromBuf = JSNativePointer::Cast(fromBlock.GetTaggedObject())->GetExternalPointer(); + void *toBuf = JSNativePointer::Cast(toBlock.GetTaggedObject())->GetExternalPointer(); + auto *from = static_cast(fromBuf); + auto *to = static_cast(toBuf); + if (memcpy_s(to, count, from + fromIndex, count) != EOK) { // NOLINT + LOG_ECMA(FATAL) << "memcpy_s failed"; + UNREACHABLE(); + } +} + +void JSArrayBuffer::Attach(JSThread *thread, JSTaggedValue arrayBufferByteLength, JSTaggedValue arrayBufferData) +{ + ASSERT(arrayBufferData.IsNativePointer()); + SetArrayBufferByteLength(thread, arrayBufferByteLength); + SetArrayBufferData(thread, arrayBufferData); + EcmaVM *vm = thread->GetEcmaVM(); + vm->PushToArrayDataList(JSNativePointer::Cast(arrayBufferData.GetHeapObject())); +} + +void JSArrayBuffer::Detach(JSThread *thread) +{ + JSTaggedValue arrayBufferData = GetArrayBufferData(); + // already detached. + if (arrayBufferData.IsNull()) { + return; + } + + EcmaVM *vm = thread->GetEcmaVM(); + // remove vm's control over arrayBufferData. + JSNativePointer *jsNativePointer = JSNativePointer::Cast(arrayBufferData.GetHeapObject()); + vm->RemoveArrayDataList(jsNativePointer); + jsNativePointer->Destroy(); + + SetArrayBufferData(thread, JSTaggedValue::Null()); + SetArrayBufferByteLength(thread, JSTaggedValue(0)); +} +} // namespace panda::ecmascript diff --git a/runtime/js_arraybuffer.h b/runtime/js_arraybuffer.h new file mode 100644 index 000000000..39d7a1c88 --- /dev/null +++ b/runtime/js_arraybuffer.h @@ -0,0 +1,48 @@ +/* + * Copyright (c) 2021-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_JSARRAYBUFFER_H +#define ECMASCRIPT_JSARRAYBUFFER_H + +#include "plugins/ecmascript/runtime/js_object.h" + +namespace panda::ecmascript { +class JSArrayBuffer final : public JSObject { +public: + CAST_NO_CHECK(JSArrayBuffer); + + // 6.2.6.2 + static void CopyDataBlockBytes(JSTaggedValue toBlock, JSTaggedValue fromBlock, int32_t fromIndex, int32_t count); + + void Attach(JSThread *thread, JSTaggedValue arrayBufferByteLength, JSTaggedValue arrayBufferData); + void Detach(JSThread *thread); + + bool IsDetach() + { + JSTaggedValue arrayBufferData = GetArrayBufferData(); + return arrayBufferData == JSTaggedValue::Null(); + } + + static constexpr size_t ARRAY_BUFFER_BYTE_LENGTH_OFFSET = JSObject::SIZE; + ACCESSORS(ArrayBufferByteLength, ARRAY_BUFFER_BYTE_LENGTH_OFFSET, ARRAY_BUFFER_DATA_OFFSET) + ACCESSORS(ArrayBufferData, ARRAY_BUFFER_DATA_OFFSET, ARRAY_BUFFER_SHARED_OFFSET) + ACCESSORS(Shared, ARRAY_BUFFER_SHARED_OFFSET, SIZE) + + DECL_DUMP() + DECL_VISIT_OBJECT_FOR_JS_OBJECT(JSObject, ARRAY_BUFFER_BYTE_LENGTH_OFFSET, SIZE) +}; +} // namespace panda::ecmascript + +#endif // ECMASCRIPT_JSARRAYBUFFER_H diff --git a/runtime/js_arraylist.cpp b/runtime/js_arraylist.cpp new file mode 100644 index 000000000..01f8ca1c1 --- /dev/null +++ b/runtime/js_arraylist.cpp @@ -0,0 +1,121 @@ +/* + * Copyright (c) 2021-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 "js_arraylist.h" + +#include "plugins/ecmascript/runtime/js_tagged_value.h" +#include "plugins/ecmascript/runtime/object_factory.h" + +namespace panda::ecmascript { +void JSArrayList::Add(JSThread *thread, const JSHandle &arrayList, const JSHandle &value) +{ + // GrowCapacity + uint32_t length = arrayList->GetLength().GetArrayLength(); + JSHandle elements = GrowCapacity(thread, arrayList, length + 1); + + ASSERT(!elements->IsDictionaryMode()); + elements->Set(thread, length, value); + arrayList->SetLength(thread, JSTaggedValue(++length)); +} + +JSHandle JSArrayList::GrowCapacity(const JSThread *thread, const JSHandle &obj, + uint32_t capacity) +{ + JSHandle oldElements(thread, obj->GetElements()); + uint32_t oldLength = oldElements->GetLength(); + if (capacity < oldLength) { + return oldElements; + } + uint32_t newCapacity = ComputeCapacity(capacity); + JSHandle newElements = + thread->GetEcmaVM()->GetFactory()->CopyArray(oldElements, oldLength, newCapacity); + + obj->SetElements(thread, newElements); + return newElements; +} + +JSTaggedValue JSArrayList::Get(JSThread *thread, const uint32_t index) +{ + if (index >= GetLength().GetArrayLength()) { + THROW_RANGE_ERROR_AND_RETURN(thread, "Get property index out-of-bounds", JSTaggedValue::Exception()); + } + + TaggedArray *elements = TaggedArray::Cast(GetElements().GetTaggedObject()); + return elements->Get(index); +} + +JSTaggedValue JSArrayList::Set(JSThread *thread, const uint32_t index, JSTaggedValue value) +{ + if (index >= GetLength().GetArrayLength()) { + THROW_RANGE_ERROR_AND_RETURN(thread, "Set property index out-of-bounds", JSTaggedValue::Exception()); + } + + TaggedArray *elements = TaggedArray::Cast(GetElements().GetTaggedObject()); + elements->Set(thread, index, value); + return JSTaggedValue::Undefined(); +} + +bool JSArrayList::Delete(JSThread *thread, const JSHandle &obj, const JSHandle &key) +{ + uint32_t index = 0; + if (UNLIKELY(JSTaggedValue::ToElementIndex(key.GetTaggedValue(), &index))) { + THROW_TYPE_ERROR_AND_RETURN(thread, "Can not delete a type other than number", false); + } + uint32_t length = obj->GetLength().GetArrayLength(); + if (index >= length) { + THROW_RANGE_ERROR_AND_RETURN(thread, "Delete property index out-of-bounds", false); + } + TaggedArray *elements = TaggedArray::Cast(obj->GetElements().GetTaggedObject()); + for (uint32_t i = 0; i < length - 1; i++) { + elements->Set(thread, i, elements->Get(i + 1)); + } + obj->SetLength(thread, JSTaggedValue(--length)); + return true; +} + +bool JSArrayList::Has(JSTaggedValue value) const +{ + TaggedArray *elements = TaggedArray::Cast(GetElements().GetTaggedObject()); + return !(elements->GetIdx(value) == TaggedArray::MAX_ARRAY_INDEX); +} + +JSHandle JSArrayList::OwnKeys(JSThread *thread, const JSHandle &obj) +{ + uint32_t length = obj->GetLength().GetArrayLength(); + ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); + JSHandle keys = factory->NewTaggedArray(length); + + for (uint32_t i = 0; i < length; i++) { + keys->Set(thread, i, JSTaggedValue(i)); + } + + return keys; +} + +bool JSArrayList::GetOwnProperty(JSThread *thread, const JSHandle &obj, + const JSHandle &key, PropertyDescriptor &desc) +{ + uint32_t index = 0; + if (UNLIKELY(JSTaggedValue::ToElementIndex(key.GetTaggedValue(), &index))) { + THROW_TYPE_ERROR_AND_RETURN(thread, "Can not get property whose type is not number", false); + } + + uint32_t length = obj->GetLength().GetArrayLength(); + if (index >= length) { + THROW_RANGE_ERROR_AND_RETURN(thread, "Get property index out-of-bounds", false); + } + return JSObject::GetOwnProperty(thread, JSHandle::Cast(obj), key, desc); +} +} // namespace panda::ecmascript diff --git a/runtime/js_arraylist.h b/runtime/js_arraylist.h new file mode 100644 index 000000000..23e2fe247 --- /dev/null +++ b/runtime/js_arraylist.h @@ -0,0 +1,64 @@ +/* + * Copyright (c) 2021-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_JSARRAYLIST_H +#define ECMASCRIPT_JSARRAYLIST_H + +#include "js_object.h" +#include "js_tagged_value-inl.h" + +namespace panda::ecmascript { +class JSArrayList : public JSObject { +public: + static constexpr int DEFAULT_CAPACITY_LENGTH = 10; + static JSArrayList *Cast(ObjectHeader *object) + { + return static_cast(object); + } + + static void Add(JSThread *thread, const JSHandle &arrayList, const JSHandle &value); + + JSTaggedValue Get(JSThread *thread, uint32_t index); + + JSTaggedValue Set(JSThread *thread, uint32_t index, JSTaggedValue value); + bool Has(JSTaggedValue value) const; + + static bool Delete(JSThread *thread, const JSHandle &obj, const JSHandle &key); + static JSHandle OwnKeys(JSThread *thread, const JSHandle &obj); + static bool GetOwnProperty(JSThread *thread, const JSHandle &obj, const JSHandle &key, + PropertyDescriptor &desc); + + inline int GetSize() const + { + return GetLength().GetArrayLength(); + } + + static constexpr size_t LENGTH_OFFSET = JSObject::SIZE; + ACCESSORS(Length, LENGTH_OFFSET, SIZE); + + DECL_VISIT_OBJECT_FOR_JS_OBJECT(JSObject, LENGTH_OFFSET, SIZE) + DECL_DUMP() +private: + inline static uint32_t ComputeCapacity(uint32_t oldCapacity) + { + uint32_t newCapacity = oldCapacity + (oldCapacity >> 1U); + return newCapacity > DEFAULT_CAPACITY_LENGTH ? newCapacity : DEFAULT_CAPACITY_LENGTH; + } + static JSHandle GrowCapacity(const JSThread *thread, const JSHandle &obj, + uint32_t capacity); +}; +} // namespace panda::ecmascript + +#endif // ECMASCRIPT_JSARRAYLIST_H diff --git a/runtime/js_async_from_sync_iterator_object.cpp b/runtime/js_async_from_sync_iterator_object.cpp new file mode 100644 index 000000000..183369f91 --- /dev/null +++ b/runtime/js_async_from_sync_iterator_object.cpp @@ -0,0 +1,98 @@ +/* + * Copyright (c) Huawei Technologies Co., Ltd. 2019-2021. All rights reserved. + * Description: + */ +#include "plugins/ecmascript/runtime/builtins/builtins_promise.h" +#include "plugins/ecmascript/runtime/base/builtins_base.h" +#include "plugins/ecmascript/runtime/internal_call_params.h" +#include "plugins/ecmascript/runtime/js_async_from_sync_iterator_object.h" +#include "plugins/ecmascript/runtime/js_promise.h" +#include "plugins/ecmascript/runtime/js_iterator.h" +#include "plugins/ecmascript/runtime/global_env.h" +#include "plugins/ecmascript/runtime/generator_helper.h" + +namespace panda::ecmascript { +using BuiltinsPromise = builtins::BuiltinsPromise; + +JSTaggedValue JSAsyncFromSyncIteratorValueUnwrapFunction::AsyncFromSyncIteratorValueUnwrapFunction( + EcmaRuntimeCallInfo *argv) +{ + JSThread *thread = argv->GetThread(); + [[maybe_unused]] EcmaHandleScope handleScope(thread); + + // 1. Let F be the active function object. + JSHandle func(base::BuiltinsBase::GetConstructor(argv)); + + // 2. Return ! CreateIterResultObject(value, F.[[Done]]). + JSHandle value = base::BuiltinsBase::GetCallArg(argv, 0); + return JSIterator::CreateIterResultObject(thread, value, func->GetDone().IsTrue()).GetTaggedValue(); +} + +JSTaggedValue JSAsyncFromSyncIteratorObject::CreateAsyncFromSyncIterator(JSThread *thread, + const JSHandle &iterator, + const JSHandle &nextMethod) +{ + [[maybe_unused]] EcmaHandleScope handleScope(thread); + + ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); + + // 1. Let asyncIterator be ! OrdinaryObjectCreate(%AsyncFromSyncIteratorPrototype%, « [[SyncIteratorRecord]] »). + JSHandle asyncIterator = factory->NewJSAsyncFromSyncIteratorObject(); + + // 2. Set asyncIterator.[[SyncIteratorRecord]] to syncIteratorRecord. + asyncIterator->SetIterator(thread, iterator); + asyncIterator->SetNextMethod(thread, nextMethod); + + // 3. Let nextMethod be ! Get(asyncIterator, "next"). + // 4. Let iteratorRecord be the Record { [[Iterator]]: asyncIterator, [[NextMethod]]: nextMethod, [[Done]]: false }. + // 5. Return iteratorRecord. + return asyncIterator.GetTaggedValue(); +} + +JSTaggedValue JSAsyncFromSyncIteratorObject::AsyncFromSyncIteratorContinuation( + JSThread *thread, const JSHandle &result, const JSHandle &promiseCapability) +{ + [[maybe_unused]] EcmaHandleScope handleScope(thread); + + ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); + JSHandle env = thread->GetEcmaVM()->GetGlobalEnv(); + + // 1. Let done be IteratorComplete(result). + JSHandle done(thread, JSTaggedValue(JSIterator::IteratorComplete(thread, result))); + + // 2. IfAbruptRejectPromise(done, promiseCapability). + RETURN_REJECT_PROMISE_IF_ABRUPT_THROWN_VALUE(thread, done, promiseCapability); + + // 3. Let value be IteratorValue(result). + JSHandle value = JSIterator::IteratorValue(thread, result); + + // 4. IfAbruptRejectPromise(value, promiseCapability). + RETURN_REJECT_PROMISE_IF_ABRUPT_THROWN_VALUE(thread, value, promiseCapability); + + // 5. Let valueWrapper be PromiseResolve(%Promise%, value). + JSHandle valueWrapper( + thread, JSPromise::PromiseResolve(thread, JSHandle::Cast(env->GetPromiseFunction()), value)); + + // 6. IfAbruptRejectPromise(valueWrapper, promiseCapability). + RETURN_REJECT_PROMISE_IF_ABRUPT_THROWN_VALUE(thread, valueWrapper, promiseCapability); + + // 7. 7. Let steps be the algorithm steps defined in Async-from-Sync Iterator Value Unwrap Functions. + // 8. Let length be the number of non-optional parameters of the function definition in Async-from-Sync Iterator + // Value Unwrap Functions. + // 9. Let onFulfilled be ! CreateBuiltinFunction(steps, length, "", « [[Done]] »). + JSHandle onFulfilled = + factory->NewJSAsyncFromSyncIteratorValueUnwrapFunction(reinterpret_cast( + JSAsyncFromSyncIteratorValueUnwrapFunction::AsyncFromSyncIteratorValueUnwrapFunction)); + + // 10. Set onFulfilled.[[Done]] to done. + onFulfilled->SetDone(thread, done.GetTaggedValue()); + + // 11. Perform ! PerformPromiseThen(valueWrapper, onFulfilled, undefined, promiseCapability). + [[maybe_unused]] JSTaggedValue thenResult = BuiltinsPromise::PerformPromiseThen( + thread, JSHandle::Cast(valueWrapper), JSHandle::Cast(onFulfilled), + thread->GlobalConstants()->GetHandledUndefined(), JSHandle::Cast(promiseCapability)); + + // 12. Return promiseCapability.[[Promise]]. + return promiseCapability->GetPromise(); +} +} // namespace panda::ecmascript diff --git a/runtime/js_async_from_sync_iterator_object.h b/runtime/js_async_from_sync_iterator_object.h new file mode 100644 index 000000000..b906e8360 --- /dev/null +++ b/runtime/js_async_from_sync_iterator_object.h @@ -0,0 +1,49 @@ +/* + * Copyright (c) Huawei Technologies Co., Ltd. 2019-2021. All rights reserved. + * Description: + */ +#ifndef ECMASCRIPT_JS_ASYNC_FROM_SYNC_ITERATOR_OBJECT_H +#define ECMASCRIPT_JS_ASYNC_FROM_SYNC_ITERATOR_OBJECT_H + +#include "plugins/ecmascript/runtime/ecma_macros.h" +#include "include/object_header.h" +#include "plugins/ecmascript/runtime/js_tagged_value.h" +#include "plugins/ecmascript/runtime/js_function.h" + +namespace panda::ecmascript { +class JSAsyncFromSyncIteratorValueUnwrapFunction : public JSFunction { +public: + CAST_CHECK(JSAsyncFromSyncIteratorValueUnwrapFunction, IsJSAsyncFromSyncIteratorValueUnwrapFunction); + + // 27.1.4.2.4 Async-from-Sync Iterator Value Unwrap Functions + static JSTaggedValue AsyncFromSyncIteratorValueUnwrapFunction(EcmaRuntimeCallInfo *argv); + + static constexpr size_t DONE_OFFSET = JSFunction::SIZE; + ACCESSORS(Done, DONE_OFFSET, SIZE); + + DECL_VISIT_OBJECT_FOR_JS_OBJECT(JSFunction, DONE_OFFSET, SIZE) + + DECL_DUMP() +}; + +class JSAsyncFromSyncIteratorObject : public JSObject { +public: + CAST_CHECK(JSAsyncFromSyncIteratorObject, IsJSAsyncFromSyncIteratorObject); + + // 27.1.4.1 CreateAsyncFromSyncIterator ( syncIteratorRecord ) + static JSTaggedValue CreateAsyncFromSyncIterator(JSThread *thread, const JSHandle &iterator, + const JSHandle &nextMethod); + + // 27.1.4.4 AsyncFromSyncIteratorContinuation ( result, promiseCapability ) + static JSTaggedValue AsyncFromSyncIteratorContinuation(JSThread *thread, const JSHandle &result, + const JSHandle &promiseCapability); + + static constexpr size_t ITERATOR_OFFSET = JSObject::SIZE; + ACCESSORS(Iterator, ITERATOR_OFFSET, NEXT_METHOD_OFFSET); + ACCESSORS(NextMethod, NEXT_METHOD_OFFSET, SIZE); + + DECL_VISIT_OBJECT_FOR_JS_OBJECT(JSObject, ITERATOR_OFFSET, SIZE) +}; +} // namespace panda::ecmascript + +#endif // ECMASCRIPT_JS_ASYNC_FROM_SYNC_ITERATOR_OBJECT_H diff --git a/runtime/js_async_function.cpp b/runtime/js_async_function.cpp new file mode 100644 index 000000000..ee243c5ef --- /dev/null +++ b/runtime/js_async_function.cpp @@ -0,0 +1,121 @@ +/* + * Copyright (c) 2021-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 "plugins/ecmascript/runtime/js_async_function.h" + +#include "plugins/ecmascript/runtime/builtins/builtins_promise.h" +#include "plugins/ecmascript/runtime/builtins/builtins_promise_handler.h" +#include "plugins/ecmascript/runtime/ecma_vm.h" +#include "plugins/ecmascript/runtime/generator_helper.h" +#include "plugins/ecmascript/runtime/global_env.h" +#include "plugins/ecmascript/runtime/internal_call_params.h" +#include "plugins/ecmascript/runtime/js_promise.h" +#include "plugins/ecmascript/runtime/js_tagged_value-inl.h" +#include "plugins/ecmascript/runtime/object_factory.h" + +namespace panda::ecmascript { +using BuiltinsPromiseHandler = builtins::BuiltinsPromiseHandler; +using BuiltinsPromise = builtins::BuiltinsPromise; + +JSTaggedValue JSAsyncFuncObject::AsyncFunctionAwait(JSThread *thread, const JSHandle &asyncFuncObj, + const JSHandle &value) +{ + // 1.Let asyncContext be the running execution context. + ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); + JSHandle env = thread->GetEcmaVM()->GetGlobalEnv(); + + JSHandle asyncCtxt(thread, asyncFuncObj->GetGeneratorContext()); + + // 2. Let promise be ? PromiseResolve(%Promise%, value). + JSTaggedValue promiseVal = + JSPromise::PromiseResolve(thread, JSHandle::Cast(env->GetPromiseFunction()), value); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + JSHandle promise(thread, JSPromise::Cast(promiseVal.GetHeapObject())); + + // 4.Let onFulfilled be a new built-in function object as defined in AsyncFunction Awaited Fulfilled. + JSHandle fulFunc = + factory->NewJSAsyncAwaitStatusFunction(reinterpret_cast(BuiltinsPromiseHandler::AsyncAwaitFulfilled)); + + // 5.Let onRejected be a new built-in function object as defined in AsyncFunction Awaited Rejected. + JSHandle rejFunc = + factory->NewJSAsyncAwaitStatusFunction(reinterpret_cast(BuiltinsPromiseHandler::AsyncAwaitRejected)); + + // 6.Set onFulfilled.[[AsyncContext]] to asyncContext. + // 7.Set onRejected.[[AsyncContext]] to asyncContext. + fulFunc->SetAsyncContext(thread, asyncCtxt); + rejFunc->SetAsyncContext(thread, asyncCtxt); + + // 8.Let throwawayCapability be ! NewPromiseCapability(%Promise%). + // 9.Set throwawayCapability.[[Promise]].[[PromiseIsHandled]] to true. + JSHandle tcap = + JSPromise::NewPromiseCapability(thread, JSHandle::Cast(env->GetPromiseFunction())); + JSHandle(thread, tcap->GetPromise())->SetPromiseIsHandled(thread, JSTaggedValue::Undefined()); + + // 10.Perform ! PerformPromiseThen(promiseCapability.[[Promise]], onFulfilled, onRejected, throwawayCapability). + [[maybe_unused]] JSTaggedValue pres = BuiltinsPromise::PerformPromiseThen( + thread, promise, JSHandle::Cast(fulFunc), JSHandle::Cast(rejFunc), + JSHandle::Cast(tcap)); + + // 11.Remove asyncContext from the execution context stack and restore the execution context that + // is at the top of the execution context stack as the running execution context. + // 12.Set the code evaluation state of asyncContext such that when evaluation is resumed with a Completion + // resumptionValue the following steps will be performed: + // a.Return resumptionValue. + // 13.Return. + + return JSTaggedValue::Hole(); +} + +JSHandle JSAsyncAwaitStatusFunction::AsyncFunctionAwaitFulfilled( + JSThread *thread, const JSHandle &func, const JSHandle &value) +{ + // 1.Let asyncContext be F.[[AsyncContext]]. + JSHandle asyncCtxt(thread, func->GetAsyncContext()); + + // 2.Let prevContext be the running execution context. + // 3.Suspend prevContext. + // 4.Push asyncContext onto the execution context stack; asyncContext is now the running execution context. + // 5.Resume the suspended evaluation of asyncContext using NormalCompletion(value) as the result of the + // operation that suspended it. Let result be the value returned by the resumed computation. + JSHandle result = + GeneratorHelper::Continue(thread, asyncCtxt, GeneratorResumeMode::NEXT, value.GetTaggedValue()); + // 6.Assert: When we reach this step, asyncContext has already been removed from the execution context stack + // and prevContext is the currently running execution context. + + // 7.Return Completion(result). + return result; +} + +JSHandle JSAsyncAwaitStatusFunction::AsyncFunctionAwaitRejected( + JSThread *thread, const JSHandle &func, const JSHandle &reason) +{ + // 1.Let asyncContext be F.[[AsyncContext]]. + JSHandle asyncCtxt(thread, func->GetAsyncContext()); + + // 2.Let prevContext be the running execution context. + // 3.Suspend prevContext. + // 4.Push asyncContext onto the execution context stack; asyncContext is now the running execution context. + // 5.Resume the suspended evaluation of asyncContext using Completion{[[Type]]: throw, + // [[Value]]: reason, [[Target]]: empty} as the result of the operation that suspended it. + // Let result be the value returned by the resumed computation. + JSHandle result = + GeneratorHelper::Continue(thread, asyncCtxt, GeneratorResumeMode::THROW, reason.GetTaggedValue()); + // 6.Assert: When we reach this step, asyncContext has already been removed from the execution context stack + // and prevContext is the currently running execution context. + + // 7.Return Completion(result). + return result; +} +} // namespace panda::ecmascript diff --git a/runtime/js_async_function.h b/runtime/js_async_function.h new file mode 100644 index 000000000..fdcaef53f --- /dev/null +++ b/runtime/js_async_function.h @@ -0,0 +1,60 @@ +/* + * Copyright (c) 2021-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_JS_ASYNC_FUNCTION_H +#define ECMASCRIPT_JS_ASYNC_FUNCTION_H + +#include "plugins/ecmascript/runtime/ecma_macros.h" +#include "plugins/ecmascript/runtime/js_tagged_value.h" +#include "plugins/ecmascript/runtime/js_generator_object.h" + +namespace panda::ecmascript { +class JSAsyncAwaitStatusFunction : public JSFunction { +public: + CAST_CHECK(JSAsyncAwaitStatusFunction, IsJSAsyncAwaitStatusFunction); + + static JSHandle AsyncFunctionAwaitFulfilled(JSThread *thread, + const JSHandle &func, + const JSHandle &value); + + static JSHandle AsyncFunctionAwaitRejected(JSThread *thread, + const JSHandle &func, + const JSHandle &reason); + + static constexpr size_t ASYNC_CONTEXT_OFFSET = JSFunction::SIZE; + ACCESSORS(AsyncContext, ASYNC_CONTEXT_OFFSET, SIZE); + + DECL_VISIT_OBJECT_FOR_JS_OBJECT(JSFunction, ASYNC_CONTEXT_OFFSET, SIZE) + + DECL_DUMP() +}; + +class JSAsyncFuncObject : public JSGeneratorObject { +public: + CAST_CHECK(JSAsyncFuncObject, IsAsyncFuncObject); + + static JSTaggedValue AsyncFunctionAwait(JSThread *thread, const JSHandle &asyncFuncObj, + const JSHandle &value); + + static constexpr size_t GENERATOR_PROMISE_OFFSET = JSGeneratorObject::SIZE; + ACCESSORS(Promise, GENERATOR_PROMISE_OFFSET, SIZE); + + DECL_VISIT_OBJECT_FOR_JS_OBJECT(JSGeneratorObject, GENERATOR_PROMISE_OFFSET, SIZE) + + DECL_DUMP() +}; +} // namespace panda::ecmascript + +#endif // ECMASCRIPT_JS_ASYNC_FUNCTION_H diff --git a/runtime/js_async_generator_object.cpp b/runtime/js_async_generator_object.cpp new file mode 100644 index 000000000..395acb70c --- /dev/null +++ b/runtime/js_async_generator_object.cpp @@ -0,0 +1,364 @@ +/* + * Copyright (c) Huawei Technologies Co., Ltd. 2019-2021. All rights reserved. + * Description: + */ +#include "plugins/ecmascript/runtime/base/builtins_base.h" +#include "plugins/ecmascript/runtime/builtins/builtins_promise.h" +#include "plugins/ecmascript/runtime/internal_call_params.h" +#include "plugins/ecmascript/runtime/js_async_generator_object.h" +#include "plugins/ecmascript/runtime/js_promise.h" +#include "plugins/ecmascript/runtime/js_iterator.h" +#include "plugins/ecmascript/runtime/global_env.h" +#include "plugins/ecmascript/runtime/generator_helper.h" + +namespace panda::ecmascript { +enum class AsyncGeneratorRequest : panda::array_size_t { COMPLETION, PROMISE_CAPABILITY }; +using BuiltinsPromise = builtins::BuiltinsPromise; + +JSTaggedValue JSAsyncGeneratorResolveNextFunction::AsyncGeneratorResolveNextFulfilled(EcmaRuntimeCallInfo *argv) +{ + JSThread *thread = argv->GetThread(); + [[maybe_unused]] EcmaHandleScope handleScope(thread); + + // 1. Let F be the active function object. + JSHandle thisFunc(base::BuiltinsBase::GetConstructor(argv)); + JSHandle asyncGenObject(thread, thisFunc->GetAsyncGenerator()); + + // 2. Set F.[[Generator]].[[AsyncGeneratorState]] to completed. + asyncGenObject->SetState(thread, JSGeneratorState::COMPLETED); + + // 3. Return ! AsyncGeneratorResolve(F.[[Generator]], value, true). + JSHandle value = base::BuiltinsBase::GetCallArg(argv, 0); + return JSAsyncGeneratorObject::AsyncGeneratorResolve(thread, JSHandle::Cast(asyncGenObject), value, + true) + .GetTaggedValue(); +} + +JSTaggedValue JSAsyncGeneratorResolveNextFunction::AsyncGeneratorResolveNextRejected(EcmaRuntimeCallInfo *argv) +{ + JSThread *thread = argv->GetThread(); + [[maybe_unused]] EcmaHandleScope handleScope(thread); + + // 1. Let F be the active function object. + JSHandle thisFunc(base::BuiltinsBase::GetConstructor(argv)); + JSHandle asyncGenObject(thread, thisFunc->GetAsyncGenerator()); + + // 2. Set F.[[Generator]].[[AsyncGeneratorState]] to completed. + asyncGenObject->SetState(thread, JSGeneratorState::COMPLETED); + + // 3. Return ! AsyncGeneratorReject(F.[[Generator]], reason). + JSHandle reason = base::BuiltinsBase::GetCallArg(argv, 0); + return JSAsyncGeneratorObject::AsyncGeneratorReject(thread, JSHandle::Cast(asyncGenObject), reason) + .GetTaggedValue(); +} + +// 27.6.3.3 AsyncGeneratorValidate(generator) +JSTaggedValue JSAsyncGeneratorObject::AsyncGeneratorValidate(JSThread *thread, const JSHandle &generator) +{ + if (!generator->IsAsyncGeneratorObject()) { + THROW_TYPE_ERROR_AND_RETURN(thread, "Not an async generator object.", JSTaggedValue::Exception()); + } + + return JSTaggedValue::Hole(); +} + +// 27.6.3.4 AsyncGeneratorResolve (generator, value, done) +JSHandle JSAsyncGeneratorObject::AsyncGeneratorResolve(JSThread *thread, + const JSHandle &generator, + const JSHandle &value, bool done) +{ + [[maybe_unused]] EcmaHandleScope handleScope(thread); + + const GlobalEnvConstants *globalConst = thread->GlobalConstants(); + + // 1. Assert: generator is an AsyncGenerator instance. + ASSERT(generator->IsAsyncGeneratorObject()); + JSAsyncGeneratorObject *asyncGenObject = JSAsyncGeneratorObject::Cast(generator->GetHeapObject()); + + // 2. Let queue be generator.[[AsyncGeneratorQueue]]. + JSHandle queue(thread, TaggedQueue::Cast(asyncGenObject->GetAsyncGeneratorQueue().GetHeapObject())); + + // 3. Assert: queue is not an empty List. + ASSERT(!queue->Empty()); + + // 4. Let next be the first element of queue. + // 5. Remove the first element from queue. + JSHandle nextAsyncGeneratorRequest(thread, queue->Pop(thread)); + + // 6. Let promiseCapability be next.[[Capability]]. + JSTaggedValue promiseCapabilityValue = + nextAsyncGeneratorRequest->Get(thread, helpers::ToUnderlying(AsyncGeneratorRequest::PROMISE_CAPABILITY)); + JSHandle promiseCapability(thread, + PromiseCapability::Cast(promiseCapabilityValue.GetHeapObject())); + + // 7. Let iteratorResult be ! CreateIterResultObject(value, done). + JSHandle iteratorResult = JSIterator::CreateIterResultObject(thread, value, done); + + // 8. Perform ! Call(promiseCapability.[[Resolve]], undefined, « iteratorResult »). + JSHandle thisArg = globalConst->GetHandledUndefined(); + InternalCallParams *arguments = thread->GetInternalCallParams(); + arguments->MakeArgv(iteratorResult); + JSHandle resolve(thread, promiseCapability->GetResolve()); + [[maybe_unused]] JSTaggedValue res = JSFunction::Call(thread, resolve, thisArg, 1, arguments->GetArgv()); + + // 9. Perform ! AsyncGeneratorResumeNext(generator). + JSAsyncGeneratorObject::AsyncGeneratorResumeNext(thread, generator); + + // 10. Return undefined. + return globalConst->GetHandledUndefined(); +} + +// 27.6.3.5 AsyncGeneratorReject (generator, exception) +JSHandle JSAsyncGeneratorObject::AsyncGeneratorReject(JSThread *thread, + const JSHandle &generator, + const JSHandle &exception) +{ + [[maybe_unused]] EcmaHandleScope handleScope(thread); + + const GlobalEnvConstants *globalConst = thread->GlobalConstants(); + + // 1. Assert: generator is an AsyncGenerator instance. + ASSERT(generator->IsAsyncGeneratorObject()); + JSAsyncGeneratorObject *asyncGenObject = JSAsyncGeneratorObject::Cast(generator->GetHeapObject()); + + // 2. Let queue be generator.[[AsyncGeneratorQueue]]. + JSHandle queue(thread, TaggedQueue::Cast(asyncGenObject->GetAsyncGeneratorQueue().GetHeapObject())); + + // 3. Assert: queue is not an empty List. + ASSERT(!queue->Empty()); + + // 4. Let next be the first element of queue. + // 5. Remove the first element from queue. + JSTaggedValue next = queue->Pop(thread); + JSHandle nextAsyncGeneratorRequest(thread, TaggedArray::Cast(next.GetHeapObject())); + + // 6. Let promiseCapability be next.[[Capability]]. + JSTaggedValue promiseCapabilityValue = + nextAsyncGeneratorRequest->Get(thread, helpers::ToUnderlying(AsyncGeneratorRequest::PROMISE_CAPABILITY)); + JSHandle promiseCapability(thread, + PromiseCapability::Cast(promiseCapabilityValue.GetHeapObject())); + + // 7. Perform ! Call(promiseCapability.[[Reject]], undefined, « exception »). + JSHandle thisArg = globalConst->GetHandledUndefined(); + InternalCallParams *arguments = thread->GetInternalCallParams(); + arguments->MakeArgv(exception); + JSHandle reject(thread, promiseCapability->GetReject()); + [[maybe_unused]] JSTaggedValue res = JSFunction::Call(thread, reject, thisArg, 1, arguments->GetArgv()); + + // 9. Perform ! AsyncGeneratorResumeNext(generator). + JSAsyncGeneratorObject::AsyncGeneratorResumeNext(thread, generator); + + // 10. Return undefined. + return globalConst->GetHandledUndefined(); +} + +// 27.6.3.6 AsyncGeneratorResumeNext ( generator ) +JSHandle JSAsyncGeneratorObject::AsyncGeneratorResumeNext(JSThread *thread, + const JSHandle &generator) +{ + [[maybe_unused]] EcmaHandleScope handleScope(thread); + + ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); + const GlobalEnvConstants *globalConst = thread->GlobalConstants(); + JSHandle env = thread->GetEcmaVM()->GetGlobalEnv(); + + // 1. Assert: generator is an AsyncGenerator instance. + ASSERT(generator->IsAsyncGeneratorObject()); + JSHandle asyncGenObject = JSHandle::Cast(generator); + + // 2. Let state be generator.[[AsyncGeneratorState]]. + JSTaggedValue state = asyncGenObject->GetGeneratorState(); + + // 3. Assert: state is not executing. + ASSERT(!JSGeneratorObject::IsState(state, JSGeneratorState::EXECUTING)); + + // 4. If state is awaiting-return, return undefined. + if (JSGeneratorObject::IsState(state, JSGeneratorState::AWAITING_RETURN)) { + return globalConst->GetHandledUndefined(); + } + + // 5. Let queue be generator.[[AsyncGeneratorQueue]]. + JSHandle queue(thread, TaggedQueue::Cast(asyncGenObject->GetAsyncGeneratorQueue().GetHeapObject())); + + // 6. If queue is an empty List, return undefined. + if (queue->Empty()) { + return globalConst->GetHandledUndefined(); + } + + // 7. Let next be the value of the first element of queue. + JSTaggedValue next = queue->Front(); + + // 8. Assert: next is an AsyncGeneratorRequest record. + JSHandle nextAsyncGeneratorRequest(thread, next); + + // 9. Let completion be next.[[Completion]]. + JSHandle completion( + thread, nextAsyncGeneratorRequest->Get(thread, helpers::ToUnderlying(AsyncGeneratorRequest::COMPLETION))); + + // 10. If completion is an abrupt completion, then + if (completion->IsAbrupt()) { + // a. If state is suspendedStart, then + if (JSGeneratorObject::IsState(state, JSGeneratorState::SUSPENDED_START)) { + // i. Set generator.[[AsyncGeneratorState]] to completed. + asyncGenObject->SetState(thread, JSGeneratorState::COMPLETED); + // ii. Set state to completed. + state = JSTaggedValue(static_cast(JSGeneratorState::COMPLETED)); + } + + // b. If state is completed, then + if (JSGeneratorObject::IsState(state, JSGeneratorState::COMPLETED)) { + // i. If completion.[[Type]] is return, then + if (completion->IsReturn()) { + // 1. Set generator.[[AsyncGeneratorState]] to awaiting-return. + asyncGenObject->SetState(thread, JSGeneratorState::AWAITING_RETURN); + + // 2. Let promise be ? PromiseResolve(%Promise%, completion.[[Value]]). + JSHandle promise( + thread, JSPromise::PromiseResolve(thread, env->GetPromiseFunction(), + JSHandle(thread, completion->GetValue()))); + RETURN_HANDLE_IF_ABRUPT_COMPLETION(JSTaggedValue, thread); + + // 3. Let stepsFulfilled be the algorithm steps defined in AsyncGeneratorResumeNext Return Processor + // Fulfilled Functions. + // 4. Let lengthFulfilled be the number of non-optional parameters of the function definition in + // AsyncGeneratorResumeNext Return Processor Fulfilled Functions. + // 5. Let onFulfilled be ! CreateBuiltinFunction(stepsFulfilled, lengthFulfilled, "", « [[Generator]] + // »). + JSHandle onFulfilled = + factory->NewJSAsyncGeneratorResolveNextFunction(reinterpret_cast( + JSAsyncGeneratorResolveNextFunction::AsyncGeneratorResolveNextFulfilled)); + + // 6. Set onFulfilled.[[Generator]] to generator. + onFulfilled->SetAsyncGenerator(thread, asyncGenObject.GetTaggedValue()); + + // 7. Let stepsRejected be the algorithm steps defined in AsyncGeneratorResumeNext Return Processor + // Rejected Functions. + // 8. Let lengthRejected be the number of non-optional parameters of the function definition in + // AsyncGeneratorResumeNext Return Processor Rejected Functions. + // 9. Let onRejected be ! CreateBuiltinFunction(stepsRejected, lengthRejected, "", « [[Generator]] »). + JSHandle onRejected = + factory->NewJSAsyncGeneratorResolveNextFunction(reinterpret_cast( + JSAsyncGeneratorResolveNextFunction::AsyncGeneratorResolveNextRejected)); + + // 10. Set onRejected.[[Generator]] to generator. + onRejected->SetAsyncGenerator(thread, asyncGenObject.GetTaggedValue()); + + // 11. Perform ! PerformPromiseThen(promise, onFulfilled, onRejected). + [[maybe_unused]] JSTaggedValue thenResult = BuiltinsPromise::PerformPromiseThen( + thread, promise, JSHandle::Cast(onFulfilled), + JSHandle::Cast(onRejected), globalConst->GetHandledUndefined()); + + // 12. Return undefined. + return globalConst->GetHandledUndefined(); + } + + // ii. Else + // 1. Assert: completion.[[Type]] is throw + ASSERT(completion->IsThrow()); + + // 2. Perform ! AsyncGeneratorReject(generator, completion.[[Value]]). + [[maybe_unused]] JSHandle rejectResult = + AsyncGeneratorReject(thread, generator, JSHandle(thread, completion->GetValue())); + + // 3. Return undefined. + return globalConst->GetHandledUndefined(); + } + } else if (JSGeneratorObject::IsState(state, JSGeneratorState::COMPLETED)) { + // 11. Else if state is completed, return ! AsyncGeneratorResolve(generator, undefined, true). + return AsyncGeneratorResolve(thread, generator, globalConst->GetHandledUndefined(), true); + } + + // 12. Assert: state is either suspendedStart or suspendedYield. + ASSERT(JSGeneratorObject::IsState(state, JSGeneratorState::SUSPENDED_START) || + JSGeneratorObject::IsState(state, JSGeneratorState::SUSPENDED_YIELD)); + + // 13. Let genContext be generator.[[AsyncGeneratorContext]]. + JSHandle genContext(thread, asyncGenObject->GetGeneratorContext()); + + // 16. Set generator.[[AsyncGeneratorState]] to executing + asyncGenObject->SetState(thread, JSGeneratorState::EXECUTING); + + // 14. Let callerContext be the running execution context. + // 15. Suspend callerContext. + // 17. Push genContext onto the execution context stack; genContext is now the running execution context. */ + // 18. Resume the suspended evaluation of genContext using completion as the result of the operation that suspended + // it. Let result be the completion record returned by the resumed computation. + + if (completion->IsReturn()) { + [[maybe_unused]] JSHandle result = + GeneratorHelper::Continue(thread, genContext, GeneratorResumeMode::RETURN, completion->GetValue()); + } else if (completion->IsThrow()) { + [[maybe_unused]] JSHandle result = + GeneratorHelper::Continue(thread, genContext, GeneratorResumeMode::THROW, completion->GetValue()); + } else { + [[maybe_unused]] JSHandle result = + GeneratorHelper::Continue(thread, genContext, GeneratorResumeMode::NEXT, completion->GetValue()); + } + // 19. Assert: result is never an abrupt completion. + // 20. Assert: When we return here, genContext has already been removed from the execution context stack and + // callerContext is the currently running execution context. + + // 21. Return undefined. + return globalConst->GetHandledUndefined(); +} + +// 27.6.3.7 AsyncGeneratorEnqueue(generator, value) +JSHandle JSAsyncGeneratorObject::AsyncGeneratorEnqueue(JSThread *thread, + const JSHandle &generator, + const JSHandle &completion) +{ + [[maybe_unused]] EcmaHandleScope handleScope(thread); + + ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); + JSHandle env = thread->GetEcmaVM()->GetGlobalEnv(); + + // 1. Let promiseCapability be ! NewPromiseCapability(%Promise%). + JSHandle promiseCapability = + JSPromise::NewPromiseCapability(thread, JSHandle::Cast(env->GetPromiseFunction())); + + // 2. Let check be AsyncGeneratorValidate(generator, generatorBrand). + [[maybe_unused]] JSTaggedValue check = AsyncGeneratorValidate(thread, generator); + + // 3. If check is an abrupt completion, then + if (thread->HasPendingException()) { + // a. Let badGeneratorError be a newly created TypeError object. + JSTaggedValue badGeneratorError = thread->GetException(); + thread->ClearException(); + + // b. Perform ! Call(promiseCapability.[[Reject]], undefined, « badGeneratorError »). + const GlobalEnvConstants *globalConst = thread->GlobalConstants(); + JSHandle thisArg = globalConst->GetHandledUndefined(); + InternalCallParams *arguments = thread->GetInternalCallParams(); + arguments->MakeArgv(badGeneratorError); + JSHandle reject(thread, promiseCapability->GetReject()); + [[maybe_unused]] JSTaggedValue res = JSFunction::Call(thread, reject, thisArg, 1, arguments->GetArgv()); + + // c. Return promiseCapability.[[Promise]]. + return JSHandle(thread, promiseCapability->GetPromise()); + } + + JSHandle asyncGenObject(thread, JSAsyncGeneratorObject::Cast(generator->GetHeapObject())); + + // 4. Let queue be generator.[[AsyncGeneratorQueue]]. + JSHandle queue(thread, TaggedQueue::Cast(asyncGenObject->GetAsyncGeneratorQueue().GetHeapObject())); + + // 5. Let request be AsyncGeneratorRequest { [[Completion]]: completion, [[Capability]]: promiseCapability }. + JSHandle request = factory->NewTaggedArray(2); + request->Set(thread, helpers::ToUnderlying(AsyncGeneratorRequest::COMPLETION), completion.GetTaggedValue()); + request->Set(thread, helpers::ToUnderlying(AsyncGeneratorRequest::PROMISE_CAPABILITY), + promiseCapability.GetTaggedValue()); + JSHandle requestValue(thread, request.GetTaggedValue()); + + // 6. Append request to the end of queue. + asyncGenObject->SetAsyncGeneratorQueue(thread, JSTaggedValue(TaggedQueue::Push(thread, queue, requestValue))); + + // 7. Let state be generator.[[AsyncGeneratorState]]. + // 8. If state is not executing, then + if (!asyncGenObject->IsExecuting()) { + AsyncGeneratorResumeNext(thread, generator); + } + + // 9. Return promiseCapability.[[Promise]]. + return JSHandle(thread, promiseCapability->GetPromise()); +} +} // namespace panda::ecmascript diff --git a/runtime/js_async_generator_object.h b/runtime/js_async_generator_object.h new file mode 100644 index 000000000..661b57449 --- /dev/null +++ b/runtime/js_async_generator_object.h @@ -0,0 +1,62 @@ +/* + * Copyright (c) Huawei Technologies Co., Ltd. 2019-2021. All rights reserved. + * Description: + */ +#ifndef ECMASCRIPT_JS_ASYNC_GENERATOR_OBJECT_H +#define ECMASCRIPT_JS_ASYNC_GENERATOR_OBJECT_H + +#include "plugins/ecmascript/runtime/ecma_macros.h" +#include "include/object_header.h" +#include "plugins/ecmascript/runtime/js_tagged_value.h" +#include "plugins/ecmascript/runtime/js_generator_object.h" +#include "plugins/ecmascript/runtime/js_async_function.h" + +namespace panda::ecmascript { +class JSAsyncGeneratorResolveNextFunction : public JSFunction { +public: + CAST_CHECK(JSAsyncGeneratorResolveNextFunction, IsJSAsyncGeneratorResolveNextFunction); + + // 27.6.3.6.1 AsyncGeneratorResumeNext Return Processor Fulfilled Functions + static JSTaggedValue AsyncGeneratorResolveNextFulfilled(EcmaRuntimeCallInfo *argv); + + // 27.6.3.6.2 AsyncGeneratorResumeNext Return Processor Rejected Functions + static JSTaggedValue AsyncGeneratorResolveNextRejected(EcmaRuntimeCallInfo *argv); + + static constexpr size_t ASYNC_GENERATOR_OFFSET = JSFunction::SIZE; + ACCESSORS(AsyncGenerator, ASYNC_GENERATOR_OFFSET, SIZE); + + DECL_VISIT_OBJECT_FOR_JS_OBJECT(JSFunction, ASYNC_GENERATOR_OFFSET, SIZE) + + DECL_DUMP() +}; + +class JSAsyncGeneratorObject : public JSAsyncFuncObject { +public: + CAST_CHECK(JSAsyncGeneratorObject, IsAsyncGeneratorObject); + + // 27.6.3.3 AsyncGeneratorValidate(generator) + static JSTaggedValue AsyncGeneratorValidate(JSThread *thread, const JSHandle &generator); + + // 27.6.3.4 AsyncGeneratorResolve (generator, value, done) + static JSHandle AsyncGeneratorResolve(JSThread *thread, const JSHandle &generator, + const JSHandle &value, bool done); + + // 27.6.3.5 AsyncGeneratorReject (generator, exception) + static JSHandle AsyncGeneratorReject(JSThread *thread, const JSHandle &generator, + const JSHandle &exception); + + // 27.6.3.6 AsyncGeneratorResumeNext (generator) + static JSHandle AsyncGeneratorResumeNext(JSThread *thread, const JSHandle &generator); + + // 27.6.3.7 AsyncGeneratorEnqueue(generator, value) + static JSHandle AsyncGeneratorEnqueue(JSThread *thread, const JSHandle &generator, + const JSHandle &completion); + + static constexpr size_t ASYNC_QUEUE_OFFSET = JSAsyncFuncObject::SIZE; + ACCESSORS(AsyncGeneratorQueue, ASYNC_QUEUE_OFFSET, SIZE); + + DECL_VISIT_OBJECT_FOR_JS_OBJECT(JSAsyncFuncObject, ASYNC_QUEUE_OFFSET, SIZE) +}; +} // namespace panda::ecmascript + +#endif // ECMASCRIPT_JS_ASYNC_GENERATOR_OBJECT_H diff --git a/runtime/js_collator.cpp b/runtime/js_collator.cpp new file mode 100644 index 000000000..571c66a03 --- /dev/null +++ b/runtime/js_collator.cpp @@ -0,0 +1,441 @@ +/* + * Copyright (c) 2021-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 "plugins/ecmascript/runtime/js_collator.h" + +#include "unicode/udata.h" + +#include "plugins/ecmascript/runtime/global_env.h" +#include "plugins/ecmascript/runtime/mem/c_string.h" + +namespace panda::ecmascript { +// NOLINTNEXTLINE (readability-identifier-naming, fuchsia-statically-constructed-objects) +const CString JSCollator::uIcuDataColl = U_ICUDATA_NAME U_TREE_SEPARATOR_STRING "coll"; +// NOLINTNEXTLINE(fuchsia-statically-constructed-objects) +const std::map JSCollator::caseFirstMap = { + {"upper", CaseFirstOption::UPPER}, {"lower", CaseFirstOption::LOWER}, {"false", CaseFirstOption::FALSE_OPTION}}; +// NOLINTNEXTLINE(fuchsia-statically-constructed-objects) +const std::map JSCollator::uColAttributeValueMap = { + {CaseFirstOption::UPPER, UCOL_UPPER_FIRST}, + {CaseFirstOption::LOWER, UCOL_LOWER_FIRST}, + {CaseFirstOption::FALSE_OPTION, UCOL_OFF}, + {CaseFirstOption::UNDEFINED, UCOL_OFF}}; + +JSHandle JSCollator::GetAvailableLocales(JSThread *thread) +{ + const char *key = nullptr; + const char *path = JSCollator::uIcuDataColl.c_str(); + JSHandle availableLocales = JSLocale::GetAvailableLocales(thread, key, path); + return availableLocales; +} + +/* static */ +void JSCollator::SetIcuCollator(JSThread *thread, const JSHandle &collator, icu::Collator *icuCollator, + const DeleteEntryPoint &callback) +{ + EcmaVM *ecmaVm = thread->GetEcmaVM(); + ObjectFactory *factory = ecmaVm->GetFactory(); + + ASSERT(icuCollator != nullptr); + JSTaggedValue data = collator->GetIcuField(); + if (data.IsJSNativePointer()) { + JSNativePointer *native = JSNativePointer::Cast(data.GetTaggedObject()); + native->ResetExternalPointer(icuCollator); + return; + } + JSHandle pointer = factory->NewJSNativePointer(icuCollator); + pointer->SetDeleter(callback); + collator->SetIcuField(thread, pointer.GetTaggedValue()); + ecmaVm->PushToArrayDataList(*pointer); +} + +// NOLINTNEXTLINE(readability-function-size), CODECHECK-NOLINTNEXTLINE(C_RULE_ID_FUNCTION_SIZE) +JSHandle JSCollator::InitializeCollator(JSThread *thread, const JSHandle &collator, + const JSHandle &locales, + const JSHandle &options) +{ + EcmaVM *ecmaVm = thread->GetEcmaVM(); + ObjectFactory *factory = ecmaVm->GetFactory(); + const GlobalEnvConstants *globalConst = thread->GlobalConstants(); + // 1. Let requestedLocales be ? CanonicalizeLocaleList(locales). + JSHandle requestedLocales = JSLocale::CanonicalizeLocaleList(thread, locales); + RETURN_HANDLE_IF_ABRUPT_COMPLETION(JSCollator, thread); + + // 2. If options is undefined, then + // a. Let options be ObjectCreate(null). + // 3. Else, + // a. Let options be ? ToObject(options). + JSHandle optionsObject; + if (options->IsUndefined()) { + JSHandle nullValue = globalConst->GetHandledNull(); + optionsObject = factory->OrdinaryNewJSObjectCreate(nullValue); + } else { + optionsObject = JSTaggedValue::ToObject(thread, options); + RETURN_HANDLE_IF_ABRUPT_COMPLETION(JSCollator, thread); + } + // 4. Let usage be ? GetOption(options, "usage", "string", « "sort", "search" », "sort"). + auto usage = JSLocale::GetOptionOfString(thread, optionsObject, globalConst->GetHandledUsageString(), + {UsageOption::SORT, UsageOption::SEARCH}, {"sort", "search"}, + UsageOption::SORT); + RETURN_HANDLE_IF_ABRUPT_COMPLETION(JSCollator, thread); + JSHandle usageValue(thread, JSTaggedValue(static_cast(usage))); + collator->SetUsage(thread, usageValue); + + // 5. Let matcher be ? GetOption(options, "localeMatcher", "string", « "lookup", "best fit" », "best fit"). + auto matcher = JSLocale::GetOptionOfString( + thread, optionsObject, globalConst->GetHandledLocaleMatcherString(), + {LocaleMatcherOption::LOOKUP, LocaleMatcherOption::BEST_FIT}, {"lookup", "best fit"}, + LocaleMatcherOption::BEST_FIT); + RETURN_HANDLE_IF_ABRUPT_COMPLETION(JSCollator, thread); + + // 6. Let collation be ? GetOption(options, "collation", "string", undefined, undefined). + // 7. If collation is not undefined, then + // a. If collation does not match the Unicode Locale Identifier type nonterminal, throw a RangeError exception. + JSHandle collation = + JSLocale::GetOption(thread, optionsObject, globalConst->GetHandledCollationString(), OptionType::STRING, + globalConst->GetHandledUndefined(), globalConst->GetHandledUndefined()); + RETURN_HANDLE_IF_ABRUPT_COMPLETION(JSCollator, thread); + collator->SetCollation(thread, collation); + std::string collationStr; + if (!collation->IsUndefined()) { + JSHandle collationEcmaStr = JSHandle::Cast(collation); + collationStr = JSLocale::ConvertToStdString(collationEcmaStr); + if (!JSLocale::IsWellAlphaNumList(collationStr)) { + THROW_RANGE_ERROR_AND_RETURN(thread, "invalid collation", collator); + } + } + + // 8. Let numeric be ? GetOption(options, "numeric", "boolean", undefined, undefined). + bool numeric = false; + bool foundNumeric = + JSLocale::GetOptionOfBool(thread, optionsObject, globalConst->GetHandledNumericString(), false, &numeric); + RETURN_HANDLE_IF_ABRUPT_COMPLETION(JSCollator, thread); + JSHandle numericValue(thread, JSTaggedValue(numeric)); + collator->SetNumeric(thread, numericValue); + + // 14. Let caseFirst be ? GetOption(options, "caseFirst", "string", « "upper", "lower", "false" », undefined). + auto caseFirst = JSLocale::GetOptionOfString( + thread, optionsObject, globalConst->GetHandledCaseFirstString(), + {CaseFirstOption::UPPER, CaseFirstOption::LOWER, CaseFirstOption::FALSE_OPTION}, {"upper", "lower", "false"}, + CaseFirstOption::UNDEFINED); + RETURN_HANDLE_IF_ABRUPT_COMPLETION(JSCollator, thread); + JSHandle caseFirstValue(thread, JSTaggedValue(static_cast(caseFirst))); + collator->SetCaseFirst(thread, caseFirstValue); + + // 16. Let relevantExtensionKeys be %Collator%.[[RelevantExtensionKeys]]. + std::set relevantExtensionKeys = {"co", "kn", "kf"}; + + // 17. Let r be ResolveLocale(%Collator%.[[AvailableLocales]], requestedLocales, opt, + // %Collator%.[[RelevantExtensionKeys]], localeData). + JSHandle availableLocales; + if (requestedLocales->GetLength() == 0) { + availableLocales = factory->EmptyArray(); + } else { + availableLocales = GetAvailableLocales(thread); + } + ResolvedLocale r = + JSLocale::ResolveLocale(thread, availableLocales, requestedLocales, matcher, relevantExtensionKeys); + icu::Locale icuLocale = r.localeData; + JSHandle localeStr = JSLocale::ToLanguageTag(thread, icuLocale); + collator->SetLocale(thread, localeStr.GetTaggedValue()); + ASSERT_PRINT(!icuLocale.isBogus(), "icuLocale is bogus"); + + // If collation is undefined iterate RelevantExtensionKeys to find "co" + // if found, set ICU collator UnicodeKeyword to iterator->second + UErrorCode status = U_ZERO_ERROR; + if (!collation->IsUndefined()) { + auto extensionIter = r.extensions.find("co"); + if (extensionIter != r.extensions.end() && extensionIter->second != collationStr) { + icuLocale.setUnicodeKeywordValue("co", nullptr, status); + ASSERT_PRINT(U_SUCCESS(status), "icuLocale set co failed"); + } + } + + // If usage is serach set co-serach to icu locale key word value + // Eles set collation string to icu locale key word value + if (usage == UsageOption::SEARCH) { + icuLocale.setUnicodeKeywordValue("co", "search", status); + ASSERT(U_SUCCESS(status)); + } else { + if (!collationStr.empty() && JSLocale::IsWellCollation(icuLocale, collationStr)) { + icuLocale.setUnicodeKeywordValue("co", collationStr, status); + ASSERT(U_SUCCESS(status)); + } + } + + std::unique_ptr icuCollator(icu::Collator::createInstance(icuLocale, status)); + if (U_FAILURE(status) || icuCollator == nullptr) { // NOLINT(readability-implicit-bool-conversion) + status = U_ZERO_ERROR; + icu::Locale localeName(icuLocale.getBaseName()); + icuCollator.reset(icu::Collator::createInstance(localeName, status)); + if (U_FAILURE(status) || icuCollator == nullptr) { // NOLINT(readability-implicit-bool-conversion) + THROW_RANGE_ERROR_AND_RETURN(thread, "invalid collation", collator); + } + } + ASSERT(U_SUCCESS(status)); + icu::Locale collatorLocale(icuCollator->getLocale(ULOC_VALID_LOCALE, status)); + + icuCollator->setAttribute(UCOL_NORMALIZATION_MODE, UCOL_ON, status); + ASSERT(U_SUCCESS(status)); + + // If numeric is found set ICU collator UCOL_NUMERIC_COLLATION to numeric + // Else iterate RelevantExtensionKeys to find "kn" + // if found, set ICU collator UCOL_NUMERIC_COLLATION to iterator->second + status = U_ZERO_ERROR; + if (foundNumeric) { + ASSERT(icuCollator.get() != nullptr); + icuCollator->setAttribute(UCOL_NUMERIC_COLLATION, numeric ? UCOL_ON : UCOL_OFF, status); + ASSERT(U_SUCCESS(status)); + } else { + auto extensionIter = r.extensions.find("kn"); + if (extensionIter != r.extensions.end()) { + ASSERT(icuCollator.get() != nullptr); + bool found = (extensionIter->second == "true"); + JSHandle isNumeric(thread, JSTaggedValue(found)); + collator->SetNumeric(thread, isNumeric); + icuCollator->setAttribute(UCOL_NUMERIC_COLLATION, found ? UCOL_ON : UCOL_OFF, status); + ASSERT(U_SUCCESS(status)); + } + } + + // If caseFirst is not undefined set ICU collator UColAttributeValue to caseFirst + // Else iterate RelevantExtensionKeys to find "kf" + // if found, set ICU collator UColAttributeValue to iterator->second + status = U_ZERO_ERROR; + if (caseFirst != CaseFirstOption::UNDEFINED) { + ASSERT(icuCollator.get() != nullptr); + icuCollator->setAttribute(UCOL_CASE_FIRST, OptionToUColAttribute(caseFirst), status); + ASSERT(U_SUCCESS(status)); + } else { + auto extensionIter = r.extensions.find("kf"); + if (extensionIter != r.extensions.end()) { + ASSERT(icuCollator.get() != nullptr); + auto mapIter = caseFirstMap.find(extensionIter->second); + if (mapIter != caseFirstMap.end()) { + icuCollator->setAttribute(UCOL_CASE_FIRST, OptionToUColAttribute(mapIter->second), status); + JSHandle value(thread, JSTaggedValue(static_cast(mapIter->second))); + collator->SetCaseFirst(thread, value); + } else { + icuCollator->setAttribute(UCOL_CASE_FIRST, OptionToUColAttribute(CaseFirstOption::UNDEFINED), status); + } + ASSERT(U_SUCCESS(status)); + } + } + + // 24. Let sensitivity be ? GetOption(options, "sensitivity", "string", « "base", "accent", "case", "variant" », + // undefined). + auto sensitivity = JSLocale::GetOptionOfString( + thread, optionsObject, globalConst->GetHandledSensitivityString(), + {SensitivityOption::BASE, SensitivityOption::ACCENT, SensitivityOption::CASE, SensitivityOption::VARIANT}, + {"base", "accent", "case", "variant"}, SensitivityOption::UNDEFINED); + RETURN_HANDLE_IF_ABRUPT_COMPLETION(JSCollator, thread); + // 25. If sensitivity is undefined, then + // a. If usage is "sort", then + // i. Let sensitivity be "variant". + if (sensitivity == SensitivityOption::UNDEFINED) { + if (usage == UsageOption::SORT) { + sensitivity = SensitivityOption::VARIANT; + } + } + JSHandle sensitivityValue(thread, JSTaggedValue(static_cast(sensitivity))); + collator->SetSensitivity(thread, sensitivityValue); + + // Trans SensitivityOption to Icu strength option + switch (sensitivity) { + case SensitivityOption::BASE: + icuCollator->setStrength(icu::Collator::PRIMARY); + break; + case SensitivityOption::ACCENT: + icuCollator->setStrength(icu::Collator::SECONDARY); + break; + case SensitivityOption::CASE: + icuCollator->setStrength(icu::Collator::PRIMARY); + icuCollator->setAttribute(UCOL_CASE_LEVEL, UCOL_ON, status); + break; + case SensitivityOption::VARIANT: + icuCollator->setStrength(icu::Collator::TERTIARY); + break; + case SensitivityOption::UNDEFINED: + break; + case SensitivityOption::EXCEPTION: + UNREACHABLE(); + } + + // 27. Let ignorePunctuation be ? GetOption(options, "ignorePunctuation", "boolean", undefined, false). + // 28. Set collator.[[IgnorePunctuation]] to ignorePunctuation. + bool ignorePunctuation = false; + JSLocale::GetOptionOfBool(thread, optionsObject, globalConst->GetHandledIgnorePunctuationString(), false, + &ignorePunctuation); + JSHandle ignorePunctuationValue(thread, JSTaggedValue(ignorePunctuation)); + collator->SetIgnorePunctuation(thread, ignorePunctuationValue); + if (ignorePunctuation) { + status = U_ZERO_ERROR; + icuCollator->setAttribute(UCOL_ALTERNATE_HANDLING, UCOL_SHIFTED, status); + ASSERT(U_SUCCESS(status)); + } + + SetIcuCollator(thread, collator, icuCollator.release(), JSCollator::FreeIcuCollator); + collator->SetBoundCompare(thread, JSTaggedValue::Undefined()); + // 29. Return collator. + return collator; +} + +UColAttributeValue JSCollator::OptionToUColAttribute(CaseFirstOption caseFirstOption) +{ + auto iter = uColAttributeValueMap.find(caseFirstOption); + if (iter != uColAttributeValueMap.end()) { + return iter->second; + } + UNREACHABLE(); +} + +JSHandle OptionsToEcmaString(JSThread *thread, UsageOption usage) +{ + JSMutableHandle result(thread, JSTaggedValue::Undefined()); + auto globalConst = thread->GlobalConstants(); + switch (usage) { + case UsageOption::SORT: + result.Update(globalConst->GetSortString()); + break; + case UsageOption::SEARCH: + result.Update(globalConst->GetSearchString()); + break; + default: + UNREACHABLE(); + } + return result; +} + +JSHandle OptionsToEcmaString(JSThread *thread, SensitivityOption sensitivity) +{ + JSMutableHandle result(thread, JSTaggedValue::Undefined()); + auto globalConst = thread->GlobalConstants(); + switch (sensitivity) { + case SensitivityOption::BASE: + result.Update(globalConst->GetBaseString()); + break; + case SensitivityOption::ACCENT: + result.Update(globalConst->GetAccentString()); + break; + case SensitivityOption::CASE: + result.Update(globalConst->GetCaseString()); + break; + case SensitivityOption::VARIANT: + result.Update(globalConst->GetVariantString()); + break; + case SensitivityOption::UNDEFINED: + break; + default: + UNREACHABLE(); + } + return result; +} + +JSHandle OptionsToEcmaString(JSThread *thread, CaseFirstOption caseFirst) +{ + JSMutableHandle result(thread, JSTaggedValue::Undefined()); + auto globalConst = thread->GlobalConstants(); + switch (caseFirst) { + case CaseFirstOption::UPPER: + result.Update(globalConst->GetUpperString()); + break; + case CaseFirstOption::LOWER: + result.Update(globalConst->GetLowerString()); + break; + case CaseFirstOption::FALSE_OPTION: + result.Update(globalConst->GetFalseString()); + break; + case CaseFirstOption::UNDEFINED: + result.Update(globalConst->GetUpperString()); + break; + default: + UNREACHABLE(); + } + return result; +} + +// 11.3.4 Intl.Collator.prototype.resolvedOptions () +JSHandle JSCollator::ResolvedOptions(JSThread *thread, const JSHandle &collator) +{ + auto ecmaVm = thread->GetEcmaVM(); + auto globalConst = thread->GlobalConstants(); + ObjectFactory *factory = ecmaVm->GetFactory(); + JSHandle env = ecmaVm->GetGlobalEnv(); + JSHandle ctor = env->GetObjectFunction(); + JSHandle funCtor = JSHandle::Cast(env->GetObjectFunction()); + JSHandle options(factory->NewJSObjectByConstructor(funCtor, ctor)); + + // [[Locale]] + JSHandle property = globalConst->GetHandledLocaleString(); + JSHandle locale(thread, collator->GetLocale()); + JSObject::CreateDataPropertyOrThrow(thread, options, property, locale); + + // [[Usage]] + UsageOption usageOption = static_cast(collator->GetUsage().GetNumber()); + JSHandle usageValue = OptionsToEcmaString(thread, usageOption); + JSObject::CreateDataProperty(thread, options, globalConst->GetHandledUsageString(), usageValue); + + // [[Sensitivity]] + auto sentivityOption = static_cast(collator->GetSensitivity().GetNumber()); + JSHandle sensitivityValue = OptionsToEcmaString(thread, sentivityOption); + JSObject::CreateDataProperty(thread, options, globalConst->GetHandledSensitivityString(), sensitivityValue); + + // [[IgnorePunctuation]] + JSHandle ignorePunctuationValue(thread, collator->GetIgnorePunctuation()); + JSObject::CreateDataProperty(thread, options, globalConst->GetHandledIgnorePunctuationString(), + ignorePunctuationValue); + + // [[Collation]] + JSMutableHandle collationValue(thread, collator->GetCollation()); + if (collationValue->IsUndefined()) { + collationValue.Update(globalConst->GetDefaultString()); + } + JSObject::CreateDataProperty(thread, options, globalConst->GetHandledCollationString(), collationValue); + + // [[Numeric]] + JSHandle numericValue(thread, collator->GetNumeric()); + JSObject::CreateDataProperty(thread, options, globalConst->GetHandledNumericString(), numericValue); + + // [[CaseFirst]] + CaseFirstOption caseFirstOption = static_cast(collator->GetCaseFirst().GetNumber()); + // In Ecma402 spec, caseFirst is an optional property so we set it to Upper when input is undefined + // the requirement maybe change in the future + JSHandle caseFirstValue = OptionsToEcmaString(thread, caseFirstOption); + JSObject::CreateDataProperty(thread, options, globalConst->GetHandledCaseFirstString(), caseFirstValue); + return options; +} + +icu::UnicodeString EcmaStringToUString(const JSHandle &string) +{ + std::string stdString(ConvertToString(*string, StringConvertedUsage::LOGICOPERATION)); + icu::StringPiece sp(stdString); + icu::UnicodeString uString = icu::UnicodeString::fromUTF8(sp); + return uString; +} + +JSTaggedValue JSCollator::CompareStrings(const icu::Collator *icuCollator, const JSHandle &string1, + const JSHandle &string2) +{ + icu::UnicodeString uString1 = EcmaStringToUString(string1); + icu::UnicodeString uString2 = EcmaStringToUString(string2); + + UCollationResult result; + UErrorCode status = U_ZERO_ERROR; + result = icuCollator->compare(uString1, uString2, status); + ASSERT(U_SUCCESS(status)); + + return JSTaggedValue(result); +} +} // namespace panda::ecmascript diff --git a/runtime/js_collator.h b/runtime/js_collator.h new file mode 100644 index 000000000..0780d6755 --- /dev/null +++ b/runtime/js_collator.h @@ -0,0 +1,100 @@ +/* + * Copyright (c) 2021-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_JS_COLLATOR_H +#define ECMASCRIPT_JS_COLLATOR_H + +#include "js_locale.h" + +#include "unicode/udata.h" + +namespace panda::ecmascript { +enum class UsageOption : uint8_t { SORT = 0x01, SEARCH, EXCEPTION }; +enum class CaseFirstOption : uint8_t { UPPER = 0x01, LOWER, FALSE_OPTION, UNDEFINED, EXCEPTION }; +enum class SensitivityOption : uint8_t { BASE = 0x01, ACCENT, CASE, VARIANT, UNDEFINED, EXCEPTION }; + +class JSCollator : public JSObject { +public: + // NOLINTNEXTLINE (readability-identifier-naming, fuchsia-statically-constructed-objects) + static const CString uIcuDataColl; + + static const std::map caseFirstMap; + + static const std::map uColAttributeValueMap; + + CAST_CHECK(JSCollator, IsJSCollator); + + static constexpr size_t ICU_FIELD_OFFSET = JSObject::SIZE; + + // icu field. + ACCESSORS(IcuField, ICU_FIELD_OFFSET, LOCALE_OFFSET) + ACCESSORS(Locale, LOCALE_OFFSET, USAGE_OFFSET) + ACCESSORS(Usage, USAGE_OFFSET, SENSITIVITY_OFFSET) + ACCESSORS(Sensitivity, SENSITIVITY_OFFSET, IGNORE_PUNCTUATION_OFFSET) + ACCESSORS(IgnorePunctuation, IGNORE_PUNCTUATION_OFFSET, COLLATION_OFFSET) + ACCESSORS(Collation, COLLATION_OFFSET, NUMERIC_OFFSET) + ACCESSORS(Numeric, NUMERIC_OFFSET, CASE_FIRST_OFFSET) + ACCESSORS(CaseFirst, CASE_FIRST_OFFSET, BOUND_COMPARE_OFFSET) + ACCESSORS(BoundCompare, BOUND_COMPARE_OFFSET, SIZE) + + DECL_VISIT_OBJECT_FOR_JS_OBJECT(JSObject, ICU_FIELD_OFFSET, SIZE) + DECL_DUMP() + + icu::Collator *GetIcuCollator() const + { + ASSERT(GetIcuField().IsJSNativePointer()); + auto result = JSNativePointer::Cast(GetIcuField().GetTaggedObject())->GetExternalPointer(); + return reinterpret_cast(result); + } + + static void FreeIcuCollator(void *pointer, [[maybe_unused]] void *hint = nullptr) + { + if (pointer == nullptr) { + return; + } + auto icuCollator = reinterpret_cast(pointer); + icuCollator->~Collator(); + } + + static void SetIcuCollator(JSThread *thread, const JSHandle &collator, + icu::Collator *icuCollator, const DeleteEntryPoint &callback); + + // 11.1.1 InitializeCollator ( collator, locales, options ) + static JSHandle InitializeCollator(JSThread *thread, const JSHandle &collator, + const JSHandle &locales, + const JSHandle &options); + + // 11.3.4 Intl.Collator.prototype.resolvedOptions () + static JSHandle ResolvedOptions(JSThread *thread, const JSHandle &collator); + + static JSHandle GetAvailableLocales(JSThread *thread); + + static JSTaggedValue CompareStrings(const icu::Collator *icuCollator, const JSHandle &string1, + const JSHandle &string2); + +private: + static CaseFirstOption StringToCaseForstOption(const std::string &str); + + static UColAttributeValue OptionToUColAttribute(CaseFirstOption caseFirstOption); + + static std::set BuildLocaleSet(const std::vector &availableLocales, const char *path, + const char *key); + + static void SetNumericOption(icu::Collator *icuCollator, bool numeric); + + static void SetCaseFirstOption(icu::Collator *icuCollator, CaseFirstOption caseFirstOption); +}; +} // namespace panda::ecmascript +#endif // ECMASCRIPT_JS_COLLATOR_H \ No newline at end of file diff --git a/runtime/js_dataview.cpp b/runtime/js_dataview.cpp new file mode 100644 index 000000000..61296c4ce --- /dev/null +++ b/runtime/js_dataview.cpp @@ -0,0 +1,45 @@ +/* + * Copyright (c) 2021-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 "plugins/ecmascript/runtime/js_dataview.h" + +namespace panda::ecmascript { +int32_t JSDataView::GetElementSize(DataViewType type) +{ + int32_t size; + switch (type) { + case DataViewType::INT8: + case DataViewType::UINT8: + case DataViewType::UINT8_CLAMPED: + size = 1; + break; + case DataViewType::INT16: + case DataViewType::UINT16: + size = 2; // 2 means the length + break; + case DataViewType::INT32: + case DataViewType::UINT32: + case DataViewType::FLOAT32: + size = 4; // 4 means the length + break; + case DataViewType::FLOAT64: + size = 8; // 8 means the length + break; + default: + UNREACHABLE(); + } + return size; +} +} // namespace panda::ecmascript diff --git a/runtime/js_dataview.h b/runtime/js_dataview.h new file mode 100644 index 000000000..9bfeb393e --- /dev/null +++ b/runtime/js_dataview.h @@ -0,0 +1,41 @@ +/* + * Copyright (c) 2021-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_JSDATAVIEW_H +#define ECMASCRIPT_JSDATAVIEW_H + +#include "plugins/ecmascript/runtime/js_object.h" + +namespace panda::ecmascript { +enum class DataViewType : uint8_t { FLOAT32 = 0, FLOAT64, INT8, INT16, INT32, UINT8, UINT16, UINT32, UINT8_CLAMPED }; +class JSDataView : public JSObject { +public: + CAST_CHECK(JSDataView, IsDataView); + + static int32_t GetElementSize(DataViewType type); + + static constexpr size_t DATA_VIEW_OFFSET = JSObject::SIZE; + ACCESSORS(DataView, DATA_VIEW_OFFSET, VIEW_ARRAY_BUFFER_OFFSET); + ACCESSORS(ViewedArrayBuffer, VIEW_ARRAY_BUFFER_OFFSET, BYTE_LENGTH_OFFSET); + ACCESSORS(ByteLength, BYTE_LENGTH_OFFSET, BYTE_OFFSET_OFFSET); + ACCESSORS(ByteOffset, BYTE_OFFSET_OFFSET, SIZE); + + DECL_VISIT_OBJECT_FOR_JS_OBJECT(JSObject, DATA_VIEW_OFFSET, SIZE) + + DECL_DUMP() +}; +} // namespace panda::ecmascript + +#endif // ECMASCRIPT_JSDATAVIEW_H diff --git a/runtime/js_date.cpp b/runtime/js_date.cpp new file mode 100644 index 000000000..54ce155d3 --- /dev/null +++ b/runtime/js_date.cpp @@ -0,0 +1,1000 @@ +/* + * Copyright (c) 2021-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 "js_date.h" +#include +#include +#include +#include "base/builtins_base.h" + +namespace panda::ecmascript { +using NumberHelper = base::NumberHelper; +void DateUtils::TransferTimeToDate(int64_t timeMs, std::array *date) +{ + (*date)[HOUR] = Mod(timeMs, MS_PER_DAY); // ms from hour, minutes, second, ms + (*date)[DAYS] = (timeMs - (*date)[HOUR]) / MS_PER_DAY; // days from year, month, day + (*date)[MS] = (*date)[HOUR] % MS_PER_SECOND; // ms + (*date)[HOUR] = ((*date)[HOUR] - (*date)[MS]) / MS_PER_SECOND; // s from hour, minutes, second + (*date)[SEC] = (*date)[HOUR] % SEC_PER_MINUTE; // second + (*date)[HOUR] = ((*date)[HOUR] - (*date)[SEC]) / SEC_PER_MINUTE; // min from hour, minutes + (*date)[MIN] = (*date)[HOUR] % SEC_PER_MINUTE; // min + (*date)[HOUR] = ((*date)[HOUR] - (*date)[MIN]) / SEC_PER_MINUTE; // hour + (*date)[WEEKDAY] = Mod(((*date)[DAYS] + LEAP_NUMBER[0]), DAY_PER_WEEK); // weekday + (*date)[YEAR] = GetYearFromDays(&((*date)[DAYS])); // year +} +// static +bool DateUtils::IsLeap(int64_t year) +{ + return year % LEAP_NUMBER[0] == 0 && (year % LEAP_NUMBER[1] != 0 || year % LEAP_NUMBER[2] == 0); // 2: means index +} + +// static +int64_t DateUtils::GetDaysInYear(int64_t year) +{ + int64_t number; + number = IsLeap(year) ? (DAYS_IN_YEAR + 1) : DAYS_IN_YEAR; + return number; +} + +// static +int64_t DateUtils::GetDaysFromYear(int64_t year) +{ + return DAYS_IN_YEAR * (year - YEAR_NUMBER[0]) + FloorDiv(year - YEAR_NUMBER[1], LEAP_NUMBER[0]) - + FloorDiv(year - YEAR_NUMBER[2], LEAP_NUMBER[1]) + // 2: year index + FloorDiv(year - YEAR_NUMBER[3], LEAP_NUMBER[2]); // 3, 2: year index +} + +// static +int64_t DateUtils::FloorDiv(int64_t a, int64_t b) +{ + ASSERT(b != 0); + int64_t m = a % b; + int64_t res = m < 0 ? ((a - m - b) / b) : ((a - m) / b); + return res; +} + +// static +int64_t DateUtils::GetYearFromDays(int64_t *days) +{ + int64_t realDay; + int64_t dayTemp = 0; + int64_t d = *days; + int64_t year = FloorDiv(d * APPROXIMATION_NUMBER[0], APPROXIMATION_NUMBER[1]) + YEAR_NUMBER[0]; + realDay = d - GetDaysFromYear(year); + while (realDay != 0) { + if (realDay < 0) { + year--; + } else { + dayTemp = GetDaysInYear(year); + if (realDay < dayTemp) { + break; + } + year++; + } + realDay = d - GetDaysFromYear(year); + } + *days = realDay; + return year; +} + +// static +int64_t DateUtils::Mod(int64_t a, int b) +{ + ASSERT(b != 0); + int64_t m = a % b; + int64_t res = m < 0 ? (m + b) : m; + return res; +} + +// static +// 20.4.1.11 +double JSDate::MakeTime(double hour, double min, double sec, double ms) +{ + if (std::isfinite(hour) && std::isfinite(min) && std::isfinite(sec) && std::isfinite(ms)) { + double hourInteger = NumberHelper::TruncateDouble(hour); + double minInteger = NumberHelper::TruncateDouble(min); + double secInteger = NumberHelper::TruncateDouble(sec); + double msInteger = NumberHelper::TruncateDouble(ms); + return hourInteger * MS_PER_HOUR + minInteger * MS_PER_MINUTE + secInteger * MS_PER_SECOND + msInteger; + } + return base::NAN_VALUE; +} + +// static +// 20.4.1.12 +double JSDate::MakeDay(double year, double month, double date) +{ + if (std::isfinite(year) && std::isfinite(month) && std::isfinite(date)) { + double yearInteger = NumberHelper::TruncateDouble(year); + double monthInteger = NumberHelper::TruncateDouble(month); + int64_t y = static_cast(yearInteger) + static_cast(monthInteger / MOUTH_PER_YEAR); + int64_t m = static_cast(monthInteger) % MOUTH_PER_YEAR; + if (m < 0) { + m += MOUTH_PER_YEAR; + y -= 1; + } + + int64_t days = DateUtils::GetDaysFromYear(y); + int index = DateUtils::IsLeap(year) ? 1 : 0; + days += DAYS_FROM_MONTH[index][m]; + return static_cast(days - 1) + NumberHelper::TruncateDouble(date); + } + return base::NAN_VALUE; +} + +// static +// 20.4.1.13 +double JSDate::MakeDate(double day, double time) +{ + if (std::isfinite(day) && std::isfinite(time)) { + return time + day * MS_PER_DAY; + } + return base::NAN_VALUE; +} + +// static +// 20.4.1.14 +double JSDate::TimeClip(double time) +{ + if (-MAX_TIME_IN_MS <= time && time <= MAX_TIME_IN_MS) { + return NumberHelper::TruncateDouble(time); + } + return base::NAN_VALUE; +} + +// 20.4.1.8 +double JSDate::LocalTime(double timeMs) const +{ + return timeMs + GetLocalOffsetFromOS(timeMs, true); +} + +// 20.4.1.9 +double JSDate::UTCTime(double timeMs) const +{ + return timeMs - GetLocalOffsetFromOS(timeMs, false); +} + +// static +int JSDate::GetSignedNumFromString(const CString &str, int len, int *index) +{ + int res = 0; + GetNumFromString(str, len, index, &res); + if (str.at(0) == NEG) { + return -res; + } + return res; +} + +// static +bool JSDate::GetNumFromString(const CString &str, int len, int *index, int *num) +{ + int indexStr = *index; + char oneByte = 0; + while (indexStr < len) { + oneByte = str.at(indexStr); + if (oneByte >= '0' && oneByte <= '9') { + break; + } + indexStr++; + } + if (indexStr >= len) { + return false; + } + int value = 0; + while (indexStr < len) { + oneByte = str.at(indexStr); + int val = oneByte - '0'; + if (val >= 0 && val <= NUM_NINE) { + value = value * TEN + val; + indexStr++; + } else { + break; + } + } + *num = value; + *index = indexStr; + return true; +} + +// 20.4.1.7 +int64_t JSDate::GetLocalOffsetInMin(const JSThread *thread, int64_t timeMs, bool isLocal) +{ + if (!isLocal) { + return 0; + } + double localOffset = this->GetLocalOffset().GetDouble(); + if (localOffset == MAX_DOUBLE) { + localOffset = static_cast(GetLocalOffsetFromOS(timeMs, isLocal)); + SetLocalOffset(thread, JSTaggedValue(localOffset)); + } + return localOffset; +} + +// static +JSTaggedValue JSDate::LocalParseStringToMs(const CString &str) +{ + int year = 0; + int month = 0; + int date = 1; + int hours = 0; + int minutes = 0; + int seconds = 0; + int ms = 0; + int index = 0; + int len = str.length(); + bool isLocal = false; + CString::size_type indexGmt; + CString::size_type indexPlus = CString::npos; + std::array monthName = { + "Jan", "Feb", "Mar", "Apr", "May", "Jun", + "Jul", "Aug", "Sep", "Oct", "Nov", "Dec" + }; + int localTime = 0; + int localHours = 0; + int localMinutes = 0; + int64_t localMs = 0; + CString::size_type localSpace; + localSpace = str.find(' ', index); + CString strMonth = str.substr(localSpace + 1, LENGTH_MONTH_NAME); + for (int i = 0; i < MOUTH_PER_YEAR; i++) { + if (strMonth == monthName[i]) { + month = i; + break; + } + } + index += (LENGTH_MONTH_NAME + 1); + GetNumFromString(str, len, &index, &date); + GetNumFromString(str, len, &index, &year); + indexGmt = str.find("GMT", index); + if (indexGmt == CString::npos) { + GetNumFromString(str, len, &index, &hours); + GetNumFromString(str, len, &index, &minutes); + GetNumFromString(str, len, &index, &seconds); + isLocal = true; + localMs -= (GetLocalOffsetFromOS(localMs, true) * MS_PER_MINUTE); + } else { + indexPlus = str.find(PLUS, indexGmt); + int indexLocal = static_cast(indexGmt); + GetNumFromString(str, indexGmt, &index, &hours); + GetNumFromString(str, indexGmt, &index, &minutes); + GetNumFromString(str, indexGmt, &index, &seconds); + GetNumFromString(str, len, &indexLocal, &localTime); + localHours = localTime / HUNDRED; + localMinutes = localTime % HUNDRED; + localMs = static_cast(MakeTime(localHours, localMinutes, 0, 0)); + if (indexPlus != CString::npos) { + localMs = -localMs; + } + } + double day = MakeDay(year, month, date); + double time = MakeTime(hours, minutes, seconds, ms); + double timeValue = TimeClip(MakeDate(day, time)); + if (std::isnan(timeValue)) { + return JSTaggedValue(timeValue); + } + if (isLocal && timeValue < CHINA_1901_MS && (-localMs / MS_PER_MINUTE) == CHINA_AFTER_1901_MIN) { + timeValue += static_cast(localMs - CHINA_BEFORE_1901_MS); + } else { + timeValue += localMs; + } + return JSTaggedValue(timeValue); +} + +// static +JSTaggedValue JSDate::UtcParseStringToMs(const CString &str) +{ + int year = 0; + int month = 0; + int date = 1; + int hours = 0; + int minutes = 0; + int seconds = 0; + int ms = 0; + int index = 0; + int len = str.length(); + CString::size_type indexGmt; + CString::size_type indexPlus = CString::npos; + int localTime = 0; + int localHours = 0; + int localMinutes = 0; + int64_t localMs = 0; + bool isLocal = false; + std::array monthName = { + "Jan", "Feb", "Mar", "Apr", "May", "Jun", + "Jul", "Aug", "Sep", "Oct", "Nov", "Dec" + }; + GetNumFromString(str, len, &index, &date); + CString strMonth = str.substr(index + 1, LENGTH_MONTH_NAME); + for (int i = 0; i < MOUTH_PER_YEAR; i++) { + if (strMonth == monthName[i]) { + month = i; + break; + } + } + index += (LENGTH_MONTH_NAME + 1); + GetNumFromString(str, len, &index, &year); + indexGmt = str.find("GMT", index); + if (indexGmt == CString::npos) { + GetNumFromString(str, len, &index, &hours); + GetNumFromString(str, len, &index, &minutes); + GetNumFromString(str, len, &index, &seconds); + isLocal = true; + localMs -= (GetLocalOffsetFromOS(localMs, true) * MS_PER_MINUTE); + } else { + indexPlus = str.find(PLUS, indexGmt); + int indexLocal = static_cast(indexGmt); + GetNumFromString(str, indexGmt, &index, &hours); + GetNumFromString(str, indexGmt, &index, &minutes); + GetNumFromString(str, indexGmt, &index, &seconds); + GetNumFromString(str, len, &indexLocal, &localTime); + localHours = localTime / HUNDRED; + localMinutes = localTime % HUNDRED; + localMs = static_cast(MakeTime(localHours, localMinutes, 0, 0)); + if (indexPlus != CString::npos) { + localMs = -localMs; + } + } + double day = MakeDay(year, month, date); + double time = MakeTime(hours, minutes, seconds, ms); + double timeValue = TimeClip(MakeDate(day, time)); + if (std::isnan(timeValue)) { + return JSTaggedValue(timeValue); + } + if (isLocal && timeValue < CHINA_1901_MS && (-localMs / MS_PER_MINUTE) == CHINA_AFTER_1901_MIN) { + timeValue += static_cast(localMs - CHINA_BEFORE_1901_MS); + } else { + timeValue += localMs; + } + return JSTaggedValue(timeValue); +} +// static +JSTaggedValue JSDate::IsoParseStringToMs(const CString &str) +{ + char flag = 0; + int year; + int month = 1; + int date = 1; + int hours = 0; + int minutes = 0; + int seconds = 0; + int ms = 0; + int index = 0; + int len = str.length(); + year = GetSignedNumFromString(str, len, &index); + CString::size_type indexT = str.find(FLAG_TIME, index); + CString::size_type indexZ = str.find(FLAG_UTC, index); + CString::size_type indexEndFlag = 0; + int64_t localMs = 0; + if (indexZ != CString::npos) { + indexEndFlag = indexZ; + } else if (len >= MIN_LENGTH && str.at(len - INDEX_PLUS_NEG) == NEG) { + indexEndFlag = len - INDEX_PLUS_NEG; + flag = NEG; + } else if (len >= MIN_LENGTH && str.at(len - INDEX_PLUS_NEG) == PLUS) { + indexEndFlag = len - INDEX_PLUS_NEG; + flag = PLUS; + } + if (indexT != CString::npos) { + if ((indexT - index) == LENGTH_PER_TIME) { + GetNumFromString(str, len, &index, &month); + } else if ((indexT - index) == (LENGTH_PER_TIME + LENGTH_PER_TIME)) { + GetNumFromString(str, len, &index, &month); + GetNumFromString(str, len, &index, &date); + } + GetNumFromString(str, len, &index, &hours); + GetNumFromString(str, len, &index, &minutes); + if (indexEndFlag > 0) { + if (indexEndFlag - index == LENGTH_PER_TIME) { + GetNumFromString(str, len, &index, &seconds); + } else if (indexEndFlag - index == (LENGTH_PER_TIME + LENGTH_PER_TIME + 1)) { + GetNumFromString(str, len, &index, &seconds); + GetNumFromString(str, len, &index, &ms); + } + } else { + if (len - index == LENGTH_PER_TIME) { + GetNumFromString(str, len, &index, &seconds); + } else if (len - index == (LENGTH_PER_TIME + LENGTH_PER_TIME + 1)) { + GetNumFromString(str, len, &index, &seconds); + GetNumFromString(str, len, &index, &ms); + } + } + } else { + GetNumFromString(str, len, &index, &month); + GetNumFromString(str, len, &index, &date); + } + if (indexEndFlag > 0) { + int localHours = 0; + int localMinutes = 0; + if (indexZ == CString::npos) { + GetNumFromString(str, len, &index, &localHours); + GetNumFromString(str, len, &index, &localMinutes); + if (flag == PLUS) { + localMs = static_cast(-MakeTime(localHours, localMinutes, 0, 0)); + } else { + localMs = static_cast(MakeTime(localHours, localMinutes, 0, 0)); + } + } + } + if (indexEndFlag == 0 && indexT != CString::npos) { + localMs -= (GetLocalOffsetFromOS(localMs, true) * MS_PER_MINUTE); + } + + double day = MakeDay(year, month - 1, date); + double time = MakeTime(hours, minutes, seconds, ms); + double timeValue = TimeClip(MakeDate(day, time)); + if (std::isnan(timeValue)) { + return JSTaggedValue(timeValue); + } + if (flag == 0 && timeValue < CHINA_1901_MS && (-localMs / MS_PER_MINUTE) == CHINA_AFTER_1901_MIN) { + timeValue += static_cast(localMs - CHINA_BEFORE_1901_MS); + } else { + timeValue += localMs; + } + return JSTaggedValue(timeValue); +} + +// 20.4.3.2 static +JSTaggedValue JSDate::Parse(EcmaRuntimeCallInfo *argv) +{ + ASSERT(argv); + JSThread *thread = argv->GetThread(); + const CString isoPriStr = "(^|(\\+|-)(\\d{2}))"; + const CString isoDateStr = + "(((\\d{4})-(0?[1-9]|1[0-2])-(0?[1-9]|1[0-9]|2[0-9]|3[0-1]))" + "|((\\d{4})-(0?[1-9]|1[0-2]))|(\\d{4}))"; + const CString isoTimeStr = + "((T([01][0-9]|2[0-3]):([0-5][0-9]):([0-5][0-9])" + "\\.(\\d{3}))|(T([01][0-9]|2[0-3]):([0-5][0-9]):([0-5][0-9]))|" + "(T([01][0-9]|2[0-3]):([0-5][0-9]))|(T([01][0-9]|2[0-3])))" + "($|Z|((\\+|-)(([01][0-9]|2[0-3]):([0-5][0-9]))))"; + const CString isoRegStr = isoPriStr + isoDateStr + "($|Z|(" + isoTimeStr + "))"; + const CString utcDateStr = + "^\\D*(0?[1-9]|1[0-9]|2[0-9]|3[0-1]) " + "(Jan|Feb|Mar|Apr|May|Jun|Jul|Aug|Sep|Oct|Nov|Dec) (\\d{4})"; + const CString timeStr = + "(( ([01][0-9]|2[0-3]):([0-5][0-9]):([0-5][0-9]))|( ([01][0-9]|2[0-3]):([0-5][0-9])))" + "($| *| GMT *| GMT((\\+|-)(\\d{4})) *)"; + const CString utcRegStr = utcDateStr + "($| *| GMT *| GMT((\\+|-)(\\d{4})) *|(" + timeStr + "))"; + const CString localDateStr = + "^[a-zA-Z]* (Jan|Feb|Mar|Apr|May|Jun|Jul|Aug|Sep|Oct|Nov|Dec) " + "(0?[1-9]|1[0-9]|2[0-9]|3[0-1]) (\\d{4})"; + const CString localRegStr = localDateStr + "($| *| GMT *| GMT((\\+|-)(\\d{4})) *|(" + timeStr + "))"; + + std::regex isoReg(isoRegStr); + std::regex utcReg(utcRegStr); + std::regex localReg(localRegStr); + JSHandle msg = base::BuiltinsBase::GetCallArg(argv, 0); + JSHandle str = JSHandle::Cast(JSTaggedValue::ToString(thread, msg)); + RETURN_VALUE_IF_ABRUPT_COMPLETION(thread, JSTaggedValue::Exception()); + CString date = ConvertToString(EcmaString::Cast(str->GetTaggedObject())); + if (std::regex_match(date, isoReg)) { + return IsoParseStringToMs(date); + } + if (std::regex_match(date, utcReg)) { + return UtcParseStringToMs(date); + } + if (std::regex_match(date, localReg)) { + return LocalParseStringToMs(date); + } + return JSTaggedValue(base::NAN_VALUE); +} + +// 20.4.3.1 +JSTaggedValue JSDate::Now() +{ + // time from now is in ms. + int64_t ans; + struct timeval tv { + }; + gettimeofday(&tv, nullptr); + ans = static_cast(tv.tv_sec) * MS_PER_SECOND + (tv.tv_usec / MS_PER_SECOND); + return JSTaggedValue(static_cast(ans)); +} + +// 20.4.4.2 static +JSTaggedValue JSDate::UTC(EcmaRuntimeCallInfo *argv) +{ + double year; + double month = 0; + double date = 1; + double hours = 0; + double minutes = 0; + double seconds = 0; + double ms = 0; + JSThread *thread = argv->GetThread(); + JSHandle yearArg = base::BuiltinsBase::GetCallArg(argv, 0); + JSTaggedNumber yearValue = JSTaggedValue::ToNumber(thread, yearArg); + RETURN_VALUE_IF_ABRUPT_COMPLETION(thread, JSTaggedValue::Exception()); + if (yearValue.IsNumber()) { + year = yearValue.GetNumber(); + if (std::isfinite(year) && !yearValue.IsInt()) { + year = NumberHelper::TruncateDouble(year); + } + if (year >= 0 && year <= (HUNDRED - 1)) { + year = year + NINETEEN_HUNDRED_YEAR; + } + } else { + year = base::NAN_VALUE; + } + uint32_t index = 1; + uint32_t numArgs = argv->GetArgsNumber(); + JSTaggedValue res; + if (numArgs > index) { + JSHandle value = base::BuiltinsBase::GetCallArg(argv, index); + res = JSTaggedValue::ToNumber(thread, value); + RETURN_VALUE_IF_ABRUPT_COMPLETION(thread, JSTaggedValue::Exception()); + month = res.GetNumber(); + index++; + } + if (numArgs > index) { + JSHandle value = base::BuiltinsBase::GetCallArg(argv, index); + res = JSTaggedValue::ToNumber(thread, value); + RETURN_VALUE_IF_ABRUPT_COMPLETION(thread, JSTaggedValue::Exception()); + date = res.GetNumber(); + index++; + } + if (numArgs > index) { + JSHandle value = base::BuiltinsBase::GetCallArg(argv, index); + res = JSTaggedValue::ToNumber(thread, value); + RETURN_VALUE_IF_ABRUPT_COMPLETION(thread, JSTaggedValue::Exception()); + hours = res.GetNumber(); + index++; + } + if (numArgs > index) { + JSHandle value = base::BuiltinsBase::GetCallArg(argv, index); + res = JSTaggedValue::ToNumber(thread, value); + RETURN_VALUE_IF_ABRUPT_COMPLETION(thread, JSTaggedValue::Exception()); + minutes = res.GetNumber(); + index++; + } + if (numArgs > index) { + JSHandle value = base::BuiltinsBase::GetCallArg(argv, index); + res = JSTaggedValue::ToNumber(thread, value); + RETURN_VALUE_IF_ABRUPT_COMPLETION(thread, JSTaggedValue::Exception()); + seconds = res.GetNumber(); + index++; + } + if (numArgs > index) { + JSHandle value = base::BuiltinsBase::GetCallArg(argv, index); + res = JSTaggedValue::ToNumber(thread, value); + RETURN_VALUE_IF_ABRUPT_COMPLETION(thread, JSTaggedValue::Exception()); + ms = res.GetNumber(); + } + double day = MakeDay(year, month, date); + double time = MakeTime(hours, minutes, seconds, ms); + return JSTaggedValue(TimeClip(MakeDate(day, time))); +} + +int64_t JSDate::GetLocalOffsetFromOS(int64_t timeMs, bool isLocal) +{ + // Preserve the old behavior for non-ICU implementation by ignoring both timeMs and is_utc. + if (!isLocal) { + return 0; + } + timeMs /= JSDate::THOUSAND; + time_t tv = std::time(reinterpret_cast(&timeMs)); + struct tm tm { + }; + // localtime_r is only suitable for linux. + struct tm *t = localtime_r(&tv, &tm); + // tm_gmtoff includes any daylight savings offset. + return t->tm_gmtoff / SEC_PER_MINUTE; +} + +// 20.4.4.10 +JSTaggedValue JSDate::GetTime() const +{ + return GetTimeValue(); +} + +// static +CString JSDate::StrToTargetLength(const CString &str, int length) +{ + int len; + if (str[0] == NEG) { + len = static_cast(str.length() - 1); + } else { + len = static_cast(str.length()); + } + int dif = length - len; + CString sub; + for (int i = 0; i < dif; i++) { + sub += '0'; + } + if (str[0] == NEG) { + sub = NEG + sub + str.substr(1, len); + } else { + sub = sub + str; + } + return sub; +} + +bool JSDate::GetThisDateValues(std::array *date, bool isLocal) const +{ + double timeMs = this->GetTimeValue().GetDouble(); + if (std::isnan(timeMs)) { + return false; + } + GetDateValues(timeMs, date, isLocal); + return true; +} + +// 20.4.4.35 +JSTaggedValue JSDate::ToDateString(JSThread *thread) const +{ + std::array monthName = { + "Jan", "Feb", "Mar", "Apr", "May", "Jun", + "Jul", "Aug", "Sep", "Oct", "Nov", "Dec" + }; + std::array weekdayName = {"Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"}; + std::array fields = {0}; + if (!GetThisDateValues(&fields, true)) { + return JSTaggedValue(base::NAN_VALUE); + } + CString year = StrToTargetLength(ToCString(fields[YEAR]), STR_LENGTH_YEAR); + CString day = StrToTargetLength(ToCString(fields[DAYS]), STR_LENGTH_OTHERS); + CString str = weekdayName[fields[WEEKDAY]] + SPACE + monthName[fields[MONTH]] + SPACE + day + SPACE + year; + JSHandle result = thread->GetEcmaVM()->GetFactory()->NewFromCanBeCompressString(str); + return result.GetTaggedValue(); +} + +// static +CString JSDate::ToDateString(double timeMs) +{ + if (std::isnan(timeMs)) { + return "Invalid Date"; + } + std::array monthName = { + "Jan", "Feb", "Mar", "Apr", "May", "Jun", + "Jul", "Aug", "Sep", "Oct", "Nov", "Dec" + }; + std::array weekdayName = {"Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"}; + std::array fields = {0}; + GetDateValues(timeMs, &fields, true); + CString localTime; + int localMin = 0; + localMin = GetLocalOffsetFromOS(localMin, true); + if (timeMs < CHINA_BEFORE_1900_MS && localMin == CHINA_AFTER_1901_MIN) { + localMin = CHINA_BEFORE_1901_MIN; + } + if (localMin >= 0) { + localTime += PLUS; + } else if (localMin < 0) { + localTime += NEG; + localMin = -localMin; + } + localTime = localTime + StrToTargetLength(ToCString(localMin / MINUTE_PER_HOUR), STR_LENGTH_OTHERS); + localTime = localTime + StrToTargetLength(ToCString(localMin % MINUTE_PER_HOUR), STR_LENGTH_OTHERS); + CString year = ToCString(fields[YEAR]); + year = StrToTargetLength(year, STR_LENGTH_YEAR); + CString weekday = weekdayName[fields[WEEKDAY]]; + CString month = monthName[fields[MONTH]]; + CString day = StrToTargetLength(ToCString(fields[DAYS]), STR_LENGTH_OTHERS); + CString hour = StrToTargetLength(ToCString(fields[HOUR]), STR_LENGTH_OTHERS); + CString minute = StrToTargetLength(ToCString(fields[MIN]), STR_LENGTH_OTHERS); + CString second = StrToTargetLength(ToCString(fields[SEC]), STR_LENGTH_OTHERS); + CString str = weekday + SPACE + month + SPACE + day + SPACE + year + SPACE + hour + COLON + minute + COLON + + second + SPACE + "GMT" + localTime; + return str; +} +// 20.4.4.36 +JSTaggedValue JSDate::ToISOString(JSThread *thread) const +{ + std::array fields = {0}; + if (!GetThisDateValues(&fields, false)) { + return JSTaggedValue(base::NAN_VALUE); + } + CString year = ToCString(fields[YEAR]); + if (year[0] == NEG) { + year = StrToTargetLength(year, STR_LENGTH_YEAR + STR_LENGTH_OTHERS); + } else if (year.length() > STR_LENGTH_YEAR) { + year = PLUS + StrToTargetLength(year, STR_LENGTH_YEAR + STR_LENGTH_OTHERS); + } else { + year = StrToTargetLength(year, STR_LENGTH_YEAR); + } + CString month = StrToTargetLength(ToCString(fields[MONTH] + 1), STR_LENGTH_OTHERS); + CString day = StrToTargetLength(ToCString(fields[DAYS]), STR_LENGTH_OTHERS); + CString hour = StrToTargetLength(ToCString(fields[HOUR]), STR_LENGTH_OTHERS); + CString minute = StrToTargetLength(ToCString(fields[MIN]), STR_LENGTH_OTHERS); + CString second = StrToTargetLength(ToCString(fields[SEC]), STR_LENGTH_OTHERS); + CString ms = StrToTargetLength(ToCString(fields[MS]), STR_LENGTH_OTHERS + 1); + CString str = + year + NEG + month + NEG + day + FLAG_TIME + hour + COLON + minute + COLON + second + POINT + ms + FLAG_UTC; + return thread->GetEcmaVM()->GetFactory()->NewFromCanBeCompressString(str).GetTaggedValue(); +} + +CString JSDate::GetLocaleTimeStr(const std::array &fields) const +{ + CString hour; + if (fields[HOUR] > MOUTH_PER_YEAR) { + hour = ToCString(fields[HOUR] - MOUTH_PER_YEAR); + } else { + hour = ToCString(fields[HOUR]); + } + CString minute = ToCString(fields[MIN]); + CString second = StrToTargetLength(ToCString(fields[SEC]), STR_LENGTH_OTHERS); + CString str = hour + COLON + minute + COLON + second; + if (fields[HOUR] >= MOUTH_PER_YEAR) { + str = "下午" + str; + } else { + str = "上午" + str; + } + return str; +} + +CString JSDate::GetLocaleDateStr(const std::array &fields) const +{ + CString year; + if (fields[YEAR] < 0) { + year = ToCString(-fields[YEAR] + 1); + } else { + year = ToCString(fields[YEAR]); + } + CString month = ToCString(fields[MONTH] + 1); + CString day = ToCString(fields[DAYS]); + CString str = year + VIRGULE + month + VIRGULE + day; + return str; +} + +// 20.4.4.38 +JSTaggedValue JSDate::ToLocaleDateString(JSThread *thread) const +{ + std::array fields = {0}; + if (!GetThisDateValues(&fields, true)) { + return JSTaggedValue(base::NAN_VALUE); + } + CString str = GetLocaleDateStr(fields); + return thread->GetEcmaVM()->GetFactory()->NewFromCanBeCompressString(str).GetTaggedValue(); +} + +// 20.4.4.39 +JSTaggedValue JSDate::ToLocaleString(JSThread *thread) const +{ + std::array fields = {0}; + if (!GetThisDateValues(&fields, true)) { + return JSTaggedValue(base::NAN_VALUE); + } + CString strDate = GetLocaleDateStr(fields); + CString strTime = GetLocaleTimeStr(fields); + return thread->GetEcmaVM()->GetFactory()->NewFromCanBeCompressString(strDate + SPACE + strTime).GetTaggedValue(); +} + +// 20.4.4.40 +JSTaggedValue JSDate::ToLocaleTimeString(JSThread *thread) const +{ + std::array fields = {0}; + if (!GetThisDateValues(&fields, true)) { + return JSTaggedValue(base::NAN_VALUE); + } + CString str = GetLocaleTimeStr(fields); + return thread->GetEcmaVM()->GetFactory()->NewFromCanBeCompressString(str).GetTaggedValue(); +} + +// 20.4.4.41 +JSTaggedValue JSDate::ToString(JSThread *thread) const +{ + std::array weekdayName = {"Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"}; + std::array monthName = { + "Jan", "Feb", "Mar", "Apr", "May", "Jun", + "Jul", "Aug", "Sep", "Oct", "Nov", "Dec" + }; + int localMin = 0; + std::array fields = {0}; + if (!GetThisDateValues(&fields, true)) { + return JSTaggedValue(base::NAN_VALUE); + } + CString localTime; + localMin = GetLocalOffsetFromOS(localMin, true); + if (static_cast(this->GetTimeValue().GetDouble()) < CHINA_BEFORE_1900_MS && + localMin == CHINA_AFTER_1901_MIN) { + localMin = CHINA_BEFORE_1901_MIN; + } + if (localMin >= 0) { + localTime += PLUS; + } else if (localMin < 0) { + localTime += NEG; + localMin = -localMin; + } + localTime = localTime + StrToTargetLength(ToCString(localMin / MINUTE_PER_HOUR), STR_LENGTH_OTHERS); + localTime = localTime + StrToTargetLength(ToCString(localMin % MINUTE_PER_HOUR), STR_LENGTH_OTHERS); + CString year = ToCString(fields[YEAR]); + year = StrToTargetLength(year, STR_LENGTH_YEAR); + CString weekday = weekdayName[fields[WEEKDAY]]; + CString month = monthName[fields[MONTH]]; + CString day = StrToTargetLength(ToCString(fields[DAYS]), STR_LENGTH_OTHERS); + CString hour = StrToTargetLength(ToCString(fields[HOUR]), STR_LENGTH_OTHERS); + CString minute = StrToTargetLength(ToCString(fields[MIN]), STR_LENGTH_OTHERS); + CString second = StrToTargetLength(ToCString(fields[SEC]), STR_LENGTH_OTHERS); + CString str = weekday + SPACE + month + SPACE + day + SPACE + year + SPACE + hour + COLON + minute + COLON + + second + SPACE + "GMT" + localTime; + return thread->GetEcmaVM()->GetFactory()->NewFromCanBeCompressString(str).GetTaggedValue(); +} + +// 20.4.4.42 +JSTaggedValue JSDate::ToTimeString(JSThread *thread) const +{ + int localMin = 0; + std::array fields = {0}; + if (!GetThisDateValues(&fields, true)) { + return JSTaggedValue(base::NAN_VALUE); + } + CString localTime; + localMin = GetLocalOffsetFromOS(localMin, true); + if (static_cast(this->GetTimeValue().GetDouble()) < CHINA_BEFORE_1900_MS && + localMin == CHINA_AFTER_1901_MIN) { + localMin = CHINA_BEFORE_1901_MIN; + } + if (localMin >= 0) { + localTime += PLUS; + } else { + localTime += NEG; + localMin = -localMin; + } + localTime = localTime + StrToTargetLength(ToCString(localMin / MINUTE_PER_HOUR), STR_LENGTH_OTHERS); + localTime = localTime + StrToTargetLength(ToCString(localMin % MINUTE_PER_HOUR), STR_LENGTH_OTHERS); + CString hour = StrToTargetLength(ToCString(fields[HOUR]), STR_LENGTH_OTHERS); + CString minute = StrToTargetLength(ToCString(fields[MIN]), STR_LENGTH_OTHERS); + CString second = StrToTargetLength(ToCString(fields[SEC]), STR_LENGTH_OTHERS); + CString str = hour + COLON + minute + COLON + second + SPACE + "GMT" + localTime; + return thread->GetEcmaVM()->GetFactory()->NewFromCanBeCompressString(str).GetTaggedValue(); +} + +// 20.4.4.43 +JSTaggedValue JSDate::ToUTCString(JSThread *thread) const +{ + std::array weekdayName = {"Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"}; + std::array monthName = { + "Jan", "Feb", "Mar", "Apr", "May", "Jun", + "Jul", "Aug", "Sep", "Oct", "Nov", "Dec" + }; + std::array fields = {0}; + if (!GetThisDateValues(&fields, false)) { + return JSTaggedValue(base::NAN_VALUE); + } + CString year = ToCString(fields[YEAR]); + year = StrToTargetLength(year, STR_LENGTH_YEAR); + CString weekday = weekdayName[fields[WEEKDAY]]; + CString month = monthName[fields[MONTH]]; + CString day = StrToTargetLength(ToCString(fields[DAYS]), STR_LENGTH_OTHERS); + CString hour = StrToTargetLength(ToCString(fields[HOUR]), STR_LENGTH_OTHERS); + CString minute = StrToTargetLength(ToCString(fields[MIN]), STR_LENGTH_OTHERS); + CString second = StrToTargetLength(ToCString(fields[SEC]), STR_LENGTH_OTHERS); + CString ms = StrToTargetLength(ToCString(fields[MS]), STR_LENGTH_OTHERS); + CString str = weekday + COMMA + SPACE + day + SPACE + month + SPACE + year + SPACE + hour + COLON + minute + COLON + + second + SPACE + "GMT"; + return thread->GetEcmaVM()->GetFactory()->NewFromCanBeCompressString(str).GetTaggedValue(); +} + +// 20.4.4.44 +JSTaggedValue JSDate::ValueOf() const +{ + return this->GetTimeValue(); +} + +// static +void JSDate::GetDateValues(double timeMs, std::array *date, bool isLocal) +{ + int64_t tz = 0; + int64_t timeMsInt; + int month = 0; + timeMsInt = static_cast(timeMs); + if (isLocal) { // timezone offset + tz = GetLocalOffsetFromOS(timeMsInt, isLocal); + if (timeMsInt < CHINA_BEFORE_1900_MS && tz == CHINA_AFTER_1901_MIN) { + timeMsInt += CHINA_BEFORE_1901_ADDMS; + timeMsInt += tz * MS_PER_SECOND * SEC_PER_MINUTE; + tz = CHINA_BEFORE_1901_MIN; + } else { + timeMsInt += tz * MS_PER_SECOND * SEC_PER_MINUTE; + } + } + + DateUtils::TransferTimeToDate(timeMsInt, date); + + int index = DateUtils::IsLeap((*date)[YEAR]) ? 1 : 0; + int left = 0; + int right = MONTH_PER_YEAR; + while (left <= right) { + int middle = (left + right) / 2; // 2 : half + if (DAYS_FROM_MONTH[index][middle] <= (*date)[DAYS] && DAYS_FROM_MONTH[index][middle + 1] > (*date)[DAYS]) { + month = middle; + (*date)[DAYS] -= DAYS_FROM_MONTH[index][month]; + break; + } + if ((*date)[DAYS] > DAYS_FROM_MONTH[index][middle]) { + left = middle + 1; + } else if ((*date)[DAYS] < DAYS_FROM_MONTH[index][middle]) { + right = middle - 1; + } + } + + (*date)[MONTH] = month; + (*date)[DAYS] = (*date)[DAYS] + 1; + (*date)[TIMEZONE] = -tz; +} + +double JSDate::GetDateValue(double timeMs, uint8_t code, bool isLocal) const +{ + if (std::isnan(timeMs)) { + return base::NAN_VALUE; + } + std::array date = {0}; + GetDateValues(timeMs, &date, isLocal); + return static_cast(date[code]); +} + +JSTaggedValue JSDate::SetDateValue(EcmaRuntimeCallInfo *argv, uint32_t code, bool isLocal) const +{ + // get date values. + std::array date = {0}; + double timeMs = this->GetTimeValue().GetDouble(); + if (std::isnan(timeMs)) { + return JSTaggedValue(base::NAN_VALUE); + } + + // get values from argv. + uint32_t argc = argv->GetArgsNumber(); + if (argc == 0) { + return JSTaggedValue(base::NAN_VALUE); + } + + uint32_t firstValue = code & CODE_FLAG; + uint32_t endValue = (code >> CODE_4_BIT) & CODE_FLAG; + uint32_t count = endValue - firstValue; + + if (argc < count) { + count = argc; + } + + GetDateValues(timeMs, &date, isLocal); + + for (uint32_t i = 0; i < count; i++) { + JSHandle value = base::BuiltinsBase::GetCallArg(argv, i); + JSThread *thread = argv->GetThread(); + JSTaggedNumber res = JSTaggedValue::ToNumber(thread, value); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + double temp = res.GetNumber(); + if (std::isnan(temp)) { + return JSTaggedValue(base::NAN_VALUE); + } + date[firstValue + i] = NumberHelper::TruncateDouble(temp); + } + // set date values. + return JSTaggedValue(SetDateValues(&date, isLocal)); +} + +// static +double JSDate::SetDateValues(const std::array *date, bool isLocal) +{ + int64_t month = DateUtils::Mod((*date)[MONTH], MONTH_PER_YEAR); + int64_t year = (*date)[YEAR] + ((*date)[MONTH] - month) / MONTH_PER_YEAR; + int64_t days = DateUtils::GetDaysFromYear(year); + int index = DateUtils::IsLeap(year) ? 1 : 0; + days += DAYS_FROM_MONTH[index][month]; + + days += (*date)[DAYS] - 1; + int64_t millisecond = + (((*date)[HOUR] * MIN_PER_HOUR + (*date)[MIN]) * SEC_PER_MINUTE + (*date)[SEC]) * MS_PER_SECOND + (*date)[MS]; + int64_t result = days * MS_PER_DAY + millisecond; + if (isLocal) { + int64_t offset = GetLocalOffsetFromOS(result, isLocal) * SEC_PER_MINUTE * MS_PER_SECOND; + if (result < CHINA_1901_MS && (offset / MS_PER_MINUTE) == CHINA_AFTER_1901_MIN) { + offset += CHINA_BEFORE_1901_ADDMS; + } + result -= offset; + } + return TimeClip(result); +} +} // namespace panda::ecmascript diff --git a/runtime/js_date.h b/runtime/js_date.h new file mode 100644 index 000000000..8f998e9c5 --- /dev/null +++ b/runtime/js_date.h @@ -0,0 +1,192 @@ +/* + * Copyright (c) 2021-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_JSDATE_H +#define ECMASCRIPT_JSDATE_H + +#include + +#include "plugins/ecmascript/runtime/ecma_runtime_call_info.h" +#include "plugins/ecmascript/runtime/js_tagged_value-inl.h" + +namespace panda::ecmascript { +static constexpr int64_t DAYS_IN_YEAR = 365; +static constexpr std::array APPROXIMATION_NUMBER = {100000, 3652425}; +static constexpr int64_t CHINA_BEFORE_1900_MS = -2177481943000; +static constexpr int64_t CHINA_1901_MS = -2177452800000; +static constexpr int CHINA_BEFORE_1901_ADDMS = 343000; +static constexpr int MS_PER_SECOND = 1000; +static constexpr int SEC_PER_MINUTE = 60; +static constexpr int MIN_PER_HOUR = 60; +static constexpr int MS_PER_HOUR = 3600 * 1000; +static constexpr int MS_PER_DAY = 86400000; +static constexpr int DAY_PER_WEEK = 7; +static constexpr int DATE_LENGTH = 9; +// the index in the Date Fields +static constexpr uint8_t YEAR = 0; +static constexpr uint8_t MONTH = 1; +static constexpr uint8_t DAYS = 2; +static constexpr uint8_t HOUR = 3; +static constexpr uint8_t MIN = 4; +static constexpr uint8_t SEC = 5; +static constexpr uint8_t MS = 6; +static constexpr uint8_t WEEKDAY = 7; +static constexpr uint8_t TIMEZONE = 8; +static constexpr int CHINA_BEFORE_1901_MIN = 485; +static constexpr int CHINA_AFTER_1901_MIN = 480; +static constexpr int CHINA_BEFORE_1901_MS = 343000; +static constexpr std::array LEAP_NUMBER = {4, 100, 400}; +static constexpr std::array YEAR_NUMBER = {1970, 1969, 1901, 1601}; + +class DateUtils { +public: + static void TransferTimeToDate(int64_t timeMs, std::array *date); + static int64_t Mod(int64_t a, int b); + static bool IsLeap(int64_t year); + static int64_t GetDaysInYear(int64_t year); + static int64_t GetDaysFromYear(int64_t year); + // return the year, update days. + static int64_t GetYearFromDays(int64_t *days); + static int64_t FloorDiv(int64_t a, int64_t b); +}; +class JSDate : public JSObject { +public: + CAST_CHECK(JSDate, IsDate); + + static constexpr size_t TIME_VALUE_OFFSET = JSObject::SIZE; + ACCESSORS(TimeValue, TIME_VALUE_OFFSET, LOCAL_TIME_OFFSET) + ACCESSORS(LocalOffset, LOCAL_TIME_OFFSET, SIZE) // localoffset in min + + DECL_VISIT_OBJECT_FOR_JS_OBJECT(JSObject, TIME_VALUE_OFFSET, SIZE) + + static double MakeDay(double year, double month, double date); + static double MakeTime(double hour, double min, double sec, double ms); + static double MakeDate(double day, double time); + static double TimeClip(double time); + static JSTaggedValue LocalParseStringToMs(const CString &str); + static JSTaggedValue UtcParseStringToMs(const CString &str); + static JSTaggedValue IsoParseStringToMs(const CString &str); + static int GetSignedNumFromString(const CString &str, int len, int *index); + static bool GetNumFromString(const CString &str, int len, int *index, int *num); + + // 20.4.1.7 + int64_t GetLocalOffsetInMin(const JSThread *thread, int64_t timeMs, bool isLocal); + + // 20.4.1.8 + double LocalTime(double timeMs) const; + + // 20.4.1.9 + double UTCTime(double timeMs) const; + + // 20.4.3.1 + static JSTaggedValue Now(); + + // 20.4.3.2 + static JSTaggedValue Parse(EcmaRuntimeCallInfo *argv); + + // 20.4.3.4 + static JSTaggedValue UTC(EcmaRuntimeCallInfo *argv); + + // 20.4.4.10 + JSTaggedValue GetTime() const; + + // 20.4.4.19 + JSTaggedValue GetUTCSeconds(); + + // 20.4.4.35 + JSTaggedValue ToDateString(JSThread *thread) const; + static CString ToDateString(double timeMs); + + // 20.4.4.36 + JSTaggedValue ToISOString(JSThread *thread) const; + + // 20.4.4.38 + JSTaggedValue ToLocaleDateString(JSThread *thread) const; + + // 20.4.4.39 + JSTaggedValue ToLocaleString(JSThread *thread) const; + + // 20.4.4.40 + JSTaggedValue ToLocaleTimeString(JSThread *thread) const; + + // 20.4.4.41 + JSTaggedValue ToString(JSThread *thread) const; + + // 20.4.4.42 + JSTaggedValue ToTimeString(JSThread *thread) const; + + // 20.4.4.43 + JSTaggedValue ToUTCString(JSThread *thread) const; + + // 20.4.4.44 + JSTaggedValue ValueOf() const; + + JSTaggedValue SetDateValue(EcmaRuntimeCallInfo *argv, uint32_t code, bool isLocal) const; + double GetDateValue(double timeMs, uint8_t code, bool isLocal) const; + + static constexpr double MAX_DOUBLE = std::numeric_limits::max(); + static constexpr double MAX_INT = std::numeric_limits::max(); + static constexpr uint16_t NINETEEN_HUNDRED_YEAR = 1900; + static constexpr uint16_t HUNDRED = 100; + static constexpr uint16_t THOUSAND = 1000; + static double SetDateValues(const std::array *date, bool isLocal); + static void GetDateValues(double timeMs, std::array *date, bool isLocal); + static int64_t GetLocalOffsetFromOS(int64_t timeMs, bool isLocal); + static CString StrToTargetLength(const CString &str, int length); + DECL_DUMP() + +private: + bool GetThisDateValues(std::array *date, bool isLocal) const; + CString GetLocaleTimeStr(const std::array &fields) const; + CString GetLocaleDateStr(const std::array &fields) const; + static int64_t MathMod(int64_t a, int b); + + static constexpr int MINUTE_PER_HOUR = 60; + static constexpr int MOUTH_PER_YEAR = 12; + static constexpr std::array MONTH_DAYS = {31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31}; + // NOLINTNEXTLINE(readability-braces-around-statements, bugprone-suspicious-semicolon, modernize-avoid-c-arrays) + static constexpr int DAYS_FROM_MONTH[2][13] = {{0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334, 365}, + {0, 31, 60, 91, 121, 152, 182, 213, 244, 274, 305, 335, 366}}; + static constexpr int STR_LENGTH_YEAR = 4; + static constexpr int STR_LENGTH_OTHERS = 2; + static constexpr int MONTH_PER_YEAR = 12; + static constexpr int YEAR_DELTA = 399999; + static constexpr int LINE_YEAR = 1970; + static constexpr int CENTURY = 100; + static constexpr char NEG = '-'; + static constexpr char PLUS = '+'; + static constexpr char SPACE = ' '; + static constexpr char COLON = ':'; + static constexpr char POINT = '.'; + static constexpr int LENGTH_MONTH_NAME = 3; + static constexpr int MS_PER_MINUTE = 60000; + static constexpr int64_t MAX_TIME_IN_MS = static_cast(864000000) * 10000000; + static constexpr int TEN = 10; + static constexpr char FLAG_TIME = 'T'; + static constexpr char FLAG_UTC = 'Z'; + static constexpr char VIRGULE = '/'; + static constexpr char COMMA = ','; + static constexpr int LENGTH_PER_TIME = 3; + static constexpr int MIN_LENGTH = 10; + static constexpr int INDEX_PLUS_NEG = 6; + static constexpr int NUM_NINE = 9; + static constexpr int ORIGIN_YEAR = 1901; + + static constexpr uint32_t CODE_FLAG = 0x0FULL; + static constexpr size_t CODE_4_BIT = 4; +}; +} // namespace panda::ecmascript + +#endif // ECMASCRIPT_JSDATE_H diff --git a/runtime/js_date_time_format.cpp b/runtime/js_date_time_format.cpp new file mode 100644 index 000000000..fcfd45912 --- /dev/null +++ b/runtime/js_date_time_format.cpp @@ -0,0 +1,1471 @@ +/* + * Copyright (c) 2021-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 "js_date_time_format.h" + +#include "ecma_macros.h" +#include "global_env.h" +#include "js_array.h" +#include "js_date.h" +#include "js_intl.h" +#include "js_locale.h" +#include "js_object-inl.h" +#include "object_factory.h" + +namespace panda::ecmascript { +struct CommonDateFormatPart { + int32_t fField = 0; // NOLINT(misc-non-private-member-variables-in-classes) + int32_t fBeginIndex = 0; // NOLINT(misc-non-private-member-variables-in-classes) + int32_t fEndIndex = 0; // NOLINT(misc-non-private-member-variables-in-classes) + int32_t index = 0; // NOLINT(misc-non-private-member-variables-in-classes) + bool isPreExist = false; // NOLINT(misc-non-private-member-variables-in-classes) + + CommonDateFormatPart() = default; + CommonDateFormatPart(int32_t fField, int32_t fBeginIndex, int32_t fEndIndex, int32_t index, bool isPreExist) + : fField(fField), fBeginIndex(fBeginIndex), fEndIndex(fEndIndex), index(index), isPreExist(isPreExist) + { + } + + ~CommonDateFormatPart() = default; + + DEFAULT_COPY_SEMANTIC(CommonDateFormatPart); + DEFAULT_MOVE_SEMANTIC(CommonDateFormatPart); +}; + +namespace { +// NOLINTNEXTLINE(fuchsia-statically-constructed-objects) +const std::vector ICU_LONG_SHORT = {"long", "short"}; +// NOLINTNEXTLINE(fuchsia-statically-constructed-objects) +// NOLINTNEXTLINE(fuchsia-statically-constructed-objects) +const std::vector ICU_NARROW_LONG_SHORT = {"narrow", "long", "short"}; +// NOLINTNEXTLINE(fuchsia-statically-constructed-objects) +const std::vector ICU2_DIGIT_NUMERIC = {"2-digit", "numeric"}; +// NOLINTNEXTLINE(fuchsia-statically-constructed-objects) +const std::vector ICU_NARROW_LONG_SHORT2_DIGIT_NUMERIC = {"narrow", "long", "short", "2-digit", "numeric"}; +// NOLINTNEXTLINE(fuchsia-statically-constructed-objects) +const std::vector ICU_WEEKDAY_PE = {{"EEEEE", "narrow"}, {"EEEE", "long"}, {"EEE", "short"}, + {"ccccc", "narrow"}, {"cccc", "long"}, {"ccc", "short"}}; +// NOLINTNEXTLINE(fuchsia-statically-constructed-objects) +const std::vector ICU_ERA_PE = {{"GGGGG", "narrow"}, {"GGGG", "long"}, {"GGG", "short"}}; +// NOLINTNEXTLINE(fuchsia-statically-constructed-objects) +const std::vector ICU_YEAR_PE = {{"yy", "2-digit"}, {"y", "numeric"}}; +// NOLINTNEXTLINE(fuchsia-statically-constructed-objects) +const std::vector ICU_MONTH_PE = { + {"MMMMM", "narrow"}, {"MMMM", "long"}, {"MMM", "short"}, {"MM", "2-digit"}, {"M", "numeric"}, + {"LLLLL", "narrow"}, {"LLLL", "long"}, {"LLL", "short"}, {"LL", "2-digit"}, {"L", "numeric"}}; +// NOLINTNEXTLINE(fuchsia-statically-constructed-objects) +const std::vector ICU_DAY_PE = {{"dd", "2-digit"}, {"d", "numeric"}}; +// NOLINTNEXTLINE(fuchsia-statically-constructed-objects) +const std::vector ICU_DAY_PERIOD_PE = {{"BBBBB", "narrow"}, {"bbbbb", "narrow"}, {"BBBB", "long"}, + {"bbbb", "long"}, {"B", "short"}, {"b", "short"}}; +// NOLINTNEXTLINE(fuchsia-statically-constructed-objects) +const std::vector ICU_HOUR_PE = {{"HH", "2-digit"}, {"H", "numeric"}, {"hh", "2-digit"}, + {"h", "numeric"}, {"kk", "2-digit"}, {"k", "numeric"}, + {"KK", "2-digit"}, {"K", "numeric"}}; +// NOLINTNEXTLINE(fuchsia-statically-constructed-objects) +const std::vector ICU_MINUTE_PE = {{"mm", "2-digit"}, {"m", "numeric"}}; +// NOLINTNEXTLINE(fuchsia-statically-constructed-objects) +const std::vector ICU_SECOND_PE = {{"ss", "2-digit"}, {"s", "numeric"}}; +// NOLINTNEXTLINE(fuchsia-statically-constructed-objects) +const std::vector ICU_YIME_ZONE_NAME_PE = {{"zzzz", "long"}, {"z", "short"}}; + +// NOLINTNEXTLINE(fuchsia-statically-constructed-objects) +const std::map HOUR_CYCLE_MAP = { + {'K', HourCycleOption::H11}, {'h', HourCycleOption::H12}, {'H', HourCycleOption::H23}, {'k', HourCycleOption::H24}}; +// NOLINTNEXTLINE(fuchsia-statically-constructed-objects) +const std::map TO_HOUR_CYCLE_MAP = {{"h11", HourCycleOption::H11}, + {"h12", HourCycleOption::H12}, + {"h23", HourCycleOption::H23}, + {"h24", HourCycleOption::H24}}; + +// The value of the [[RelevantExtensionKeys]] internal slot is « "ca", "nu", "hc" ». +// NOLINTNEXTLINE(fuchsia-statically-constructed-objects) +const std::set RELEVANT_EXTENSION_KEYS = {"nu", "ca", "hc"}; + +} // namespace + +icu::Locale *JSDateTimeFormat::GetIcuLocale() const +{ + ASSERT(GetLocaleIcu().IsJSNativePointer()); + auto result = JSNativePointer::Cast(GetLocaleIcu().GetTaggedObject())->GetExternalPointer(); + return reinterpret_cast(result); +} + +/* static */ +void JSDateTimeFormat::SetIcuLocale(JSThread *thread, JSHandle obj, const icu::Locale &icuLocale, + const DeleteEntryPoint &callback) +{ + EcmaVM *ecmaVm = thread->GetEcmaVM(); + ObjectFactory *factory = ecmaVm->GetFactory(); + icu::Locale *icuPointer = ecmaVm->GetRegionFactory()->New(icuLocale); + ASSERT(icuPointer != nullptr); + JSTaggedValue data = obj->GetLocaleIcu(); + if (data.IsHeapObject() && data.IsJSNativePointer()) { + JSNativePointer *native = JSNativePointer::Cast(data.GetTaggedObject()); + native->ResetExternalPointer(icuPointer); + return; + } + JSHandle pointer = factory->NewJSNativePointer(icuPointer); + pointer->SetDeleter(callback); + obj->SetLocaleIcu(thread, pointer.GetTaggedValue()); + ecmaVm->PushToArrayDataList(*pointer); +} + +void JSDateTimeFormat::FreeIcuLocale(void *pointer, void *data) +{ + if (pointer == nullptr) { + return; + } + auto icuLocale = reinterpret_cast(pointer); + icuLocale->~Locale(); + if (data != nullptr) { + reinterpret_cast(data)->GetRegionFactory()->FreeBuffer(pointer); + } +} + +icu::SimpleDateFormat *JSDateTimeFormat::GetIcuSimpleDateFormat() const +{ + ASSERT(GetSimpleDateTimeFormatIcu().IsJSNativePointer()); + auto result = JSNativePointer::Cast(GetSimpleDateTimeFormatIcu().GetTaggedObject())->GetExternalPointer(); + return reinterpret_cast(result); +} + +/* static */ +void JSDateTimeFormat::SetIcuSimpleDateFormat(JSThread *thread, JSHandle obj, + const icu::SimpleDateFormat &icuSimpleDateTimeFormat, + const DeleteEntryPoint &callback) +{ + EcmaVM *ecmaVm = thread->GetEcmaVM(); + ObjectFactory *factory = ecmaVm->GetFactory(); + icu::SimpleDateFormat *icuPointer = ecmaVm->GetRegionFactory()->New(icuSimpleDateTimeFormat); + ASSERT(icuPointer != nullptr); + JSTaggedValue data = obj->GetSimpleDateTimeFormatIcu(); + if (data.IsHeapObject() && data.IsJSNativePointer()) { + JSNativePointer *native = JSNativePointer::Cast(data.GetTaggedObject()); + native->ResetExternalPointer(icuPointer); + return; + } + JSHandle pointer = factory->NewJSNativePointer(icuPointer); + pointer->SetDeleter(callback); + obj->SetSimpleDateTimeFormatIcu(thread, pointer.GetTaggedValue()); + ecmaVm->PushToArrayDataList(*pointer); +} + +void JSDateTimeFormat::FreeSimpleDateFormat(void *pointer, void *data) +{ + if (pointer == nullptr) { + return; + } + auto icuSimpleDateFormat = reinterpret_cast(pointer); + icuSimpleDateFormat->~SimpleDateFormat(); + if (data != nullptr) { + reinterpret_cast(data)->GetRegionFactory()->FreeBuffer(pointer); + } +} + +JSHandle JSDateTimeFormat::ToValueString(JSThread *thread, const Value value) +{ + auto globalConst = thread->GlobalConstants(); + JSMutableHandle result(thread, JSTaggedValue::Undefined()); + switch (value) { + case Value::SHARED: + result.Update(globalConst->GetHandledSharedString().GetTaggedValue()); + break; + case Value::START_RANGE: + result.Update(globalConst->GetHandledStartRangeString().GetTaggedValue()); + break; + case Value::END_RANGE: + result.Update(globalConst->GetHandledEndRangeString().GetTaggedValue()); + break; + default: + UNREACHABLE(); + } + return result; +} + +// 13.1.1 InitializeDateTimeFormat (dateTimeFormat, locales, options) +// NOLINTNEXTLINE(readability-function-size) +JSHandle JSDateTimeFormat::InitializeDateTimeFormat(JSThread *thread, + const JSHandle &dateTimeFormat, + const JSHandle &locales, + const JSHandle &options) +{ + EcmaVM *ecmaVm = thread->GetEcmaVM(); + ObjectFactory *factory = ecmaVm->GetFactory(); + const GlobalEnvConstants *globalConst = thread->GlobalConstants(); + + // 1. Let requestedLocales be ? CanonicalizeLocaleList(locales). + JSHandle requestedLocales = JSLocale::CanonicalizeLocaleList(thread, locales); + RETURN_HANDLE_IF_ABRUPT_COMPLETION(JSDateTimeFormat, thread); + + // 2. Let options be ? ToDateTimeOptions(options, "any", "date"). + JSHandle dateTimeOptions = ToDateTimeOptions(thread, options, RequiredOption::ANY, DefaultsOption::DATE); + RETURN_HANDLE_IF_ABRUPT_COMPLETION(JSDateTimeFormat, thread); + + // 4. Let matcher be ? GetOption(options, "localeMatcher", "string", « "lookup", "best fit" », "best fit"). + auto matcher = JSLocale::GetOptionOfString( + thread, dateTimeOptions, globalConst->GetHandledLocaleMatcherString(), + {LocaleMatcherOption::LOOKUP, LocaleMatcherOption::BEST_FIT}, {"lookup", "best fit"}, + LocaleMatcherOption::BEST_FIT); + RETURN_HANDLE_IF_ABRUPT_COMPLETION(JSDateTimeFormat, thread); + + // 6. Let calendar be ? GetOption(options, "calendar", "string", undefined, undefined). + JSHandle calendar = + JSLocale::GetOption(thread, dateTimeOptions, globalConst->GetHandledCalendarString(), OptionType::STRING, + globalConst->GetHandledUndefined(), globalConst->GetHandledUndefined()); + RETURN_HANDLE_IF_ABRUPT_COMPLETION(JSDateTimeFormat, thread); + dateTimeFormat->SetCalendar(thread, calendar); + + // 7. If calendar is not undefined, then + // a. If calendar does not match the Unicode Locale Identifier type nonterminal, throw a RangeError exception. + std::string calendarStr; + if (!calendar->IsUndefined()) { + JSHandle calendarEcmaStr = JSHandle::Cast(calendar); + calendarStr = JSLocale::ConvertToStdString(calendarEcmaStr); + if (!JSLocale::IsNormativeCalendar(calendarStr)) { + THROW_RANGE_ERROR_AND_RETURN(thread, "invalid calendar", dateTimeFormat); + } + } + + // 9. Let numberingSystem be ? GetOption(options, "numberingSystem", "string", undefined, undefined). + JSHandle numberingSystem = + JSLocale::GetOption(thread, dateTimeOptions, globalConst->GetHandledNumberingSystemString(), OptionType::STRING, + globalConst->GetHandledUndefined(), globalConst->GetHandledUndefined()); + RETURN_HANDLE_IF_ABRUPT_COMPLETION(JSDateTimeFormat, thread); + dateTimeFormat->SetNumberingSystem(thread, numberingSystem); + + // 10. If numberingSystem is not undefined, then + // a. If numberingSystem does not match the Unicode Locale Identifier type nonterminal, throw a RangeError + // exception. + std::string nsStr; + if (!numberingSystem->IsUndefined()) { + JSHandle nsEcmaStr = JSHandle::Cast(numberingSystem); + nsStr = JSLocale::ConvertToStdString(nsEcmaStr); + if (!JSLocale::IsWellNumberingSystem(nsStr)) { + THROW_RANGE_ERROR_AND_RETURN(thread, "invalid numberingSystem", dateTimeFormat); + } + } + + // 12. Let hour12 be ? GetOption(options, "hour12", "boolean", undefined, undefined). + JSHandle hour12 = + JSLocale::GetOption(thread, dateTimeOptions, globalConst->GetHandledHour12String(), OptionType::BOOLEAN, + globalConst->GetHandledUndefined(), globalConst->GetHandledUndefined()); + RETURN_HANDLE_IF_ABRUPT_COMPLETION(JSDateTimeFormat, thread); + + // 13. Let hourCycle be ? GetOption(options, "hourCycle", "string", « "h11", "h12", "h23", "h24" », undefined). + auto hourCycle = JSLocale::GetOptionOfString( + thread, dateTimeOptions, globalConst->GetHandledHourCycleString(), + {HourCycleOption::H11, HourCycleOption::H12, HourCycleOption::H23, HourCycleOption::H24}, + {"h11", "h12", "h23", "h24"}, HourCycleOption::UNDEFINED); + RETURN_HANDLE_IF_ABRUPT_COMPLETION(JSDateTimeFormat, thread); + + // 14. If hour12 is not undefined, then + // a. Let hourCycle be null. + if (!hour12->IsUndefined()) { + hourCycle = HourCycleOption::UNDEFINED; + } + + // 16. Let localeData be %DateTimeFormat%.[[LocaleData]]. + JSHandle availableLocales = + (requestedLocales->GetLength() == 0) ? factory->EmptyArray() : GainAvailableLocales(thread); + + // 17. Let r be ResolveLocale(%DateTimeFormat%.[[AvailableLocales]], requestedLocales, opt, %DateTimeFormat% + // .[[RelevantExtensionKeys]], localeData). + ResolvedLocale resolvedLocale = + JSLocale::ResolveLocale(thread, availableLocales, requestedLocales, matcher, RELEVANT_EXTENSION_KEYS); + + // 18. Set icuLocale to r.[[locale]]. + icu::Locale icuLocale = resolvedLocale.localeData; + ASSERT_PRINT(!icuLocale.isBogus(), "icuLocale is bogus"); + UErrorCode status = U_ZERO_ERROR; + + // Set resolvedIcuLocaleCopy to a copy of icuLocale. + // Set icuLocale.[[ca]] to calendar. + // Set icuLocale.[[nu]] to numberingSystem. + icu::Locale resolvedIcuLocaleCopy(icuLocale); + if (!calendar->IsUndefined() && JSLocale::IsWellCalendar(icuLocale, calendarStr)) { + icuLocale.setUnicodeKeywordValue("ca", calendarStr, status); + } + if (!numberingSystem->IsUndefined() && JSLocale::IsWellNumberingSystem(nsStr)) { + icuLocale.setUnicodeKeywordValue("nu", nsStr, status); + } + + // 24. Let timeZone be ? Get(options, "timeZone"). + OperationResult operationResult = + JSObject::GetProperty(thread, dateTimeOptions, globalConst->GetHandledTimeZoneString()); + RETURN_HANDLE_IF_ABRUPT_COMPLETION(JSDateTimeFormat, thread); + dateTimeFormat->SetTimeZone(thread, operationResult.GetValue()); + + // 25. If timeZone is not undefined, then + // a. Let timeZone be ? ToString(timeZone). + // b. If the result of IsValidTimeZoneName(timeZone) is false, then + // i. Throw a RangeError exception. + std::unique_ptr icuTimeZone; + if (!operationResult.GetValue()->IsUndefined()) { + JSHandle timezone = JSTaggedValue::ToString(thread, operationResult.GetValue()); + RETURN_HANDLE_IF_ABRUPT_COMPLETION(JSDateTimeFormat, thread); + icuTimeZone = ConstructTimeZone(JSLocale::ConvertToStdString(timezone)); + if (icuTimeZone == nullptr) { + THROW_RANGE_ERROR_AND_RETURN(thread, "invalid timeZone", dateTimeFormat); + } + } else { + // 26. Else, + // a. Let timeZone be DefaultTimeZone(). + icuTimeZone = std::unique_ptr(icu::TimeZone::createDefault()); + } + + // 36.a. Let hcDefault be dataLocaleData.[[hourCycle]]. + std::unique_ptr generator( + icu::DateTimePatternGenerator::createInstance(icuLocale, status)); + ASSERT_PRINT(U_SUCCESS(status), "constructGenerator failed"); + HourCycleOption hcDefault = OptionToHourCycle(generator->getDefaultHourCycle(status)); + // b. Let hc be dateTimeFormat.[[HourCycle]]. + HourCycleOption hc = HourCycleOption::UNDEFINED; + hc = (hourCycle == HourCycleOption::UNDEFINED) ? OptionToHourCycle(resolvedLocale.extensions.find("hc")->second) + : hourCycle; + // c. If hc is null, then + // i. Set hc to hcDefault. + if (hc == HourCycleOption::UNDEFINED) { + hc = hcDefault; + } + // d. If hour12 is not undefined, then + if (!hour12->IsUndefined()) { + // i. If hour12 is true, then + if (JSTaggedValue::SameValue(hour12.GetTaggedValue(), JSTaggedValue::True())) { + // 1. If hcDefault is "h11" or "h23", then + if (hcDefault == HourCycleOption::H11 || hcDefault == HourCycleOption::H23) { + // a. Set hc to "h11". + hc = HourCycleOption::H11; + } else { + // 2. Else, + // a. Set hc to "h12". + hc = HourCycleOption::H12; + } + } else { + // ii. Else, + // 2. If hcDefault is "h11" or "h23", then + if (hcDefault == HourCycleOption::H11 || hcDefault == HourCycleOption::H23) { + // a. Set hc to "h23". + hc = HourCycleOption::H23; + } else { + // 3. Else, + // a. Set hc to "h24". + hc = HourCycleOption::H24; + } + } + } + + // Set isHourDefined be false when dateTimeFormat.[[Hour]] is not undefined. + bool isHourDefined = false; + + // 29. For each row of Table 6, except the header row, in table order, do + // a. Let prop be the name given in the Property column of the row. + // b. Let value be ? GetOption(options, prop, "string", « the strings given in the Values column of the + // row », undefined). + // c. Set opt.[[]] to value. + std::string skeleton; + std::vector data = GetIcuPatternDesc(hc); + for (const IcuPatternDesc &item : data) { + // prop be [[TimeZoneName]] + if (item.property == "timeZoneName") { + int secondDigitsString = JSLocale::GetNumberOption( + thread, dateTimeOptions, globalConst->GetHandledFractionalSecondDigitsString(), 1, 3, 0); + skeleton.append(secondDigitsString, 'S'); + } + JSHandle property(thread, factory->NewFromStdString(item.property).GetTaggedValue()); + std::string value; + bool isFind = JSLocale::GetOptionOfString(thread, dateTimeOptions, property, item.allowedValues, &value); + RETURN_HANDLE_IF_ABRUPT_COMPLETION(JSDateTimeFormat, thread); + if (isFind) { + skeleton += item.map.find(value)->second; + // [[Hour]] is defined. + isHourDefined = (item.property == "hour") ? true : isHourDefined; + } + } + + // 13.1.3 BasicFormatMatcher (options, formats) + [[maybe_unused]] auto formatMatcher = JSLocale::GetOptionOfString( + thread, dateTimeOptions, globalConst->GetHandledFormatMatcherString(), + {FormatMatcherOption::BASIC, FormatMatcherOption::BEST_FIT}, {"basic", "best fit"}, + FormatMatcherOption::BEST_FIT); + RETURN_HANDLE_IF_ABRUPT_COMPLETION(JSDateTimeFormat, thread); + + // Let dateStyle be ? GetOption(options, "string", «"full", "long", "medium", "short"», undefined). + // Set dateTimeFormat.[[dateStyle]] + auto dateStyle = JSLocale::GetOptionOfString( + thread, dateTimeOptions, globalConst->GetHandledDateStyleString(), + {DateTimeStyleOption::FULL, DateTimeStyleOption::LONG, DateTimeStyleOption::MEDIUM, DateTimeStyleOption::SHORT}, + {"full", "long", "medium", "short"}, DateTimeStyleOption::UNDEFINED); + RETURN_HANDLE_IF_ABRUPT_COMPLETION(JSDateTimeFormat, thread); + dateTimeFormat->SetDateStyle(thread, JSTaggedValue(static_cast(dateStyle))); + + // Let timeStyle be ? GetOption(options, "string", «"full", "long", "medium", "short"», undefined). + // Set dateTimeFormat.[[timeStyle]] + auto timeStyle = JSLocale::GetOptionOfString( + thread, dateTimeOptions, globalConst->GetHandledTimeStyleString(), + {DateTimeStyleOption::FULL, DateTimeStyleOption::LONG, DateTimeStyleOption::MEDIUM, DateTimeStyleOption::SHORT}, + {"full", "long", "medium", "short"}, DateTimeStyleOption::UNDEFINED); + RETURN_HANDLE_IF_ABRUPT_COMPLETION(JSDateTimeFormat, thread); + dateTimeFormat->SetTimeStyle(thread, JSTaggedValue(static_cast(timeStyle))); + + HourCycleOption dtfHourCycle = HourCycleOption::UNDEFINED; + + // If dateTimeFormat.[[Hour]] is defined, then + if (isHourDefined) { + // e. Set dateTimeFormat.[[HourCycle]] to hc. + dtfHourCycle = hc; + } else { + // 37. Else, + // a. Set dateTimeFormat.[[HourCycle]] to undefined. + dtfHourCycle = HourCycleOption::UNDEFINED; + } + + // Set dateTimeFormat.[[hourCycle]]. + dateTimeFormat->SetHourCycle(thread, JSTaggedValue(static_cast(dtfHourCycle))); + + // Set dateTimeFormat.[[icuLocale]]. + JSDateTimeFormat::SetIcuLocale(thread, dateTimeFormat, icuLocale, JSDateTimeFormat::FreeIcuLocale); + + // Creates a Calendar using the given timezone and given locale. + // Set dateTimeFormat.[[icuSimpleDateFormat]]. + icu::UnicodeString dtfSkeleton(skeleton.c_str()); + status = U_ZERO_ERROR; + icu::UnicodeString pattern = ChangeHourCyclePattern( + generator->getBestPattern(dtfSkeleton, UDATPG_MATCH_HOUR_FIELD_LENGTH, status), dtfHourCycle); + ASSERT_PRINT((U_SUCCESS(status) != 0), "get best pattern failed"); + auto simpleDateFormatIcu(std::make_unique(pattern, icuLocale, status)); + if (U_FAILURE(status) != 0) { + simpleDateFormatIcu = std::unique_ptr(); + } + ASSERT_PRINT(simpleDateFormatIcu != nullptr, "invalid icuSimpleDateFormat"); + std::unique_ptr calendarPtr = BuildCalendar(icuLocale, *icuTimeZone); + ASSERT_PRINT(calendarPtr != nullptr, "invalid calendar"); + simpleDateFormatIcu->adoptCalendar(calendarPtr.release()); + SetIcuSimpleDateFormat(thread, dateTimeFormat, *simpleDateFormatIcu, JSDateTimeFormat::FreeSimpleDateFormat); + + // Set dateTimeFormat.[[iso8601]]. + bool iso8601 = strstr(icuLocale.getName(), "calendar=iso8601") != nullptr; + dateTimeFormat->SetIso8601(thread, JSTaggedValue(iso8601)); + + // Set dateTimeFormat.[[locale]]. + if (!hour12->IsUndefined() || hourCycle != HourCycleOption::UNDEFINED) { + if ((resolvedLocale.extensions.find("hc") != resolvedLocale.extensions.end()) && + (dtfHourCycle != OptionToHourCycle((resolvedLocale.extensions.find("hc")->second)))) { + resolvedIcuLocaleCopy.setUnicodeKeywordValue("hc", nullptr, status); + ASSERT_PRINT(U_SUCCESS(status), "resolvedIcuLocaleCopy set hc failed"); + } + } + JSHandle localeStr = JSLocale::ToLanguageTag(thread, resolvedIcuLocaleCopy); + dateTimeFormat->SetLocale(thread, localeStr.GetTaggedValue()); + + // Set dateTimeFormat.[[boundFormat]]. + dateTimeFormat->SetBoundFormat(thread, JSTaggedValue::Undefined()); + + // 39. Return dateTimeFormat. + return dateTimeFormat; +} + +// 13.1.2 ToDateTimeOptions (options, required, defaults) +JSHandle JSDateTimeFormat::ToDateTimeOptions(JSThread *thread, const JSHandle &options, + const RequiredOption &required, const DefaultsOption &defaults) +{ + EcmaVM *ecmaVm = thread->GetEcmaVM(); + ObjectFactory *factory = ecmaVm->GetFactory(); + + // 1. If options is undefined, let options be null; otherwise let options be ? ToObject(options). + JSHandle optionsResult(thread, JSTaggedValue::Null()); + if (!options->IsUndefined()) { + optionsResult = JSTaggedValue::ToObject(thread, options); + RETURN_HANDLE_IF_ABRUPT_COMPLETION(JSObject, thread); + } + + // 2. Let options be ObjectCreate(options). + optionsResult = JSObject::ObjectCreate(thread, optionsResult); + + // 3. Let needDefaults be true. + bool needDefaults = true; + + // 4. If required is "date" or "any", then + // a. For each of the property names "weekday", "year", "month", "day", do + // i. Let prop be the property name. + // ii. Let value be ? Get(options, prop). + // iii. If value is not undefined, let needDefaults be false. + auto globalConst = thread->GlobalConstants(); + if (required == RequiredOption::DATE || required == RequiredOption::ANY) { + JSHandle array = factory->NewTaggedArray(CAPACITY_4); + array->Set(thread, 0, globalConst->GetHandledWeekdayString()); + array->Set(thread, 1, globalConst->GetHandledYearString()); + array->Set(thread, 2, globalConst->GetHandledMonthString()); // 2 means the third slot + array->Set(thread, 3, globalConst->GetHandledDayString()); // 3 means the fourth slot + JSMutableHandle key(thread, JSTaggedValue::Undefined()); + uint32_t len = array->GetLength(); + for (uint32_t i = 0; i < len; i++) { + key.Update(array->Get(thread, i)); + OperationResult operationResult = JSObject::GetProperty(thread, optionsResult, key); + RETURN_HANDLE_IF_ABRUPT_COMPLETION(JSObject, thread); + if (!operationResult.GetValue()->IsUndefined()) { + needDefaults = false; + } + } + } + + // 5. If required is "time" or "any", then + // a. For each of the property names "dayPeriod", "hour", "minute", "second", "fractionalSecondDigits", do + // i. Let prop be the property name. + // ii. Let value be ? Get(options, prop). + // iii. If value is not undefined, let needDefaults be false. + if (required == RequiredOption::TIME || required == RequiredOption::ANY) { + JSHandle array = factory->NewTaggedArray(CAPACITY_5); + array->Set(thread, 0, globalConst->GetHandledDayPeriodString()); + array->Set(thread, 1, globalConst->GetHandledHourString()); + array->Set(thread, 2, globalConst->GetHandledMinuteString()); // 2 means the second slot + array->Set(thread, 3, globalConst->GetHandledSecondString()); // 3 means the third slot + array->Set(thread, 4, globalConst->GetHandledFractionalSecondDigitsString()); // 4 means the fourth slot + JSMutableHandle key(thread, JSTaggedValue::Undefined()); + uint32_t len = array->GetLength(); + for (uint32_t i = 0; i < len; i++) { + key.Update(array->Get(thread, i)); + OperationResult operationResult = JSObject::GetProperty(thread, optionsResult, key); + RETURN_HANDLE_IF_ABRUPT_COMPLETION(JSObject, thread); + if (!operationResult.GetValue()->IsUndefined()) { + needDefaults = false; + } + } + } + + // Let dateStyle/timeStyle be ? Get(options, "dateStyle"/"timeStyle"). + OperationResult dateStyleResult = + JSObject::GetProperty(thread, optionsResult, globalConst->GetHandledDateStyleString()); + RETURN_HANDLE_IF_ABRUPT_COMPLETION(JSObject, thread); + JSHandle dateStyle = dateStyleResult.GetValue(); + OperationResult timeStyleResult = + JSObject::GetProperty(thread, optionsResult, globalConst->GetHandledTimeStyleString()); + RETURN_HANDLE_IF_ABRUPT_COMPLETION(JSObject, thread); + JSHandle timeStyle = timeStyleResult.GetValue(); + + // If dateStyle is not undefined or timeStyle is not undefined, let needDefaults be false. + if (!dateStyle->IsUndefined() || !timeStyle->IsUndefined()) { + needDefaults = false; + } + + // If required is "date"/"time" and timeStyle is not undefined, throw a TypeError exception. + if (required == RequiredOption::DATE && !timeStyle->IsUndefined()) { + THROW_TYPE_ERROR_AND_RETURN(thread, "timeStyle is not undefined", optionsResult); + } + if (required == RequiredOption::TIME && !dateStyle->IsUndefined()) { + THROW_TYPE_ERROR_AND_RETURN(thread, "dateStyle is not undefined", optionsResult); + } + + // 6. If needDefaults is true and defaults is either "date" or "all", then + // a. For each of the property names "year", "month", "day", do + // i. Perform ? CreateDataPropertyOrThrow(options, prop, "numeric"). + if (needDefaults && (defaults == DefaultsOption::DATE || defaults == DefaultsOption::ALL)) { + JSHandle array = factory->NewTaggedArray(CAPACITY_3); + array->Set(thread, 0, globalConst->GetHandledYearString()); + array->Set(thread, 1, globalConst->GetHandledMonthString()); + array->Set(thread, 2, globalConst->GetHandledDayString()); // 2 means the third slot + JSMutableHandle key(thread, JSTaggedValue::Undefined()); + uint32_t len = array->GetLength(); + for (uint32_t i = 0; i < len; i++) { + key.Update(array->Get(thread, i)); + JSObject::CreateDataPropertyOrThrow(thread, optionsResult, key, globalConst->GetHandledNumericString()); + RETURN_HANDLE_IF_ABRUPT_COMPLETION(JSObject, thread); + } + } + + // 7. If needDefaults is true and defaults is either "time" or "all", then + // a. For each of the property names "hour", "minute", "second", do + // i. Perform ? CreateDataPropertyOrThrow(options, prop, "numeric"). + if (needDefaults && (defaults == DefaultsOption::TIME || defaults == DefaultsOption::ALL)) { + JSHandle array = factory->NewTaggedArray(CAPACITY_3); + array->Set(thread, 0, globalConst->GetHandledHourString()); + array->Set(thread, 1, globalConst->GetHandledMinuteString()); + array->Set(thread, 2, globalConst->GetHandledSecondString()); // 2 means the third slot + JSMutableHandle key(thread, JSTaggedValue::Undefined()); + uint32_t len = array->GetLength(); + for (uint32_t i = 0; i < len; i++) { + key.Update(array->Get(thread, i)); + JSObject::CreateDataPropertyOrThrow(thread, optionsResult, key, globalConst->GetHandledNumericString()); + RETURN_HANDLE_IF_ABRUPT_COMPLETION(JSObject, thread); + } + } + + // 8. Return options. + return optionsResult; +} + +// 13.1.7 FormatDateTime(dateTimeFormat, x) +JSHandle JSDateTimeFormat::FormatDateTime(JSThread *thread, + const JSHandle &dateTimeFormat, double x) +{ + icu::SimpleDateFormat *simpleDateFormat = dateTimeFormat->GetIcuSimpleDateFormat(); + // 1. Let parts be ? PartitionDateTimePattern(dateTimeFormat, x). + double xValue = JSDate::TimeClip(x); + if (std::isnan(xValue)) { + THROW_RANGE_ERROR_AND_RETURN(thread, "Invalid time value", thread->GetEcmaVM()->GetFactory()->GetEmptyString()); + } + + // 2. Let result be the empty String. + icu::UnicodeString result; + + // 3. Set result to the string-concatenation of result and part.[[Value]]. + simpleDateFormat->format(xValue, result); + + // 4. Return result. + return JSLocale::IcuToString(thread, result); +} + +// 13.1.8 FormatDateTimeToParts (dateTimeFormat, x) +JSHandle JSDateTimeFormat::FormatDateTimeToParts(JSThread *thread, + const JSHandle &dateTimeFormat, double x) +{ + icu::SimpleDateFormat *simpleDateFormat = dateTimeFormat->GetIcuSimpleDateFormat(); + ASSERT(simpleDateFormat != nullptr); + UErrorCode status = U_ZERO_ERROR; + icu::FieldPositionIterator fieldPositionIter; + icu::UnicodeString formattedParts; + simpleDateFormat->format(x, formattedParts, &fieldPositionIter, status); + if (U_FAILURE(status) != 0) { + THROW_TYPE_ERROR_AND_RETURN(thread, "format failed", thread->GetEcmaVM()->GetFactory()->NewJSArray()); + } + + // 2. Let result be ArrayCreate(0). + JSHandle result(JSArray::ArrayCreate(thread, JSTaggedNumber(0))); + if (formattedParts.isBogus() != 0) { + return result; + } + + // 3. Let n be 0. + int32_t index = 0; + int32_t preEdgePos = 0; + std::vector parts; + icu::FieldPosition fieldPosition; + while (fieldPositionIter.next(fieldPosition) != 0) { + int32_t fField = fieldPosition.getField(); + int32_t fBeginIndex = fieldPosition.getBeginIndex(); + int32_t fEndIndex = fieldPosition.getEndIndex(); + if (preEdgePos < fBeginIndex) { + parts.emplace_back(CommonDateFormatPart(fField, preEdgePos, fBeginIndex, index, true)); + ++index; + } + parts.emplace_back(CommonDateFormatPart(fField, fBeginIndex, fEndIndex, index, false)); + preEdgePos = fEndIndex; + ++index; + } + int32_t length = formattedParts.length(); + if (preEdgePos < length) { + parts.emplace_back(CommonDateFormatPart(-1, preEdgePos, length, index, true)); + } + JSMutableHandle substring(thread, JSTaggedValue::Undefined()); + + // 4. For each part in parts, do + for (auto part : parts) { + substring.Update( + JSLocale::IcuToString(thread, formattedParts, part.fBeginIndex, part.fEndIndex).GetTaggedValue()); + RETURN_HANDLE_IF_ABRUPT_COMPLETION(JSArray, thread); + // Let O be ObjectCreate(%ObjectPrototype%). + // Perform ! CreateDataPropertyOrThrow(O, "type", part.[[Type]]). + // Perform ! CreateDataPropertyOrThrow(O, "value", part.[[Value]]). + // Perform ! CreateDataProperty(result, ! ToString(n), O). + if (part.isPreExist) { + JSLocale::PutElement(thread, part.index, result, ConvertFieldIdToDateType(thread, -1), + JSHandle::Cast(substring)); + } else { + JSLocale::PutElement(thread, part.index, result, ConvertFieldIdToDateType(thread, part.fField), + JSHandle::Cast(substring)); + } + } + + // 5. Return result. + return result; +} + +// 13.1.10 UnwrapDateTimeFormat(dtf) +JSHandle JSDateTimeFormat::UnwrapDateTimeFormat(JSThread *thread, + const JSHandle &dateTimeFormat) +{ + // 1. Assert: Type(dtf) is Object. + ASSERT_PRINT(dateTimeFormat->IsJSObject(), "dateTimeFormat is not object"); + + // 2. If dateTimeFormat does not have an [[InitializedDateTimeFormat]] internal slot + // and ? InstanceofOperator(dateTimeFormat, %DateTimeFormat%) is true, then + // a. Let dateTimeFormat be ? Get(dateTimeFormat, %Intl%.[[FallbackSymbol]]). + JSHandle env = thread->GetEcmaVM()->GetGlobalEnv(); + bool isInstanceOf = JSFunction::InstanceOf(thread, dateTimeFormat, env->GetDateTimeFormatFunction()); + RETURN_VALUE_IF_ABRUPT_COMPLETION(thread, dateTimeFormat); + if (!dateTimeFormat->IsJSDateTimeFormat() && isInstanceOf) { + JSHandle key(thread, JSHandle::Cast(env->GetIntlFunction())->GetFallbackSymbol()); + OperationResult operationResult = JSTaggedValue::GetProperty(thread, dateTimeFormat, key); + RETURN_VALUE_IF_ABRUPT_COMPLETION(thread, dateTimeFormat); + return operationResult.GetValue(); + } + + // 3. Perform ? RequireInternalSlot(dateTimeFormat, [[InitializedDateTimeFormat]]). + if (!dateTimeFormat->IsJSDateTimeFormat()) { + THROW_TYPE_ERROR_AND_RETURN(thread, "is not JSDateTimeFormat", + JSHandle(thread, JSTaggedValue::Exception())); + } + + // 4. Return dateTimeFormat. + return dateTimeFormat; +} + +JSHandle ToHourCycleEcmaString(JSThread *thread, int32_t hc) +{ + JSMutableHandle result(thread, JSTaggedValue::Undefined()); + auto globalConst = thread->GlobalConstants(); + switch (hc) { + case static_cast(HourCycleOption::H11): + result.Update(globalConst->GetHandledH11String().GetTaggedValue()); + break; + case static_cast(HourCycleOption::H12): + result.Update(globalConst->GetHandledH12String().GetTaggedValue()); + break; + case static_cast(HourCycleOption::H23): + result.Update(globalConst->GetHandledH23String().GetTaggedValue()); + break; + case static_cast(HourCycleOption::H24): + result.Update(globalConst->GetHandledH24String().GetTaggedValue()); + break; + default: + UNREACHABLE(); + } + return result; +} + +JSHandle ToDateTimeStyleEcmaString(JSThread *thread, int32_t style) +{ + JSMutableHandle result(thread, JSTaggedValue::Undefined()); + auto globalConst = thread->GlobalConstants(); + switch (style) { + case static_cast(DateTimeStyleOption::FULL): + result.Update(globalConst->GetHandledFullString().GetTaggedValue()); + break; + case static_cast(DateTimeStyleOption::LONG): + result.Update(globalConst->GetHandledLongString().GetTaggedValue()); + break; + case static_cast(DateTimeStyleOption::MEDIUM): + result.Update(globalConst->GetHandledMediumString().GetTaggedValue()); + break; + case static_cast(DateTimeStyleOption::SHORT): + result.Update(globalConst->GetHandledShortString().GetTaggedValue()); + break; + default: + UNREACHABLE(); + } + return result; +} + +// 13.4.5 Intl.DateTimeFormat.prototype.resolvedOptions () +void JSDateTimeFormat::ResolvedOptions(JSThread *thread, const JSHandle &dateTimeFormat, + const JSHandle &options) +{ // Table 8: Resolved Options of DateTimeFormat Instances + // Internal Slot Property + // [[Locale]] "locale" + // [[Calendar]] "calendar" + // [[NumberingSystem]] "numberingSystem" + // [[TimeZone]] "timeZone" + // [[HourCycle]] "hourCycle" + // "hour12" + // [[Weekday]] "weekday" + // [[Era]] "era" + // [[Year]] "year" + // [[Month]] "month" + // [[Day]] "day" + // [[Hour]] "hour" + // [[Minute]] "minute" + // [[Second]] "second" + // [[TimeZoneName]] "timeZoneName" + auto globalConst = thread->GlobalConstants(); + ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); + + // 5. For each row of Table 8, except the header row, in table order, do + // Let p be the Property value of the current row. + // [[Locale]] + JSHandle locale(thread, dateTimeFormat->GetLocale()); + JSHandle property = globalConst->GetHandledLocaleString(); + JSObject::CreateDataPropertyOrThrow(thread, options, property, locale); + // [[Calendar]] + JSMutableHandle calendarValue(thread, dateTimeFormat->GetCalendar()); + icu::SimpleDateFormat *icuSimpleDateFormat = dateTimeFormat->GetIcuSimpleDateFormat(); + const icu::Calendar *calendar = icuSimpleDateFormat->getCalendar(); + std::string icuCalendar = calendar->getType(); + if (icuCalendar == "gregorian") { + if (dateTimeFormat->GetIso8601() == JSTaggedValue::True()) { + calendarValue.Update(globalConst->GetHandledIso8601String().GetTaggedValue()); + } else { + calendarValue.Update(globalConst->GetHandledGregoryString().GetTaggedValue()); + } + } else if (icuCalendar == "ethiopic-amete-alem") { + calendarValue.Update(globalConst->GetHandledEthioaaString().GetTaggedValue()); + } + property = globalConst->GetHandledCalendarString(); + JSObject::CreateDataPropertyOrThrow(thread, options, property, calendarValue); + // [[NumberingSystem]] + JSHandle numberingSystem(thread, dateTimeFormat->GetNumberingSystem()); + if (numberingSystem->IsUndefined()) { + numberingSystem = globalConst->GetHandledLatnString(); + } + property = globalConst->GetHandledNumberingSystemString(); + JSObject::CreateDataPropertyOrThrow(thread, options, property, numberingSystem); + // [[TimeZone]] + JSMutableHandle timezoneValue(thread, dateTimeFormat->GetTimeZone()); + const icu::TimeZone &icuTZ = calendar->getTimeZone(); + icu::UnicodeString timezone; + icuTZ.getID(timezone); + UErrorCode status = U_ZERO_ERROR; + icu::UnicodeString canonicalTimezone; + icu::TimeZone::getCanonicalID(timezone, canonicalTimezone, status); + if (U_SUCCESS(status) != 0) { + if ((canonicalTimezone == UNICODE_STRING_SIMPLE("Etc/UTC")) != 0 || + (canonicalTimezone == UNICODE_STRING_SIMPLE("Etc/GMT")) != 0) { + timezoneValue.Update(globalConst->GetUTCString()); + } else { + timezoneValue.Update(JSLocale::IcuToString(thread, canonicalTimezone).GetTaggedValue()); + } + } + property = globalConst->GetHandledTimeZoneString(); + JSObject::CreateDataPropertyOrThrow(thread, options, property, timezoneValue); + // [[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". + // i. Let hc be dtf.[[HourCycle]]. + JSHandle hcValue; + HourCycleOption hc = static_cast(dateTimeFormat->GetHourCycle().GetInt()); + if (hc != HourCycleOption::UNDEFINED) { + property = globalConst->GetHandledHourCycleString(); + hcValue = ToHourCycleEcmaString(thread, dateTimeFormat->GetHourCycle().GetInt()); + JSObject::CreateDataPropertyOrThrow(thread, options, property, hcValue); + if (hc == HourCycleOption::H11 || hc == HourCycleOption::H12) { + JSHandle trueValue(thread, JSTaggedValue::True()); + hcValue = trueValue; + } else if (hc == HourCycleOption::H23 || hc == HourCycleOption::H24) { + JSHandle falseValue(thread, JSTaggedValue::False()); + hcValue = falseValue; + } + property = globalConst->GetHandledHour12String(); + JSObject::CreateDataPropertyOrThrow(thread, options, property, hcValue); + } + // [[DateStyle]], [[TimeStyle]]. + icu::UnicodeString patternUnicode; + icuSimpleDateFormat->toPattern(patternUnicode); + std::string pattern; + patternUnicode.toUTF8String(pattern); + if (dateTimeFormat->GetDateStyle() == JSTaggedValue(static_cast(DateTimeStyleOption::UNDEFINED)) && + dateTimeFormat->GetTimeStyle() == JSTaggedValue(static_cast(DateTimeStyleOption::UNDEFINED))) { + for (const auto &item : BuildIcuPatternDescs()) { + // fractionalSecondsDigits need to be added before timeZoneName. + if (item.property == "timeZoneName") { + int tmpResult = count(pattern.begin(), pattern.end(), 'S'); + int fsd = (tmpResult >= STRING_LENGTH_3) ? STRING_LENGTH_3 : tmpResult; + if (fsd > 0) { + JSHandle fsdValue(thread, JSTaggedValue(fsd)); + property = globalConst->GetHandledFractionalSecondDigitsString(); + JSObject::CreateDataPropertyOrThrow(thread, options, property, fsdValue); + } + } + property = JSHandle::Cast(factory->NewFromStdString(item.property)); + for (const auto &pair : item.pairs) { + if (pattern.find(pair.first) != std::string::npos) { + hcValue = JSHandle::Cast(factory->NewFromStdString(pair.second)); + JSObject::CreateDataPropertyOrThrow(thread, options, property, hcValue); + break; + } + } + } + } + if (dateTimeFormat->GetDateStyle() != JSTaggedValue(static_cast(DateTimeStyleOption::UNDEFINED))) { + property = globalConst->GetHandledDateStyleString(); + hcValue = ToDateTimeStyleEcmaString(thread, dateTimeFormat->GetDateStyle().GetInt()); + JSObject::CreateDataPropertyOrThrow(thread, options, property, hcValue); + } + if (dateTimeFormat->GetTimeStyle() != JSTaggedValue(static_cast(DateTimeStyleOption::UNDEFINED))) { + property = globalConst->GetHandledTimeStyleString(); + hcValue = ToDateTimeStyleEcmaString(thread, dateTimeFormat->GetTimeStyle().GetInt()); + JSObject::CreateDataPropertyOrThrow(thread, options, property, hcValue); + } +} + +// Use dateInterval(x, y) construct datetimeformatrange +icu::FormattedDateInterval JSDateTimeFormat::ConstructDTFRange(JSThread *thread, const JSHandle &dtf, + double x, double y) +{ + std::unique_ptr dateIntervalFormat(ConstructDateIntervalFormat(dtf)); + if (dateIntervalFormat == nullptr) { + icu::FormattedDateInterval emptyValue; + THROW_TYPE_ERROR_AND_RETURN(thread, "create dateIntervalFormat failed", emptyValue); + } + UErrorCode status = U_ZERO_ERROR; + icu::DateInterval dateInterval(x, y); + icu::FormattedDateInterval formatted = dateIntervalFormat->formatToValue(dateInterval, status); + return formatted; +} + +JSHandle JSDateTimeFormat::NormDateTimeRange(JSThread *thread, const JSHandle &dtf, + double x, double y) +{ + ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); + JSHandle result = factory->GetEmptyString(); + // 1. Let x be TimeClip(x). + x = JSDate::TimeClip(x); + // 2. If x is NaN, throw a RangeError exception. + if (std::isnan(x)) { + THROW_RANGE_ERROR_AND_RETURN(thread, "x is NaN", result); + } + // 3. Let y be TimeClip(y). + y = JSDate::TimeClip(y); + // 4. If y is NaN, throw a RangeError exception. + if (std::isnan(y)) { + THROW_RANGE_ERROR_AND_RETURN(thread, "y is NaN", result); + } + + icu::FormattedDateInterval formatted = ConstructDTFRange(thread, dtf, x, y); + RETURN_HANDLE_IF_ABRUPT_COMPLETION(EcmaString, thread); + + // Formatted to string. + bool outputRange = false; + UErrorCode status = U_ZERO_ERROR; + icu::UnicodeString formatResult = formatted.toString(status); + if (U_FAILURE(status) != 0) { + THROW_TYPE_ERROR_AND_RETURN(thread, "format to string failed", + thread->GetEcmaVM()->GetFactory()->GetEmptyString()); + } + icu::ConstrainedFieldPosition cfpos; + while (formatted.nextPosition(cfpos, status) != 0) { + if (cfpos.getCategory() == UFIELD_CATEGORY_DATE_INTERVAL_SPAN) { + outputRange = true; + break; + } + } + result = JSLocale::IcuToString(thread, formatResult); + if (!outputRange) { + return FormatDateTime(thread, dtf, x); + } + return result; +} + +JSHandle JSDateTimeFormat::NormDateTimeRangeToParts(JSThread *thread, const JSHandle &dtf, + double x, double y) +{ + JSHandle result(JSArray::ArrayCreate(thread, JSTaggedNumber(0))); + // 1. Let x be TimeClip(x). + x = JSDate::TimeClip(x); + // 2. If x is NaN, throw a RangeError exception. + if (std::isnan(x)) { + THROW_RANGE_ERROR_AND_RETURN(thread, "x is invalid time value", result); + } + // 3. Let y be TimeClip(y). + y = JSDate::TimeClip(y); + // 4. If y is NaN, throw a RangeError exception. + if (std::isnan(y)) { + THROW_RANGE_ERROR_AND_RETURN(thread, "y is invalid time value", result); + } + + icu::FormattedDateInterval formatted = ConstructDTFRange(thread, dtf, x, y); + RETURN_HANDLE_IF_ABRUPT_COMPLETION(JSArray, thread); + return ConstructFDateIntervalToJSArray(thread, formatted); +} + +JSHandle JSDateTimeFormat::GainAvailableLocales(JSThread *thread) +{ + JSHandle env = thread->GetEcmaVM()->GetGlobalEnv(); + JSHandle dateTimeFormatLocales = env->GetDateTimeFormatLocales(); + const char *key = "calendar"; + const char *path = nullptr; + if (dateTimeFormatLocales->IsUndefined()) { + JSHandle availableLocales = JSLocale::GetAvailableLocales(thread, key, path); + env->SetDateTimeFormatLocales(thread, availableLocales); + return availableLocales; + } + return JSHandle::Cast(dateTimeFormatLocales); +} + +JSHandle JSDateTimeFormat::ConstructFDateIntervalToJSArray(JSThread *thread, + const icu::FormattedDateInterval &formatted) +{ + UErrorCode status = U_ZERO_ERROR; + icu::UnicodeString formattedValue = formatted.toTempString(status); + // Let result be ArrayCreate(0). + JSHandle array(JSArray::ArrayCreate(thread, JSTaggedNumber(0))); + // Let index be 0. + int index = 0; + int32_t preEndPos = 0; + std::array begin {}; + std::array end {}; + begin[0] = begin[1] = end[0] = end[1] = 0; + std::vector parts; + + /** + * From ICU header file document @unumberformatter.h + * Sets a constraint on the field category. + * + * When this instance of ConstrainedFieldPosition is passed to FormattedValue#nextPosition, + * positions are skipped unless they have the given category. + * + * Any previously set constraints are cleared. + * + * For example, to loop over only the number-related fields: + * + * ConstrainedFieldPosition cfpo; + * cfpo.constrainCategory(UFIELDCATEGORY_NUMBER_FORMAT); + * while (fmtval.nextPosition(cfpo, status)) { + * // handle the number-related field position + * } + */ + JSMutableHandle substring(thread, JSTaggedValue::Undefined()); + icu::ConstrainedFieldPosition cfpos; + while (formatted.nextPosition(cfpos, status) != 0) { + int32_t fCategory = cfpos.getCategory(); + int32_t fField = cfpos.getField(); + int32_t fStart = cfpos.getStart(); + int32_t fLimit = cfpos.getLimit(); + + // 2 means the number of elements in category + if (fCategory == UFIELD_CATEGORY_DATE_INTERVAL_SPAN && (fField == 0 || fField == 1)) { + begin[fField] = fStart; + end[fField] = fLimit; + } + if (fCategory == UFIELD_CATEGORY_DATE) { + if (preEndPos < fStart) { + parts.emplace_back(CommonDateFormatPart(fField, preEndPos, fStart, index, true)); + index++; + } + parts.emplace_back(CommonDateFormatPart(fField, fStart, fLimit, index, false)); + preEndPos = fLimit; + ++index; + } + } + if (U_FAILURE(status) != 0) { + THROW_TYPE_ERROR_AND_RETURN(thread, "format date interval error", array); + } + int32_t length = formattedValue.length(); + if (length > preEndPos) { + parts.emplace_back(CommonDateFormatPart(-1, preEndPos, length, index, true)); + } + for (auto part : parts) { + substring.Update( + JSLocale::IcuToString(thread, formattedValue, part.fBeginIndex, part.fEndIndex).GetTaggedValue()); + RETURN_HANDLE_IF_ABRUPT_COMPLETION(JSArray, thread); + JSHandle element; + if (part.isPreExist) { + element = JSLocale::PutElement(thread, part.index, array, ConvertFieldIdToDateType(thread, -1), + JSHandle::Cast(substring)); + } else { + element = JSLocale::PutElement(thread, part.index, array, ConvertFieldIdToDateType(thread, part.fField), + JSHandle::Cast(substring)); + } + JSHandle value = JSHandle::Cast( + ToValueString(thread, TrackValue(part.fBeginIndex, part.fEndIndex, begin, end))); + JSObject::SetProperty(thread, element, thread->GlobalConstants()->GetHandledSourceString(), value, true); + } + return array; +} + +Value JSDateTimeFormat::TrackValue(int32_t beginning, int32_t ending, std::array begin, + std::array end) +{ + Value value = Value::SHARED; + if ((begin[0] <= beginning) && (beginning <= end[0]) && (begin[0] <= ending) && (ending <= end[0])) { + value = Value::START_RANGE; + } else if ((begin[1] <= beginning) && (beginning <= end[1]) && (begin[1] <= ending) && (ending <= end[1])) { + value = Value::END_RANGE; + } + return value; +} + +std::vector BuildIcuPatternDescs() +{ + std::vector items = {IcuPatternDesc("weekday", ICU_WEEKDAY_PE, ICU_NARROW_LONG_SHORT), + IcuPatternDesc("era", ICU_ERA_PE, ICU_NARROW_LONG_SHORT), + IcuPatternDesc("year", ICU_YEAR_PE, ICU2_DIGIT_NUMERIC), + IcuPatternDesc("month", ICU_MONTH_PE, ICU_NARROW_LONG_SHORT2_DIGIT_NUMERIC), + IcuPatternDesc("day", ICU_DAY_PE, ICU2_DIGIT_NUMERIC), + IcuPatternDesc("dayPeriod", ICU_DAY_PERIOD_PE, ICU_NARROW_LONG_SHORT), + IcuPatternDesc("hour", ICU_HOUR_PE, ICU2_DIGIT_NUMERIC), + IcuPatternDesc("minute", ICU_MINUTE_PE, ICU2_DIGIT_NUMERIC), + IcuPatternDesc("second", ICU_SECOND_PE, ICU2_DIGIT_NUMERIC), + IcuPatternDesc("timeZoneName", ICU_YIME_ZONE_NAME_PE, ICU_LONG_SHORT)}; + return items; +} + +std::vector InitializePattern(const IcuPatternDesc &hourData) +{ + std::vector result; + std::vector items = BuildIcuPatternDescs(); + auto item = items.begin(); + while (static_cast(item != items.end()) != 0) { + if (item->property != "hour") { + result.emplace_back(IcuPatternDesc(item->property, item->pairs, item->allowedValues)); + } else { + result.emplace_back(hourData); + } + item++; + } + return result; +} + +std::vector JSDateTimeFormat::GetIcuPatternDesc(const HourCycleOption &hourCycle) +{ + if (hourCycle == HourCycleOption::H11) { + Pattern h11("KK", "K"); + return h11.Get(); + } + if (hourCycle == HourCycleOption::H12) { + Pattern h12("hh", "h"); + return h12.Get(); + } + if (hourCycle == HourCycleOption::H23) { + Pattern h23("HH", "H"); + return h23.Get(); + } + if (hourCycle == HourCycleOption::H24) { + Pattern h24("kk", "k"); + return h24.Get(); + } + if (hourCycle == HourCycleOption::UNDEFINED) { + Pattern pattern("jj", "j"); + return pattern.Get(); + } + UNREACHABLE(); +} + +icu::UnicodeString JSDateTimeFormat::ChangeHourCyclePattern(const icu::UnicodeString &pattern, HourCycleOption hc) +{ + if (hc == HourCycleOption::UNDEFINED || hc == HourCycleOption::EXCEPTION) { + return pattern; + } + icu::UnicodeString result; + char16_t key = u'\0'; + auto mapIter = + std::find_if(HOUR_CYCLE_MAP.begin(), HOUR_CYCLE_MAP.end(), + [hc](const std::map::value_type item) { return item.second == hc; }); + if (mapIter != HOUR_CYCLE_MAP.end()) { + key = mapIter->first; + } + bool needChange = true; + char16_t last = u'\0'; + for (int32_t i = 0; i < pattern.length(); i++) { + char16_t ch = pattern.charAt(i); + if (ch == '\'') { + needChange = !needChange; + result.append(ch); + } else if (HOUR_CYCLE_MAP.find(ch) != HOUR_CYCLE_MAP.end()) { + result = (needChange && last == u'd') ? result.append(' ') : result; + result.append(needChange ? key : ch); + } else { + result.append(ch); + } + last = ch; + } + return result; +} + +std::unique_ptr JSDateTimeFormat::CreateICUSimpleDateFormat(const icu::Locale &icuLocale, + const icu::UnicodeString &skeleton, + icu::DateTimePatternGenerator *gn, + HourCycleOption hc) +{ + // See https://github.com/tc39/ecma402/issues/225 + UErrorCode status = U_ZERO_ERROR; + icu::UnicodeString pattern = + ChangeHourCyclePattern(gn->getBestPattern(skeleton, UDATPG_MATCH_HOUR_FIELD_LENGTH, status), hc); + ASSERT_PRINT((U_SUCCESS(status) != 0), "get best pattern failed"); + + status = U_ZERO_ERROR; + auto dateFormat(std::make_unique(pattern, icuLocale, status)); + if (U_FAILURE(status) != 0) { + return std::unique_ptr(); + } + ASSERT_PRINT(dateFormat != nullptr, "dateFormat failed"); + return dateFormat; +} + +std::unique_ptr JSDateTimeFormat::BuildCalendar(const icu::Locale &locale, const icu::TimeZone &timeZone) +{ + UErrorCode status = U_ZERO_ERROR; + std::unique_ptr calendar(icu::Calendar::createInstance(timeZone, locale, status)); + ASSERT_PRINT(U_SUCCESS(status), "buildCalendar failed"); + ASSERT_PRINT(calendar.get() != nullptr, "calender is nullptr"); + + /** + * Return the class ID for this class. + * + * This is useful only for comparing to a return value from getDynamicClassID(). For example: + * + * Base* polymorphic_pointer = createPolymorphicObject(); + * if (polymorphic_pointer->getDynamicClassID() == + * Derived::getStaticClassID()) ... + */ + if (calendar->getDynamicClassID() == icu::GregorianCalendar::getStaticClassID()) { + auto gregorianCalendar = static_cast(calendar.get()); + // ECMAScript start time, value = -(2**53) + const double beginTime = -9007199254740992; + gregorianCalendar->setGregorianChange(beginTime, status); + ASSERT(U_SUCCESS(status)); + } + return calendar; +} + +std::unique_ptr JSDateTimeFormat::ConstructTimeZone(const std::string &timezone) +{ + if (timezone.empty()) { + return std::unique_ptr(); + } + std::string canonicalized = ConstructFormattedTimeZoneID(timezone); + + std::unique_ptr tz(icu::TimeZone::createTimeZone(canonicalized.c_str())); + if (!JSLocale::IsValidTimeZoneName(*tz)) { + return std::unique_ptr(); + } + return tz; +} + +std::map JSDateTimeFormat::GetSpecialTimeZoneMap() +{ + std::vector specicalTimeZones = { + "America/Argentina/ComodRivadavia" + "America/Knox_IN" + "Antarctica/McMurdo" + "Australia/ACT" + "Australia/LHI" + "Australia/NSW" + "Antarctica/DumontDUrville" + "Brazil/DeNoronha" + "CET" + "CST6CDT" + "Chile/EasterIsland" + "EET" + "EST" + "EST5EDT" + "GB" + "GB-Eire" + "HST" + "MET" + "MST" + "MST7MDT" + "Mexico/BajaNorte" + "Mexico/BajaSur" + "NZ" + "NZ-CHAT" + "PRC" + "PST8PDT" + "ROC" + "ROK" + "UCT" + "W-SU" + "WET"}; + std::map map; + for (const auto &item : specicalTimeZones) { + std::string upper(item); + transform(upper.begin(), upper.end(), upper.begin(), toupper); + map.insert({upper, item}); + } + return map; +} + +std::string JSDateTimeFormat::ConstructFormattedTimeZoneID(const std::string &input) +{ + std::string result = input; + transform(result.begin(), result.end(), result.begin(), toupper); + static const std::vector tzStyleEntry = {"GMT", "ETC/UTC", "ETC/UCT", "GMT0", + "ETC/GMT", "GMT+0", "GMT-0"}; + // NOLINTNEXTLINE(abseil-string-find-startswith) + if (result.find("SYSTEMV/") == 0) { + result.replace(0, STRING_LENGTH_8, "SystemV/"); + } // NOLINTNEXTLINE(abseil-string-find-startswith) + else if (result.find("US/") == 0) { + result = (result.length() == STRING_LENGTH_3) ? result : "US/" + ToTitleCaseTimezonePosition(input.substr(3)); + } // NOLINTNEXTLINE(abseil-string-find-startswith) + else if (result.find("ETC/GMT") == 0 && result.length() > STRING_LENGTH_7) { + result = ConstructGMTTimeZoneID(input); + } else if (count(tzStyleEntry.begin(), tzStyleEntry.end(), result) != 0) { + result = "UTC"; + } + return result; +} + +std::string JSDateTimeFormat::ToTitleCaseFunction(const std::string &input) +{ + std::string result(input); + transform(result.begin(), result.end(), result.begin(), tolower); + result[0] = toupper(result[0]); + return result; +} + +bool JSDateTimeFormat::IsValidTimeZoneInput(const std::string &input) +{ + std::regex r("[a-zA-Z_-/]*"); + bool isValid = regex_match(input, r); + return isValid; +} + +std::string JSDateTimeFormat::ToTitleCaseTimezonePosition(const std::string &input) +{ + if (!IsValidTimeZoneInput(input)) { + return std::string(); + } + std::vector titleEntry; + std::vector charEntry; + int32_t leftPosition = 0; + int32_t titleLength = 0; + for (size_t i = 0; i < input.length(); i++) { + if (input[i] == '_' || input[i] == '-' || input[i] == '/') { + std::string s(1, input[i]); + charEntry.emplace_back(s); + titleLength = i - leftPosition; + titleEntry.emplace_back(input.substr(leftPosition, titleLength)); + leftPosition = i + 1; + } else { + continue; + } + } + std::string result; + for (size_t i = 0; i < titleEntry.size() - 1; i++) { + std::string titleValue = ToTitleCaseFunction(titleEntry[i]); + if (titleValue == "Of" || titleValue == "Es" || titleValue == "Au") { + titleValue[0] = tolower(titleValue[0]); + } + result += titleValue + charEntry[i]; + } + result = result + ToTitleCaseFunction(titleEntry[titleEntry.size() - 1]); + return result; +} + +std::string JSDateTimeFormat::ConstructGMTTimeZoneID(const std::string &input) +{ + if (input.length() < STRING_LENGTH_8 || input.length() > STRING_LENGTH_10) { + return ""; + } + std::string ret = "Etc/GMT"; + if (regex_match(input.substr(7), std::regex("[+-][1][0-4]")) || + (regex_match(input.substr(7), std::regex("[+-][0-9]")) || input.substr(7) == "0")) { + return ret + input.substr(7); + } + return ""; +} + +std::string JSDateTimeFormat::ToHourCycleString(int32_t hc) +{ + auto mapIter = std::find_if(TO_HOUR_CYCLE_MAP.begin(), TO_HOUR_CYCLE_MAP.end(), + [hc](const std::map::value_type &item) { + return static_cast(item.second) == hc; + }); + if (mapIter != TO_HOUR_CYCLE_MAP.end()) { + return mapIter->first; + } + return ""; +} + +HourCycleOption JSDateTimeFormat::OptionToHourCycle(const std::string &hc) +{ + auto iter = TO_HOUR_CYCLE_MAP.find(hc); + if (iter != TO_HOUR_CYCLE_MAP.end()) { + return iter->second; + } + return HourCycleOption::UNDEFINED; +} + +HourCycleOption JSDateTimeFormat::OptionToHourCycle(UDateFormatHourCycle hc) +{ + HourCycleOption hcOption = HourCycleOption::UNDEFINED; + switch (hc) { + case UDAT_HOUR_CYCLE_11: + hcOption = HourCycleOption::H11; + break; + case UDAT_HOUR_CYCLE_12: + hcOption = HourCycleOption::H12; + break; + case UDAT_HOUR_CYCLE_23: + hcOption = HourCycleOption::H23; + break; + case UDAT_HOUR_CYCLE_24: + hcOption = HourCycleOption::H24; + break; + default: + UNREACHABLE(); + } + return hcOption; +} + +JSHandle JSDateTimeFormat::ConvertFieldIdToDateType(JSThread *thread, int32_t fieldId) +{ + JSMutableHandle result(thread, JSTaggedValue::Undefined()); + auto globalConst = thread->GlobalConstants(); + if (fieldId == -1) { + result.Update(globalConst->GetHandledLiteralString().GetTaggedValue()); + } else if (fieldId == UDAT_YEAR_FIELD || fieldId == UDAT_EXTENDED_YEAR_FIELD) { + result.Update(globalConst->GetHandledYearString().GetTaggedValue()); + } else if (fieldId == UDAT_YEAR_NAME_FIELD) { + result.Update(globalConst->GetHandledYearNameString().GetTaggedValue()); + } else if (fieldId == UDAT_MONTH_FIELD || fieldId == UDAT_STANDALONE_MONTH_FIELD) { + result.Update(globalConst->GetHandledMonthString().GetTaggedValue()); + } else if (fieldId == UDAT_DATE_FIELD) { + result.Update(globalConst->GetHandledDayString().GetTaggedValue()); + } else if (fieldId == UDAT_HOUR_OF_DAY1_FIELD || fieldId == UDAT_HOUR_OF_DAY0_FIELD || + fieldId == UDAT_HOUR1_FIELD || fieldId == UDAT_HOUR0_FIELD) { + result.Update(globalConst->GetHandledHourString().GetTaggedValue()); + } else if (fieldId == UDAT_MINUTE_FIELD) { + result.Update(globalConst->GetHandledMinuteString().GetTaggedValue()); + } else if (fieldId == UDAT_SECOND_FIELD) { + result.Update(globalConst->GetHandledSecondString().GetTaggedValue()); + } else if (fieldId == UDAT_DAY_OF_WEEK_FIELD || fieldId == UDAT_DOW_LOCAL_FIELD || + fieldId == UDAT_STANDALONE_DAY_FIELD) { + result.Update(globalConst->GetHandledWeekdayString().GetTaggedValue()); + } else if (fieldId == UDAT_AM_PM_FIELD || fieldId == UDAT_AM_PM_MIDNIGHT_NOON_FIELD || + fieldId == UDAT_FLEXIBLE_DAY_PERIOD_FIELD) { + result.Update(globalConst->GetHandledDayPeriodString().GetTaggedValue()); + } else if (fieldId == UDAT_TIMEZONE_FIELD || fieldId == UDAT_TIMEZONE_RFC_FIELD || + fieldId == UDAT_TIMEZONE_GENERIC_FIELD || fieldId == UDAT_TIMEZONE_SPECIAL_FIELD || + fieldId == UDAT_TIMEZONE_LOCALIZED_GMT_OFFSET_FIELD || fieldId == UDAT_TIMEZONE_ISO_FIELD || + fieldId == UDAT_TIMEZONE_ISO_LOCAL_FIELD) { + result.Update(globalConst->GetHandledTimeZoneNameString().GetTaggedValue()); + } else if (fieldId == UDAT_ERA_FIELD) { + result.Update(globalConst->GetHandledEraString().GetTaggedValue()); + } else if (fieldId == UDAT_FRACTIONAL_SECOND_FIELD) { + result.Update(globalConst->GetHandledFractionalSecondString().GetTaggedValue()); + } else if (fieldId == UDAT_RELATED_YEAR_FIELD) { + result.Update(globalConst->GetHandledRelatedYearString().GetTaggedValue()); + } else if (fieldId == UDAT_QUARTER_FIELD || fieldId == UDAT_STANDALONE_QUARTER_FIELD) { + UNREACHABLE(); + } + return result; +} + +std::unique_ptr JSDateTimeFormat::ConstructDateIntervalFormat( + const JSHandle &dtf) +{ + icu::SimpleDateFormat *icuSimpleDateFormat = dtf->GetIcuSimpleDateFormat(); + icu::Locale locale = *(dtf->GetIcuLocale()); + std::string hcString = ToHourCycleString(dtf->GetHourCycle().GetInt()); + UErrorCode status = U_ZERO_ERROR; + // Sets the Unicode value for a Unicode keyword. + if (!hcString.empty()) { + locale.setUnicodeKeywordValue("hc", hcString, status); + } + icu::UnicodeString pattern; + // Return a pattern string describing this date format. + pattern = icuSimpleDateFormat->toPattern(pattern); + // Utility to return a unique skeleton from a given pattern. + icu::UnicodeString skeleton = icu::DateTimePatternGenerator::staticGetSkeleton(pattern, status); + // Construct a DateIntervalFormat from skeleton and a given locale. + std::unique_ptr dateIntervalFormat( + icu::DateIntervalFormat::createInstance(skeleton, locale, status)); + if (U_FAILURE(status) != 0) { + return nullptr; + } + dateIntervalFormat->setTimeZone(icuSimpleDateFormat->getTimeZone()); + return dateIntervalFormat; +} +} // namespace panda::ecmascript diff --git a/runtime/js_date_time_format.h b/runtime/js_date_time_format.h new file mode 100644 index 000000000..556ea859b --- /dev/null +++ b/runtime/js_date_time_format.h @@ -0,0 +1,203 @@ +/* + * Copyright (c) 2021-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_JS_DATE_TIME_FORMAT_H +#define ECMASCRIPT_JS_DATE_TIME_FORMAT_H + +#include "js_locale.h" + +namespace panda::ecmascript { +enum class CalendarOption : uint8_t { UNDEFINED = 0x01 }; +enum class DateTimeStyleOption : uint8_t { FULL = 0x01, LONG, MEDIUM, SHORT, UNDEFINED, EXCEPTION }; +enum class DefaultsOption : uint8_t { DATE = 0x01, TIME, ALL }; +enum class HourCycleOption : uint8_t { H11 = 0x01, H12, H23, H24, UNDEFINED, EXCEPTION }; +enum class RequiredOption : uint8_t { DATE = 0x01, TIME, ANY }; +enum class Value : uint8_t { SHARED, START_RANGE, END_RANGE }; + +constexpr int CAPACITY_3 = 3; +constexpr int CAPACITY_4 = 4; +constexpr int CAPACITY_5 = 5; +constexpr int CAPACITY_8 = 8; +constexpr int STRING_LENGTH_2 = 2; +constexpr int STRING_LENGTH_3 = 3; +constexpr int STRING_LENGTH_7 = 7; +constexpr int STRING_LENGTH_8 = 8; +constexpr int STRING_LENGTH_9 = 9; +constexpr int STRING_LENGTH_10 = 10; + +class IcuPatternDesc; + +std::vector BuildIcuPatternDescs(); +std::vector InitializePattern(const IcuPatternDesc &hourData); + +using IcuPatternDescVect = std::vector; +using IcuPatternEntry = std::pair; + +class IcuPatternDesc { +public: + IcuPatternDesc(std::string propertyParam, std::vector pairsParam, + std::vector allowedValuesParam) + : property(std::move(propertyParam)), pairs(std::move(pairsParam)), allowedValues(std::move(allowedValuesParam)) + { + for (const auto &pair : pairs) { + map.insert(std::make_pair(pair.second, pair.first)); + } + } + + virtual ~IcuPatternDesc() = default; + + std::string property; // NOLINT(misc-non-private-member-variables-in-classes) + std::vector pairs; // NOLINT(misc-non-private-member-variables-in-classes) + std::map map; // NOLINT(misc-non-private-member-variables-in-classes) + std::vector allowedValues; // NOLINT(misc-non-private-member-variables-in-classes) + + DEFAULT_COPY_SEMANTIC(IcuPatternDesc); + // NOLINT(performance-noexcept-move-constructor, hicpp-noexcept-move) + DEFAULT_NOEXCEPT_MOVE_SEMANTIC(IcuPatternDesc); +}; + +class Pattern { +public: + Pattern(const std::string &data1, const std::string &data2) + : data(InitializePattern( + IcuPatternDesc("hour", {{data1, "2-digit"}, {data2, "numeric"}}, {"2-digit", "numeric"}))) + { + } + virtual ~Pattern() = default; + std::vector Get() const + { + return data; + } + +private: + std::vector data {}; + NO_COPY_SEMANTIC(Pattern); + NO_MOVE_SEMANTIC(Pattern); +}; + +class JSDateTimeFormat : public JSObject { +public: + CAST_CHECK(JSDateTimeFormat, IsJSDateTimeFormat); + + static constexpr size_t LOCALE_OFFSET = JSObject::SIZE; + + ACCESSORS(Locale, LOCALE_OFFSET, CALENDAR_OFFSET) + ACCESSORS(Calendar, CALENDAR_OFFSET, NUMBER_STRING_SYSTEM_OFFSET) + ACCESSORS(NumberingSystem, NUMBER_STRING_SYSTEM_OFFSET, TIME_ZONE_OFFSET) + ACCESSORS(TimeZone, TIME_ZONE_OFFSET, HOUR_CYCLE_OFFSET) + ACCESSORS(HourCycle, HOUR_CYCLE_OFFSET, LOCALE_ICU_OFFSET) + ACCESSORS(LocaleIcu, LOCALE_ICU_OFFSET, SIMPLE_DATE_TIME_FORMAT_ICU_OFFSET) + ACCESSORS(SimpleDateTimeFormatIcu, SIMPLE_DATE_TIME_FORMAT_ICU_OFFSET, ISO8601_OFFSET) + ACCESSORS(Iso8601, ISO8601_OFFSET, DATE_STYLE_OFFSET) + ACCESSORS(DateStyle, DATE_STYLE_OFFSET, TIME_STYLE_OFFSET) + ACCESSORS(TimeStyle, TIME_STYLE_OFFSET, BOUND_FORMAT_OFFSET) + ACCESSORS(BoundFormat, BOUND_FORMAT_OFFSET, SIZE) + + DECL_VISIT_OBJECT_FOR_JS_OBJECT(JSObject, LOCALE_OFFSET, SIZE) + DECL_DUMP() + + icu::Locale *GetIcuLocale() const; + static void SetIcuLocale(JSThread *thread, JSHandle obj, const icu::Locale &icuLocale, + const DeleteEntryPoint &callback); + static void FreeIcuLocale(void *pointer, void *data); + + icu::SimpleDateFormat *GetIcuSimpleDateFormat() const; + static void SetIcuSimpleDateFormat(JSThread *thread, JSHandle obj, + const icu::SimpleDateFormat &icuSimpleDateTimeFormat, + const DeleteEntryPoint &callback); + static void FreeSimpleDateFormat(void *pointer, void *data); + + // 13.1.1 InitializeDateTimeFormat (dateTimeFormat, locales, options) + static JSHandle InitializeDateTimeFormat(JSThread *thread, + const JSHandle &dateTimeFormat, + const JSHandle &locales, + const JSHandle &options); + + // 13.1.2 ToDateTimeOptions (options, required, defaults) + static JSHandle ToDateTimeOptions(JSThread *thread, const JSHandle &options, + const RequiredOption &required, const DefaultsOption &defaults); + + // 13.1.7 FormatDateTime(dateTimeFormat, x) + static JSHandle FormatDateTime(JSThread *thread, const JSHandle &dateTimeFormat, + double x); + + // 13.1.8 FormatDateTimeToParts (dateTimeFormat, x) + static JSHandle FormatDateTimeToParts(JSThread *thread, const JSHandle &dateTimeFormat, + double x); + + // 13.1.10 UnwrapDateTimeFormat(dtf) + static JSHandle UnwrapDateTimeFormat(JSThread *thread, + const JSHandle &dateTimeFormat); + + static JSHandle GainAvailableLocales(JSThread *thread); + + static void ResolvedOptions(JSThread *thread, const JSHandle &dateTimeFormat, + const JSHandle &options); + + static JSHandle NormDateTimeRange(JSThread *thread, const JSHandle &dtf, double x, + double y); + + static JSHandle NormDateTimeRangeToParts(JSThread *thread, const JSHandle &dtf, double x, + double y); + +private: + static HourCycleOption OptionToHourCycle(const std::string &hc); + + static Value TrackValue(int32_t beginning, int32_t ending, std::array begin, + std::array end); + + static HourCycleOption OptionToHourCycle(UDateFormatHourCycle hc); + + static std::string ToHourCycleString(int32_t hc); + + static std::unique_ptr ConstructTimeZone(const std::string &timezone); + + static std::string ConstructFormattedTimeZoneID(const std::string &input); + + static std::string ToTitleCaseTimezonePosition(const std::string &input); + + static std::unique_ptr ConstructDateIntervalFormat(const JSHandle &dtf); + + static std::string ConstructGMTTimeZoneID(const std::string &input); + + static std::unique_ptr BuildCalendar(const icu::Locale &locale, const icu::TimeZone &timeZone); + + static std::map GetSpecialTimeZoneMap(); + + static JSHandle ConstructFDateIntervalToJSArray(JSThread *thread, + const icu::FormattedDateInterval &formatted); + + static std::vector GetIcuPatternDesc(const HourCycleOption &hourCycle); + + static std::unique_ptr CreateICUSimpleDateFormat(const icu::Locale &icuLocale, + const icu::UnicodeString &skeleton, + icu::DateTimePatternGenerator *gn, + HourCycleOption hc); + + static JSHandle ConvertFieldIdToDateType(JSThread *thread, int32_t fieldId); + + static icu::UnicodeString ChangeHourCyclePattern(const icu::UnicodeString &pattern, HourCycleOption hc); + + static std::string ToTitleCaseFunction(const std::string &input); + + static bool IsValidTimeZoneInput(const std::string &input); + + static JSHandle ToValueString(JSThread *thread, Value value); + + static icu::FormattedDateInterval ConstructDTFRange(JSThread *thread, const JSHandle &dtf, + double x, double y); +}; +} // namespace panda::ecmascript +#endif // ECMASCRIPT_JS_DATE_TIME_FORMAT_H diff --git a/runtime/js_for_in_iterator.cpp b/runtime/js_for_in_iterator.cpp new file mode 100644 index 000000000..2715b7228 --- /dev/null +++ b/runtime/js_for_in_iterator.cpp @@ -0,0 +1,313 @@ +/* + * Copyright (c) 2021-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 "plugins/ecmascript/runtime/js_for_in_iterator.h" + +#include "plugins/ecmascript/runtime/base/builtins_base.h" +#include "plugins/ecmascript/runtime/global_dictionary-inl.h" +#include "plugins/ecmascript/runtime/global_env.h" +#include "plugins/ecmascript/runtime/js_object-inl.h" +#include "plugins/ecmascript/runtime/js_primitive_ref.h" +#include "plugins/ecmascript/runtime/object_factory.h" +#include "plugins/ecmascript/runtime/tagged_array-inl.h" +#include "plugins/ecmascript/runtime/tagged_dictionary.h" +#include "plugins/ecmascript/runtime/tagged_queue-inl.h" +#include "plugins/ecmascript/runtime/tagged_queue.h" + +namespace panda::ecmascript { +using BuiltinsBase = base::BuiltinsBase; + +bool JSForInIterator::CheckObjProto(const JSThread *thread, const JSHandle &it) +{ + JSHandle env = thread->GetEcmaVM()->GetGlobalEnv(); + JSHandle object(thread, it->GetObject()); + if (!object->IsJSObject()) { + return false; + } + auto *hclass = object->GetTaggedObject()->GetClass(); + JSType jsType = hclass->GetObjectType(); + if (jsType != JSType::JS_OBJECT) { + return false; + } + JSTaggedValue proto = hclass->GetPrototype(); + if (!proto.IsJSObject()) { + return false; + } + return hclass->GetPrototype().GetTaggedObject()->GetClass() == + env->GetObjectFunctionPrototypeClass().GetTaggedValue().GetTaggedObject()->GetClass(); +} + +void JSForInIterator::FastGetAllEnumKeys(const JSThread *thread, const JSHandle &it, + const JSHandle &object) +{ + JSMutableHandle value(thread, JSTaggedValue::Undefined()); + ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); + JSHandle obj(object); + uint32_t numOfElements = obj->GetNumberOfElements(); + uint32_t numOfKeys = obj->GetNumberOfKeys(); + JSHandle remaining = factory->NewTaggedQueue(numOfElements + numOfKeys + 1); + if (numOfElements > 0) { + uint32_t elementIndex = 0; + if (obj->IsJSPrimitiveRef() && JSPrimitiveRef::Cast(*obj)->IsString()) { + elementIndex = JSPrimitiveRef::Cast(*obj)->GetStringLength(); + for (uint32_t i = 0; i < elementIndex; i++) { + value.Update(factory->NewFromCanBeCompressString(ToCString(i)).GetTaggedValue()); + TaggedQueue::PushFixedQueue(thread, remaining, value); + } + } else { + JSHandle elements(thread, obj->GetElements()); + if (!elements->IsDictionaryMode()) { + uint32_t elementsLen = elements->GetLength(); + for (uint32_t i = 0; i < elementsLen; ++i) { + if (!elements->Get(i).IsHole()) { + value.Update(factory->NewFromCanBeCompressString(ToCString(i)).GetTaggedValue()); + TaggedQueue::PushFixedQueue(thread, remaining, value); + } + } + } else { + JSHandle numberDic(elements); + int size = numberDic->Size(); + CVector sortArr; + for (int hashIndex = 0; hashIndex < size; hashIndex++) { + JSTaggedValue key = numberDic->GetKey(hashIndex); + if (!key.IsUndefined() && !key.IsHole()) { + PropertyAttributes attr = numberDic->GetAttributes(hashIndex); + if (attr.IsEnumerable()) { + sortArr.push_back(JSTaggedValue(static_cast(key.GetInt()))); + } + } + } + std::sort(sortArr.begin(), sortArr.end(), NumberDictionary::CompKey); + for (const auto &entry : sortArr) { + value.Update(factory->NewFromCanBeCompressString(ToCString(entry.GetInt())).GetTaggedValue()); + TaggedQueue::PushFixedQueue(thread, remaining, value); + } + } + } + } + if (numOfKeys > 0) { + if (obj->IsJSGlobalObject()) { + GlobalDictionary *dict = GlobalDictionary::Cast(obj->GetProperties().GetTaggedObject()); + int size = dict->Size(); + CVector> sortArr; + for (int hashIndex = 0; hashIndex < size; hashIndex++) { + JSTaggedValue key = dict->GetKey(hashIndex); + if (!key.IsUndefined() && !key.IsHole()) { + PropertyAttributes attr = dict->GetAttributes(hashIndex); + if (attr.IsEnumerable()) { + std::pair pair(key, attr.GetOffset()); + sortArr.emplace_back(pair); + } + } + } + std::sort(sortArr.begin(), sortArr.end(), GlobalDictionary::CompKey); + for (const auto &entry : sortArr) { + JSTaggedValue nameKey = entry.first; + if (nameKey.IsString()) { + value.Update(nameKey); + TaggedQueue::PushFixedQueue(thread, remaining, value); + } + } + } else { + JSHandle propertiesArr(thread, obj->GetProperties()); + if (!propertiesArr->IsDictionaryMode()) { + JSHClass *jsHclass = obj->GetJSHClass(); + JSTaggedValue enumCache = jsHclass->GetEnumCache(); + if (!enumCache.IsNull()) { + JSHandle cache(thread, enumCache); + uint32_t length = cache->GetLength(); + if (length != numOfKeys) { + JSHandle layoutInfoHandle(thread, jsHclass->GetLayout()); + for (uint32_t i = 0; i < numOfKeys; i++) { + JSTaggedValue key = layoutInfoHandle->GetKey(i); + if (key.IsString()) { + value.Update(key); + if (layoutInfoHandle->GetAttr(i).IsEnumerable()) { + TaggedQueue::PushFixedQueue(thread, remaining, value); + } + } + } + } else { + for (uint32_t i = 0; i < length; i++) { + JSTaggedValue key = cache->Get(i); + if (key.IsString()) { + value.Update(key); + TaggedQueue::PushFixedQueue(thread, remaining, value); + } + } + } + } else { + JSHandle layoutInfoHandle(thread, jsHclass->GetLayout()); + for (uint32_t i = 0; i < numOfKeys; i++) { + JSTaggedValue key = layoutInfoHandle->GetKey(i); + if (key.IsString()) { + value.Update(key); + if (layoutInfoHandle->GetAttr(i).IsEnumerable()) { + TaggedQueue::PushFixedQueue(thread, remaining, value); + } + } + } + } + } else { + JSHandle nameDic(propertiesArr); + int size = nameDic->Size(); + CVector> sortArr; + for (int hashIndex = 0; hashIndex < size; hashIndex++) { + JSTaggedValue key = nameDic->GetKey(hashIndex); + if (key.IsString()) { + PropertyAttributes attr = nameDic->GetAttributes(hashIndex); + if (attr.IsEnumerable()) { + std::pair pair(key, attr); + sortArr.emplace_back(pair); + } + } + } + std::sort(sortArr.begin(), sortArr.end(), NameDictionary::CompKey); + for (const auto &entry : sortArr) { + value.Update(entry.first); + TaggedQueue::PushFixedQueue(thread, remaining, value); + } + } + } + } + it->SetRemainingKeys(thread, remaining); + it->SetWasVisited(thread, JSTaggedValue::True()); +} + +static bool IsInVisitedKeys(const JSHandle &key, const JSMutableHandle &visited) +{ + uint32_t len = visited->Size(); + for (uint32_t i = 0; i < len; i++) { + if (JSTaggedValue::SameValue(key.GetTaggedValue(), visited->Get(i))) { + return true; + } + } + + return false; +} + +void JSForInIterator::SlowGetAllEnumKeys(JSThread *thread, const JSHandle &it, + const JSHandle &object) +{ + JSMutableHandle visited(thread, it->GetVisitedKeys()); + JSMutableHandle value(thread, JSTaggedValue::Undefined()); + JSMutableHandle remaining(thread, it->GetRemainingKeys()); + JSHandle arr = JSTaggedValue::GetOwnPropertyKeys(thread, object); + uint32_t len = arr->GetLength(); + for (uint32_t i = 0; i < len; i++) { + value.Update(arr->Get(i)); + PropertyDescriptor desc(thread); + if (value->IsString() && JSTaggedValue::GetOwnProperty(thread, object, value, desc) && + !IsInVisitedKeys(JSHandle(thread, value.GetTaggedValue()), visited)) { + if (!desc.IsEnumerable()) { + TaggedQueue *newVisitedQueue = TaggedQueue::Push(thread, visited, value); + visited.Update(JSTaggedValue(newVisitedQueue)); + continue; + } + + TaggedQueue *newQueue = TaggedQueue::Push(thread, remaining, value); + remaining.Update(JSTaggedValue(newQueue)); + } + } + it->SetRemainingKeys(thread, remaining); + it->SetVisitedKeys(thread, visited); + it->SetWasVisited(thread, JSTaggedValue::True()); +} + +static JSTaggedNumber GetTaggedQueueLengthWithStartOffset(const JSHandle &tq) +{ + if (tq->Empty()) { + return JSTaggedNumber(0); + } + + return JSTaggedNumber(tq->Size()); +} + +std::pair JSForInIterator::NextInternal(JSThread *thread, const JSHandle &it) +{ + bool notModiObjProto = true; + notModiObjProto = CheckObjProto(thread, it); + while (true) { + JSHandle object(thread, it->GetObject()); + if (object->IsNull() || object->IsUndefined()) { + return std::make_pair(JSTaggedValue::Undefined(), true); + } + + JSMutableHandle remaining(thread, it->GetRemainingKeys()); + if (it->GetWasVisited().IsFalse()) { + if (object->IsJSObject() && notModiObjProto) { + FastGetAllEnumKeys(thread, it, object); + } else { + SlowGetAllEnumKeys(thread, it, object); + } + remaining.Update(it->GetRemainingKeys()); + it->SetFastRemainingIndex(GetTaggedQueueLengthWithStartOffset(remaining)); + } + + JSMutableHandle visited(thread, it->GetVisitedKeys()); + + uint32_t fast_index = it->GetFastRemainingIndex().GetInt(); + uint32_t remain_length = GetTaggedQueueLengthWithStartOffset(remaining).GetInt(); + + if (fast_index != remain_length) { + // Restore correct state of FastRemainingIndex. + uint32_t element_count = remain_length - fast_index; + for (uint32_t i = 0; i < element_count; ++i) { + JSHandle key(thread, remaining->Pop(thread)); + auto newQueue = JSTaggedValue(TaggedQueue::Push(thread, visited, key)); + visited.Update(newQueue); + it->SetVisitedKeys(thread, newQueue); + } + it->SetFastRemainingIndex(GetTaggedQueueLengthWithStartOffset(remaining)); + } + + if (!remaining->Empty()) { + JSTaggedValue r = remaining->Pop(thread); + if (object->IsJSObject() && notModiObjProto) { + it->SetFastRemainingIndex(GetTaggedQueueLengthWithStartOffset(remaining)); + return std::make_pair(r, false); + } + JSHandle key(thread, r); + + auto newQueue = JSTaggedValue(TaggedQueue::Push(thread, visited, key)); + it->SetFastRemainingIndex(GetTaggedQueueLengthWithStartOffset(remaining)); + visited.Update(newQueue); + it->SetVisitedKeys(thread, newQueue); + return std::make_pair(key.GetTaggedValue(), false); + } + + if (notModiObjProto) { + return std::make_pair(JSTaggedValue::Undefined(), true); + } + + JSTaggedValue proto = JSHandle::Cast(object)->GetPrototype(thread); + it->SetObject(thread, proto); + it->SetWasVisited(thread, JSTaggedValue::False()); + } +} + +// 13.7.5.16.2.1 %ForInIteratorPrototype%.next ( ) +JSTaggedValue JSForInIterator::Next(EcmaRuntimeCallInfo *msg) +{ + ASSERT(msg); + JSThread *thread = msg->GetThread(); + [[maybe_unused]] EcmaHandleScope handleScope(thread); + // 1. Let O be the this value. + JSHandle it(BuiltinsBase::GetThis(msg)); + ASSERT(it->IsForinIterator()); + std::pair res = NextInternal(thread, it); + return res.first; +} +} // namespace panda::ecmascript diff --git a/runtime/js_for_in_iterator.h b/runtime/js_for_in_iterator.h new file mode 100644 index 000000000..845114566 --- /dev/null +++ b/runtime/js_for_in_iterator.h @@ -0,0 +1,58 @@ +/* + * Copyright (c) 2021-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_JS_FORIN_ITERATOR_H +#define ECMASCRIPT_JS_FORIN_ITERATOR_H + +#include +#include "plugins/ecmascript/runtime/js_tagged_value-inl.h" +#include "plugins/ecmascript/runtime/tagged_array-inl.h" +#include "js_object.h" + +namespace panda::ecmascript { +class JSForInIterator : public JSObject { +public: + CAST_CHECK(JSForInIterator, IsForinIterator); + + static std::pair NextInternal(JSThread *thread, const JSHandle &it); + + static JSTaggedValue Next(EcmaRuntimeCallInfo *msg); + + static bool CheckObjProto(const JSThread *thread, const JSHandle &it); + + static void FastGetAllEnumKeys(const JSThread *thread, const JSHandle &it, + const JSHandle &object); + + static void SlowGetAllEnumKeys(JSThread *thread, const JSHandle &it, + const JSHandle &object); + + static constexpr size_t OBJECT_OFFSET = JSObject::SIZE; + + ACCESSORS(Object, OBJECT_OFFSET, WAS_VISITED_OFFSET) + + ACCESSORS(WasVisited, WAS_VISITED_OFFSET, VISITED_KEYS_OFFSET) + + ACCESSORS(VisitedKeys, VISITED_KEYS_OFFSET, FAST_REMAINING_INDEX_OFFSET) + + ACCESSORS(FastRemainingIndex, FAST_REMAINING_INDEX_OFFSET, REMAINING_KEYS_OFFSET) + + ACCESSORS(RemainingKeys, REMAINING_KEYS_OFFSET, SIZE) + + DECL_VISIT_OBJECT_FOR_JS_OBJECT(JSObject, OBJECT_OFFSET, SIZE) + DECL_DUMP() +}; +} // namespace panda::ecmascript + +#endif // ECMASCRIPT_JS_FORIN_ITERATOR_H diff --git a/runtime/js_function.cpp b/runtime/js_function.cpp new file mode 100644 index 000000000..5090fdb8d --- /dev/null +++ b/runtime/js_function.cpp @@ -0,0 +1,664 @@ +/* + * Copyright (c) 2021-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 "js_function.h" + +#include "plugins/ecmascript/runtime/base/error_type.h" +#include "plugins/ecmascript/runtime/class_info_extractor.h" +#include "plugins/ecmascript/runtime/ecma_macros.h" +#include "plugins/ecmascript/runtime/ecma_runtime_call_info.h" +#include "plugins/ecmascript/runtime/global_env.h" +#include "plugins/ecmascript/runtime/internal_call_params.h" +#include "plugins/ecmascript/runtime/interpreter/interpreter-inl.h" +#include "plugins/ecmascript/runtime/js_handle.h" +#include "plugins/ecmascript/runtime/js_promise.h" +#include "plugins/ecmascript/runtime/js_proxy.h" +#include "plugins/ecmascript/runtime/js_tagged_value-inl.h" +#include "plugins/ecmascript/runtime/mem/c_containers.h" +#include "plugins/ecmascript/runtime/object_factory.h" +#include "plugins/ecmascript/runtime/tagged_array.h" + +namespace panda::ecmascript { +void JSFunction::InitializeJSFunction(JSThread *thread, const JSHandle &func, FunctionKind kind, + bool strict) +{ + FunctionMode thisMode; + if (IsArrowFunction(kind)) { + thisMode = FunctionMode::LEXICAL; + } else if (strict) { + thisMode = FunctionMode::STRICT; + } else { + thisMode = FunctionMode::GLOBAL; + } + + int32_t flag = FunctionKindBit::Encode(kind) | StrictBit::Encode(strict) | ThisModeBit::Encode(thisMode); + func->SetProtoOrDynClass(thread, JSTaggedValue::Hole(), SKIP_BARRIER); + func->SetHomeObject(thread, JSTaggedValue::Undefined(), SKIP_BARRIER); + func->SetLexicalEnv(thread, JSTaggedValue::Undefined(), SKIP_BARRIER); + func->SetConstantPool(thread, JSTaggedValue::Undefined(), SKIP_BARRIER); + func->SetProfileTypeInfo(thread, JSTaggedValue::Undefined(), SKIP_BARRIER); + func->SetFunctionExtraInfo(thread, JSTaggedValue::Undefined()); + func->SetFunctionInfoFlag(thread, JSTaggedValue(flag)); + + // Set the [[Realm]] internal slot of F to the running execution context's Realm + auto *ecmaVm = thread->GetEcmaVM(); + ObjectFactory *factory = ecmaVm->GetFactory(); + JSHandle lexicalEnv = factory->NewLexicalEnv(0); + lexicalEnv->SetParentEnv(thread, ecmaVm->GetGlobalEnv().GetTaggedValue()); + func->SetLexicalEnv(thread, lexicalEnv.GetTaggedValue()); + + ASSERT(!func->IsPropertiesDict()); + auto globalConst = thread->GlobalConstants(); + if (HasPrototype(kind)) { + JSHandle accessor = globalConst->GetHandledFunctionPrototypeAccessor(); + if (kind == FunctionKind::BASE_CONSTRUCTOR || kind == FunctionKind::GENERATOR_FUNCTION || + kind == FunctionKind::ASYNC_GENERATOR_FUNCTION) { + func->SetPropertyInlinedProps(thread, PROTOTYPE_INLINE_PROPERTY_INDEX, accessor.GetTaggedValue()); + accessor = globalConst->GetHandledFunctionNameAccessor(); + func->SetPropertyInlinedProps(thread, NAME_INLINE_PROPERTY_INDEX, accessor.GetTaggedValue()); + } else if (!JSFunction::IsClassConstructor(kind)) { // class ctor do nothing + PropertyDescriptor desc(thread, accessor, kind != FunctionKind::BUILTIN_CONSTRUCTOR, false, false); + [[maybe_unused]] bool success = JSObject::DefineOwnProperty(thread, JSHandle(func), + globalConst->GetHandledPrototypeString(), desc); + ASSERT(success); + } + } else if (HasAccessor(kind)) { + JSHandle accessor = globalConst->GetHandledFunctionNameAccessor(); + func->SetPropertyInlinedProps(thread, NAME_INLINE_PROPERTY_INDEX, accessor.GetTaggedValue()); + } +} + +JSHandle JSFunction::NewJSFunctionPrototype(JSThread *thread, ObjectFactory *factory, + const JSHandle &func) +{ + JSHandle env = thread->GetEcmaVM()->GetGlobalEnv(); + const GlobalEnvConstants *globalConst = thread->GlobalConstants(); + JSHandle objFun = env->GetObjectFunction(); + JSHandle funPro = factory->NewJSObjectByConstructor(JSHandle(objFun), objFun); + func->SetFunctionPrototype(thread, funPro.GetTaggedValue()); + + // set "constructor" in prototype + JSHandle constructorKey = globalConst->GetHandledConstructorString(); + PropertyDescriptor descriptor(thread, JSHandle::Cast(func), true, false, true); + JSObject::DefineOwnProperty(thread, funPro, constructorKey, descriptor); + + return funPro; +} + +JSHClass *JSFunction::GetOrCreateInitialJSHClass(JSThread *thread, const JSHandle &fun) +{ + JSTaggedValue protoOrDyn(fun->GetProtoOrDynClass()); + if (protoOrDyn.IsJSHClass()) { + return reinterpret_cast(protoOrDyn.GetTaggedObject()); + } + + ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); + JSHandle proto; + if (!fun->HasFunctionPrototype()) { + proto = JSHandle::Cast(NewJSFunctionPrototype(thread, factory, fun)); + } else { + proto = JSHandle(thread, fun->GetProtoOrDynClass()); + } + + JSHandle dynclass = factory->NewEcmaDynClass(JSObject::SIZE, JSType::JS_OBJECT, proto); + fun->SetProtoOrDynClass(thread, dynclass); + return *dynclass; +} + +JSTaggedValue JSFunction::PrototypeGetter(JSThread *thread, const JSHandle &self) +{ + JSHandle func = JSHandle::Cast(self); + if (!func->HasFunctionPrototype()) { + ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); + NewJSFunctionPrototype(thread, factory, func); + } + return JSFunction::Cast(*self)->GetFunctionPrototype(); +} + +bool JSFunction::PrototypeSetter(JSThread *thread, const JSHandle &self, const JSHandle &value, + [[maybe_unused]] bool mayThrow) +{ + JSHandle func(self); + JSTaggedValue protoOrDyn = func->GetProtoOrDynClass(); + if (protoOrDyn.IsJSHClass()) { + // need transtion + JSHandle dynclass(thread, protoOrDyn); + JSHandle newDynclass = JSHClass::TransitionProto(thread, dynclass, value); + if (value->IsECMAObject()) { + JSObject::Cast(value->GetTaggedObject())->GetJSHClass()->SetIsPrototype(true); + } + func->SetProtoOrDynClass(thread, newDynclass); + } else { + func->SetFunctionPrototype(thread, value.GetTaggedValue()); + } + return true; +} + +JSTaggedValue JSFunction::NameGetter(JSThread *thread, const JSHandle &self) +{ + JSMethod *target = JSHandle::Cast(self)->GetCallTarget(); + if (target->GetPandaFile() == nullptr) { + return JSTaggedValue::Undefined(); + } + CString funcName = target->ParseFunctionName(); + if (funcName.empty()) { + return thread->GlobalConstants()->GetEmptyString(); + } + if (JSHandle::Cast(self)->GetFunctionKind() == FunctionKind::GETTER_FUNCTION) { + funcName.insert(0, "get "); + } + if (JSHandle::Cast(self)->GetFunctionKind() == FunctionKind::SETTER_FUNCTION) { + funcName.insert(0, "set "); + } + + ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); + return factory->NewFromString(funcName).GetTaggedValue(); +} + +bool JSFunction::OrdinaryHasInstance(JSThread *thread, const JSHandle &constructor, + const JSHandle &obj) +{ + // 1. If IsCallable(C) is false, return false. + if (!constructor->IsCallable()) { + return false; + } + + // 2. If C has a [[BoundTargetFunction]] internal slot, then + // a. Let BC be the value of C's [[BoundTargetFunction]] internal slot. + // b. Return InstanceofOperator(O,BC) (see 12.9.4). + if (constructor->IsBoundFunction()) { + JSHandle boundFunction(thread, JSBoundFunction::Cast(constructor->GetTaggedObject())); + JSTaggedValue boundTarget = boundFunction->GetBoundTarget(); + return JSObject::InstanceOf(thread, obj, JSHandle(thread, boundTarget)); + } + // 3. If Type(O) is not Object, return false + if (!obj->IsECMAObject()) { + return false; + } + + // 4. Let P be Get(C, "prototype"). + const GlobalEnvConstants *globalConst = thread->GlobalConstants(); + JSHandle constructorPrototype = + JSTaggedValue::GetProperty(thread, constructor, globalConst->GetHandledPrototypeString()).GetValue(); + + // 5. ReturnIfAbrupt(P). + // no throw exception, so needn't return + RETURN_VALUE_IF_ABRUPT_COMPLETION(thread, false); + + // 6. If Type(P) is not Object, throw a TypeError exception. + if (!constructorPrototype->IsECMAObject()) { + THROW_TYPE_ERROR_AND_RETURN(thread, "HasInstance: is not Object", false); + } + + // 7. Repeat + // a.Let O be O.[[GetPrototypeOf]](). + // b.ReturnIfAbrupt(O). + // c.If O is null, return false. + // d.If SameValue(P, O) is true, return true. + JSTaggedValue objPrototype = obj.GetTaggedValue(); + while (!objPrototype.IsNull()) { + if (JSTaggedValue::SameValue(objPrototype, constructorPrototype.GetTaggedValue())) { + return true; + } + objPrototype = JSObject::Cast(objPrototype)->GetPrototype(thread); + RETURN_VALUE_IF_ABRUPT_COMPLETION(thread, false); + } + return false; +} + +bool JSFunction::MakeConstructor(JSThread *thread, const JSHandle &func, + const JSHandle &proto, bool writable) +{ + ASSERT_PRINT(proto->IsHeapObject() || proto->IsUndefined(), "proto must be JSObject or Undefined"); + ASSERT_PRINT(func->IsConstructor(), "func must be Constructor type"); + JSHandle env = thread->GetEcmaVM()->GetGlobalEnv(); + const GlobalEnvConstants *globalConst = thread->GlobalConstants(); + JSHandle constructorKey = globalConst->GetHandledConstructorString(); + + ASSERT_PRINT(func->GetProtoOrDynClass().IsHole() && func->IsExtensible(), + "function doesn't has proto_type property and is extensible object"); + ASSERT_PRINT(JSObject::HasProperty(thread, JSHandle(func), constructorKey), + "function must have constructor"); + + // proto.constructor = func + bool status = false; + if (proto->IsUndefined()) { + // Let prototype be ObjectCreate(%ObjectPrototype%). + JSHandle objPrototype = env->GetObjectFunctionPrototype(); + PropertyDescriptor constructorDesc(thread, JSHandle::Cast(func), writable, false, true); + status = JSTaggedValue::DefinePropertyOrThrow(thread, objPrototype, constructorKey, constructorDesc); + } else { + PropertyDescriptor constructorDesc(thread, JSHandle::Cast(func), writable, false, true); + status = JSTaggedValue::DefinePropertyOrThrow(thread, proto, constructorKey, constructorDesc); + } + + ASSERT_PRINT(status, "DefineProperty construct failed"); + // func.prototype = proto + // Let status be DefinePropertyOrThrow(F, "prototype", PropertyDescriptor{[[Value]]: + // prototype, [[Writable]]: writablePrototype, [[Enumerable]]: false, [[Configurable]]: false}). + func->SetFunctionPrototype(thread, proto.GetTaggedValue()); + + ASSERT_PRINT(status, "DefineProperty proto_type failed"); + return status; +} + +JSTaggedValue JSFunction::Call(JSThread *thread, const JSHandle &func, + const JSHandle &thisArg, uint32_t argc, + const JSTaggedType argv[] // NOLINT(modernize-avoid-c-arrays) +) +{ + // 1. ReturnIfAbrupt(F). + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + // 2. If argumentsList was not passed, let argumentsList be a new empty List. + // 3. If IsCallable(F) is false, throw a TypeError exception. + if (!func->IsCallable()) { + THROW_TYPE_ERROR_AND_RETURN(thread, "Callable is false", JSTaggedValue::Exception()); + } + + if (func->IsJSFunction()) { + return JSFunction::CallInternal(thread, JSHandle(func), thisArg, argc, argv); + } + + if (func->IsBoundFunction()) { + return JSBoundFunction::CallInternal(thread, JSHandle(func)); + } + + if (func->IsJSProxy()) { + return JSProxy::CallInternal(thread, JSHandle(func), thisArg, argc, argv); + } + + THROW_TYPE_ERROR_AND_RETURN(thread, "Call NonCallable", JSTaggedValue::Exception()); +} + +JSTaggedValue JSFunction::Construct(JSThread *thread, const JSHandle &func, uint32_t argc, + const JSTaggedType argv[], // NOLINT(modernize-avoid-c-arrays) + const JSHandle &newTarget) +{ + JSMutableHandle target(thread, newTarget.GetTaggedValue()); + if (target->IsUndefined()) { + target.Update(func.GetTaggedValue()); + } + if (!(func->IsConstructor() && target->IsConstructor())) { + THROW_TYPE_ERROR_AND_RETURN(thread, "Constructor is false", JSTaggedValue::Exception()); + } + if (func->IsJSFunction()) { + return JSFunction::ConstructInternal(thread, JSHandle(func), argc, argv, target); + } + if (func->IsBoundFunction()) { + return JSBoundFunction::ConstructInternal(thread, JSHandle(func), target); + } + if (func->IsJSProxy()) { + return JSProxy::ConstructInternal(thread, JSHandle(func), argc, argv, target); + } + THROW_TYPE_ERROR_AND_RETURN(thread, "Constructor NonConstructor", JSTaggedValue::Exception()); +} + +JSTaggedValue JSFunction::Invoke(JSThread *thread, const JSHandle &thisArg, + const JSHandle &key, uint32_t argc, + const JSTaggedType argv[] // NOLINT(modernize-avoid-c-arrays) +) +{ + ASSERT(JSTaggedValue::IsPropertyKey(key)); + JSHandle func(JSTaggedValue::GetProperty(thread, thisArg, key).GetValue()); + return JSFunction::Call(thread, func, thisArg, argc, argv); +} + +// [[Call]] +JSTaggedValue JSFunction::CallInternal(JSThread *thread, const JSHandle &func, + const JSHandle &thisArg, uint32_t argc, + const JSTaggedType argv[] // NOLINT(modernize-avoid-c-arrays) +) +{ + if (!func->IsBuiltinsConstructor() && func->IsClassConstructor()) { + THROW_TYPE_ERROR_AND_RETURN(thread, "class constructor cannot call", JSTaggedValue::Exception()); + } + + constexpr uint32_t firstArgIndex = 3; + const array_size_t size = argc + firstArgIndex; + CVector values; + values.reserve(size); + values.clear(); + + [[maybe_unused]] Method *target = func->GetCallTarget(); + ASSERT(target != nullptr); + + values.emplace_back(func.GetTaggedType()); + values.emplace_back(JSTaggedValue::VALUE_UNDEFINED); + values.emplace_back(thisArg.GetTaggedType()); + + for (array_size_t i = 0; i < argc; ++i) { + // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-pointer-arithmetic) + values.emplace_back(argv[i]); + } + + return EcmaInterpreter::Execute(thread, JSHandle::Cast(func), values.size(), values.data()); +} + +// [[Construct]] +JSTaggedValue JSFunction::ConstructInternal(JSThread *thread, const JSHandle &func, uint32_t argc, + const JSTaggedType argv[], // NOLINT(modernize-avoid-c-arrays) + const JSHandle &newTarget) +{ + ASSERT(newTarget->IsECMAObject()); + if (!func->IsConstructor()) { + THROW_TYPE_ERROR_AND_RETURN(thread, "Constructor is false", JSTaggedValue::Exception()); + } + + JSHandle obj(thread, JSTaggedValue::Undefined()); + if (!func->IsBuiltinsConstructor() && func->IsBase()) { + ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); + obj = JSHandle(factory->NewJSObjectByConstructor(func, newTarget)); + } + + constexpr uint32_t firstArgIndex = 3; + const array_size_t size = argc + firstArgIndex; + CVector values; + values.reserve(size); + values.clear(); + + ASSERT(func->GetCallTarget() != nullptr); + + values.emplace_back(func.GetTaggedType()); + values.emplace_back(newTarget.GetTaggedType()); + values.emplace_back(obj.GetTaggedType()); + + for (array_size_t i = 0; i < argc; ++i) { + // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-pointer-arithmetic) + values.emplace_back(argv[i]); + } + + JSTaggedValue resultValue = + EcmaInterpreter::Execute(thread, JSHandle::Cast(func), values.size(), values.data()); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + // 9.3.2 [[Construct]] (argumentsList, newTarget) + if (func->IsBuiltinsConstructor() || resultValue.IsECMAObject()) { + return resultValue; + } + + if (func->IsBase()) { + return obj.GetTaggedValue(); + } + + // derived ctor(sub class) return the obj which created by base ctor(parent class) + if (func->IsDerivedConstructor()) { + return resultValue; + } + + if (!resultValue.IsUndefined()) { + THROW_TYPE_ERROR_AND_RETURN(thread, "function is non-constructor", JSTaggedValue::Exception()); + } + return obj.GetTaggedValue(); +} + +JSHandle JSFunctionBase::GetFunctionName(JSThread *thread, const JSHandle &func) +{ + JSHandle nameKey = thread->GlobalConstants()->GetHandledNameString(); + + return JSObject::GetProperty(thread, JSHandle(func), nameKey).GetValue(); +} + +bool JSFunctionBase::SetFunctionName(JSThread *thread, const JSHandle &func, + const JSHandle &name, const JSHandle &prefix) +{ + ASSERT_PRINT(func->IsExtensible(), "Function must be extensible"); + ASSERT_PRINT(name->IsStringOrSymbol(), "name must be string or symbol"); + bool needPrefix = false; + ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); + if (!prefix->IsUndefined()) { + ASSERT_PRINT(prefix->IsString(), "prefix must be string"); + needPrefix = true; + } + // If Type(name) is Symbol, then + // Let description be name’s [[Description]] value. + // If description is undefined, let name be the empty String. + // Else, let name be the concatenation of "[", description, and "]". + JSHandle functionName; + if (name->IsSymbol()) { + JSTaggedValue description = JSHandle::Cast(name)->GetDescription(); + JSHandle descriptionHandle(thread, description); + if (description.IsUndefined()) { + functionName = factory->GetEmptyString(); + } else { + JSHandle leftBrackets = factory->NewFromCanBeCompressString("["); + JSHandle rightBrackets = factory->NewFromCanBeCompressString("]"); + functionName = factory->ConcatFromString(leftBrackets, descriptionHandle); + functionName = factory->ConcatFromString(functionName, rightBrackets); + } + } else { + functionName = JSHandle::Cast(name); + } + EcmaString *newString; + if (needPrefix) { + JSHandle handlePrefixString = JSTaggedValue::ToString(thread, prefix); + JSHandle spaceString(factory->NewFromCanBeCompressString(" ")); + JSHandle concatString = factory->ConcatFromString(handlePrefixString, spaceString); + newString = *factory->ConcatFromString(concatString, functionName); + } else { + newString = *functionName; + } + JSHandle nameHandle(thread, newString); + JSHandle nameKey = thread->GlobalConstants()->GetHandledNameString(); + PropertyDescriptor nameDesc(thread, nameHandle, false, false, true); + JSHandle funcHandle(func); + return JSTaggedValue::DefinePropertyOrThrow(thread, funcHandle, nameKey, nameDesc); +} + +bool JSFunction::SetFunctionLength(JSThread *thread, const JSHandle &func, JSTaggedValue length, bool cfg) +{ + ASSERT_PRINT(func->IsExtensible(), "Function must be extensible"); + ASSERT_PRINT(length.IsInteger(), "length must be integer"); + JSHandle lengthKeyHandle = thread->GlobalConstants()->GetHandledLengthString(); + ASSERT_PRINT(!JSTaggedValue::Less(thread, JSHandle(thread, length), + JSHandle(thread, JSTaggedValue(0))), + "length must be non negtive integer"); + PropertyDescriptor lengthDesc(thread, JSHandle(thread, length), false, false, cfg); + JSHandle funcHandle(func); + return JSTaggedValue::DefinePropertyOrThrow(thread, funcHandle, lengthKeyHandle, lengthDesc); +} + +JSTaggedValue JSBoundFunction::CallInternal(JSThread *thread, const JSHandle &func) +{ + JSHandle target(thread, func->GetBoundTarget()); + JSHandle boundThis(thread, func->GetBoundThis()); + + InternalCallParams *params = thread->GetInternalCallParams(); + params->MakeBoundArgv(thread, func); + return JSFunction::Call(thread, target, boundThis, params->GetLength(), params->GetArgv()); +} + +// 9.4.1.2[[Construct]](argumentsList, newTarget) +JSTaggedValue JSBoundFunction::ConstructInternal(JSThread *thread, const JSHandle &func, + const JSHandle &newTarget) +{ + JSHandle target(thread, func->GetBoundTarget()); + ASSERT(target->IsConstructor()); + JSMutableHandle newTargetMutable(thread, newTarget.GetTaggedValue()); + if (JSTaggedValue::SameValue(func.GetTaggedValue(), newTarget.GetTaggedValue())) { + newTargetMutable.Update(target.GetTaggedValue()); + } + InternalCallParams *params = thread->GetInternalCallParams(); + params->MakeBoundArgv(thread, func); + return JSFunction::Construct(thread, target, params->GetLength(), params->GetArgv(), newTargetMutable); +} + +void JSProxyRevocFunction::ProxyRevocFunctions(const JSThread *thread, const JSHandle &revoker) +{ + // 1.Let p be the value of F’s [[RevocableProxy]] internal slot. + JSTaggedValue proxy = revoker->GetRevocableProxy(); + // 2.If p is null, return undefined. + if (proxy.IsNull()) { + return; + } + + // 3.Set the value of F’s [[RevocableProxy]] internal slot to null. + revoker->SetRevocableProxy(thread, JSTaggedValue::Null()); + + // 4.Assert: p is a Proxy object. + ASSERT(proxy.IsJSProxy()); + JSHandle proxyHandle(thread, proxy); + + // 5 ~ 6 Set internal slot of p to null. + proxyHandle->SetTarget(thread, JSTaggedValue::Null()); + proxyHandle->SetHandler(thread, JSTaggedValue::Null()); +} + +JSTaggedValue JSFunction::AccessCallerArgumentsThrowTypeError([[maybe_unused]] EcmaRuntimeCallInfo *argv) +{ + THROW_TYPE_ERROR_AND_RETURN(argv->GetThread(), + "Under strict mode, 'caller' and 'arguments' properties must not be accessed.", + JSTaggedValue::Exception()); +} + +JSTaggedValue JSIntlBoundFunction::IntlNameGetter(JSThread *thread, [[maybe_unused]] const JSHandle &self) +{ + return thread->GlobalConstants()->GetEmptyString(); +} + +void JSFunction::SetFunctionNameNoPrefix(JSThread *thread, JSFunction *func, JSTaggedValue name) +{ + ASSERT_PRINT(func->IsExtensible(), "Function must be extensible"); + ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); + + JSHandle funcHandle(thread, func); + { + JSMutableHandle nameHandle(thread, JSTaggedValue::Undefined()); + if (!name.IsSymbol()) { + nameHandle.Update(name); + } else { + JSHandle nameBegin(thread, name); + JSTaggedValue description = JSSymbol::Cast(name.GetTaggedObject())->GetDescription(); + if (description.IsUndefined()) { + nameHandle.Update(thread->GlobalConstants()->GetEmptyString()); + } else { + JSHandle concatName; + JSHandle leftBrackets = factory->NewFromCanBeCompressString("["); + JSHandle rightBrackets = factory->NewFromCanBeCompressString("]"); + concatName = factory->ConcatFromString( + leftBrackets, + JSHandle(thread, JSSymbol::Cast(nameBegin->GetHeapObject())->GetDescription())); + concatName = factory->ConcatFromString(concatName, rightBrackets); + nameHandle.Update(concatName.GetTaggedValue()); + } + } + PropertyDescriptor nameDesc(thread, nameHandle, false, false, true); + JSTaggedValue::DefinePropertyOrThrow(thread, JSHandle(thread, funcHandle.GetTaggedValue()), + thread->GlobalConstants()->GetHandledNameString(), nameDesc); + } +} + +JSHandle JSFunction::GetInstanceJSHClass(JSThread *thread, JSHandle constructor, + JSHandle newTarget) +{ + JSHandle ctorInitialJSHClass(thread, JSFunction::GetOrCreateInitialJSHClass(thread, constructor)); + // newTarget is construct itself + if (newTarget.GetTaggedValue() == constructor.GetTaggedValue()) { + return ctorInitialJSHClass; + } + + // newTarget is derived-class of constructor + if (newTarget->IsJSFunction()) { + JSHandle newTargetFunc = JSHandle::Cast(newTarget); + if (newTargetFunc->IsDerivedConstructor()) { + JSTaggedValue newTargetProto = JSHandle::Cast(newTarget)->GetPrototype(thread); + if (newTargetProto == constructor.GetTaggedValue()) { + return GetOrCreateDerivedJSHClass(thread, newTargetFunc, constructor, ctorInitialJSHClass); + } + } + } + + // ECMA2015 9.1.15 3.Let proto be Get(constructor, "prototype"). + JSMutableHandle prototype(thread, JSTaggedValue::Undefined()); + if (newTarget->IsJSFunction()) { + JSHandle newTargetFunc = JSHandle::Cast(newTarget); + FunctionKind kind = newTargetFunc->GetFunctionKind(); + if (HasPrototype(kind)) { + prototype.Update(PrototypeGetter(thread, JSHandle::Cast(newTargetFunc))); + } + } else { + // Such case: bound function and define a "prototype" property. + JSHandle customizePrototype = + JSTaggedValue::GetProperty(thread, newTarget, thread->GlobalConstants()->GetHandledPrototypeString()) + .GetValue(); + RETURN_HANDLE_IF_ABRUPT_COMPLETION(JSHClass, thread); + prototype.Update(customizePrototype.GetTaggedValue()); + // Reload JSHClass of constructor, where the lookup of 'prototype' property may change it. + ctorInitialJSHClass = JSHandle(thread, JSFunction::GetOrCreateInitialJSHClass(thread, constructor)); + } + + if (!prototype->IsECMAObject()) { + prototype.Update(constructor->GetFunctionPrototype()); + } + + JSHandle newJSHClass = JSHClass::Clone(thread, ctorInitialJSHClass); + newJSHClass->SetPrototype(thread, prototype); + + return newJSHClass; +} + +JSHandle JSFunction::GetOrCreateDerivedJSHClass(JSThread *thread, JSHandle derived, + [[maybe_unused]] JSHandle constructor, + JSHandle ctorInitialJSHClass) +{ + JSTaggedValue protoOrDyn(derived->GetProtoOrDynClass()); + // has cached JSHClass, return directly + if (protoOrDyn.IsJSHClass()) { + return JSHandle(thread, protoOrDyn); + } + + JSHandle newJSHClass = JSHClass::Clone(thread, ctorInitialJSHClass); + // guarante derived has function prototype + JSHandle prototype(thread, derived->GetProtoOrDynClass()); + ASSERT(!prototype->IsHole()); + newJSHClass->SetPrototype(thread, prototype); + derived->SetProtoOrDynClass(thread, newJSHClass); + return newJSHClass; +} + +// Those interface below is discarded +void JSFunction::InitializeJSFunction(JSThread *thread, [[maybe_unused]] const JSHandle &env, + const JSHandle &func, FunctionKind kind, bool strict) +{ + InitializeJSFunction(thread, func, kind, strict); +} + +bool JSFunction::IsDynClass(JSTaggedValue object) +{ + return object.IsJSHClass(); +} + +DynClass *JSFunction::GetOrCreateInitialDynClass(JSThread *thread, const JSHandle &fun) +{ + return reinterpret_cast(JSFunction::GetOrCreateInitialJSHClass(thread, fun)); +} + +JSHandle JSFunction::GetInstanceDynClass(JSThread *thread, JSHandle constructor, + JSHandle newTarget) +{ + return JSHandle(JSFunction::GetInstanceJSHClass(thread, constructor, newTarget)); +} + +bool JSFunction::NameSetter(JSThread *thread, const JSHandle &self, const JSHandle &value, + [[maybe_unused]] bool mayThrow) +{ + if (self->IsPropertiesDict()) { + // replace setter with value + JSHandle nameString = thread->GlobalConstants()->GetHandledNameString(); + return self->UpdatePropertyInDictionary(thread, nameString.GetTaggedValue(), value.GetTaggedValue()); + } + self->SetPropertyInlinedProps(thread, NAME_INLINE_PROPERTY_INDEX, value.GetTaggedValue()); + return true; +} + +} // namespace panda::ecmascript diff --git a/runtime/js_function.h b/runtime/js_function.h new file mode 100644 index 000000000..731025035 --- /dev/null +++ b/runtime/js_function.h @@ -0,0 +1,434 @@ +/* + * Copyright (c) 2021-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_JSFUCNTION_H +#define ECMASCRIPT_JSFUCNTION_H + +#include "plugins/ecmascript/runtime/accessor_data.h" +#include "plugins/ecmascript/runtime/ecma_macros.h" +#include "plugins/ecmascript/runtime/ecma_runtime_call_info.h" +#include "plugins/ecmascript/runtime/js_function_extra_info.h" +#include "plugins/ecmascript/runtime/js_object-inl.h" +#include "plugins/ecmascript/runtime/lexical_env.h" + +namespace panda::ecmascript { +using panda::coretypes::DynClass; +class JSThread; + +class JSFunctionBase : public JSObject { +public: + CAST_CHECK(JSFunctionBase, IsJSFunctionBase); + + inline void SetConstructor(bool flag) + { + JSHClass *hclass = GetJSHClass(); + hclass->SetConstructor(flag); + } + + static bool SetFunctionName(JSThread *thread, const JSHandle &func, + const JSHandle &name, const JSHandle &prefix); + static JSHandle GetFunctionName(JSThread *thread, const JSHandle &func); + + void SetCallTarget([[maybe_unused]] const JSThread *thread, JSMethod *p) + { + SetMethod(p); + } + + static constexpr size_t METHOD_OFFSET = JSObject::SIZE; + SET_GET_NATIVE_FIELD(Method, JSMethod, METHOD_OFFSET, SIZE) + + DECL_VISIT_OBJECT_FOR_JS_OBJECT(JSObject, SIZE, SIZE) +}; + +class JSFunction : public JSFunctionBase { +public: + static constexpr int LENGTH_OF_INLINE_PROPERTIES = 3; + static constexpr int LENGTH_INLINE_PROPERTY_INDEX = 0; + static constexpr int NAME_INLINE_PROPERTY_INDEX = 1; + static constexpr int PROTOTYPE_INLINE_PROPERTY_INDEX = 2; + static constexpr int CLASS_PROTOTYPE_INLINE_PROPERTY_INDEX = 1; + + /* -------------- Common API Begin, Don't change those interface!!! ----------------- */ + CAST_CHECK(JSFunction, IsJSFunction); + + static void InitializeJSFunction(JSThread *thread, const JSHandle &env, const JSHandle &func, + FunctionKind kind = FunctionKind::NORMAL_FUNCTION, bool strict = true); + // ecma6 7.3 + static bool OrdinaryHasInstance(JSThread *thread, const JSHandle &constructor, + const JSHandle &obj); + + static JSTaggedValue SpeciesConstructor(const JSHandle &func, + const JSHandle &defaultConstructor); + + // ecma6 9.2 + // 7.3.12 Call(F, V, argumentsList) + + static JSTaggedValue Call(JSThread *thread, const JSHandle &func, + const JSHandle &thisArg, uint32_t argc, + const JSTaggedType argv[] // NOLINT(modernize-avoid-c-arrays) + ); + + static JSTaggedValue Construct(JSThread *thread, const JSHandle &func, uint32_t argc, + const JSTaggedType argv[], // NOLINT(modernize-avoid-c-arrays) + const JSHandle &newTarget); + static JSTaggedValue Invoke(JSThread *thread, const JSHandle &thisArg, + const JSHandle &key, uint32_t argc, + const JSTaggedType argv[] // NOLINT(modernize-avoid-c-arrays) + ); + // 9.2.1[[Call]](thisArgument, argumentsList) + // 9.3.1[[Call]](thisArgument, argumentsList) + static JSTaggedValue CallInternal(JSThread *thread, const JSHandle &func, + const JSHandle &thisArg, uint32_t argc, + const JSTaggedType argv[] // NOLINT(modernize-avoid-c-arrays) + ); + // 9.2.2[[Construct]](argumentsList, newTarget) + // 9.3.2[[Construct]](argumentsList, newTarget) + static JSTaggedValue ConstructInternal(JSThread *thread, const JSHandle &func, uint32_t argc, + const JSTaggedType argv[], // NOLINT(modernize-avoid-c-arrays) + const JSHandle &newTarget); + + static bool AddRestrictedFunctionProperties(const JSHandle &func, const JSHandle &realm); + static bool MakeConstructor(JSThread *thread, const JSHandle &func, + const JSHandle &proto, bool writable = true); + static bool SetFunctionLength(JSThread *thread, const JSHandle &func, JSTaggedValue length, + bool cfg = true); + static JSHandle NewJSFunctionPrototype(JSThread *thread, ObjectFactory *factory, + const JSHandle &func); + static DynClass *GetOrCreateInitialDynClass(JSThread *thread, const JSHandle &fun); + static JSTaggedValue AccessCallerArgumentsThrowTypeError(EcmaRuntimeCallInfo *argv); + static bool IsDynClass(JSTaggedValue object); + static JSTaggedValue PrototypeGetter(JSThread *thread, const JSHandle &self); + static bool PrototypeSetter(JSThread *thread, const JSHandle &self, const JSHandle &value, + bool mayThrow); + static JSTaggedValue NameGetter(JSThread *thread, const JSHandle &self); + static bool NameSetter(JSThread *thread, const JSHandle &self, const JSHandle &value, + bool mayThrow); + static JSTaggedValue LengthGetter(JSThread *thread, const JSHandle &self); + static bool LengthSetter(JSThread *thread, const JSHandle &self, const JSHandle &value, + bool mayThrow); + static void SetFunctionNameNoPrefix(JSThread *thread, JSFunction *func, JSTaggedValue name); + static JSHandle GetInstanceDynClass(JSThread *thread, JSHandle constructor, + JSHandle newTarget); + + inline JSTaggedValue GetFunctionPrototype() const + { + ASSERT(HasFunctionPrototype()); + JSTaggedValue protoOrDyn = GetProtoOrDynClass(); + if (protoOrDyn.IsJSHClass()) { + return JSHClass::Cast(protoOrDyn.GetTaggedObject())->GetPrototype(); + } + + return protoOrDyn; + } + + inline void SetFunctionPrototype(const JSThread *thread, JSTaggedValue proto) + { + SetProtoOrDynClass(thread, proto); + if (proto.IsJSHClass()) { + proto = JSHClass::Cast(proto.GetTaggedObject())->GetPrototype(); + } + if (proto.IsECMAObject()) { + proto.GetTaggedObject()->GetClass()->SetIsPrototype(true); + } + } + + inline bool HasInitialDynClass() const + { + JSTaggedValue protoOrDyn = GetProtoOrDynClass(); + return protoOrDyn.IsJSHClass(); + } + + inline bool HasFunctionPrototype() const + { + JSTaggedValue protoOrDyn = GetProtoOrDynClass(); + return !protoOrDyn.IsHole(); + } + + inline DynClass *GetInitialDynClass() const + { + ASSERT(HasInitialDynClass()); + JSTaggedValue protoOrDyn = GetProtoOrDynClass(); + return reinterpret_cast(protoOrDyn.GetTaggedObject()); + } + + inline void SetFunctionLength(const JSThread *thread, JSTaggedValue length) + { + ASSERT(!IsPropertiesDict()); + SetPropertyInlinedProps(thread, LENGTH_INLINE_PROPERTY_INDEX, length); + } + + inline void SetupFunctionLength(const JSThread *thread) + { + SetFunctionLength(thread, GetMethod()->GetLength()); + } + + inline bool IsBase() const + { + FunctionKind kind = GetFunctionKind(); + return kind <= FunctionKind::CLASS_CONSTRUCTOR; + } + + inline bool IsDerivedConstructor() const + { + FunctionKind kind = GetFunctionKind(); + return kind == FunctionKind::DERIVED_CONSTRUCTOR; + } + + inline void SetFunctionKind(const JSThread *thread, FunctionKind kind) + { + JSTaggedType oldValue = GetFunctionInfoFlag().GetRawData(); + SetFunctionInfoFlag(thread, JSTaggedValue(FunctionKindBit::Update(oldValue, kind))); + } + + inline void SetStrict(const JSThread *thread, bool flag) + { + JSTaggedType oldValue = GetFunctionInfoFlag().GetRawData(); + SetFunctionInfoFlag(thread, JSTaggedValue(StrictBit::Update(oldValue, flag))); + } + + inline void SetResolved(const JSThread *thread) + { + TaggedType oldValue = GetFunctionInfoFlag().GetRawData(); + SetFunctionInfoFlag(thread, JSTaggedValue(ResolvedBit::Update(oldValue, true))); + } + + inline bool IsResolved() const + { + return ResolvedBit::Decode(GetFunctionInfoFlag().GetInt()); + } + + inline void SetFunctionMode(const JSThread *thread, FunctionMode mode) + { + JSTaggedType oldValue = GetFunctionInfoFlag().GetRawData(); + SetFunctionInfoFlag(thread, JSTaggedValue(ThisModeBit::Update(oldValue, mode))); + } + + inline FunctionKind GetFunctionKind() const + { + return FunctionKindBit::Decode(GetFunctionInfoFlag().GetInt()); + } + + inline bool IsStrict() const + { + return StrictBit::Decode(GetFunctionInfoFlag().GetInt()); + } + + inline FunctionMode GetFunctionMode() const + { + return ThisModeBit::Decode(GetFunctionInfoFlag().GetInt()); + } + + inline static bool IsArrowFunction(FunctionKind kind) + { + return (kind >= ARROW_FUNCTION) && (kind <= ASYNC_ARROW_FUNCTION); + } + + inline static bool IsClassConstructor(FunctionKind kind) + { + return (kind == CLASS_CONSTRUCTOR) || (kind == DERIVED_CONSTRUCTOR); + } + + inline static bool IsConstructorKind(FunctionKind kind) + { + return (kind >= BUILTIN_PROXY_CONSTRUCTOR) && (kind <= DERIVED_CONSTRUCTOR); + } + + inline static bool IsBuiltinConstructor(FunctionKind kind) + { + return kind >= BUILTIN_PROXY_CONSTRUCTOR && kind <= BUILTIN_CONSTRUCTOR; + } + + inline static bool HasPrototype(FunctionKind kind) + { + return kind >= BUILTIN_CONSTRUCTOR && kind <= ASYNC_GENERATOR_FUNCTION; + } + + inline static bool HasAccessor(FunctionKind kind) + { + return kind <= ASYNC_FUNCTION; + } + + inline bool IsClassConstructor() const + { + return GetClass()->IsClassConstructor(); + } + + inline void SetClassConstructor(bool flag) + { + GetClass()->SetClassConstructor(flag); + } + + /* -------------- Common API End, Don't change those interface!!! ----------------- */ + static void InitializeJSFunction(JSThread *thread, const JSHandle &func, + FunctionKind kind = FunctionKind::NORMAL_FUNCTION, bool strict = true); + static JSHClass *GetOrCreateInitialJSHClass(JSThread *thread, const JSHandle &fun); + static JSHandle GetInstanceJSHClass(JSThread *thread, JSHandle constructor, + JSHandle newTarget); + + static constexpr size_t PROTO_OR_DYNCLASS_OFFSET = JSFunctionBase::SIZE; + ACCESSORS(ProtoOrDynClass, PROTO_OR_DYNCLASS_OFFSET, LEXICAL_ENV_OFFSET) + ACCESSORS(LexicalEnv, LEXICAL_ENV_OFFSET, HOME_OBJECT_OFFSET) + ACCESSORS(HomeObject, HOME_OBJECT_OFFSET, FUNCTION_INFO_FLAG_OFFSET) + ACCESSORS(FunctionInfoFlag, FUNCTION_INFO_FLAG_OFFSET, FUNCTION_EXTRA_INFO_OFFSET) + ACCESSORS(FunctionExtraInfo, FUNCTION_EXTRA_INFO_OFFSET, CONSTANT_POOL_OFFSET) + ACCESSORS(ConstantPool, CONSTANT_POOL_OFFSET, PROFILE_TYPE_INFO_OFFSET) + ACCESSORS(ProfileTypeInfo, PROFILE_TYPE_INFO_OFFSET, SIZE) + + static constexpr uint32_t FUNCTION_KIND_BIT_NUM = 5; + using FunctionKindBit = BitField; + using StrictBit = FunctionKindBit::NextFlag; + + using ResolvedBit = StrictBit::NextFlag; + using ThisModeBit = ResolvedBit::NextField; // 2: means this flag occupies two digits. + + DECL_DUMP() + + DECL_VISIT_OBJECT_FOR_JS_OBJECT(JSFunctionBase, PROTO_OR_DYNCLASS_OFFSET, SIZE) + +private: + static JSHandle GetOrCreateDerivedJSHClass(JSThread *thread, JSHandle derived, + JSHandle constructor, + JSHandle ctorInitialJSHClass); +}; + +class JSGeneratorFunction : public JSFunction { +public: + CAST_CHECK(JSGeneratorFunction, IsGeneratorFunction); + + static constexpr size_t SIZE = JSFunction::SIZE; + + DECL_VISIT_OBJECT_FOR_JS_OBJECT(JSFunction, SIZE, SIZE) + + DECL_DUMP() +}; + +class JSAsyncFunction : public JSFunction { +public: + CAST_CHECK(JSAsyncFunction, IsJSAsyncFunction); + + static constexpr size_t SIZE = JSFunction::SIZE; + + DECL_VISIT_OBJECT_FOR_JS_OBJECT(JSFunction, SIZE, SIZE) + + DECL_DUMP() +}; + +class JSAsyncGeneratorFunction : public JSFunction { +public: + CAST_CHECK(JSAsyncGeneratorFunction, IsAsyncGeneratorFunction); + + static constexpr size_t SIZE = JSFunction::SIZE; + + DECL_VISIT_OBJECT_FOR_JS_OBJECT(JSFunction, SIZE, SIZE) + + DECL_DUMP() +}; + +class JSBoundFunction : public JSFunctionBase { +public: + CAST_CHECK(JSBoundFunction, IsBoundFunction); + + // 9.4.1.1[[Call]](thisArgument, argumentsList) + static JSTaggedValue CallInternal(JSThread *thread, const JSHandle &func); + + // 9.4.1.2[[Construct]](argumentsList, newTarget) + static JSTaggedValue ConstructInternal(JSThread *thread, const JSHandle &func, + const JSHandle &newTarget); + + static constexpr size_t BOUND_TARGET_OFFSET = JSFunctionBase::SIZE; + ACCESSORS(BoundTarget, BOUND_TARGET_OFFSET, BOUND_THIS_OFFSET); + ACCESSORS(BoundThis, BOUND_THIS_OFFSET, BOUND_ARGUMENTS_OFFSET); + ACCESSORS(BoundArguments, BOUND_ARGUMENTS_OFFSET, SIZE); + + DECL_VISIT_OBJECT_FOR_JS_OBJECT(JSFunctionBase, BOUND_TARGET_OFFSET, SIZE) + + DECL_DUMP() +}; + +class JSProxyRevocFunction : public JSFunction { +public: + CAST_CHECK(JSProxyRevocFunction, IsProxyRevocFunction); + + static void ProxyRevocFunctions(const JSThread *thread, const JSHandle &revoker); + + static constexpr size_t REVOCABLE_PROXY_OFFSET = JSFunction::SIZE; + ACCESSORS(RevocableProxy, REVOCABLE_PROXY_OFFSET, SIZE); + + DECL_VISIT_OBJECT_FOR_JS_OBJECT(JSFunction, REVOCABLE_PROXY_OFFSET, SIZE) + + DECL_DUMP() +}; + +// ResolveFunction/RejectFunction +class JSPromiseReactionsFunction : public JSFunction { +public: + CAST_CHECK(JSPromiseReactionsFunction, IsJSPromiseReactionFunction); + + static constexpr size_t PROMISE_OFFSET = JSFunction::SIZE; + ACCESSORS(Promise, PROMISE_OFFSET, ALREADY_RESOLVED_OFFSET); + ACCESSORS(AlreadyResolved, ALREADY_RESOLVED_OFFSET, SIZE); + + DECL_VISIT_OBJECT_FOR_JS_OBJECT(JSFunction, PROMISE_OFFSET, SIZE) + + DECL_DUMP() +}; + +// ExecutorFunction +class JSPromiseExecutorFunction : public JSFunction { +public: + CAST_CHECK(JSPromiseExecutorFunction, IsJSPromiseExecutorFunction); + + static constexpr size_t CAPABILITY_OFFSET = JSFunction::SIZE; + ACCESSORS(Capability, CAPABILITY_OFFSET, SIZE); + + DECL_VISIT_OBJECT_FOR_JS_OBJECT(JSFunction, CAPABILITY_OFFSET, SIZE) + + DECL_DUMP() +}; + +class JSPromiseAllResolveElementFunction : public JSFunction { +public: + CAST_CHECK(JSPromiseAllResolveElementFunction, IsJSPromiseAllResolveElementFunction); + + static constexpr size_t INDEX_OFFSET = JSFunction::SIZE; + ACCESSORS(Index, INDEX_OFFSET, VALUES_OFFSET); + ACCESSORS(Values, VALUES_OFFSET, CAPABILITIES_OFFSET); + ACCESSORS(Capabilities, CAPABILITIES_OFFSET, REMAINING_ELEMENTS_OFFSET); + ACCESSORS(RemainingElements, REMAINING_ELEMENTS_OFFSET, ALREADY_CALLED_OFFSET); + ACCESSORS(AlreadyCalled, ALREADY_CALLED_OFFSET, SIZE); + + DECL_VISIT_OBJECT_FOR_JS_OBJECT(JSFunction, INDEX_OFFSET, SIZE) + + DECL_DUMP() +}; + +class JSIntlBoundFunction : public JSFunction { +public: + CAST_CHECK(JSIntlBoundFunction, IsJSIntlBoundFunction); + + static JSTaggedValue IntlNameGetter(JSThread *thread, const JSHandle &self); + + static constexpr size_t NUMBER_FORMAT_OFFSET = JSFunction::SIZE; + + ACCESSORS(NumberFormat, NUMBER_FORMAT_OFFSET, DATETIME_FORMAT_OFFSET); + ACCESSORS(DateTimeFormat, DATETIME_FORMAT_OFFSET, COLLATOR_OFFSET); + ACCESSORS(Collator, COLLATOR_OFFSET, SIZE); + + DECL_VISIT_OBJECT_FOR_JS_OBJECT(JSFunction, NUMBER_FORMAT_OFFSET, SIZE) + DECL_DUMP() +}; +} // namespace panda::ecmascript + +#endif // ECMASCRIPT_JSFUCNTION_H diff --git a/runtime/js_function_extra_info.h b/runtime/js_function_extra_info.h new file mode 100644 index 000000000..28ed128ab --- /dev/null +++ b/runtime/js_function_extra_info.h @@ -0,0 +1,43 @@ +/* + * Copyright (c) 2021-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_JS_FUNCTION_INFO_H +#define ECMASCRIPT_JS_FUNCTION_INFO_H + +#include "utils/bit_field.h" +#include "plugins/ecmascript/runtime/ecma_macros.h" +#include "plugins/ecmascript/runtime/ecma_string.h" +#include "js_tagged_value-inl.h" +#include "js_function_kind.h" + +namespace panda::ecmascript { +class JSFunctionExtraInfo : public TaggedObject { +public: + static JSFunctionExtraInfo *Cast(ObjectHeader *object) + { + ASSERT(JSTaggedValue(object).IsJSFunctionExtraInfo()); + return static_cast(object); + } + + static constexpr size_t CALL_BACK_OFFSET = sizeof(ObjectHeader); + ACCESSORS(Callback, CALL_BACK_OFFSET, VM_OFFSET); + ACCESSORS(Vm, VM_OFFSET, DATA_OFFSET); + ACCESSORS(Data, DATA_OFFSET, SIZE); + + DECL_VISIT_OBJECT(CALL_BACK_OFFSET, SIZE) + DECL_DUMP() +}; +} // namespace panda::ecmascript +#endif diff --git a/runtime/js_function_kind.h b/runtime/js_function_kind.h new file mode 100644 index 000000000..dc34067e2 --- /dev/null +++ b/runtime/js_function_kind.h @@ -0,0 +1,64 @@ +/* + * Copyright (c) 2021-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_JS_FUNCTION_KIND_H +#define ECMASCRIPT_JS_FUNCTION_KIND_H + +#include + +namespace panda::ecmascript { +enum FunctionKind : uint8_t { + NORMAL_FUNCTION = 0, + // BEGIN arrow functions + ARROW_FUNCTION, + // BEGIN async functions + ASYNC_ARROW_FUNCTION, + // END arrow functions + ASYNC_FUNCTION, + // END async functions + // BEGIN constructable functions + BUILTIN_PROXY_CONSTRUCTOR, + BUILTIN_CONSTRUCTOR, + // BEGIN base constructors + BASE_CONSTRUCTOR, + // BEGIN default constructors + DEFAULT_BASE_CONSTRUCTOR, + // END base constructors + // BEGIN class constructors + CLASS_CONSTRUCTOR, + // END default constructors + DERIVED_CONSTRUCTOR, + // END class constructors + GENERATOR_FUNCTION, + ASYNC_GENERATOR_FUNCTION, + // END generators + // END constructable functions. + + // BEGIN accessors + GETTER_FUNCTION, + SETTER_FUNCTION, + // END accessors + + LAST_FUNCTION_KIND, +}; + +enum FunctionMode : uint8_t { + LEXICAL, + STRICT, + GLOBAL, +}; +} // namespace panda::ecmascript + +#endif // ECMASCRIPT_JS_FUNCTION_KIND_H diff --git a/runtime/js_generator_object.cpp b/runtime/js_generator_object.cpp new file mode 100644 index 000000000..05ce1f44e --- /dev/null +++ b/runtime/js_generator_object.cpp @@ -0,0 +1,146 @@ +/* + * Copyright (c) 2021-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 "js_generator_object.h" +#include "generator_helper.h" +#include "js_iterator.h" +#include "js_tagged_value-inl.h" + +namespace panda::ecmascript { +JSTaggedValue JSGeneratorObject::GeneratorValidate(JSThread *thread, const JSHandle &obj) +{ + // 1.Perform ? RequireInternalSlot(generator, [[GeneratorState]]). + // 2.Assert: generator also has a [[GeneratorContext]] internal slot. + if (!obj->IsECMAObject()) { + THROW_TYPE_ERROR_AND_RETURN(thread, "", JSTaggedValue::Undefined()); + } + JSHandle toObj = JSTaggedValue::ToObject(thread, obj); + if (!toObj->IsGeneratorObject()) { + THROW_TYPE_ERROR_AND_RETURN(thread, "", JSTaggedValue::Undefined()); + } + + // 3.Let state be generator.[[GeneratorState]]. + JSHandle generator(thread, JSGeneratorObject::Cast(*(toObj))); + JSTaggedValue state = generator->GetGeneratorState(); + // 4.If state is executing, throw a TypeError exception. + if (state == JSTaggedValue(static_cast(JSGeneratorState::EXECUTING))) { + THROW_TYPE_ERROR_AND_RETURN(thread, "", JSTaggedValue::Undefined()); + } + // 5.Return state. + return state; +} + +JSHandle JSGeneratorObject::GeneratorResume(JSThread *thread, + const JSHandle &generator, + JSTaggedValue value) +{ + // 1.Let state be ? GeneratorValidate(generator). + JSHandle gen(thread, generator.GetTaggedValue()); + JSTaggedValue state = GeneratorValidate(thread, gen); + RETURN_HANDLE_IF_ABRUPT_COMPLETION(JSTaggedValue, thread); + + // 2.If state is completed, return CreateIterResultObject(undefined, true). + if (JSGeneratorObject::IsState(state, JSGeneratorState::COMPLETED)) { + JSHandle valueHandle(thread, JSTaggedValue::Undefined()); + return JSHandle::Cast(JSIterator::CreateIterResultObject(thread, valueHandle, true)); + } + + // 3.Assert: state is either suspendedStart or suspendedYield. + ASSERT_PRINT(JSGeneratorObject::IsState(state, JSGeneratorState::SUSPENDED_START) || + JSGeneratorObject::IsState(state, JSGeneratorState::SUSPENDED_YIELD), + "state is neither suspendedStart nor suspendedYield"); + + // 4.Let genContext be generator.[[GeneratorContext]]. + JSHandle genContext(thread, generator->GetGeneratorContext()); + + // 5.Let methodContext be the running execution context. + // 6.Suspend methodContext. + + // 7.Set generator.[[GeneratorState]] to executing. + generator->SetState(thread, JSGeneratorState::EXECUTING); + + // 8.Push genContext onto the execution context stack; genContext is now the running execution context. + // 9.Resume the suspended evaluation of genContext using NormalCompletion(value) as the result of the operation + // that suspended it. Let result be the value returned by the resumed computation. + // 10.Assert: When we return here, genContext has already been removed from the execution context stack and + // methodContext is the currently running execution context. + // 11.Return Completion(result). + JSHandle result = GeneratorHelper::Continue(thread, genContext, GeneratorResumeMode::NEXT, value); + return result; +} + +JSHandle JSGeneratorObject::GeneratorResumeAbrupt(JSThread *thread, + const JSHandle &generator, + const JSHandle &abruptCompletion) +{ + // 1.Let state be ? GeneratorValidate(generator). + JSHandle gen(thread, generator.GetTaggedValue()); + JSTaggedValue state = GeneratorValidate(thread, gen); + RETURN_HANDLE_IF_ABRUPT_COMPLETION(JSTaggedValue, thread); + + // 2.If state is suspendedStart, then + // a.Set generator.[[GeneratorState]] to completed. + // b.Once a generator enters the completed state it never leaves it and its associated execution context is + // never resumed. Any execution state associated with generator can be discarded at this point. + // c.Set state to completed. + if (JSGeneratorObject::IsState(state, JSGeneratorState::SUSPENDED_START)) { + generator->SetState(thread, JSGeneratorState::COMPLETED); + state = JSTaggedValue(static_cast(JSGeneratorState::COMPLETED)); + } + + // 3.If state is completed, then + // a.If abruptCompletion.[[Type]] is return, then + // i.Return CreateIterResultObject(abruptCompletion.[[Value]], true). + // b.Return Completion(abruptCompletion). + if (JSGeneratorObject::IsState(state, JSGeneratorState::COMPLETED)) { + JSHandle valueHandle(thread, abruptCompletion->GetValue()); + JSHandle result = + JSHandle::Cast(JSIterator::CreateIterResultObject(thread, valueHandle, true)); + + if (abruptCompletion->IsReturn()) { + return result; + } + THROW_NEW_ERROR_AND_RETURN_VALUE(thread, valueHandle.GetTaggedValue(), result); + } + + // 4.Assert: state is suspendedYield. + ASSERT_PRINT(JSGeneratorObject::IsState(state, JSGeneratorState::SUSPENDED_YIELD), "state is not suspendedYield"); + + // 5.Let genContext be generator.[[GeneratorContext]]. + JSHandle genContext(thread, generator->GetGeneratorContext()); + + // 6.Let methodContext be the running execution context. + // 7.Suspend methodContext. + + // 8.Set generator.[[GeneratorState]] to executing. + generator->SetState(thread, JSGeneratorState::EXECUTING); + + // 9.Push genContext onto the execution context stack; genContext is now the running execution context. + // 10.Resume the suspended evaluation of genContext using abruptCompletion as the result of the operation that + // suspended it. Let result be the completion record returned by the resumed computation. + // 11.Assert: When we return here, genContext has already been removed from the execution context stack and + // methodContext is the currently running execution context. + // 12.Return Completion(result). + JSHandle result; + if (abruptCompletion->IsReturn()) { + result = + GeneratorHelper::Continue(thread, genContext, GeneratorResumeMode::RETURN, abruptCompletion->GetValue()); + } else { + result = + GeneratorHelper::Continue(thread, genContext, GeneratorResumeMode::THROW, abruptCompletion->GetValue()); + } + return result; +} +} // namespace panda::ecmascript diff --git a/runtime/js_generator_object.h b/runtime/js_generator_object.h new file mode 100644 index 000000000..307329140 --- /dev/null +++ b/runtime/js_generator_object.h @@ -0,0 +1,101 @@ +/* + * Copyright (c) 2021-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_JS_GENERATOR_OBJECT_H +#define ECMASCRIPT_JS_GENERATOR_OBJECT_H + +#include "plugins/ecmascript/runtime/js_function.h" + +namespace panda { +namespace ecmascript { +enum class JSGeneratorState { + UNDEFINED = 0, + SUSPENDED_START, + SUSPENDED_YIELD, + EXECUTING, + COMPLETED, + AWAITING_RETURN, +}; + +class GeneratorContext : TaggedObject { +public: + CAST_CHECK(GeneratorContext, IsGeneratorContext); + + static constexpr size_t GENERATOR_REGS_ARRAY_OFFSET = TaggedObjectSize(); + ACCESSORS(RegsArray, GENERATOR_REGS_ARRAY_OFFSET, GENERATOR_METHOD_OFFSET) + ACCESSORS(Method, GENERATOR_METHOD_OFFSET, GENERATOR_ACC_OFFSET) + ACCESSORS(Acc, GENERATOR_ACC_OFFSET, GENERATOR_NREGS_OFFSET) + ACCESSORS(NRegs, GENERATOR_NREGS_OFFSET, GENERATOR_BC_OFFSET_OFFSET) + ACCESSORS(BCOffset, GENERATOR_BC_OFFSET_OFFSET, GENERATOR_GENERATOR_OBJECT_OFFSET) + ACCESSORS(GeneratorObject, GENERATOR_GENERATOR_OBJECT_OFFSET, GENERATOR_LEXICALENV_OFFSET) + ACCESSORS(LexicalEnv, GENERATOR_LEXICALENV_OFFSET, SIZE) + + DECL_VISIT_OBJECT(GENERATOR_REGS_ARRAY_OFFSET, SIZE) + DECL_DUMP() +}; + +class JSGeneratorObject : public JSObject { +public: + CAST_CHECK(JSGeneratorObject, IsGeneratorObject); + + static constexpr size_t GENERATOR_STATE_OFFSET = JSObject::SIZE; + ACCESSORS(GeneratorState, GENERATOR_STATE_OFFSET, GENERATOR_CONTEXT_OFFSET) + ACCESSORS(GeneratorContext, GENERATOR_CONTEXT_OFFSET, GENERATOR_RESUME_RESULT_OFFSET) + ACCESSORS(ResumeResult, GENERATOR_RESUME_RESULT_OFFSET, GENERATOR_RESUME_MODE_OFFSET) + ACCESSORS(ResumeMode, GENERATOR_RESUME_MODE_OFFSET, SIZE) + + // 26.4.3.2 GeneratorValidate(generator) + static JSTaggedValue GeneratorValidate(JSThread *thread, const JSHandle &obj); + + // 26.4.3.3 GeneratorResume(generator, value) + static JSHandle GeneratorResume(JSThread *thread, const JSHandle &generator, + JSTaggedValue value); + + // 26.4.3.4 GeneratorResumeAbrupt(generator, abruptCompletion) + static JSHandle GeneratorResumeAbrupt(JSThread *thread, const JSHandle &generator, + const JSHandle &abruptCompletion); + + inline bool IsSuspendYield() const + { + return GetGeneratorState() == JSTaggedValue(static_cast(JSGeneratorState::SUSPENDED_YIELD)); + } + + static inline bool IsState(JSTaggedValue stateValue, JSGeneratorState state) + { + return stateValue == JSTaggedValue(static_cast(state)); + } + + inline bool IsExecuting() const + { + return GetGeneratorState() == JSTaggedValue(static_cast(JSGeneratorState::EXECUTING)); + } + + inline bool IsSuspendedStart() const + { + return GetGeneratorState() == JSTaggedValue(static_cast(JSGeneratorState::SUSPENDED_START)); + } + + inline void SetState(JSThread *thread, JSGeneratorState state) + { + SetGeneratorState(thread, JSTaggedValue(static_cast(state))); + } + + DECL_VISIT_OBJECT_FOR_JS_OBJECT(JSObject, GENERATOR_STATE_OFFSET, SIZE) + DECL_DUMP() +}; +} // namespace ecmascript +} // namespace panda + +#endif // ECMASCRIPT_JS_GENERATOR_OBJECT_H diff --git a/runtime/js_global_object.h b/runtime/js_global_object.h new file mode 100644 index 000000000..a8336a1f4 --- /dev/null +++ b/runtime/js_global_object.h @@ -0,0 +1,37 @@ +/* + * Copyright (c) 2021-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_JSGLOBALOBJECT_H +#define ECMASCRIPT_JSGLOBALOBJECT_H + +#include "plugins/ecmascript/runtime/js_object.h" +#include "plugins/ecmascript/runtime/js_tagged_value.h" + +namespace panda::ecmascript { +class JSGlobalObject : public JSObject { +public: + static JSGlobalObject *Cast(ObjectHeader *object) + { + ASSERT(JSTaggedValue(object).IsECMAObject()); + return static_cast(object); + } + + static constexpr size_t SIZE = JSObject::SIZE; + + DECL_VISIT_OBJECT_FOR_JS_OBJECT(JSObject, SIZE, SIZE) +}; +} // namespace panda::ecmascript + +#endif // ECMASCRIPT_JSGLOBALOBJECT_H diff --git a/runtime/js_handle.h b/runtime/js_handle.h new file mode 100644 index 000000000..a899668c9 --- /dev/null +++ b/runtime/js_handle.h @@ -0,0 +1,201 @@ +/* + * Copyright (c) 2021-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_JSHANDLE_H +#define ECMASCRIPT_JSHANDLE_H + +#include + +#include "plugins/ecmascript/runtime/ecma_handle_scope-inl.h" +#include "plugins/ecmascript/runtime/js_tagged_value.h" +#include "handle_base.h" + +namespace panda::test { +class JSHandleTest; +} // namespace panda::test + +namespace panda::ecmascript { +class TaggedArray; +class LinkedHashMap; +class LinkedHashSet; +class NameDictionary; + +template +class JSHandle : public HandleBase { +public: + inline explicit JSHandle() : HandleBase(reinterpret_cast(nullptr)) {} + ~JSHandle() = default; + DEFAULT_NOEXCEPT_MOVE_SEMANTIC(JSHandle); + DEFAULT_COPY_SEMANTIC(JSHandle); + + explicit JSHandle(const JSThread *thread, JSTaggedValue value) : HandleBase() + { + address_ = EcmaHandleScope::NewHandle(const_cast(thread), value.GetRawData()); + } + + explicit JSHandle(const JSThread *thread, const ObjectHeader *value) : HandleBase() + { + address_ = EcmaHandleScope::NewHandle(const_cast(thread), JSTaggedValue(value).GetRawData()); + } + + explicit JSHandle(const JSThread *thread, const TaggedObject *value) : HandleBase() + { + address_ = EcmaHandleScope::NewHandle(const_cast(thread), JSTaggedValue(value).GetRawData()); + } + + template + explicit JSHandle(const JSHandle &handle) : HandleBase(handle.GetAddress()) + { + } + + template + inline static JSHandle Cast(const JSHandle &handle) + { + T::Cast(handle.GetTaggedValue().GetTaggedObject()); + return JSHandle(handle.GetAddress()); + } + + inline JSTaggedValue GetTaggedValue() const + { + if (GetAddress() == 0U) { + return JSTaggedValue::Undefined(); + } + return *(reinterpret_cast(GetAddress())); // NOLINT(clang-analyzer-core.NullDereference) + } + + inline JSTaggedType GetTaggedType() const + { + if (GetAddress() == 0U) { + return JSTaggedValue::Undefined().GetRawData(); + } + return *reinterpret_cast(GetAddress()); // NOLINT(clang-analyzer-core.NullDereference) + } + + inline T *operator*() const + { + return T::Cast(GetTaggedValue().GetTaggedObject()); + } + + inline T *operator->() const + { + return T::Cast(GetTaggedValue().GetTaggedObject()); + } + + inline bool operator==(const JSHandle &other) const + { + return GetTaggedType() == other.GetTaggedType(); + } + + inline bool operator!=(const JSHandle &other) const + { + return GetTaggedType() != other.GetTaggedType(); + } + + inline bool IsEmpty() const + { + return GetAddress() == 0U; + } + + template + R *GetObject() const + { + return reinterpret_cast(GetTaggedValue().GetTaggedObject()); + } + + inline explicit JSHandle(uintptr_t slot) : HandleBase(slot) + { + if (!std::is_convertible::value) { + // NOLINTNEXTLINE(clang-analyzer-core.CallAndMessage) + T::Cast((*reinterpret_cast(slot)).GetTaggedObject()); + } + } + + void Dump() const DUMP_API_ATTR + { + GetTaggedValue().D(); + } + +private: + inline explicit JSHandle(const JSTaggedType *slot) : HandleBase(reinterpret_cast(slot)) {} + inline explicit JSHandle(const T *const *slot) : HandleBase(reinterpret_cast(slot)) {} + + friend class EcmaVM; + friend class GlobalEnv; + friend class JSHandleTest; + friend class GlobalHandleCollection; +}; + +template <> +inline JSTaggedValue *JSHandle::operator->() const +{ + return reinterpret_cast(GetAddress()); +} + +template <> +inline JSTaggedValue *JSHandle::operator*() const +{ + return reinterpret_cast(GetAddress()); +} + +template <> +inline JSTaggedNumber *JSHandle::operator->() const +{ + return reinterpret_cast(GetAddress()); +} + +template <> +inline JSTaggedNumber *JSHandle::operator*() const +{ + return reinterpret_cast(GetAddress()); +} + +template +class JSMutableHandle : public JSHandle { +public: + JSMutableHandle() = default; + ~JSMutableHandle() = default; + DEFAULT_NOEXCEPT_MOVE_SEMANTIC(JSMutableHandle); + DEFAULT_COPY_SEMANTIC(JSMutableHandle); + + explicit JSMutableHandle(const JSThread *thread, JSTaggedValue value) : JSHandle(thread, value) {} + explicit JSMutableHandle(const JSThread *thread, const TaggedArray *value) : JSHandle(thread, value) {} + template + explicit JSMutableHandle(const JSThread *thread, const JSHandle &handle) + : JSHandle(thread, handle.GetTaggedValue()) + { + } + + template + explicit JSMutableHandle(const JSHandle &handle) : JSHandle(handle) + { + } + + void Update(JSTaggedValue value) + { + auto addr = reinterpret_cast(this->GetAddress()); + // NOLINTNEXTLINE(clang-analyzer-core.CallAndMessage) + *addr = value; + } + + template + void Update(const JSHandle &handle) + { + auto addr = reinterpret_cast(this->GetAddress()); + *addr = handle.GetTaggedValue(); + } +}; +} // namespace panda::ecmascript + +#endif // ECMASCRIPT_JSHANDLE_H diff --git a/runtime/js_hclass-inl.h b/runtime/js_hclass-inl.h new file mode 100644 index 000000000..c962c1121 --- /dev/null +++ b/runtime/js_hclass-inl.h @@ -0,0 +1,186 @@ +/* + * Copyright (c) 2021-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_JS_HCLASS_INL_H +#define ECMASCRIPT_JS_HCLASS_INL_H + +#include "plugins/ecmascript/runtime/js_hclass.h" + +#include "plugins/ecmascript/runtime/layout_info-inl.h" +#include "plugins/ecmascript/runtime/transitions_dictionary.h" +#include "plugins/ecmascript/runtime/mem/assert_scope.h" + +namespace panda::ecmascript { +inline JSHClass *JSHClass::Cast(const TaggedObject *object) +{ + ASSERT(JSTaggedValue(object).IsJSHClass()); + return static_cast(const_cast(object)); +} + +void JSHClass::AddTransitions(const JSThread *thread, const JSHandle &parent, const JSHandle &child, + const JSHandle &key, PropertyAttributes attributes) +{ + JSTaggedValue transitions = parent->GetTransitions(); + if (transitions.IsNull()) { + parent->SetTransitions(thread, child.GetTaggedValue()); + child->SetParent(thread, parent.GetTaggedValue()); + return; + } + JSMutableHandle dict(thread, JSTaggedValue::Undefined()); + if (transitions.IsJSHClass()) { + auto cachedHClass = JSHClass::Cast(transitions.GetTaggedObject()); + int32_t last = cachedHClass->NumberOfProps() - 1; + LayoutInfo *layoutInfo = LayoutInfo::Cast(cachedHClass->GetLayout().GetTaggedObject()); + auto attr = JSHandle(thread, JSTaggedValue(layoutInfo->GetAttr(last).GetPropertyMetaData())); + auto lastKey = JSHandle(thread, layoutInfo->GetKey(last)); + auto lastHClass = JSHandle(thread, cachedHClass); + dict.Update(TransitionsDictionary::Create(thread)); + transitions = TransitionsDictionary::PutIfAbsent(thread, dict, lastKey, lastHClass, attr).GetTaggedValue(); + } + auto attr = JSHandle(thread, JSTaggedValue(attributes.GetPropertyMetaData())); + dict.Update(transitions); + transitions = + TransitionsDictionary::PutIfAbsent(thread, dict, key, JSHandle(child), attr).GetTaggedValue(); + parent->SetTransitions(thread, transitions); + child->SetParent(thread, parent.GetTaggedValue()); +} + +void JSHClass::AddExtensionTransitions(const JSThread *thread, const JSHandle &parent, + const JSHandle &child, const JSHandle &key) +{ + auto attr = JSHandle(thread, PropertyAttributes(0).GetTaggedValue()); + AddProtoTransitions(thread, parent, child, key, attr); +} + +void JSHClass::AddProtoTransitions(const JSThread *thread, const JSHandle &parent, + const JSHandle &child, const JSHandle &key, + const JSHandle &proto) +{ + JSTaggedValue transitions = parent->GetTransitions(); + JSMutableHandle dict(thread, JSTaggedValue::Undefined()); + if (transitions.IsNull()) { + transitions = TransitionsDictionary::Create(thread).GetTaggedValue(); + } else if (transitions.IsJSHClass()) { + auto cachedHClass = JSHClass::Cast(transitions.GetTaggedObject()); + int32_t last = cachedHClass->NumberOfProps() - 1; + LayoutInfo *layoutInfo = LayoutInfo::Cast(cachedHClass->GetLayout().GetTaggedObject()); + auto attr = JSHandle(thread, JSTaggedValue(layoutInfo->GetAttr(last).GetPropertyMetaData())); + auto lastKey = JSHandle(thread, layoutInfo->GetKey(last)); + auto lastHClass = JSHandle(thread, cachedHClass); + dict.Update(TransitionsDictionary::Create(thread)); + transitions = TransitionsDictionary::PutIfAbsent(thread, dict, lastKey, lastHClass, attr).GetTaggedValue(); + } + + dict.Update(transitions); + transitions = + TransitionsDictionary::PutIfAbsent(thread, dict, key, JSHandle(child), proto).GetTaggedValue(); + parent->SetTransitions(thread, transitions); + child->SetParent(thread, parent.GetTaggedValue()); +} + +inline JSHClass *JSHClass::FindTransitions(const JSTaggedValue &key, const JSTaggedValue &attributes) +{ + DISALLOW_GARBAGE_COLLECTION; + JSTaggedValue transitions = GetTransitions(); + if (transitions.IsNull()) { + return nullptr; + } + if (transitions.IsJSHClass()) { + auto cachedHClass = JSHClass::Cast(transitions.GetTaggedObject()); + int32_t last = cachedHClass->NumberOfProps() - 1; + LayoutInfo *layoutInfo = LayoutInfo::Cast(cachedHClass->GetLayout().GetTaggedObject()); + auto attr = layoutInfo->GetAttr(last).GetPropertyMetaData(); + auto cachedKey = layoutInfo->GetKey(last); + if (attr == attributes.GetInt() && key == cachedKey) { + return cachedHClass; + } + return nullptr; + } + + ASSERT(transitions.IsTaggedArray()); + TransitionsDictionary *dict = TransitionsDictionary::Cast(transitions.GetTaggedObject()); + auto entry = dict->FindEntry(key, attributes); + if (entry == -1) { + return nullptr; + } + return JSHClass::Cast(dict->GetValue(entry).GetTaggedObject()); +} + +inline JSHClass *JSHClass::FindProtoTransitions(const JSTaggedValue &key, const JSTaggedValue &proto) +{ + DISALLOW_GARBAGE_COLLECTION; + JSTaggedValue transitions = GetTransitions(); + if (!transitions.IsTaggedArray()) { + ASSERT(transitions.IsNull() || transitions.IsJSHClass()); + return nullptr; + } + ASSERT(transitions.IsTaggedArray()); + TransitionsDictionary *dict = TransitionsDictionary::Cast(transitions.GetTaggedObject()); + auto entry = dict->FindEntry(key, proto); + if (entry == -1) { + return nullptr; + } + return JSHClass::Cast(dict->GetValue(entry).GetTaggedObject()); +} + +inline void JSHClass::UpdatePropertyMetaData(const JSThread *thread, [[maybe_unused]] const JSTaggedValue &key, + const PropertyAttributes &metaData) +{ + DISALLOW_GARBAGE_COLLECTION; + ASSERT(!GetLayout().IsNull()); + LayoutInfo *layoutInfo = LayoutInfo::Cast(GetLayout().GetTaggedObject()); + ASSERT(layoutInfo->GetLength() != 0); + int32_t entry = metaData.GetOffset(); + + layoutInfo->SetNormalAttr(thread, entry, metaData); +} + +inline bool JSHClass::HasReferenceField() +{ + auto type = GetObjectType(); + return type != JSType::STRING && type != JSType::JS_NATIVE_POINTER; +} + +inline size_t JSHClass::SizeFromJSHClass(TaggedObject *header) +{ + auto type = GetObjectType(); + size_t size = 0; + switch (type) { + case JSType::TAGGED_ARRAY: + case JSType::TAGGED_DICTIONARY: + case JSType::LINKED_HASH_SET: + case JSType::LINKED_HASH_MAP: + size = TaggedArray::ComputeSize(JSTaggedValue::TaggedTypeSize(), + reinterpret_cast(header)->GetLength()); + break; + case JSType::STRING: + size = reinterpret_cast(header)->ObjectSize(); + 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)); + break; + default: + ASSERT(GetObjectSize() != 0); + size = GetObjectSize(); + break; + } + ASSERT(AlignUp(size, static_cast(MemAlignment::MEM_ALIGN_OBJECT)) == size); + return size; +} +} // namespace panda::ecmascript + +#endif // ECMASCRIPT_JS_HCLASS_INL_H diff --git a/runtime/js_hclass.cpp b/runtime/js_hclass.cpp new file mode 100644 index 000000000..62d41219b --- /dev/null +++ b/runtime/js_hclass.cpp @@ -0,0 +1,505 @@ +/* + * Copyright (c) 2021-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 "plugins/ecmascript/runtime/js_hclass-inl.h" + +#include + +#include "plugins/ecmascript/runtime/base/config.h" +#include "plugins/ecmascript/runtime/global_env.h" +#include "plugins/ecmascript/runtime/ic/proto_change_details.h" +#include "plugins/ecmascript/runtime/js_object-inl.h" +#include "plugins/ecmascript/runtime/js_symbol.h" +#include "plugins/ecmascript/runtime/mem/c_containers.h" +#include "plugins/ecmascript/runtime/tagged_array-inl.h" +#include "plugins/ecmascript/runtime/weak_vector-inl.h" + +namespace panda::ecmascript { +// class TransitionsDictionary +JSHandle TransitionsDictionary::PutIfAbsent(const JSThread *thread, + const JSHandle &dictionary, + const JSHandle &key, + const JSHandle &value, + const JSHandle &metaData) +{ + int hash = TransitionsDictionary::Hash(key.GetTaggedValue(), metaData.GetTaggedValue()); + + /* no need to add key if exist */ + int entry = dictionary->FindEntry(key.GetTaggedValue(), metaData.GetTaggedValue()); + if (entry != -1) { + return dictionary; + } + + // Check whether the dictionary should be extended. + JSHandle newDictionary(HashTableT::GrowHashTable(thread, dictionary)); + // Compute the key object. + entry = newDictionary->FindInsertIndex(hash); + newDictionary->SetEntry(thread, entry, key.GetTaggedValue(), value.GetTaggedValue(), metaData.GetTaggedValue()); + + newDictionary->IncreaseEntries(thread); + return newDictionary; +} + +int TransitionsDictionary::FindEntry(const JSTaggedValue &key, const JSTaggedValue &metaData) +{ + size_t size = Size(); + uint32_t count = 1; + uint32_t hash = TransitionsDictionary::Hash(key, metaData); + // GrowHashTable will guarantee the hash table is never full. + for (int entry = GetFirstPosition(hash, size);; entry = GetNextPosition(entry, count++, size)) { + JSTaggedValue element = GetKey(entry); + if (element.IsHole()) { + continue; + } + if (element.IsUndefined()) { + return -1; + } + + if (TransitionsDictionary::IsMatch(key, metaData, element, GetAttributes(entry))) { + return entry; + } + } + return -1; +} + +JSHandle TransitionsDictionary::Remove(const JSThread *thread, + const JSHandle &table, + const JSHandle &key, + const JSTaggedValue &metaData) +{ + int entry = table->FindEntry(key.GetTaggedValue(), metaData); + if (entry == -1) { + return table; + } + + table->RemoveElement(thread, entry); + return TransitionsDictionary::Shrink(thread, table); +} + +void TransitionsDictionary::Rehash(const JSThread *thread, TransitionsDictionary *newTable) +{ + DISALLOW_GARBAGE_COLLECTION; + if ((newTable == nullptr) || (newTable->Size() < EntriesCount())) { + return; + } + int size = this->Size(); + // Rehash elements to new table + for (int i = 0; i < size; i++) { + int fromIndex = GetEntryIndex(i); + JSTaggedValue k = this->GetKey(i); + if (!IsKey(k)) { + continue; + } + int hash = TransitionsDictionary::Hash(k, this->GetAttributes(i)); + int insertionIndex = GetEntryIndex(newTable->FindInsertIndex(hash)); + JSTaggedValue tv = Get(fromIndex); + newTable->Set(thread, insertionIndex, tv); + for (int j = 1; j < TransitionsDictionary::ENTRY_SIZE; j++) { + tv = Get(fromIndex + j); + newTable->Set(thread, insertionIndex + j, tv); + } + } + newTable->SetEntriesCount(thread, EntriesCount()); + newTable->SetHoleEntriesCount(thread, 0); +} + +// class JSHClass +void JSHClass::Initialize(const JSThread *thread, uint32_t size, JSType type, uint32_t inlinedProps, uint32_t flags) +{ + DISALLOW_GARBAGE_COLLECTION; + + new (&hclass_) HClass(flags, panda_file::SourceLang::ECMASCRIPT); + hclass_.SetManagedObject(this); + + ClearBitField(); + if (JSType::JS_OBJECT_BEGIN <= type && type <= JSType::JS_OBJECT_END) { + SetObjectSize(size + inlinedProps * JSTaggedValue::TaggedTypeSize()); + SetInlinedPropsStart(size); + auto env = thread->GetEcmaVM()->GetGlobalEnv(); + SetLayout(thread, env->GetEmptyLayoutInfo()); + } else { + SetObjectSize(size); + SetLayout(thread, JSTaggedValue::Null()); + } + SetPrototype(thread, JSTaggedValue::Null()); + + SetObjectType(type); + SetExtensible(true); + SetIsPrototype(false); + SetElementRepresentation(Representation::NONE); + SetTransitions(thread, JSTaggedValue::Null()); + SetParent(thread, JSTaggedValue::Null()); + SetProtoChangeMarker(thread, JSTaggedValue::Null()); + SetProtoChangeDetails(thread, JSTaggedValue::Null()); + SetEnumCache(thread, JSTaggedValue::Null()); +} + +void JSHClass::Copy(const JSThread *thread, const JSHClass *jshclass) +{ + DISALLOW_GARBAGE_COLLECTION; + + // HClass fields + SetBitField(jshclass->GetBitField()); + SetBitField1(jshclass->GetBitField1()); + SetPrototype(thread, jshclass->GetPrototype()); + hclass_.SetNativeFieldMask(jshclass->GetHClass()->GetNativeFieldMask()); +} + +JSHandle JSHClass::Clone(const JSThread *thread, const JSHandle &jshclass, + bool withoutInlinedProperties) +{ + JSType type = jshclass->GetObjectType(); + uint32_t size = jshclass->IsJSObject() ? jshclass->GetInlinedPropsStartSize() : jshclass->GetObjectSize(); + uint32_t numInlinedProps = withoutInlinedProperties ? 0 : jshclass->GetInlinedProperties(); + ASSERT(jshclass->IsJSObject() || numInlinedProps == 0); + auto factory = thread->GetEcmaVM()->GetFactory(); + JSHandle newJshclass = + factory->NewEcmaDynClass(factory->hclassClass_, size, type, jshclass->GetHClass()->GetFlags(), numInlinedProps); + // Copy all + newJshclass->Copy(thread, *jshclass); + newJshclass->SetParent(thread, JSTaggedValue::Null()); + newJshclass->SetTransitions(thread, JSTaggedValue::Null()); + newJshclass->SetProtoChangeDetails(thread, JSTaggedValue::Null()); + newJshclass->SetProtoChangeMarker(thread, JSTaggedValue::Null()); + newJshclass->SetEnumCache(thread, JSTaggedValue::Null()); + // reuse Attributes first. + newJshclass->SetLayout(thread, jshclass->GetLayout()); + + return newJshclass; +} + +// use for transition to dictionary +JSHandle JSHClass::CloneWithoutInlinedProperties(const JSThread *thread, const JSHandle &jshclass) +{ + return Clone(thread, jshclass, true); +} + +void JSHClass::TransitionElementsToDictionary(const JSThread *thread, const JSHandle &obj) +{ + // property transition to slow first + if (!obj->GetJSHClass()->IsDictionaryMode()) { + JSObject::TransitionToDictionary(thread, obj); + } + obj->GetJSHClass()->SetIsDictionaryElement(true); + obj->GetJSHClass()->SetIsStableElements(false); +} + +void JSHClass::AddProperty(const JSThread *thread, const JSHandle &obj, const JSHandle &key, + const PropertyAttributes &attr) +{ + ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); + JSHandle jshclass(thread, obj->GetJSHClass()); + JSHClass *newDyn = jshclass->FindTransitions(key.GetTaggedValue(), JSTaggedValue(attr.GetPropertyMetaData())); + if (newDyn != nullptr) { +#if ECMASCRIPT_ENABLE_IC + JSHClass::NotifyHclassChanged(thread, jshclass, JSHandle(thread, newDyn)); +#endif + obj->SetClass(newDyn); + return; + } + + // 2. Create hclass + JSHandle newJshclass = JSHClass::Clone(thread, jshclass); + + // 3. Add Property and metaData + int offset = attr.GetOffset(); + newJshclass->IncNumberOfProps(); + + { + JSMutableHandle layoutInfoHandle(thread, newJshclass->GetLayout()); + + if (layoutInfoHandle->NumberOfElements() != offset) { + layoutInfoHandle.Update(factory->CopyAndReSort(layoutInfoHandle, offset, offset + 1)); + newJshclass->SetLayout(thread, layoutInfoHandle); + } else if (layoutInfoHandle->GetPropertiesCapacity() <= offset) { // need to Grow + layoutInfoHandle.Update( + factory->ExtendLayoutInfo(layoutInfoHandle, LayoutInfo::ComputeGrowCapacity(offset))); + newJshclass->SetLayout(thread, layoutInfoHandle); + } + layoutInfoHandle->AddKey(thread, offset, key.GetTaggedValue(), attr); + } + + // 4. Add newDynclass to old dynclass's transitions. + AddTransitions(thread, jshclass, newJshclass, key, attr); + + // 5. update hclass in object. +#if ECMASCRIPT_ENABLE_IC + JSHClass::NotifyHclassChanged(thread, jshclass, newJshclass); +#endif + obj->SetClass(*newJshclass); +} + +JSHandle JSHClass::TransitionExtension(const JSThread *thread, const JSHandle &jshclass) +{ + JSHandle key(thread->GlobalConstants()->GetHandledPreventExtensionsString()); + { + auto *newDyn = jshclass->FindTransitions(key.GetTaggedValue(), JSTaggedValue(0)); + if (newDyn != nullptr) { + return JSHandle(thread, newDyn); + } + } + ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); + // 2. new a hclass + JSHandle newJshclass = JSHClass::Clone(thread, jshclass); + newJshclass->SetExtensible(false); + + JSTaggedValue attrs = newJshclass->GetLayout(); + if (!attrs.IsNull()) { + ASSERT(attrs.IsTaggedArray()); + JSMutableHandle layoutInfoHandle(thread, attrs); + layoutInfoHandle.Update(factory->CopyLayoutInfo(layoutInfoHandle).GetTaggedValue()); + newJshclass->SetLayout(thread, layoutInfoHandle); + } + + // 3. Add newDynclass to old dynclass's parent's transitions. + AddExtensionTransitions(thread, jshclass, newJshclass, key); + // parent is the same as jshclass, already copy + return newJshclass; +} + +JSHandle JSHClass::TransitionProto(const JSThread *thread, const JSHandle &jshclass, + const JSHandle &proto) +{ + JSHandle key(thread->GlobalConstants()->GetHandledPrototypeString()); + + { + auto *newDyn = jshclass->FindProtoTransitions(key.GetTaggedValue(), proto.GetTaggedValue()); + if (newDyn != nullptr) { + return JSHandle(thread, newDyn); + } + } + + ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); + // 2. new a hclass + JSHandle newJshclass = JSHClass::Clone(thread, jshclass); + newJshclass->SetPrototype(thread, proto.GetTaggedValue()); + + JSTaggedValue attrs = newJshclass->GetLayout(); + { + JSMutableHandle layoutInfoHandle(thread, attrs); + layoutInfoHandle.Update(factory->CopyLayoutInfo(layoutInfoHandle).GetTaggedValue()); + newJshclass->SetLayout(thread, layoutInfoHandle); + } + + // 3. Add newJshclass to old jshclass's parent's transitions. + AddProtoTransitions(thread, jshclass, newJshclass, key, proto); + + // parent is the same as jshclass, already copy + return newJshclass; +} + +void JSHClass::SetPrototype(const JSThread *thread, JSTaggedValue proto) +{ + if (proto.IsECMAObject()) { + JSObject::Cast(proto.GetTaggedObject())->GetJSHClass()->SetIsPrototype(true); + } + SetProto(thread, proto); +} + +void JSHClass::SetPrototype(const JSThread *thread, const JSHandle &proto) +{ + SetPrototype(thread, proto.GetTaggedValue()); +} + +void JSHClass::TransitionToDictionary(const JSThread *thread, const JSHandle &obj) +{ + // 1. new a hclass + JSHandle jshclass(thread, obj->GetJSHClass()); + JSHandle newJshclass = CloneWithoutInlinedProperties(thread, jshclass); + + { + DISALLOW_GARBAGE_COLLECTION; + // 2. Copy + newJshclass->SetNumberOfProps(0); + newJshclass->SetIsDictionaryMode(true); + ASSERT(newJshclass->GetInlinedProperties() == 0); + + // 3. Add newJshclass to ? +#if ECMASCRIPT_ENABLE_IC + JSHClass::NotifyHclassChanged(thread, JSHandle(thread, obj->GetJSHClass()), newJshclass); +#endif + obj->SetClass(newJshclass); + } +} + +JSHandle JSHClass::EnableProtoChangeMarker(const JSThread *thread, const JSHandle &jshclass) +{ + JSTaggedValue proto = jshclass->GetPrototype(); + if (!proto.IsECMAObject()) { + // Return JSTaggedValue directly. No proto check is needed. + return JSHandle(thread, JSTaggedValue(false)); + } + JSHandle protoHandle(thread, proto); + JSHandle protoDyncalss(thread, protoHandle->GetJSHClass()); + RegisterOnProtoChain(thread, protoDyncalss); + JSTaggedValue protoChangeMarker = protoDyncalss->GetProtoChangeMarker(); + if (protoChangeMarker.IsProtoChangeMarker()) { + JSHandle markerHandle(thread, ProtoChangeMarker::Cast(protoChangeMarker.GetTaggedObject())); + if (!markerHandle->GetHasChanged()) { + return JSHandle(markerHandle); + } + } + JSHandle markerHandle = thread->GetEcmaVM()->GetFactory()->NewProtoChangeMarker(); + markerHandle->SetHasChanged(false); + protoDyncalss->SetProtoChangeMarker(thread, markerHandle.GetTaggedValue()); + return JSHandle(markerHandle); +} + +void JSHClass::NotifyHclassChanged(const JSThread *thread, JSHandle oldHclass, JSHandle newHclass) +{ + if (!oldHclass->IsPrototype()) { + return; + } + // The old hclass is the same as new one + if (oldHclass.GetTaggedValue() == newHclass.GetTaggedValue()) { + return; + } + newHclass->SetIsPrototype(true); + JSHClass::NoticeThroughChain(thread, oldHclass); + JSHClass::RefreshUsers(thread, oldHclass, newHclass); +} + +void JSHClass::RegisterOnProtoChain(const JSThread *thread, const JSHandle &jshclass) +{ + ASSERT(jshclass->IsPrototype()); + JSHandle user = jshclass; + JSHandle userDetails = GetProtoChangeDetails(thread, user); + + while (true) { + // Find the prototype chain as far as the hclass has not been registered. + if (userDetails->GetRegisterIndex() != JSTaggedValue(ProtoChangeDetails::UNREGISTERED)) { + return; + } + + JSTaggedValue proto = user->GetPrototype(); + if (!proto.IsHeapObject()) { + return; + } + if (proto.IsJSProxy()) { + return; + } + ASSERT(proto.IsECMAObject()); + JSHandle protoHandle(thread, proto); + JSHandle protoDetails = + GetProtoChangeDetails(thread, JSHandle(thread, protoHandle->GetJSHClass())); + JSTaggedValue listeners = protoDetails->GetChangeListener(); + JSHandle listenersHandle; + if (listeners == JSTaggedValue(0)) { + listenersHandle = JSHandle(ChangeListener::Create(thread)); + } else { + listenersHandle = JSHandle(thread, listeners); + } + uint32_t registerIndex = 0; + JSHandle newListeners = ChangeListener::Add(thread, listenersHandle, user, ®isterIndex); + userDetails->SetRegisterIndex(thread, JSTaggedValue(registerIndex)); + protoDetails->SetChangeListener(thread, newListeners.GetTaggedValue()); + userDetails = protoDetails; + user = JSHandle(thread, protoHandle->GetJSHClass()); + } +} + +bool JSHClass::UnregisterOnProtoChain(const JSThread *thread, const JSHandle &jshclass) +{ + ASSERT(jshclass->IsPrototype()); + if (!jshclass->GetProtoChangeDetails().IsProtoChangeDetails()) { + return false; + } + if (!jshclass->GetPrototype().IsECMAObject()) { + JSTaggedValue listeners = + ProtoChangeDetails::Cast(jshclass->GetProtoChangeDetails().GetTaggedObject())->GetChangeListener(); + return listeners != JSTaggedValue(0); + } + JSHandle currentDetails = GetProtoChangeDetails(thread, jshclass); + uint32_t index = currentDetails->GetRegisterIndex().GetArrayLength(); + if (JSTaggedValue(index) == JSTaggedValue(ProtoChangeDetails::UNREGISTERED)) { + return false; + } + JSTaggedValue proto = jshclass->GetPrototype(); + ASSERT(proto.IsECMAObject()); + JSTaggedValue protoDetailsValue = JSObject::Cast(proto.GetTaggedObject())->GetJSHClass()->GetProtoChangeDetails(); + ASSERT(protoDetailsValue.IsProtoChangeDetails()); + JSTaggedValue listenersValue = ProtoChangeDetails::Cast(protoDetailsValue.GetTaggedObject())->GetChangeListener(); + ASSERT(listenersValue != JSTaggedValue(0)); + JSHandle listeners(thread, listenersValue.GetTaggedObject()); + ASSERT(listeners->Get(index) == jshclass.GetTaggedValue()); + listeners->Delete(thread, index); + return true; +} + +JSHandle JSHClass::GetProtoChangeDetails(const JSThread *thread, const JSHandle &jshclass) +{ + JSTaggedValue protoDetails = jshclass->GetProtoChangeDetails(); + if (protoDetails.IsProtoChangeDetails()) { + return JSHandle(thread, protoDetails); + } + JSHandle protoDetailsHandle = thread->GetEcmaVM()->GetFactory()->NewProtoChangeDetails(); + jshclass->SetProtoChangeDetails(thread, protoDetailsHandle.GetTaggedValue()); + return protoDetailsHandle; +} + +JSHandle JSHClass::GetProtoChangeDetails(const JSThread *thread, const JSHandle &obj) +{ + JSHandle jshclass(thread, obj->GetJSHClass()); + return GetProtoChangeDetails(thread, jshclass); +} + +void JSHClass::NoticeRegisteredUser([[maybe_unused]] const JSThread *thread, const JSHandle &jshclass) +{ + ASSERT(jshclass->IsPrototype()); + JSTaggedValue markerValue = jshclass->GetProtoChangeMarker(); + if (markerValue.IsProtoChangeMarker()) { + ProtoChangeMarker *protoChangeMarker = ProtoChangeMarker::Cast(markerValue.GetTaggedObject()); + protoChangeMarker->SetHasChanged(true); + } +} + +void JSHClass::NoticeThroughChain(const JSThread *thread, const JSHandle &jshclass) +{ + NoticeRegisteredUser(thread, jshclass); + JSTaggedValue protoDetailsValue = jshclass->GetProtoChangeDetails(); + if (!protoDetailsValue.IsProtoChangeDetails()) { + return; + } + JSTaggedValue listenersValue = ProtoChangeDetails::Cast(protoDetailsValue.GetTaggedObject())->GetChangeListener(); + if (!listenersValue.IsTaggedArray()) { + return; + } + ChangeListener *listeners = ChangeListener::Cast(listenersValue.GetTaggedObject()); + for (uint32_t i = 0; i < listeners->GetEnd(); i++) { + JSTaggedValue temp = listeners->Get(i); + if (temp.IsJSHClass()) { + NoticeThroughChain(thread, JSHandle(thread, listeners->Get(i).GetTaggedObject())); + } + } +} + +void JSHClass::RefreshUsers(const JSThread *thread, const JSHandle &oldHclass, + const JSHandle &newHclass) +{ + ASSERT(oldHclass->IsPrototype()); + ASSERT(newHclass->IsPrototype()); + bool onceRegistered = UnregisterOnProtoChain(thread, oldHclass); + + newHclass->SetProtoChangeDetails(thread, oldHclass->GetProtoChangeDetails()); + oldHclass->SetProtoChangeDetails(thread, JSTaggedValue(0)); + if (onceRegistered) { + if (newHclass->GetProtoChangeDetails().IsProtoChangeDetails()) { + ProtoChangeDetails::Cast(newHclass->GetProtoChangeDetails().GetTaggedObject()) + ->SetRegisterIndex(thread, JSTaggedValue(ProtoChangeDetails::UNREGISTERED)); + } + RegisterOnProtoChain(thread, newHclass); + } +} +} // namespace panda::ecmascript diff --git a/runtime/js_hclass.h b/runtime/js_hclass.h new file mode 100644 index 000000000..234e53615 --- /dev/null +++ b/runtime/js_hclass.h @@ -0,0 +1,1124 @@ +/* + * Copyright (c) 2021-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_JS_HCLASS_H +#define ECMASCRIPT_JS_HCLASS_H + +#include "include/mem/allocator.h" +#include "plugins/ecmascript/runtime/ecma_macros.h" +#include "plugins/ecmascript/runtime/js_tagged_value.h" +#include "plugins/ecmascript/runtime/mem/tagged_object.h" +#include "plugins/ecmascript/runtime/property_attributes.h" +#include "runtime/include/coretypes/dyn_objects.h" +#include "runtime/include/hclass.h" + +#include "utils/bit_field.h" + +/* + * JS Object and JS HClass Layout + * + * Properties JS Object JS HClass + * +------------+ +------------+ +------------------+ + * |arrayClass + <---------| |JS HClass +-------------->|HClass class | + * +------------+ | +------------+ +------------------+ + * |property 0 | | |Hash | |BitField | + * +------------+ | +------------+ +------------------+ + * |property 1 | |------- |Properties | |BitField1 | + * +------------+ +------------+ +------------------+ + * |... | |------- |Elements | |Proto | + * +------------+ | +------------+ +------------------+ + * | |in-obj 0 | |Layout | + * Elements | +------------+ +------------------+ + * +------------+ | |in-obj 1 | |Transitions | + * |arrayClass + <---------| +------------+ +------------------+ + * +------------+ |... | |Parent | + * |value 0 | +------------+ +------------------+ + * +------------+ |ProtoChangeMarker | + * |value 1 | +------------------+ + * +------------+ |ProtoChangeDetails| + * |... | +------------------+ + * +------------+ |EnumCache | + * +------------------+ + * + * Proto: [[Prototype]] in Ecma spec + * Layout: record key and attr + * ProtoChangeMarker, ProtoChangeDetails: monitor [[prototype]] chain + * EnumCache: use for for-in syntax + * + */ +namespace panda::ecmascript { +class ProtoChangeDetails; + +// NOLINTNEXTLINE(cppcoreguidelines-macro-usage) +#define JSTYPE_DECL /* //////////////////////////////////////////////////////////////////////////////-PADDING */ \ + INVALID = 0, /* //////////////////////////////////////////////////////////////////////////////-PADDING */ \ + JS_OBJECT, /* JS_OBJECT_BEGIN ////////////////////////////////////////////////////////////////////// */ \ + JS_REALM, /* //////////////////////////////////////////////////////////////////////////////-PADDING */ \ + JS_FUNCTION_BASE, /* //////////////////////////////////////////////////////////////////////////////-PADDING */ \ + JS_FUNCTION, /* //////////////////////////////////////////////////////////////////////////////-PADDING */ \ + JS_PROXY_REVOC_FUNCTION, /* /////////////////////////////////////////////////////////////////-PADDING */ \ + JS_PROMISE_REACTIONS_FUNCTION, /* /////////////////////////////////////////////////////////////////-PADDING */ \ + JS_PROMISE_EXECUTOR_FUNCTION, /* /////////////////////////////////////////////////////////////////-PADDING */ \ + JS_PROMISE_ALL_RESOLVE_ELEMENT_FUNCTION, /* ///////////////////////////////////////////////////////-PADDING */ \ + JS_GENERATOR_FUNCTION, /* /////////////////////////////////////////////////////////////////////////-PADDING */ \ + JS_ASYNC_FUNCTION, /* /////////////////////////////////////////////////////////////////////////////-PADDING */ \ + JS_ASYNC_GENERATOR_FUNCTION, /* ///////////////////////////////////////////////////////////////////-PADDING */ \ + JS_INTL_BOUND_FUNCTION, /* ////////////////////////////////////////////////////////////////////////-PADDING */ \ + JS_ASYNC_AWAIT_STATUS_FUNCTION, /* ////////////////////////////////////////////////////////////////-PADDING */ \ + JS_ASYNC_GENERATOR_RESOLVE_NEXT_FUNCTION, /* //////////////////////////////////////////////////////-PADDING */ \ + JS_ASYNC_FROM_SYNC_ITERATOR_VALUE_UNWRAP_FUNCTION, /* /////////////////////////////////////////////-PADDING */ \ + JS_BOUND_FUNCTION, /* //////////////////////////////////////////////////////////////////////////////////// */ \ + \ + JS_ERROR, /* JS_ERROR_BEGIN /////////////////////////////////////////////////////////////-PADDING */ \ + JS_EVAL_ERROR, /* ////////////////////////////////////////////////////////////////////////////-PADDING */ \ + JS_RANGE_ERROR, /* ////////////////////////////////////////////////////////////////////////////-PADDING */ \ + JS_REFERENCE_ERROR, /* ////////////////////////////////////////////////////////////////////////////-PADDING */ \ + JS_TYPE_ERROR, /* ////////////////////////////////////////////////////////////////////////////-PADDING */ \ + JS_URI_ERROR, /* ////////////////////////////////////////////////////////////////////////////-PADDING */ \ + JS_SYNTAX_ERROR, /* JS_ERROR_END /////////////////////////////////////////////////////////////////////// */ \ + \ + JS_REG_EXP, /* ///////////////////////////////////////////////////////////////////////////////////-PADDING */ \ + JS_SET, /* ///////////////////////////////////////////////////////////////////////////////////-PADDING */ \ + JS_MAP, /* ///////////////////////////////////////////////////////////////////////////////////-PADDING */ \ + JS_WEAK_MAP, /* ///////////////////////////////////////////////////////////////////////////////////-PADDING */ \ + JS_WEAK_SET, /* ///////////////////////////////////////////////////////////////////////////////////-PADDING */ \ + JS_DATE, /* ///////////////////////////////////////////////////////////////////////////////////-PADDING */ \ + JS_ITERATOR, /* ///////////////////////////////////////////////////////////////////////////////////-PADDING */ \ + JS_FORIN_ITERATOR, /* ////////////////////////////////////////////////////////////////////////////-PADDING */ \ + JS_MAP_ITERATOR, /* ////////////////////////////////////////////////////////////////////////////-PADDING */ \ + JS_SET_ITERATOR, /* ////////////////////////////////////////////////////////////////////////////-PADDING */ \ + JS_ARRAY_ITERATOR, /* ////////////////////////////////////////////////////////////////////////////-PADDING */ \ + JS_STRING_ITERATOR, /* ////////////////////////////////////////////////////////////////////////////-PADDING */ \ + JS_INTL, /* ///////////////////////////////////////////////////////////////////////////////////////-PADDING */ \ + JS_LOCALE, /* /////////////////////////////////////////////////////////////////////////////////////-PADDING */ \ + JS_DATE_TIME_FORMAT, /* ///////////////////////////////////////////////////////////////////////////-PADDING */ \ + JS_RELATIVE_TIME_FORMAT, /* ///////////////////////////////////////////////////////////////////////-PADDING */ \ + JS_NUMBER_FORMAT, /* //////////////////////////////////////////////////////////////////////////////-PADDING */ \ + JS_COLLATOR, /* ///////////////////////////////////////////////////////////////////////////////////-PADDING */ \ + JS_PLURAL_RULES, /* ///////////////////////////////////////////////////////////////////////////////-PADDING */ \ + \ + JS_ARRAY_BUFFER, /* ///////////////////////////////////////////////////////////////////////////////-PADDING */ \ + JS_PROMISE, /* ///////////////////////////////////////////////////////////////////////////////-PADDING */ \ + JS_DATA_VIEW, /* /////////////////////////////////////////////////////////////////////////////////////// */ \ + JS_ARGUMENTS, /* //////////////////////////////////////////////////////////////////////////////////-PADDING */ \ + JS_GENERATOR_OBJECT, /* //////////////////////////////////////////////////////////////////////////-PADDING */ \ + JS_ASYNC_FROM_SYNC_ITERATOR_OBJECT, /* ////////////////////////////////////////////////////////////-PADDING */ \ + JS_ASYNC_FUNC_OBJECT, /* //////////////////////////////////////////////////////////////////////////-PADDING */ \ + JS_ASYNC_GENERATOR_OBJECT, /* /////////////////////////////////////////////////////////////////////-PADDING */ \ + \ + /* SPECIAL indexed objects begin, DON'T CHANGE HERE ///////////////////////////////////////////////-PADDING */ \ + JS_ARRAY, /* ////////////////////////////////////////////////////////////////////////////////-PADDING */ \ + JS_ARRAY_LIST, /* /////////////////////////////////////////////////////////////////////////////////-PADDING */ \ + JS_QUEUE, /* /////////////////////////////////////////////////////////////////////////////////-PADDING */ \ + JS_TYPED_ARRAY, /* JS_TYPED_ARRAY_BEGIN /////////////////////////////////////////////////////////////////// */ \ + JS_INT8_ARRAY, /* ////////////////////////////////////////////////////////////////////////////////-PADDING */ \ + JS_UINT8_ARRAY, /* ////////////////////////////////////////////////////////////////////////////////-PADDING */ \ + JS_UINT8_CLAMPED_ARRAY, /* ////////////////////////////////////////////////////////////////////////-PADDING */ \ + JS_INT16_ARRAY, /* ////////////////////////////////////////////////////////////////////////-PADDING */ \ + JS_UINT16_ARRAY, /* ////////////////////////////////////////////////////////////////////////-PADDING */ \ + JS_INT32_ARRAY, /* ////////////////////////////////////////////////////////////////////////-PADDING */ \ + JS_UINT32_ARRAY, /* ////////////////////////////////////////////////////////////////////////-PADDING */ \ + JS_FLOAT32_ARRAY, /* ////////////////////////////////////////////////////////////////////////-PADDING */ \ + JS_FLOAT64_ARRAY, /* JS_TYPED_ARRAY_END ///////////////////////////////////////////////////////////// */ \ + JS_PRIMITIVE_REF, /* number\boolean\string. SPECIAL indexed objects end, DON'T CHANGE HERE ////////-PADDING */ \ + JS_GLOBAL_OBJECT, /* JS_OBJECT_END/////////////////////////////////////////////////////////////////-PADDING */ \ + JS_PROXY, /* ECMA_OBJECT_END ////////////////////////////////////////////////////////////////////////////// */ \ + \ + HCLASS, /* //////////////////////////////////////////////////////////////////////////////////-PADDING */ \ + STRING, /* //////////////////////////////////////////////////////////////////////////////////-PADDING */ \ + TAGGED_ARRAY, /* //////////////////////////////////////////////////////////////////////////////////-PADDING */ \ + TAGGED_DICTIONARY, /* /////////////////////////////////////////////////////////////////////////////-PADDING */ \ + LINKED_HASH_SET, /* ///////////////////////////////////////////////////////////////////////////////-PADDING */ \ + LINKED_HASH_MAP, /* ///////////////////////////////////////////////////////////////////////////////-PADDING */ \ + FREE_OBJECT_WITH_ONE_FIELD, /* ////////////////////////////////////////////////////////////////////-PADDING */ \ + FREE_OBJECT_WITH_NONE_FIELD, /* ///////////////////////////////////////////////////////////////////-PADDING */ \ + FREE_OBJECT_WITH_TWO_FIELD, /* ////////////////////////////////////////////////////////////////////-PADDING */ \ + JS_NATIVE_POINTER, /* /////////////////////////////////////////////////////////////////////////////-PADDING */ \ + GLOBAL_ENV, /* /////////////////////////////////////////////////////////////////////////////-PADDING */ \ + ACCESSOR_DATA, /* /////////////////////////////////////////////////////////////////////////////-PADDING */ \ + INTERNAL_ACCESSOR, /* /////////////////////////////////////////////////////////////////////////////-PADDING */ \ + SYMBOL, /* ////////////////////////////////////////////////////////////////////////////////////////-PADDING */ \ + OBJECT_WRAPPER, /* //////////////////////////////////////////////////////////////////////////-PADDING */ \ + JS_GENERATOR_CONTEXT, /* //////////////////////////////////////////////////////////////////////////-PADDING */ \ + PROTOTYPE_HANDLER, /* //////////////////////////////////////////////////////////////////////////-PADDING */ \ + TRANSITION_HANDLER, /* //////////////////////////////////////////////////////////////////////////-PADDING */ \ + PROPERTY_BOX, /* /////////////////////////////////////////////////////////////////////////////////-PADDING */ \ + PROTO_CHANGE_MARKER, /* ///////////////////////////////////////////////////////////////////////////-PADDING */ \ + PROTOTYPE_INFO, /* ////////////////////////////////////////////////////////////////////////////-PADDING */ \ + TEMPLATE_MAP, /* ////////////////////////////////////////////////////////////////////////////-PADDING */ \ + PROGRAM, /* /////////////////////////////////////////////////////////////////////////////////-PADDING */ \ + LEXICAL_FUNCTION, /* ////////////////////////////////////////////////////////////////////////-PADDING */ \ + \ + PROMISE_CAPABILITY, /* JS_RECORD_BEGIN //////////////////////////////////////////////////////////////////// */ \ + PROMISE_RECORD, /* ////////////////////////////////////////////////////////////////////////////-PADDING */ \ + RESOLVING_FUNCTIONS_RECORD, /* ////////////////////////////////////////////////////////////////////-PADDING */ \ + PROMISE_REACTIONS, /* ////////////////////////////////////////////////////////////////////-PADDING */ \ + PROMISE_ITERATOR_RECORD, /* ////////////////////////////////////////////////////////////////////-PADDING */ \ + MICRO_JOB_QUEUE, /* /////////////////////////////////////////////////////////////////////////////-PADDING */ \ + PENDING_JOB, /* /////////////////////////////////////////////////////////////////////////////-PADDING */ \ + FUNCTION_EXTRA_INFO, /* ////////////////////////////////////////////////////////////////////////-PADDING */ \ + COMPLETION_RECORD, /* JS_RECORD_END /////////////////////////////////////////////////////////////////////// */ \ + MACHINE_CODE_OBJECT, /* ///////////////////////////////////////////////////////////////////////////-PADDING */ \ + ECMA_MODULE, /* /////////////////////////////////////////////////////////////////////////////////-PADDING */ \ + CLASS_INFO_EXTRACTOR, /* //////////////////////////////////////////////////////////////////////////-PADDING */ \ + JS_TYPE_LAST = CLASS_INFO_EXTRACTOR, /* ///////////////////////////////////////////////////////////-PADDING */ \ + \ + JS_FUNCTION_BEGIN = JS_FUNCTION, /* ///////////////////////////////////////////////////////////////-PADDING */ \ + JS_FUNCTION_END = JS_ASYNC_FROM_SYNC_ITERATOR_VALUE_UNWRAP_FUNCTION, /* ///////////////////////////-PADDING */ \ + \ + JS_OBJECT_BEGIN = JS_OBJECT, /* ///////////////////////////////////////////////////////////////////-PADDING */ \ + JS_OBJECT_END = JS_GLOBAL_OBJECT, /* //////////////////////////////////////////////////////////////-PADDING */ \ + \ + ECMA_OBJECT_BEGIN = JS_OBJECT, /* /////////////////////////////////////////////////////////////////-PADDING */ \ + ECMA_OBJECT_END = JS_PROXY, /* /////////////////////////////////////////////////////////////////-PADDING */ \ + \ + JS_ERROR_BEGIN = JS_ERROR, /* ////////////////////////////////////////////////////////////////-PADDING */ \ + JS_ERROR_END = JS_SYNTAX_ERROR, /* ////////////////////////////////////////////////////////////////-PADDING */ \ + \ + JS_ITERATOR_BEGIN = JS_ITERATOR, /* //////////////////////////////////////////////////////////-PADDING */ \ + JS_ITERATOR_END = JS_STRING_ITERATOR, /* //////////////////////////////////////////////////////////-PADDING */ \ + \ + JS_RECORD_BEGIN = PROMISE_CAPABILITY, /* //////////////////////////////////////////////////////////-PADDING */ \ + JS_RECORD_END = COMPLETION_RECORD, /* ///////////////////////////////////////////////////////-PADDING */ \ + \ + TAGGED_ARRAY_BEGIN = TAGGED_ARRAY, /* /////////////////////////////////////////////////////////////-PADDING */ \ + TAGGED_ARRAY_END = LINKED_HASH_MAP, /* ///////////////////////////////////////////////////////////-PADDING */ \ + \ + JS_TYPED_ARRAY_BEGIN = JS_TYPED_ARRAY, /* /////////////////////////////////////////////////////////-PADDING */ \ + JS_TYPED_ARRAY_END = JS_FLOAT64_ARRAY /* /////////////////////////////////////////////////////////-PADDING */ + +enum class JSType : uint8_t { + JSTYPE_DECL, +}; + +class JSHClass : public TaggedObject { +public: + static constexpr int TYPE_BITFIELD_NUM = 8; + using ObjectTypeBits = BitField; // 7 + using Unused1 = ObjectTypeBits::NextFlag; + using ConstrutorBit = Unused1::NextFlag; // 9 + using Unused2 = ConstrutorBit::NextFlag; // 10 + using ExtensibleBit = Unused2::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 WeakContainerBit = ClassPrototypeBit::NextFlag; // 23 + + static constexpr uint32_t DEFAULT_CAPACITY_OF_IN_OBJECTS = 4; + static constexpr uint32_t MAX_CAPACITY_OF_OUT_OBJECTS = + PropertyAttributes::MAX_CAPACITY_OF_PROPERTIES - DEFAULT_CAPACITY_OF_IN_OBJECTS; + static constexpr uint32_t OFFSET_MAX_OBJECT_SIZE_IN_WORDS_WITHOUT_INLINED = 5; + static constexpr uint32_t OFFSET_MAX_OBJECT_SIZE_IN_WORDS = + PropertyAttributes::OFFSET_BITFIELD_NUM + OFFSET_MAX_OBJECT_SIZE_IN_WORDS_WITHOUT_INLINED; + static constexpr uint32_t MAX_OBJECT_SIZE_IN_WORDS = (1U << OFFSET_MAX_OBJECT_SIZE_IN_WORDS) - 1; + + using NumberOfPropsBits = BitField; // 10 + using InlinedPropsStartBits = NumberOfPropsBits::NextField; // 15 + using ObjectSizeInWordsBits = InlinedPropsStartBits::NextField; // 30 + + static JSHClass *Cast(const TaggedObject *object); + + inline size_t SizeFromJSHClass(TaggedObject *header); + inline bool HasReferenceField(); + + // size need to add inlined property numbers + void Initialize(const JSThread *thread, uint32_t size, JSType type, uint32_t inlinedProps, uint32_t flags = 0); + + static JSHandle Clone(const JSThread *thread, const JSHandle &jshclass, + bool withoutInlinedProperties = false); + static JSHandle CloneWithoutInlinedProperties(const JSThread *thread, const JSHandle &jshclass); + + static void TransitionElementsToDictionary(const JSThread *thread, const JSHandle &obj); + static void AddProperty(const JSThread *thread, const JSHandle &obj, const JSHandle &key, + const PropertyAttributes &attr); + + static JSHandle TransitionExtension(const JSThread *thread, const JSHandle &jshclass); + static JSHandle TransitionProto(const JSThread *thread, const JSHandle &jshclass, + const JSHandle &proto); + static void TransitionToDictionary(const JSThread *thread, const JSHandle &obj); + + static JSHandle EnableProtoChangeMarker(const JSThread *thread, const JSHandle &jshclass); + + static void NotifyHclassChanged(const JSThread *thread, JSHandle oldHclass, JSHandle newHclass); + + static void RegisterOnProtoChain(const JSThread *thread, const JSHandle &jshclass); + + static bool UnregisterOnProtoChain(const JSThread *thread, const JSHandle &jshclass); + + static JSHandle GetProtoChangeDetails(const JSThread *thread, + const JSHandle &jshclass); + + static JSHandle GetProtoChangeDetails(const JSThread *thread, const JSHandle &obj); + + inline void UpdatePropertyMetaData(const JSThread *thread, const JSTaggedValue &key, + const PropertyAttributes &metaData); + + static void NoticeRegisteredUser(const JSThread *thread, const JSHandle &jshclass); + + static void NoticeThroughChain(const JSThread *thread, const JSHandle &jshclass); + + static void RefreshUsers(const JSThread *thread, const JSHandle &oldHclass, + const JSHandle &newHclass); + + inline void ClearBitField() + { + SetBitField(0UL); + SetBitField1(0UL); + } + + inline JSType GetObjectType() const + { + uint32_t bits = GetBitField(); + return ObjectTypeBits::Decode(bits); + } + + inline void SetObjectType(JSType type) + { + uint32_t bits = GetBitField(); + uint32_t newVal = ObjectTypeBits::Update(bits, type); + SetBitField(newVal); + } + + inline void SetConstructor(bool flag) const + { + ConstrutorBit::Set(flag, GetBitFieldAddr()); + } + + inline void SetExtensible(bool flag) const + { + ExtensibleBit::Set(flag, GetBitFieldAddr()); + } + + inline void SetIsPrototype(bool flag) const + { + IsPrototypeBit::Set(flag, GetBitFieldAddr()); + } + + inline void SetIsLiteral(bool flag) const + { + IsLiteralBit::Set(flag, GetBitFieldAddr()); + } + + inline void SetClassConstructor(bool flag) const + { + ClassConstructorBit::Set(flag, GetBitFieldAddr()); + } + + inline void SetClassPrototype(bool flag) const + { + ClassPrototypeBit::Set(flag, GetBitFieldAddr()); + } + + inline void SetIsDictionaryMode(bool flag) const + { + IsDictionaryBit::Set(flag, GetBitFieldAddr()); + } + inline void SetWeakContainer(bool flag) + { + WeakContainerBit::Set(flag, GetBitFieldAddr()); + } + + inline bool IsJSObject() const + { + JSType jsType = GetObjectType(); + return (JSType::JS_OBJECT_BEGIN <= jsType && jsType <= JSType::JS_OBJECT_END); + } + + inline bool IsECMAObject() const + { + JSType jsType = GetObjectType(); + return (JSType::ECMA_OBJECT_BEGIN <= jsType && jsType <= JSType::ECMA_OBJECT_END); + } + + inline bool IsRealm() const + { + return GetObjectType() == JSType::JS_REALM; + } + + inline bool IsHClass() const + { + return GetObjectType() == JSType::HCLASS; + } + + inline bool IsString() const + { + return GetObjectType() == JSType::STRING; + } + + inline bool IsSymbol() const + { + return GetObjectType() == JSType::SYMBOL; + } + + inline bool IsStringOrSymbol() const + { + JSType jsType = GetObjectType(); + return (jsType == JSType::STRING) || (jsType == JSType::SYMBOL); + } + + inline bool IsTaggedArray() const + { + JSType jsType = GetObjectType(); + return (JSType::TAGGED_ARRAY_BEGIN <= jsType && jsType <= JSType::TAGGED_ARRAY_END); + } + + inline bool IsDictionary() const + { + return GetObjectType() == JSType::TAGGED_DICTIONARY; + } + + inline bool IsJSNativePointer() const + { + return GetObjectType() == JSType::JS_NATIVE_POINTER; + } + + inline bool IsJSSymbol() const + { + return GetObjectType() == JSType::SYMBOL; + } + + inline bool IsJSArray() const + { + return GetObjectType() == JSType::JS_ARRAY; + } + + inline bool IsTypedArray() const + { + JSType jsType = GetObjectType(); + return (JSType::JS_TYPED_ARRAY_BEGIN < jsType && jsType <= JSType::JS_TYPED_ARRAY_END); + } + + inline bool IsJSTypedArray() const + { + return GetObjectType() == JSType::JS_TYPED_ARRAY; + } + + inline bool IsJSInt8Array() const + { + return GetObjectType() == JSType::JS_INT8_ARRAY; + } + + inline bool IsJSUint8Array() const + { + return GetObjectType() == JSType::JS_UINT8_ARRAY; + } + + inline bool IsJSUint8ClampedArray() const + { + return GetObjectType() == JSType::JS_UINT8_CLAMPED_ARRAY; + } + + inline bool IsJSInt16Array() const + { + return GetObjectType() == JSType::JS_INT16_ARRAY; + } + + inline bool IsJSUint16Array() const + { + return GetObjectType() == JSType::JS_UINT16_ARRAY; + } + + inline bool IsJSInt32Array() const + { + return GetObjectType() == JSType::JS_INT32_ARRAY; + } + + inline bool IsJSUint32Array() const + { + return GetObjectType() == JSType::JS_UINT32_ARRAY; + } + + inline bool IsJSFloat32Array() const + { + return GetObjectType() == JSType::JS_FLOAT32_ARRAY; + } + + inline bool IsJSFloat64Array() const + { + return GetObjectType() == JSType::JS_FLOAT64_ARRAY; + } + + inline bool IsJsGlobalEnv() const + { + return GetObjectType() == JSType::GLOBAL_ENV; + } + + inline bool IsJSFunctionBase() const + { + JSType jsType = GetObjectType(); + return jsType >= JSType::JS_FUNCTION_BASE && jsType <= JSType::JS_BOUND_FUNCTION; + } + + inline bool IsJsBoundFunction() const + { + return GetObjectType() == JSType::JS_BOUND_FUNCTION; + } + + inline bool IsJSIntlBoundFunction() const + { + return GetObjectType() == JSType::JS_INTL_BOUND_FUNCTION; + } + + inline bool IsJSProxyRevocFunction() const + { + return GetObjectType() == JSType::JS_PROXY_REVOC_FUNCTION; + } + + inline bool IsJSAsyncFunction() const + { + return GetObjectType() == JSType::JS_ASYNC_FUNCTION; + } + + inline bool IsJSAsyncGeneratorFunction() const + { + return GetObjectType() == JSType::JS_ASYNC_GENERATOR_FUNCTION; + } + + inline bool IsJSAsyncAwaitStatusFunction() const + { + return GetObjectType() == JSType::JS_ASYNC_AWAIT_STATUS_FUNCTION; + } + + inline bool IsJSAsyncGeneratorResolveNextFunction() const + { + return GetObjectType() == JSType::JS_ASYNC_GENERATOR_RESOLVE_NEXT_FUNCTION; + } + + inline bool IsJSAsyncFromSyncIteratorValueUnwrapFunction() const + { + return GetObjectType() == JSType::JS_ASYNC_FROM_SYNC_ITERATOR_VALUE_UNWRAP_FUNCTION; + } + + inline bool IsJSPromiseReactionFunction() const + { + return GetObjectType() == JSType::JS_PROMISE_REACTIONS_FUNCTION; + } + + inline bool IsJSPromiseExecutorFunction() const + { + return GetObjectType() == JSType::JS_PROMISE_EXECUTOR_FUNCTION; + } + + inline bool IsJSPromiseAllResolveElementFunction() const + { + return GetObjectType() == JSType::JS_PROMISE_ALL_RESOLVE_ELEMENT_FUNCTION; + } + + inline bool IsJSFunctionExtraInfo() const + { + return GetObjectType() == JSType::FUNCTION_EXTRA_INFO; + } + + inline bool IsMicroJobQueue() const + { + return GetObjectType() == JSType::MICRO_JOB_QUEUE; + } + + inline bool IsPendingJob() const + { + return GetObjectType() == JSType::PENDING_JOB; + } + + inline bool IsJsPrimitiveRef() const + { + return GetObjectType() == JSType::JS_PRIMITIVE_REF; + }; + + bool IsJSSet() const + { + return GetObjectType() == JSType::JS_SET; + } + + bool IsJSMap() const + { + return GetObjectType() == JSType::JS_MAP; + } + + bool IsJSWeakMap() const + { + return GetObjectType() == JSType::JS_WEAK_MAP; + } + + bool IsJSWeakSet() const + { + return GetObjectType() == JSType::JS_WEAK_SET; + } + + bool IsJSFunction() const + { + return GetObjectType() >= JSType::JS_FUNCTION_BEGIN && GetObjectType() <= JSType::JS_FUNCTION_END; + } + + inline bool IsJSError() const + { + JSType jsType = GetObjectType(); + return jsType >= JSType::JS_ERROR_BEGIN && jsType <= JSType::JS_ERROR_END; + } + + inline bool IsArguments() const + { + return GetObjectType() == JSType::JS_ARGUMENTS; + } + + inline bool IsDate() const + { + return GetObjectType() == JSType::JS_DATE; + } + + inline bool IsJSRegExp() const + { + return GetObjectType() == JSType::JS_REG_EXP; + } + + inline bool IsJSProxy() const + { + return GetObjectType() == JSType::JS_PROXY; + } + + inline bool IsJSLocale() const + { + return GetObjectType() == JSType::JS_LOCALE; + } + + inline bool IsJSIntl() const + { + return GetObjectType() == JSType::JS_INTL; + } + + inline bool IsJSDateTimeFormat() const + { + return GetObjectType() == JSType::JS_DATE_TIME_FORMAT; + } + + inline bool IsJSRelativeTimeFormat() const + { + return GetObjectType() == JSType::JS_RELATIVE_TIME_FORMAT; + } + + inline bool IsJSNumberFormat() const + { + return GetObjectType() == JSType::JS_NUMBER_FORMAT; + } + + inline bool IsJSCollator() const + { + return GetObjectType() == JSType::JS_COLLATOR; + } + + inline bool IsJSPluralRules() const + { + return GetObjectType() == JSType::JS_PLURAL_RULES; + } + + inline bool IsJSArrayList() const + { + return GetObjectType() == JSType::JS_ARRAY_LIST; + } + + inline bool IsJSQueue() const + { + return GetObjectType() == JSType::JS_QUEUE; + } + + inline bool IsSpecialContainer() const + { + return GetObjectType() >= JSType::JS_ARRAY_LIST && GetObjectType() <= JSType::JS_QUEUE; + } + + inline bool IsAccessorData() const + { + return GetObjectType() == JSType::ACCESSOR_DATA; + } + + inline bool IsInternalAccessor() const + { + return GetObjectType() == JSType::INTERNAL_ACCESSOR; + } + + inline bool IsIterator() const + { + JSType jsType = GetObjectType(); + return jsType >= JSType::JS_ITERATOR_BEGIN && jsType <= JSType::JS_ITERATOR_END; + } + + inline bool IsForinIterator() const + { + return GetObjectType() == JSType::JS_FORIN_ITERATOR; + } + + inline bool IsStringIterator() const + { + return GetObjectType() == JSType::JS_STRING_ITERATOR; + } + + inline bool IsArrayBuffer() const + { + return GetObjectType() == JSType::JS_ARRAY_BUFFER; + } + + inline bool IsDataView() const + { + return GetObjectType() == JSType::JS_DATA_VIEW; + } + + inline bool IsJSSetIterator() const + { + return GetObjectType() == JSType::JS_SET_ITERATOR; + } + + inline bool IsJSMapIterator() const + { + return GetObjectType() == JSType::JS_MAP_ITERATOR; + } + + inline bool IsJSArrayIterator() const + { + return GetObjectType() == JSType::JS_ARRAY_ITERATOR; + } + inline bool IsPrototypeHandler() const + { + return GetObjectType() == JSType::PROTOTYPE_HANDLER; + } + + inline bool IsTransitionHandler() const + { + return GetObjectType() == JSType::TRANSITION_HANDLER; + } + + inline bool IsPropertyBox() const + { + return GetObjectType() == JSType::PROPERTY_BOX; + } + inline bool IsProtoChangeMarker() const + { + return GetObjectType() == JSType::PROTO_CHANGE_MARKER; + } + + inline bool IsProtoChangeDetails() const + { + return GetObjectType() == JSType::PROTOTYPE_INFO; + } + + inline bool IsProgram() const + { + return GetObjectType() == JSType::PROGRAM; + } + + inline bool IsEcmaModule() const + { + return GetObjectType() == JSType::ECMA_MODULE; + } + + inline bool IsClassInfoExtractor() const + { + return GetObjectType() == JSType::CLASS_INFO_EXTRACTOR; + } + + inline bool IsLexicalFunction() const + { + return GetObjectType() == JSType::LEXICAL_FUNCTION; + } + + inline bool IsCallable() const + { + return hclass_.IsCallable(); + } + + inline bool IsConstructor() const + { + uint32_t bits = GetBitField(); + return ConstrutorBit::Decode(bits); + } + + inline bool IsBuiltinsCtor() const + { + return hclass_.IsBuiltinsConstructor(); + } + + inline bool IsExtensible() const + { + uint32_t bits = GetBitField(); + return ExtensibleBit::Decode(bits); + } + + inline bool IsPrototype() const + { + uint32_t bits = GetBitField(); + 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); + } + + inline bool IsJSGlobalObject() const + { + return GetObjectType() == JSType::JS_GLOBAL_OBJECT; + } + + inline bool IsClassPrototype() const + { + uint32_t bits = GetBitField(); + return ClassPrototypeBit::Decode(bits); + } + + inline bool IsWeakContainer() const + { + uint64_t bits = GetBitField(); + return WeakContainerBit::Decode(bits); + } + + inline bool IsDictionaryMode() const + { + uint32_t bits = GetBitField(); + return IsDictionaryBit::Decode(bits); + } + + inline bool IsObjectWrapper() const + { + return GetObjectType() == JSType::OBJECT_WRAPPER; + } + + inline bool IsGeneratorFunction() const + { + return GetObjectType() == JSType::JS_GENERATOR_FUNCTION; + } + + inline bool IsGeneratorObject() const + { + JSType type = GetObjectType(); + return type == JSType::JS_GENERATOR_OBJECT || type == JSType::JS_ASYNC_FUNC_OBJECT || + type == JSType::JS_ASYNC_GENERATOR_OBJECT; + } + + inline bool IsGeneratorContext() const + { + return GetObjectType() == JSType::JS_GENERATOR_CONTEXT; + } + + inline bool IsAsyncFuncObject() const + { + JSType type = GetObjectType(); + return type == JSType::JS_ASYNC_FUNC_OBJECT || type == JSType::JS_ASYNC_GENERATOR_OBJECT; + } + + inline bool IsAsyncGeneratorFunction() const + { + return GetObjectType() == JSType::JS_ASYNC_GENERATOR_FUNCTION; + } + + inline bool IsAsyncGeneratorObject() const + { + return GetObjectType() == JSType::JS_ASYNC_GENERATOR_OBJECT; + } + + inline bool IsJSAsyncFromSyncIteratorObject() const + { + return GetObjectType() == JSType::JS_ASYNC_FROM_SYNC_ITERATOR_OBJECT; + } + + inline bool IsJSPromise() const + { + return GetObjectType() == JSType::JS_PROMISE; + } + + inline bool IsResolvingFunctionsRecord() const + { + return GetObjectType() == JSType::RESOLVING_FUNCTIONS_RECORD; + } + + inline bool IsPromiseRecord() const + { + return GetObjectType() == JSType::PROMISE_RECORD; + } + + inline bool IsPromiseIteratorRecord() const + { + return GetObjectType() == JSType::PROMISE_ITERATOR_RECORD; + } + + inline bool IsPromiseCapability() const + { + return GetObjectType() == JSType::PROMISE_CAPABILITY; + } + + inline bool IsPromiseReaction() const + { + return GetObjectType() == JSType::PROMISE_REACTIONS; + } + + inline bool IsCompletionRecord() const + { + return GetObjectType() == JSType::COMPLETION_RECORD; + } + + inline bool IsRecord() const + { + JSType jsType = GetObjectType(); + return jsType >= JSType::JS_RECORD_BEGIN && jsType <= JSType::JS_RECORD_END; + } + + inline bool IsTemplateMap() const + { + return GetObjectType() == JSType::TEMPLATE_MAP; + } + + inline bool IsFreeObjectWithOneField() const + { + return GetObjectType() == JSType::FREE_OBJECT_WITH_ONE_FIELD; + } + + inline bool IsFreeObjectWithNoneField() const + { + return GetObjectType() == JSType::FREE_OBJECT_WITH_NONE_FIELD; + } + + inline bool IsFreeObjectWithTwoField() const + { + return GetObjectType() == JSType::FREE_OBJECT_WITH_TWO_FIELD; + } + + inline bool IsMachineCodeObject() const + { + return GetObjectType() == JSType::MACHINE_CODE_OBJECT; + } + + inline void SetElementRepresentation(Representation representation) + { + uint32_t bits = GetBitField(); + uint32_t newVal = ElementRepresentationBits::Update(bits, representation); + SetBitField(newVal); + } + + inline Representation GetElementRepresentation() const + { + uint32_t bits = GetBitField(); + return ElementRepresentationBits::Decode(bits); + } + + inline void UpdateRepresentation(JSTaggedValue value) + { + Representation rep = PropertyAttributes::UpdateRepresentation(GetElementRepresentation(), value); + SetElementRepresentation(rep); + } + + inline void SetIsDictionaryElement(bool value) + { + JSTaggedType newVal = DictionaryElementBits::Update(GetBitField(), value); + SetBitField(newVal); + } + inline bool IsDictionaryElement() const + { + return DictionaryElementBits::Decode(GetBitField()); + } + inline void SetIsStableElements(bool value) + { + JSTaggedType newVal = IsStableElementsBit::Update(GetBitField(), value); + SetBitField(newVal); + } + inline bool IsStableElements() const + { + return IsStableElementsBit::Decode(GetBitField()); + } + inline bool IsStableJSArguments() const + { + uint32_t bits = GetBitField(); + auto type = ObjectTypeBits::Decode(bits); + return IsStableElementsBit::Decode(bits) && (type == JSType::JS_ARGUMENTS); + } + inline bool IsStableJSArray() const + { + uint32_t bits = GetBitField(); + auto type = ObjectTypeBits::Decode(bits); + return IsStableElementsBit::Decode(bits) && (type == JSType::JS_ARRAY); + } + inline void SetHasConstructor(bool value) + { + TaggedType newVal = HasConstructorBits::Update(GetBitField(), value); + SetBitField(newVal); + } + inline bool HasConstructor() const + { + return HasConstructorBits::Decode(GetBitField()); + } + + inline void SetNumberOfProps(uint32_t num) + { + uint32_t bits = GetBitField1(); + uint32_t newVal = NumberOfPropsBits::Update(bits, num); + SetBitField1(newVal); + } + + inline void IncNumberOfProps() + { + ASSERT(NumberOfProps() < PropertyAttributes::MAX_CAPACITY_OF_PROPERTIES); + SetNumberOfProps(NumberOfProps() + 1); + } + + inline uint32_t NumberOfProps() const + { + uint32_t bits = GetBitField1(); + return NumberOfPropsBits::Decode(bits); + } + + inline int32_t GetNextInlinedPropsIndex() const + { + uint32_t inlinedProperties = GetInlinedProperties(); + uint32_t numberOfProps = NumberOfProps(); + if (numberOfProps < inlinedProperties) { + return numberOfProps; + } + return -1; + } + + inline int32_t GetNextNonInlinedPropsIndex() const + { + uint32_t inlinedProperties = GetInlinedProperties(); + uint32_t numberOfProps = NumberOfProps(); + if (numberOfProps >= inlinedProperties) { + return numberOfProps - inlinedProperties; + } + return -1; + } + + inline uint32_t GetInlinedPropertiesOffset(uint32_t index) const + { + ASSERT(index < GetInlinedProperties()); + return GetInlinedPropertiesIndex(index) * JSTaggedValue::TaggedTypeSize(); + } + + inline uint32_t GetInlinedPropertiesIndex(uint32_t index) const + { + ASSERT(index < GetInlinedProperties()); + uint32_t bits = GetBitField1(); + return InlinedPropsStartBits::Decode(bits) + index; + } + + inline void SetInlinedPropsStart(uint32_t num) + { + uint32_t bits = GetBitField1(); + uint32_t newVal = InlinedPropsStartBits::Update(bits, num / JSTaggedValue::TaggedTypeSize()); + SetBitField1(newVal); + } + + inline uint32_t GetInlinedPropsStartSize() const + { + uint32_t bits = GetBitField1(); + return InlinedPropsStartBits::Decode(bits) * JSTaggedValue::TaggedTypeSize(); + } + + inline uint32_t GetInlinedProperties() const + { + JSType type = GetObjectType(); + if (JSType::JS_OBJECT_BEGIN <= type && type <= JSType::JS_OBJECT_END) { + uint32_t bits = GetBitField1(); + return static_cast(GetObjectSize() / JSTaggedValue::TaggedTypeSize() - + InlinedPropsStartBits::Decode(bits)); + } + return 0; + } + + JSTaggedValue GetAccessor(const JSTaggedValue &key); + + HClass *GetHClass() + { + return &hclass_; + } + + const HClass *GetHClass() const + { + return &hclass_; + } + + static constexpr size_t GetHClassOffset() + { + return MEMBER_OFFSET(JSHClass, hclass_); + } + + static JSHClass *FromHClass(HClass *hclass) + { + return reinterpret_cast(reinterpret_cast(hclass) - MEMBER_OFFSET(JSHClass, hclass_)); + } + + static constexpr size_t BIT_FIELD_OFFSET = TaggedObjectSize() + HClass::GetDataOffset(); + SET_GET_PRIMITIVE_FIELD(BitField, uint32_t, BIT_FIELD_OFFSET, BIT_FIELD_OFFSET_END); + static constexpr size_t BIT_FIELD1_OFFSET = BIT_FIELD_OFFSET + sizeof(uint32_t); + SET_GET_PRIMITIVE_FIELD(BitField1, uint32_t, BIT_FIELD1_OFFSET, BIT_FIELD1_OFFSET_END); + static constexpr size_t OBJECT_SIZE_OFFSET = TaggedObjectSize() + BaseClass::GetObjectSizeOffset(); + SET_GET_PRIMITIVE_FIELD(ObjectSize, uint32_t, OBJECT_SIZE_OFFSET, OBJECT_SIZE_OFFSET_END); + static constexpr size_t PROTOTYPE_OFFSET = TaggedObjectSize() + sizeof(HClass); + ACCESSORS(Proto, PROTOTYPE_OFFSET, LAYOUT_OFFSET); + ACCESSORS(Layout, LAYOUT_OFFSET, TRANSTIONS_OFFSET); + ACCESSORS(Transitions, TRANSTIONS_OFFSET, PARENT_OFFSET); + ACCESSORS(Parent, PARENT_OFFSET, VALIDITY_CELL_OFFSET); + ACCESSORS(ProtoChangeMarker, VALIDITY_CELL_OFFSET, PROTOTYPE_INFO_OFFSET); + ACCESSORS(ProtoChangeDetails, PROTOTYPE_INFO_OFFSET, ENUM_CACHE_OFFSET); + ACCESSORS(EnumCache, ENUM_CACHE_OFFSET, SIZE); + + void SetPrototype(const JSThread *thread, JSTaggedValue proto); + void SetPrototype(const JSThread *thread, const JSHandle &proto); + inline JSTaggedValue GetPrototype() const + { + return GetProto(); + } + DECL_DUMP() + + static CString DumpJSType(JSType type); + + DECL_VISIT_OBJECT(PROTOTYPE_OFFSET, SIZE); + +private: + static inline void AddTransitions(const JSThread *thread, const JSHandle &parent, + const JSHandle &child, const JSHandle &key, + PropertyAttributes attr); + static inline void AddExtensionTransitions(const JSThread *thread, const JSHandle &parent, + const JSHandle &child, const JSHandle &key); + static inline void AddProtoTransitions(const JSThread *thread, const JSHandle &parent, + const JSHandle &child, const JSHandle &key, + const JSHandle &proto); + inline JSHClass *FindTransitions(const JSTaggedValue &key, const JSTaggedValue &attributes); + inline JSHClass *FindProtoTransitions(const JSTaggedValue &key, const JSTaggedValue &proto); + + void Copy(const JSThread *thread, const JSHClass *jshclass); + + uint32_t *GetBitFieldAddr() const + { + return reinterpret_cast(ToUintPtr(this) + BIT_FIELD_OFFSET); + } + + HClass hclass_; +}; +static_assert(JSHClass::BIT_FIELD_OFFSET % static_cast(MemAlignment::MEM_ALIGN_OBJECT) == 0); +static_assert(JSHClass::BIT_FIELD_OFFSET == JSHClass::GetHClassOffset() + HClass::GetDataOffset()); +static_assert(JSHClass::OBJECT_SIZE_OFFSET == JSHClass::GetHClassOffset() + BaseClass::GetObjectSizeOffset()); +static_assert(JSHClass::GetHClassOffset() == coretypes::DynClass::GetHClassOffset()); +} // namespace panda::ecmascript + +#endif // ECMASCRIPT_JS_HCLASS_H diff --git a/runtime/js_intl.h b/runtime/js_intl.h new file mode 100644 index 000000000..665f55dbb --- /dev/null +++ b/runtime/js_intl.h @@ -0,0 +1,39 @@ +/* + * Copyright (c) 2021-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_JSINTL_H +#define ECMASCRIPT_JSINTL_H + +#include "js_object.h" + +namespace panda::ecmascript { +class JSIntl : public JSObject { +public: + static JSIntl *Cast(ObjectHeader *object) + { + ASSERT(JSTaggedValue(object).IsJSIntl()); + return static_cast(object); + } + + static constexpr size_t FALLBACK_SYMBOL = JSObject::SIZE; + + ACCESSORS(FallbackSymbol, FALLBACK_SYMBOL, SIZE) + + DECL_VISIT_OBJECT_FOR_JS_OBJECT(JSObject, FALLBACK_SYMBOL, SIZE) + DECL_DUMP() +}; +} // namespace panda::ecmascript + +#endif // ECMASCRIPT_JSINTL_H \ No newline at end of file diff --git a/runtime/js_invoker.cpp b/runtime/js_invoker.cpp new file mode 100644 index 000000000..0ae06efe7 --- /dev/null +++ b/runtime/js_invoker.cpp @@ -0,0 +1,54 @@ +/* + * Copyright (c) 2021-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 "plugins/ecmascript/runtime/js_invoker.h" + +#include "plugins/ecmascript/runtime/ecma_runtime_call_info.h" +#include "plugins/ecmascript/runtime/internal_call_params.h" +#include "plugins/ecmascript/runtime/interpreter/interpreter-inl.h" +#include "plugins/ecmascript/runtime/js_tagged_value.h" +#include "plugins/ecmascript/runtime/js_thread.h" +#include "plugins/ecmascript/runtime/mem/c_containers.h" +#include "plugins/ecmascript/runtime/tagged_array-inl.h" +#include "libpandabase/utils/span.h" + +namespace panda::ecmascript { +JSTaggedValue JsInvoker::Invoke([[maybe_unused]] JSThread *thread) +{ + UNREACHABLE(); +} + +JSTaggedValue InvokeJsFunction(JSThread *thread, const JSHandle &func, const JSHandle &obj, + const JSHandle &newTgt, InternalCallParams *arguments) +{ + ASSERT(func->GetCallTarget() != nullptr); + + constexpr uint32_t extraSize = 3; // Include func newtarget and this + auto length = arguments->GetLength(); + CVector argArray; + argArray.reserve(length + extraSize); + + argArray.emplace_back(func.GetTaggedType()); + argArray.emplace_back(newTgt.GetTaggedType()); + argArray.emplace_back(obj.GetTaggedType()); + + for (array_size_t i = 0; i < length; ++i) { + // // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-pointer-arithmetic) + argArray.emplace_back(arguments->GetArgv()[i]); + } + + return EcmaInterpreter::Execute(thread, JSHandle::Cast(func), argArray.size(), argArray.data()); +} +} // namespace panda::ecmascript diff --git a/runtime/js_invoker.h b/runtime/js_invoker.h new file mode 100644 index 000000000..9251ed96d --- /dev/null +++ b/runtime/js_invoker.h @@ -0,0 +1,66 @@ +/* + * Copyright (c) 2021-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_INVOKE_H +#define ECMASCRIPT_INVOKE_H + +#include "plugins/ecmascript/runtime/js_function.h" +#include "plugins/ecmascript/runtime/js_handle.h" +#include "plugins/ecmascript/runtime/js_object.h" +#include "plugins/ecmascript/runtime/js_thread.h" +#include "plugins/ecmascript/runtime/mem/c_containers.h" + +namespace panda::ecmascript { +class JsInvoker { +public: + JsInvoker(const JSThread *thread, JSTaggedValue func, JSTaggedValue obj) + : JsInvoker(thread, func, obj, JSTaggedValue::Undefined()) + { + } + + JsInvoker(const JSThread *thread, JSTaggedValue func, JSTaggedValue obj, JSTaggedValue newTarget) + { + AddArgument(JSHandle(thread, func)); + AddArgument(JSHandle(thread, newTarget)); + AddArgument(JSHandle(thread, obj)); + } + + ~JsInvoker() = default; + NO_COPY_SEMANTIC(JsInvoker); + NO_MOVE_SEMANTIC(JsInvoker); + + template + void AddArgument(const JSHandle &arg) + { + args_.emplace_back(JSHandle(arg)); + } + + template + void AddArgument(JSHandle &&arg) + { + args_.emplace_back(std::move(arg)); + } + + JSTaggedValue Invoke(JSThread *thread); + +private: + CVector> args_{}; +}; + +JSTaggedValue InvokeJsFunction(JSThread *thread, const JSHandle &func, const JSHandle &obj, + const JSHandle &newTgt, InternalCallParams *arguments); +} // namespace panda::ecmascript + +#endif // ECMASCRIPT_INVOKE_H diff --git a/runtime/js_iterator.cpp b/runtime/js_iterator.cpp new file mode 100644 index 000000000..e480b1314 --- /dev/null +++ b/runtime/js_iterator.cpp @@ -0,0 +1,247 @@ +/* + * Copyright (c) 2021-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 "js_iterator.h" +#include "ecma_macros.h" +#include "ecma_vm.h" +#include "plugins/ecmascript/runtime/accessor_data.h" +#include "plugins/ecmascript/runtime/internal_call_params.h" +#include "global_env.h" +#include "js_invoker.h" +#include "js_symbol.h" +#include "object_factory.h" + +namespace panda::ecmascript { +JSTaggedValue JSIterator::IteratorCloseAndReturn(JSThread *thread, const JSHandle &iter, + [[maybe_unused]] const JSHandle &status) +{ + ASSERT(thread->HasPendingException()); + ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); + JSHandle record; + if (thread->GetException().IsObjectWrapper()) { + JSTaggedValue exception = ObjectWrapper::Cast(thread->GetException().GetTaggedObject())->GetValue(); + record = JSHandle( + factory->NewCompletionRecord(CompletionRecord::THROW, JSHandle(thread, exception))); + } else { + record = JSHandle(factory->NewCompletionRecord(CompletionRecord::NORMAL, status)); + } + JSHandle result = JSIterator::IteratorClose(thread, iter, record); + if (result->IsCompletionRecord()) { + return CompletionRecord::Cast(result->GetTaggedObject())->GetValue(); + } + return result.GetTaggedValue(); +} + +JSHandle JSIterator::GetIterator(JSThread *thread, const JSHandle &obj) +{ + // 1.ReturnIfAbrupt(obj). + RETURN_VALUE_IF_ABRUPT_COMPLETION(thread, obj); + // 2.If method was not passed, then + JSHandle env = thread->GetEcmaVM()->GetGlobalEnv(); + JSHandle iteratorSymbol = env->GetIteratorSymbol(); + JSHandle func = JSObject::GetMethod(thread, obj, iteratorSymbol); + RETURN_VALUE_IF_ABRUPT_COMPLETION(thread, obj); + + return GetIterator(thread, obj, func); +} + +JSHandle JSIterator::GetIterator(JSThread *thread, const JSHandle &obj, + const JSHandle &method) +{ + // 1.ReturnIfAbrupt(obj). + RETURN_VALUE_IF_ABRUPT_COMPLETION(thread, obj); + // 3.Let iterator be Call(method,obj). + JSTaggedValue ret = JSFunction::Call(thread, method, obj, 0, nullptr); + JSHandle iter(thread, ret); + // 4.ReturnIfAbrupt(iterator). + RETURN_VALUE_IF_ABRUPT_COMPLETION(thread, iter); + // 5.If Type(iterator) is not Object, throw a TypeError exception + if (!iter->IsECMAObject()) { + JSHandle undefinedHandle(thread, JSTaggedValue::Undefined()); + THROW_TYPE_ERROR_AND_RETURN(thread, "", undefinedHandle); + } + return iter; +} + +// TODO(frobert): Remove it, deprecated +JSHandle JSIterator::IteratorNextOld(JSThread *thread, const JSHandle &iter) +{ + // 1.If value was not passed, then Let result be Invoke(iterator, "next", «‍ »). + JSHandle key(thread->GlobalConstants()->GetHandledNextString()); + JSHandle next(JSObject::GetMethod(thread, iter, key)); + JSTaggedValue ret = JSFunction::Call(thread, next, iter, 0, nullptr); + JSHandle result(thread, ret); + // 3.ReturnIfAbrupt(result) + RETURN_VALUE_IF_ABRUPT_COMPLETION(thread, result); + // 4.If Type(result) is not Object, throw a TypeError exception. + if (!result.GetTaggedValue().IsHeapObject() || !result->IsECMAObject()) { + THROW_TYPE_ERROR_AND_RETURN(thread, "", result); + } + return result; +} + +// 7.4.2 +JSHandle JSIterator::IteratorNext(JSThread *thread, const JSHandle &iter, + const JSHandle &nextMethod) +{ + // 1.a. Let result be ? Call(iteratorRecord.[[NextMethod]], iteratorRecord.[[Iterator]]). + JSTaggedValue ret = JSFunction::Call(thread, nextMethod, iter, 0, nullptr); + JSHandle result(thread, ret); + RETURN_VALUE_IF_ABRUPT_COMPLETION(thread, result); + + // 3. If Type(result) is not Object, throw a TypeError exception. + if (!result->IsECMAObject()) { + THROW_TYPE_ERROR_AND_RETURN(thread, "", result); + } + + // 4. Return result. + return result; +} + +JSHandle JSIterator::IteratorNext(JSThread *thread, const JSHandle &iter, + const JSHandle &nextMethod, + const JSHandle &value) +{ + // 2.a. Let result be ? Call(iteratorRecord.[[NextMethod]], iteratorRecord.[[Iterator]], « value »). + InternalCallParams *arguments = thread->GetInternalCallParams(); + arguments->MakeArgv(value); + JSTaggedValue ret = JSFunction::Call(thread, nextMethod, iter, 1, arguments->GetArgv()); + JSHandle result(thread, ret); + RETURN_VALUE_IF_ABRUPT_COMPLETION(thread, result); + + // 3. If Type(result) is not Object, throw a TypeError exception. + if (!result->IsECMAObject()) { + THROW_TYPE_ERROR_AND_RETURN(thread, "", result); + } + + // 4. Return result. + return result; +} +// 7.4.3 +bool JSIterator::IteratorComplete(JSThread *thread, const JSHandle &iterResult) +{ + ASSERT_PRINT(iterResult->IsECMAObject(), "iterResult must be JSObject"); + // Return ToBoolean(Get(iterResult, "done")). + JSHandle doneStr = thread->GlobalConstants()->GetHandledDoneString(); + JSHandle done = JSTaggedValue::GetProperty(thread, iterResult, doneStr).GetValue(); + if (done->IsException()) { + return false; + } + return done->ToBoolean(); +} +// 7.4.4 +JSHandle JSIterator::IteratorValue(JSThread *thread, const JSHandle &iterResult) +{ + ASSERT_PRINT(iterResult->IsECMAObject(), "iterResult must be JSObject"); + // Return Get(iterResult, "value"). + JSHandle valueStr = thread->GlobalConstants()->GetHandledValueString(); + JSHandle value = JSTaggedValue::GetProperty(thread, iterResult, valueStr).GetValue(); + return value; +} +// 7.4.5 +JSHandle JSIterator::IteratorStep(JSThread *thread, const JSHandle &iter) +{ + // 1.Let result be IteratorNext(iterator). + JSHandle result(IteratorNextOld(thread, iter)); + // 2.ReturnIfAbrupt(result). + RETURN_VALUE_IF_ABRUPT_COMPLETION(thread, result); + // 3.Let done be IteratorComplete(result). + bool done = IteratorComplete(thread, result); + // 4.ReturnIfAbrupt(done). + JSHandle doneHandle(thread, JSTaggedValue(done)); + RETURN_VALUE_IF_ABRUPT_COMPLETION(thread, doneHandle); + // 5.If done is true, return false. + if (done) { + JSHandle falseHandle(thread, JSTaggedValue::False()); + return falseHandle; + } + return result; +} +// 7.4.6 +JSHandle JSIterator::IteratorClose(JSThread *thread, const JSHandle &iter, + const JSHandle &completion) +{ + // 1.Assert: Type(iterator) is Object. + ASSERT_PRINT(iter->IsECMAObject(), "iter must be JSObject"); + const GlobalEnvConstants *globalConst = thread->GlobalConstants(); + JSHandle exceptionOnThread; + if (thread->HasPendingException()) { + exceptionOnThread = JSHandle(thread, thread->GetException()); + thread->ClearException(); + } + JSHandle returnStr(globalConst->GetHandledReturnString()); + // 3.Let return be GetMethod(iterator, "return"). + JSHandle returnFunc(JSObject::GetMethod(thread, iter, returnStr)); + // 4.ReturnIfAbrupt(return). + RETURN_VALUE_IF_ABRUPT_COMPLETION(thread, returnFunc); + // 5.If return is undefined, return Completion(completion). + if (returnFunc->IsUndefined()) { + if (!exceptionOnThread.IsEmpty()) { + thread->SetException(exceptionOnThread.GetTaggedValue()); + } + return completion; + } + // 6.Let innerResult be Call(return, iterator, «‍ »). + JSTaggedValue ret = JSFunction::Call(thread, returnFunc, iter, 0, nullptr); + if (!exceptionOnThread.IsEmpty()) { + thread->SetException(exceptionOnThread.GetTaggedValue()); + } + JSHandle innerResult(thread, ret); + JSHandle completionRecord(thread, globalConst->GetUndefined()); + if (completion->IsCompletionRecord()) { + completionRecord = JSHandle::Cast(completion); + } + // 7.If completion.[[type]] is throw, return Completion(completion). + if (!completionRecord.GetTaggedValue().IsUndefined() && completionRecord->IsThrow()) { + if (!exceptionOnThread.IsEmpty()) { + thread->SetException(exceptionOnThread.GetTaggedValue()); + } + return completion; + } + // 8.If innerResult.[[type]] is throw, return Completion(innerResult). + if (thread->HasPendingException()) { + return innerResult; + } + // 9.If Type(innerResult.[[value]]) is not Object, throw a TypeError exception. + if (!innerResult->IsECMAObject()) { + JSHandle undefinedHandle(thread, JSTaggedValue::Undefined()); + THROW_TYPE_ERROR_AND_RETURN(thread, "", undefinedHandle); + } + if (!exceptionOnThread.IsEmpty()) { + thread->SetException(exceptionOnThread.GetTaggedValue()); + } + return completion; +} +// 7.4.7 +JSHandle JSIterator::CreateIterResultObject(JSThread *thread, const JSHandle &value, bool done) +{ + ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); + JSHandle env = thread->GetEcmaVM()->GetGlobalEnv(); + auto globalConst = thread->GlobalConstants(); + JSHandle constructor(env->GetObjectFunction()); + JSHandle valueStr = globalConst->GetHandledValueString(); + JSHandle doneStr = globalConst->GetHandledDoneString(); + JSHandle doneValue(thread, JSTaggedValue(done)); + // 2. Let obj be OrdinaryObjectCreate(%Object.prototype%). + JSHandle obj(factory->NewJSObjectByConstructor(JSHandle(constructor), constructor)); + // 3. Perform ! CreateDataPropertyOrThrow(obj, "value", value). + // 4. Perform ! CreateDataPropertyOrThrow(obj, "done", done). + JSObject::CreateDataPropertyOrThrow(thread, obj, valueStr, value); + JSObject::CreateDataPropertyOrThrow(thread, obj, doneStr, doneValue); + ASSERT_NO_ABRUPT_COMPLETION(thread); + // 5. Return obj. + return obj; +} +} // namespace panda::ecmascript diff --git a/runtime/js_iterator.h b/runtime/js_iterator.h new file mode 100644 index 000000000..893d310bf --- /dev/null +++ b/runtime/js_iterator.h @@ -0,0 +1,55 @@ +/* + * Copyright (c) 2021-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_JS_ITERATOR_H +#define ECMASCRIPT_JS_ITERATOR_H + +#include "plugins/ecmascript/runtime/js_tagged_value.h" +#include "tagged_array-inl.h" + +namespace panda::ecmascript { +enum class IterationKind { KEY = 0, VALUE, KEY_AND_VALUE }; +class JSIterator final { +public: + static JSTaggedValue IteratorCloseAndReturn(JSThread *thread, const JSHandle &iter, + const JSHandle &status); + // 7.4.1 + static JSHandle GetIterator(JSThread *thread, const JSHandle &obj); + + static JSHandle GetIterator(JSThread *thread, const JSHandle &obj, + const JSHandle &method); + // 7.4.2 + static JSHandle IteratorNextOld(JSThread *thread, const JSHandle &iter); + + static JSHandle IteratorNext(JSThread *thread, const JSHandle &iter, + const JSHandle &nextMethod, + const JSHandle &value); + + static JSHandle IteratorNext(JSThread *thread, const JSHandle &iter, + const JSHandle &nextMethod); + // 7.4.3 + static bool IteratorComplete(JSThread *thread, const JSHandle &iterResult); + // 7.4.4 + static JSHandle IteratorValue(JSThread *thread, const JSHandle &iterResult); + // 7.4.5 + static JSHandle IteratorStep(JSThread *thread, const JSHandle &iter); + // 7.4.6 + static JSHandle IteratorClose(JSThread *thread, const JSHandle &iter, + const JSHandle &completion); + // 7.4.7 + static JSHandle CreateIterResultObject(JSThread *thread, const JSHandle &value, bool done); +}; +} // namespace panda::ecmascript +#endif // ECMASCRIPT_JS_ITERATOR_H diff --git a/runtime/js_locale.cpp b/runtime/js_locale.cpp new file mode 100644 index 000000000..6f985b0fc --- /dev/null +++ b/runtime/js_locale.cpp @@ -0,0 +1,1394 @@ +/* + * Copyright (c) 2021-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 "base/string_helper.h" +#include "ecma_macros.h" +#include "ecma_vm.h" +#include "global_env.h" +#include "js_locale.h" +#include "object_factory.h" + +#include "unicode/localebuilder.h" +#include "unicode/localematcher.h" + +namespace panda::ecmascript { +// 6.2.2 IsStructurallyValidLanguageTag( locale ) +bool JSLocale::IsStructurallyValidLanguageTag(const JSHandle &tag) +{ + std::string tagCollection = ConvertToStdString(tag); + std::vector containers; + std::string substring; + std::set uniqueSubtags; + size_t address = 1; + for (auto it = tagCollection.begin(); it != tagCollection.end(); it++) { + if (*it != '-' && it != tagCollection.end() - 1) { + substring += *it; + } else { + if (it == tagCollection.end() - 1) { + substring += *it; + } + containers.push_back(substring); + if (IsVariantSubtag(substring)) { + std::transform(substring.begin(), substring.end(), substring.begin(), AsciiAlphaToLower); + if (!uniqueSubtags.insert(substring).second) { + return false; + } + } + substring.clear(); + } + } + bool result = DealwithLanguageTag(containers, address); + return result; +} + +std::string JSLocale::ConvertToStdString(const JSHandle &ecmaStr) +{ + return std::string(ConvertToString(*ecmaStr, StringConvertedUsage::LOGICOPERATION)); +} + +// 6.2.3 CanonicalizeUnicodeLocaleId( locale ) +JSHandle JSLocale::CanonicalizeUnicodeLocaleId(JSThread *thread, const JSHandle &locale) +{ + [[maybe_unused]] ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); + if (!IsStructurallyValidLanguageTag(locale)) { + THROW_RANGE_ERROR_AND_RETURN(thread, "invalid locale", factory->GetEmptyString()); + } + + if (locale->GetLength() == 0 || locale->IsUtf16()) { + THROW_RANGE_ERROR_AND_RETURN(thread, "invalid locale", factory->GetEmptyString()); + } + + std::string localeCStr = ConvertToStdString(locale); + std::transform(localeCStr.begin(), localeCStr.end(), localeCStr.begin(), AsciiAlphaToLower); + UErrorCode status = U_ZERO_ERROR; + icu::Locale formalLocale = icu::Locale::forLanguageTag(localeCStr.c_str(), status); + if ((U_FAILURE(status) != 0) || (formalLocale.isBogus() != 0)) { + THROW_RANGE_ERROR_AND_RETURN(thread, "invalid locale", factory->GetEmptyString()); + } + + // Resets the LocaleBuilder to match the locale. + // Returns an instance of Locale created from the fields set on this builder. + formalLocale = icu::LocaleBuilder().setLocale(formalLocale).build(status); + // Canonicalize the locale ID of this object according to CLDR. + formalLocale.canonicalize(status); + if ((U_FAILURE(status) != 0) || (formalLocale.isBogus() != 0)) { + THROW_RANGE_ERROR_AND_RETURN(thread, "invalid locale", factory->GetEmptyString()); + } + JSHandle languageTag = ToLanguageTag(thread, formalLocale); + RETURN_HANDLE_IF_ABRUPT_COMPLETION(EcmaString, thread); + return languageTag; +} + +// 6.2.4 DefaultLocale () +JSHandle JSLocale::DefaultLocale(JSThread *thread) +{ + icu::Locale defaultLocale; + auto globalConst = thread->GlobalConstants(); + if (strcmp(defaultLocale.getName(), "en_US_POSIX") == 0 || strcmp(defaultLocale.getName(), "c") == 0) { + return JSHandle::Cast(globalConst->GetHandledEnUsString()); + } + if (defaultLocale.isBogus() != 0) { + return JSHandle::Cast(globalConst->GetHandledUndString()); + } + return ToLanguageTag(thread, defaultLocale); +} + +// 6.4.1 IsValidTimeZoneName ( timeZone ) +bool JSLocale::IsValidTimeZoneName(const icu::TimeZone &tz) +{ + UErrorCode status = U_ZERO_ERROR; + icu::UnicodeString id; + tz.getID(id); + icu::UnicodeString canonical; + icu::TimeZone::getCanonicalID(id, canonical, status); + UBool canonicalFlag = (canonical != icu::UnicodeString("Etc/Unknown", -1, US_INV)); + return (U_SUCCESS(status) != 0) && (canonicalFlag != 0); +} + +void JSLocale::HandleLocaleExtension(size_t &start, size_t &extensionEnd, const std::string &result, size_t len) +{ + bool flag = false; + while (start < len - INTL_INDEX_TWO) { + if (result[start] != '-') { + start++; + continue; + } + if (result[start + INTL_INDEX_TWO] == '-') { + extensionEnd = start; + break; + } + if (!flag) { + start++; + } + start += INTL_INDEX_TWO; + } +} + +JSLocale::ParsedLocale JSLocale::HandleLocale(const JSHandle &localeString) +{ + std::string result = ConvertToStdString(localeString); + size_t len = result.size(); + ParsedLocale parsedResult; + + // a. The single-character subtag ’x’ as the primary subtag indicates + // that the language tag consists solely of subtags whose meaning is + // defined by private agreement. + // b. Extensions cannot be used in tags that are entirely private use. + if (IsPrivateSubTag(result, len)) { + parsedResult.base = result; + return parsedResult; + } + // If cannot find "-u-", return the whole string as base. + size_t foundExtension = result.find("-u-"); + if (foundExtension == std::string::npos) { + parsedResult.base = result; + return parsedResult; + } + // Let privateIndex be Call(%StringProto_indexOf%, foundLocale, « "-x-" »). + size_t praviteIndex = result.find("-x-"); + if (praviteIndex != std::string::npos && praviteIndex < foundExtension) { + parsedResult.base = result; + return parsedResult; + } + const std::string basis = result.substr(INTL_INDEX_ZERO, foundExtension); + size_t extensionEnd = len; + ASSERT(len > INTL_INDEX_TWO); + size_t start = foundExtension + INTL_INDEX_ONE; + HandleLocaleExtension(start, extensionEnd, result, len); + const std::string end = result.substr(extensionEnd); + parsedResult.base = basis + end; + parsedResult.extension = result.substr(foundExtension, extensionEnd - foundExtension); + return parsedResult; +} + +// 9.2.1 CanonicalizeLocaleList ( locales ) +JSHandle JSLocale::CanonicalizeLocaleList(JSThread *thread, const JSHandle &locales) +{ + ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); + // 1. If locales is undefined, then + // a. Return a new empty List. + if (locales->IsUndefined()) { + return factory->EmptyArray(); + } + // 2. Let seen be a new empty List. + JSHandle localeSeen = factory->NewTaggedArray(1); + // 3. If Type(locales) is String or Type(locales) is Object and locales has an [[InitializedLocale]] internal slot, + // then + // a. Let O be CreateArrayFromList(« locales »). + // 4. Else, + // a.Let O be ? ToObject(locales). + if (locales->IsString()) { + JSHandle tag = JSHandle::Cast(locales); + JSHandle temp = factory->NewTaggedArray(1); + temp->Set(thread, 0, tag.GetTaggedValue()); + JSHandle obj = JSArray::CreateArrayFromList(thread, temp); + JSHandle finalSeen = CanonicalizeHelper(thread, locales, obj, localeSeen); + return finalSeen; + } + if (locales->IsJSLocale()) { + JSHandle tag = JSLocale::ToString(thread, JSHandle::Cast(locales)); + JSHandle temp = factory->NewTaggedArray(1); + RETURN_HANDLE_IF_ABRUPT_COMPLETION(TaggedArray, thread); + temp->Set(thread, 0, tag.GetTaggedValue()); + JSHandle obj = JSArray::CreateArrayFromList(thread, temp); + JSHandle finalSeen = CanonicalizeHelper(thread, locales, obj, localeSeen); + return finalSeen; + } + JSHandle obj = JSTaggedValue::ToObject(thread, locales); + RETURN_HANDLE_IF_ABRUPT_COMPLETION(TaggedArray, thread); + JSHandle finalSeen = CanonicalizeHelper(thread, locales, obj, localeSeen); + return finalSeen; +} + +template +JSHandle JSLocale::CanonicalizeHelper(JSThread *thread, const JSHandle &locales, + JSHandle &obj, JSHandle &seen) +{ + (void)locales; + OperationResult operationResult = JSTaggedValue::GetProperty(thread, JSHandle::Cast(obj), + thread->GlobalConstants()->GetHandledLengthString()); + RETURN_HANDLE_IF_ABRUPT_COMPLETION(TaggedArray, thread); + JSTaggedNumber len = JSTaggedValue::ToLength(thread, operationResult.GetValue()); + RETURN_HANDLE_IF_ABRUPT_COMPLETION(TaggedArray, thread); + ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); + // 2. Let seen be a new empty List. + uint32_t requestedLocalesLen = len.ToUint32(); + seen = factory->NewTaggedArray(requestedLocalesLen); + // 6. Let k be 0. + // 7. Repeat, while k < len + JSMutableHandle pk(thread, JSTaggedValue::Undefined()); + JSMutableHandle tag(thread, JSTaggedValue::Undefined()); + uint32_t index = 0; + JSHandle objTagged = JSHandle::Cast(obj); + for (uint32_t k = 0; k < requestedLocalesLen; k++) { + // a. Let Pk be ToString(k). + JSHandle kHandle(thread, JSTaggedValue(k)); + JSHandle str = JSTaggedValue::ToString(thread, kHandle); + RETURN_HANDLE_IF_ABRUPT_COMPLETION(TaggedArray, thread); + pk.Update(str.GetTaggedValue()); + // b. Let kPresent be ? HasProperty(O, Pk). + bool kPresent = JSTaggedValue::HasProperty(thread, objTagged, pk); + RETURN_HANDLE_IF_ABRUPT_COMPLETION(TaggedArray, thread); + + // c. If kPresent is true, then + if (kPresent) { + // i. Let kValue be ? Get(O, Pk). + OperationResult result = JSTaggedValue::GetProperty(thread, objTagged, pk); + RETURN_HANDLE_IF_ABRUPT_COMPLETION(TaggedArray, thread); + JSHandle kValue = result.GetValue(); + // ii. If Type(kValue) is not String or Object, throw a TypeError exception. + if (!kValue->IsString() && !kValue->IsJSObject()) { + THROW_TYPE_ERROR_AND_RETURN(thread, "kValue is not String or Object.", factory->EmptyArray()); + } + // iii. If Type(kValue) is Object and kValue has an [[InitializedLocale]] internal slot, then + // 1. Let tag be kValue.[[Locale]]. + // iv. Else, + // 1. Let tag be ? ToString(kValue). + if (kValue->IsJSLocale()) { + JSHandle kValueStr = JSLocale::ToString(thread, JSHandle::Cast(kValue)); + RETURN_HANDLE_IF_ABRUPT_COMPLETION(TaggedArray, thread); + tag.Update(kValueStr.GetTaggedValue()); + } else { + JSHandle kValueString = JSTaggedValue::ToString(thread, kValue); + RETURN_HANDLE_IF_ABRUPT_COMPLETION(TaggedArray, thread); + JSHandle str = CanonicalizeUnicodeLocaleId(thread, kValueString); + RETURN_HANDLE_IF_ABRUPT_COMPLETION(TaggedArray, thread); + tag.Update(str.GetTaggedValue()); + } + // vii. If canonicalizedTag is not an element of seen, append canonicalizedTag as the last element of seen. + bool isExist = false; + uint32_t len = seen->GetLength(); + for (uint32_t i = 0; i < len; i++) { + if (JSTaggedValue::SameValue(seen->Get(thread, i), tag.GetTaggedValue())) { + isExist = true; + } + } + if (!isExist) { + seen->Set(thread, index++, JSHandle::Cast(tag)); + } + } + // d. Increase k by 1. + } + // set capacity + seen = TaggedArray::SetCapacity(thread, seen, index); + // 8. Return seen. + return seen; +} + +// 9.2.2 BestAvailableLocale ( availableLocales, locale ) +std::string JSLocale::BestAvailableLocale(JSThread *thread, const JSHandle &availableLocales, + const std::string &locale) +{ + // 1. Let candidate be locale. + std::string localeCandidate = locale; + std::string undefined = std::string(); + // 2. Repeat, + uint32_t length = availableLocales->GetLength(); + JSMutableHandle item(thread, JSTaggedValue::Undefined()); + while (true) { + // a. If availableLocales contains an element equal to candidate, return candidate. + for (uint32_t i = 0; i < length; ++i) { + item.Update(availableLocales->Get(thread, i)); + std::string itemStr = ConvertToStdString(item); + if (itemStr == localeCandidate) { + return localeCandidate; + } + } + // b. Let pos be the character index of the last occurrence of "-" (U+002D) within candidate. + // If that character does not occur, return undefined. + size_t pos = localeCandidate.rfind('-'); + if (pos == std::string::npos) { + return undefined; + } + // c. If pos ≥ 2 and the character "-" occurs at index pos-2 of candidate, decrease pos by 2. + if (pos >= INTL_INDEX_TWO && localeCandidate[pos - INTL_INDEX_TWO] == '-') { + pos -= INTL_INDEX_TWO; + } + // d. Let candidate be the substring of candidate from position 0, inclusive, to position pos, exclusive. + localeCandidate = localeCandidate.substr(0, pos); + } +} + +// 9.2.3 LookupMatcher ( availableLocales, requestedLocales ) +JSHandle JSLocale::LookupMatcher(JSThread *thread, const JSHandle &availableLocales, + const JSHandle &requestedLocales) +{ + MatcherResult result = {std::string(), std::string()}; + ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); + // 1. Let result be a new Record. + // 2. For each element locale of requestedLocales in List order, do + uint32_t length = requestedLocales->GetLength(); + JSMutableHandle locale(thread, JSTaggedValue::Undefined()); + for (uint32_t i = 0; i < length; ++i) { + locale.Update(requestedLocales->Get(thread, i)); + // 2. a. Let noExtensionsLocale be the String value that is locale + // with all Unicode locale extension sequences removed. + ParsedLocale parsedResult = HandleLocale(locale); + // 2. b. Let availableLocale be BestAvailableLocale(availableLocales, noExtensionsLocale). + std::string availableLocale = BestAvailableLocale(thread, availableLocales, parsedResult.base); + // 2. c. If availableLocale is not undefined, append locale to the end of subset. + if (!availableLocale.empty()) { + result = {std::string(), std::string()}; + // 2. c. i. Set result.[[locale]] to availableLocale. + result.locale = availableLocale; + // 2. c. ii. If locale and noExtensionsLocale are not the same String value, then + // 2. c. ii. 1. Let extension be the String value consisting of the first substring of locale that is a + // Unicode locale extension sequence. + if (!parsedResult.extension.empty()) { + result.extension = parsedResult.extension; + } + // 2. c. ii. 2. Set result.[[extension]] to extension. + std::string res = result.locale + result.extension; + // 2. c. iii. Return result. + return factory->NewFromStdString(res); + } + } + + // 3. Let defLocale be DefaultLocale(); + // 4. Set result.[[locale]] to defLocale. + // 5. Return result. + std::string defLocale = ConvertToStdString(DefaultLocale(thread)); + result.locale = defLocale; + return factory->NewFromStdString(result.locale); +} + +icu::LocaleMatcher BuildLocaleMatcher(JSThread *thread, uint32_t *availableLength, UErrorCode *status, + const JSHandle &availableLocales) +{ + std::string locale = JSLocale::ConvertToStdString(JSLocale::DefaultLocale(thread)); + icu::Locale defaultLocale = icu::Locale::forLanguageTag(locale, *status); + ASSERT_PRINT(U_SUCCESS(*status), "icu::Locale::forLanguageTag failed"); + icu::LocaleMatcher::Builder builder; + builder.setDefaultLocale(&defaultLocale); + uint32_t length = availableLocales->GetLength(); + + JSMutableHandle item(thread, JSTaggedValue::Undefined()); + for (*availableLength = 0; *availableLength < length; ++(*availableLength)) { + item.Update(availableLocales->Get(thread, *availableLength)); + std::string itemStr = JSLocale::ConvertToStdString(item); + icu::Locale localeForLanguageTag = icu::Locale::forLanguageTag(itemStr, *status); + if (U_SUCCESS(*status) != 0) { + builder.addSupportedLocale(localeForLanguageTag); + } else { + break; + } + } + *status = U_ZERO_ERROR; + return builder.build(*status); +} + +// 9.2.4 BestFitMatcher ( availableLocales, requestedLocales ) +JSHandle JSLocale::BestFitMatcher(JSThread *thread, const JSHandle &availableLocales, + const JSHandle &requestedLocales) +{ + ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); + UErrorCode status = U_ZERO_ERROR; + uint32_t availableLength = availableLocales->GetLength(); + icu::LocaleMatcher matcher = BuildLocaleMatcher(thread, &availableLength, &status, availableLocales); + ASSERT(U_SUCCESS(status)); + + uint32_t requestedLocalesLength = requestedLocales->GetLength(); + JSIntlIterator iter(requestedLocales, requestedLocalesLength); + auto bestFit = matcher.getBestMatch(iter, status)->toLanguageTag(status); + + if (U_FAILURE(status) != 0) { + return DefaultLocale(thread); + } + + for (uint32_t i = 0; i < requestedLocalesLength; ++i) { + if (iter[i] == bestFit) { + return JSHandle(thread, requestedLocales->Get(thread, i)); + } + } + return factory->NewFromStdString(bestFit); +} + +// 9.2.8 LookupSupportedLocales ( availableLocales, requestedLocales ) +JSHandle JSLocale::LookupSupportedLocales(JSThread *thread, const JSHandle &availableLocales, + const JSHandle &requestedLocales) +{ + uint32_t index = 0; + ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); + uint32_t length = requestedLocales->GetLength(); + // 1. Let subset be a new empty List. + JSHandle subset = factory->NewTaggedArray(length); + JSMutableHandle item(thread, JSTaggedValue::Undefined()); + // 2. For each element locale of requestedLocales in List order, do + // a. Let noExtensionsLocale be the String value that is locale with all Unicode locale extension sequences + // removed. + // b. Let availableLocale be BestAvailableLocale(availableLocales, noExtensionsLocale). + // c. If availableLocale is not undefined, append locale to the end of subset. + for (uint32_t i = 0; i < length; ++i) { + item.Update(requestedLocales->Get(thread, i)); + ParsedLocale foundationResult = HandleLocale(item); + std::string availableLocale = BestAvailableLocale(thread, availableLocales, foundationResult.base); + if (!availableLocale.empty()) { + subset->Set(thread, index++, item.GetTaggedValue()); + } + } + // 3. Return subset. + return TaggedArray::SetCapacity(thread, subset, index); +} + +// 9.2.9 BestFitSupportedLocales ( availableLocales, requestedLocales ) +JSHandle JSLocale::BestFitSupportedLocales(JSThread *thread, const JSHandle &availableLocales, + const JSHandle &requestedLocales) +{ + UErrorCode status = U_ZERO_ERROR; + uint32_t requestLength = requestedLocales->GetLength(); + uint32_t availableLength = availableLocales->GetLength(); + icu::LocaleMatcher matcher = BuildLocaleMatcher(thread, &availableLength, &status, availableLocales); + ASSERT(U_SUCCESS(status)); + + ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); + JSHandle defaultLocale = DefaultLocale(thread); + JSHandle result = factory->NewTaggedArray(requestLength); + + uint32_t index = 0; + JSMutableHandle locale(thread, JSTaggedValue::Undefined()); + for (uint32_t i = 0; i < requestLength; ++i) { + locale.Update(requestedLocales->Get(thread, i)); + if (EcmaString::StringsAreEqual(*locale, *defaultLocale)) { + result->Set(thread, index++, locale.GetTaggedValue()); + } else { + status = U_ZERO_ERROR; + std::string localeStr = ConvertToStdString(locale); + icu::Locale desired = icu::Locale::forLanguageTag(localeStr, status); + auto bestFit = matcher.getBestMatch(desired, status)->toLanguageTag(status); + if ((U_SUCCESS(status) != 0) && + EcmaString::StringsAreEqual(*locale, *(factory->NewFromStdString(bestFit)))) { + result->Set(thread, index++, locale.GetTaggedValue()); + } + } + } + result = TaggedArray::SetCapacity(thread, result, index); + return result; +} + +JSHandle JSLocale::ToLanguageTag(JSThread *thread, const icu::Locale &locale) +{ + UErrorCode status = U_ZERO_ERROR; + ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); + auto result = locale.toLanguageTag(status); + bool flag = U_FAILURE(status) == 0; + if (!flag) { + THROW_RANGE_ERROR_AND_RETURN(thread, "invalid locale", factory->GetEmptyString()); + } + size_t findBeginning = result.find("-u-"); + std::string finalRes; + std::string tempRes; + if (findBeginning == std::string::npos) { + return factory->NewFromStdString(result); + } + size_t specialBeginning = findBeginning + INTL_INDEX_THREE; + size_t specialCount = 0; + while (specialBeginning < result.size() && result[specialBeginning] != '-') { + specialCount++; + specialBeginning++; + } + if (findBeginning != std::string::npos) { + // It begin with "-u-xx" or with more elements. + tempRes = result.substr(0, findBeginning + INTL_INDEX_THREE + specialCount); + if (result.size() <= findBeginning + INTL_INDEX_THREE + specialCount) { + return factory->NewFromStdString(result); + } + std::string leftStr = result.substr(findBeginning + INTL_INDEX_THREE + specialCount + INTL_INDEX_ONE); + std::istringstream temp(leftStr); + std::string buffer; + std::vector resContainer; + while (getline(temp, buffer, '-')) { + if (buffer != "true" && buffer != "yes") { + resContainer.push_back(buffer); + } + } + for (auto &it : resContainer) { + std::string tag = "-"; + tag += it; + finalRes += tag; + } + } + if (!finalRes.empty()) { + tempRes += finalRes; + } + result = tempRes; + return factory->NewFromStdString(result); +} + +// 9.2.10 SupportedLocales ( availableLocales, requestedLocales, options ) +JSHandle JSLocale::SupportedLocales(JSThread *thread, const JSHandle &availableLocales, + const JSHandle &requestedLocales, + const JSHandle &options) +{ + const GlobalEnvConstants *globalConst = thread->GlobalConstants(); + // 1. If options is not undefined, then + // a. Let options be ? ToObject(options). + // b. Let matcher be ? GetOption(options, "localeMatcher", "string", « "lookup", "best fit" », "best fit"). + // 2. Else, let matcher be "best fit". + LocaleMatcherOption matcher = LocaleMatcherOption::BEST_FIT; + if (!options->IsUndefined()) { + JSHandle obj = JSTaggedValue::ToObject(thread, options); + RETURN_HANDLE_IF_ABRUPT_COMPLETION(JSArray, thread); + + matcher = GetOptionOfString(thread, obj, globalConst->GetHandledLocaleMatcherString(), + {LocaleMatcherOption::LOOKUP, LocaleMatcherOption::BEST_FIT}, + {"lookup", "best fit"}, LocaleMatcherOption::BEST_FIT); + RETURN_HANDLE_IF_ABRUPT_COMPLETION(JSArray, thread); + } + + // 3. If matcher is "best fit", then + // a. Let supportedLocales be BestFitSupportedLocales(availableLocales, requestedLocales). + // 4. Else, + // a. Let supportedLocales be LookupSupportedLocales(availableLocales, requestedLocales). + JSMutableHandle supportedLocales(thread, JSTaggedValue::Undefined()); + bool isBestfitSupport = false; + if (matcher == LocaleMatcherOption::BEST_FIT && isBestfitSupport) { + supportedLocales.Update(BestFitSupportedLocales(thread, availableLocales, requestedLocales).GetTaggedValue()); + } else { + supportedLocales.Update(LookupSupportedLocales(thread, availableLocales, requestedLocales).GetTaggedValue()); + } + + JSHandle subset = JSArray::CreateArrayFromList(thread, supportedLocales); + // 5. Return CreateArrayFromList(supportedLocales). + return subset; +} + +// 9.2.11 GetOption ( options, property, type, values, fallback ) +JSHandle JSLocale::GetOption(JSThread *thread, const JSHandle &options, + const JSHandle &property, OptionType type, + const JSHandle &values, + const JSHandle &fallback) +{ + // 1. Let value be ? Get(options, property). + JSHandle value = JSObject::GetProperty(thread, options, property).GetValue(); + RETURN_HANDLE_IF_ABRUPT_COMPLETION(JSTaggedValue, thread); + + // 2. If value is not undefined, then + if (!value->IsUndefined()) { + // a. Assert: type is "boolean" or "string". + ASSERT_PRINT(type == OptionType::BOOLEAN || type == OptionType::STRING, "type is not boolean or string"); + + // b. If type is "boolean", then + // i. Let value be ToBoolean(value). + if (type == OptionType::BOOLEAN) { + value = JSHandle(thread, JSTaggedValue(value->ToBoolean())); + } + // c. If type is "string", then + // i. Let value be ? ToString(value). + if (type == OptionType::STRING) { + JSHandle str = JSTaggedValue::ToString(thread, value); + RETURN_HANDLE_IF_ABRUPT_COMPLETION(JSTaggedValue, thread); + value = JSHandle(thread, str.GetTaggedValue()); + } + + // d. If values is not undefined, then + // i. If values does not contain an element equal to value, throw a RangeError exception. + if (!values->IsUndefined()) { + bool isExist = false; + JSHandle valuesArray = JSHandle::Cast(values); + uint32_t length = valuesArray->GetLength(); + for (uint32_t i = 0; i < length; i++) { + if (JSTaggedValue::SameValue(valuesArray->Get(thread, i), value.GetTaggedValue())) { + isExist = true; + } + } + if (!isExist) { + JSHandle exception(thread, JSTaggedValue::Exception()); + THROW_RANGE_ERROR_AND_RETURN(thread, "values does not contain an element equal to value", exception); + } + } + // e. Return value. + return value; + } + // 3. Else, return fallback. + return fallback; +} + +bool JSLocale::GetOptionOfString(JSThread *thread, const JSHandle &options, + const JSHandle &property, const std::vector &values, + std::string *optionValue) +{ + // 1. Let value be ? Get(options, property). + OperationResult operationResult = JSObject::GetProperty(thread, options, property); + RETURN_VALUE_IF_ABRUPT_COMPLETION(thread, false); + JSHandle value = operationResult.GetValue(); + // 2. If value is not undefined, then + if (value->IsUndefined()) { + return false; + } + // c. If type is "string" "string", then + // i. Let value be ? ToString(value). + JSHandle valueEStr = JSTaggedValue::ToString(thread, value); + RETURN_VALUE_IF_ABRUPT_COMPLETION(thread, false); + if (valueEStr->IsUtf16()) { + THROW_RANGE_ERROR_AND_RETURN(thread, "Value out of range for locale options property", false); + } + *optionValue = JSLocale::ConvertToStdString(valueEStr); + if (values.empty()) { + return true; + } + // d. If values is not undefined, then + // i. If values does not contain an element equal to value, throw a RangeError exception. + for (const auto &item : values) { + if (item == *optionValue) { + return true; + } + } + THROW_RANGE_ERROR_AND_RETURN(thread, "Value out of range for locale options property", false); +} + +// 9.2.12 DefaultNumberOption ( value, minimum, maximum, fallback ) +int JSLocale::DefaultNumberOption(JSThread *thread, const JSHandle &value, int minimum, int maximum, + int fallback) +{ + // 1. If value is not undefined, then + if (!value->IsUndefined()) { + // a. Let value be ? ToNumber(value). + JSTaggedNumber number = JSTaggedValue::ToNumber(thread, value); + RETURN_VALUE_IF_ABRUPT_COMPLETION(thread, fallback); + // b. If value is NaN or less than minimum or greater than maximum, throw a RangeError exception. + double num = JSTaggedValue(number).GetNumber(); + if (std::isnan(num) || num < minimum || num > maximum) { + THROW_RANGE_ERROR_AND_RETURN(thread, "", fallback); + } + // c. Return floor(value). + return std::floor(num); + } + // 2. Else, return fallback. + return fallback; +} + +// 9.2.13 GetNumberOption ( options, property, minimum, maximum, fallback ) +int JSLocale::GetNumberOption(JSThread *thread, const JSHandle &options, + const JSHandle &property, int min, int max, int fallback) +{ + // 1. Let value be ? Get(options, property). + JSHandle value = JSObject::GetProperty(thread, options, property).GetValue(); + RETURN_VALUE_IF_ABRUPT_COMPLETION(thread, fallback); + + // 2. Return ? DefaultNumberOption(value, minimum, maximum, fallback). + int result = DefaultNumberOption(thread, value, min, max, fallback); + RETURN_VALUE_IF_ABRUPT_COMPLETION(thread, fallback); + return result; +} + +// 9.2.5 UnicodeExtensionValue ( extension, key ) +std::string JSLocale::UnicodeExtensionValue(const std::string &extension, const std::string &key) +{ + // 1. Assert: The number of elements in key is 2. + // 2. Let size be the number of elements in extension. + ASSERT(key.size() == INTL_INDEX_TWO); + size_t size = extension.size(); + // 3. Let searchValue be the concatenation of "-" , key, and "-". + std::string searchValue = "-" + key + "-"; + // 4. Let pos be Call(%StringProto_indexOf%, extension, « searchValue »). + size_t pos = extension.find(searchValue); + // 5. If pos ≠ -1, then + if (pos != std::string::npos) { + // a. Let start be pos + 4. + size_t start = pos + INTL_INDEX_FOUR; + // b. Let end be start. + size_t end = start; + // c. Let k be start. + size_t k = start; + // d. Let done be false. + bool done = false; + // e. Repeat, while done is false + while (!done) { + // i. Let e be Call(%StringProto_indexOf%, extension, « "-", k »). + size_t e = extension.find('-', k); + size_t len; + // ii. If e = -1, let len be size - k; else let len be e - k. + if (e == std::string::npos) { + len = size - k; + } else { + len = e - k; + } + // iii. If len = 2, then + // 1. Let done be true. + if (len == INTL_INDEX_TWO) { + done = true; + // iv. Else if e = -1, then + // 1. Let end be size. + // 2. Let done be true. + } else if (e == std::string::npos) { + end = size; + done = true; + // v. Else, + // 1. Let end be e. + // 2. Let k be e + 1. + } else { + end = e; + k = e + INTL_INDEX_ONE; + } + } + // f. Return the String value equal to the substring of extension consisting of the code units at indices. + // start (inclusive) through end (exclusive). + std::string result = extension.substr(start, end - start); + return result; + } + // 6. Let searchValue be the concatenation of "-" and key. + searchValue = "-" + key; + // 7. Let pos be Call(%StringProto_indexOf%, extension, « searchValue »). + pos = extension.find(searchValue); + // 8. If pos ≠ -1 and pos + 3 = size, then + // a. Return the empty String. + if (pos != std::string::npos && pos + INTL_INDEX_THREE == size) { + return ""; + } + // 9. Return undefined. + return "undefined"; +} + +ResolvedLocale JSLocale::ResolveLocale(JSThread *thread, const JSHandle &availableLocales, + const JSHandle &requestedLocales, LocaleMatcherOption matcher, + const std::set &relevantExtensionKeys) +{ + bool isBestfitSupport = false; + std::map> localeMap = {{"hc", {"h11", "h12", "h23", "h24"}}, + {"lb", {"strict", "normal", "loose"}}, + {"kn", {"true", "false"}}, + {"kf", {"upper", "lower", "false"}}}; + + // 1. Let matcher be options.[[localeMatcher]]. + // 2. If matcher is "lookup" "lookup", then + // a. Let r be LookupMatcher(availableLocales, requestedLocales). + // 3. Else, + // a. Let r be BestFitMatcher(availableLocales, requestedLocales). + JSMutableHandle locale(thread, JSTaggedValue::Undefined()); + if (availableLocales->GetLength() == 0 && requestedLocales->GetLength() == 0) { + locale.Update(DefaultLocale(thread).GetTaggedValue()); + } else { + if (matcher == LocaleMatcherOption::BEST_FIT && isBestfitSupport) { + locale.Update(BestFitMatcher(thread, availableLocales, requestedLocales).GetTaggedValue()); + } else { + locale.Update(LookupMatcher(thread, availableLocales, requestedLocales).GetTaggedValue()); + } + } + + // 4. Let foundLocale be r.[[locale]]. + // 5. Let result be a new Record. + // 6. Set result.[[dataLocale]] to foundLocale. + // 7. Let supportedExtension be "-u". + std::string foundLocale = ConvertToStdString(locale); + icu::Locale foundLocaleData = BuildICULocale(foundLocale); + ResolvedLocale result; + result.localeData = foundLocaleData; + JSHandle tag = ToLanguageTag(thread, foundLocaleData); + result.locale = ConvertToStdString(tag); + std::string supportedExtension = "-u"; + icu::LocaleBuilder localeBuilder; + localeBuilder.setLocale(foundLocaleData).clearExtensions(); + // 8. For each element key of relevantExtensionKeys in List order, do + for (auto &key : relevantExtensionKeys) { + auto doubleMatch = foundLocale.find(key); + if (doubleMatch == std::string::npos) { + continue; + } + UErrorCode status = U_ZERO_ERROR; + std::set keyLocaleData; + std::unique_ptr wellFormKey(foundLocaleData.createKeywords(status)); + if (U_FAILURE(status) != 0) { + return result; + } + if (!wellFormKey) { + return result; + } + std::string value; + + // c. Let keyLocaleData be foundLocaleData.[[]]. + // e. Let value be keyLocaleData[0]. + if ((key != "ca") && (key != "co") && (key != "nu")) { + keyLocaleData = localeMap[key]; + value = *keyLocaleData.begin(); + } + + // g. Let supportedExtensionAddition be "". + // h. If r has an [[extension]] field, then + std::string supportedExtensionAddition; + size_t found = foundLocale.find("-u-"); + if (found != std::string::npos) { + std::string extension = foundLocale.substr(found + INTL_INDEX_ONE); + + // i. Let requestedValue be UnicodeExtensionValue(r.[[extension]], key). + std::string requestedValue = UnicodeExtensionValue(extension, key); + if (key == "kn" && requestedValue.empty()) { + requestedValue = "true"; + } + + // ii. If requestedValue is not undefined, then + if (requestedValue != "undefined") { + // 1. If requestedValue is not the empty String, then + if (!requestedValue.empty()) { + // a. If keyLocaleData contains requestedValue, then + // i. Let value be requestedValue. + // ii. Let supportedExtensionAddition be the concatenation of "-", key, "-", and value. + if (key == "ca" || key == "co") { + if (key == "co") { + bool isValidValue = IsWellCollation(foundLocaleData, requestedValue); + if (!isValidValue) { + continue; + } + value = requestedValue; + supportedExtensionAddition = std::string("-").append(key).append("-").append(value); + localeBuilder.setUnicodeLocaleKeyword(key, requestedValue); + } else { + bool isValidValue = IsWellCalendar(foundLocaleData, requestedValue); + if (!isValidValue) { + continue; + } + value = requestedValue; + supportedExtensionAddition = std::string("-").append(key).append("-").append(value); + localeBuilder.setUnicodeLocaleKeyword(key, requestedValue); + } + } else if (key == "nu") { + bool isValidValue = IsWellNumberingSystem(requestedValue); + if (!isValidValue) { + continue; + } + value = requestedValue; + supportedExtensionAddition = std::string("-").append(key).append("-").append(value); + localeBuilder.setUnicodeLocaleKeyword(key, requestedValue); + } else if (keyLocaleData.find(requestedValue) != keyLocaleData.end()) { + value = requestedValue; + supportedExtensionAddition = std::string("-").append(key).append("-").append(value); + localeBuilder.setUnicodeLocaleKeyword(key, requestedValue); + } + } + } + } + result.extensions.insert(std::pair(key, value)); + supportedExtension += supportedExtensionAddition; + } + size_t found = foundLocale.find("-u-"); + if (found != std::string::npos) { + foundLocale = foundLocale.substr(0, found); + } + + // 9. If the number of elements in supportedExtension is greater than 2, then + if (supportedExtension.size() > 2) { + // a. Let privateIndex be Call(%StringProto_indexOf%, foundLocale, « "-x-" »). + size_t privateIndex = foundLocale.find("-x-"); + // b. If privateIndex = -1, then + // i. Let foundLocale be the concatenation of foundLocale and supportedExtension. + if (privateIndex == std::string::npos) { + foundLocale = foundLocale + supportedExtension; + } else { + std::string preExtension = foundLocale.substr(0, privateIndex); + std::string postExtension = foundLocale.substr(privateIndex); + foundLocale = preExtension + supportedExtension + postExtension; + } + + tag = ToLanguageTag(thread, foundLocaleData); + if (!IsStructurallyValidLanguageTag(tag)) { + result.extensions.erase(result.extensions.begin(), result.extensions.end()); + result.locale = foundLocale; + } + tag = CanonicalizeUnicodeLocaleId(thread, tag); + foundLocale = ConvertToStdString(tag); + } + + // 10. Set result.[[locale]] to foundLocale. + result.locale = foundLocale; + UErrorCode status = U_ZERO_ERROR; + foundLocaleData = localeBuilder.build(status); + result.localeData = foundLocaleData; + + // 11. Return result. + return result; +} + +icu::Locale JSLocale::BuildICULocale(const std::string &bcp47Locale) +{ + UErrorCode status = U_ZERO_ERROR; + icu::Locale icuLocale = icu::Locale::forLanguageTag(bcp47Locale, status); + ASSERT_PRINT(U_SUCCESS(status), "forLanguageTag failed"); + ASSERT_PRINT(!icuLocale.isBogus(), "icuLocale is bogus"); + return icuLocale; +} + +JSHandle JSLocale::ConstructLocaleList(JSThread *thread, + const std::vector &icuAvailableLocales) +{ + EcmaVM *ecmaVm = thread->GetEcmaVM(); + ObjectFactory *factory = ecmaVm->GetFactory(); + JSHandle locales = factory->NewTaggedArray(icuAvailableLocales.size()); + int32_t index = 0; + for (const std::string &locale : icuAvailableLocales) { + JSHandle localeStr = factory->NewFromStdString(locale); + locales->Set(thread, index++, localeStr); + } + return locales; +} + +JSHandle JSLocale::IcuToString(JSThread *thread, const icu::UnicodeString &string) +{ + ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); + return factory->NewFromUtf16(reinterpret_cast(string.getBuffer()), string.length()); +} + +JSHandle JSLocale::IcuToString(JSThread *thread, const icu::UnicodeString &string, int32_t begin, + int32_t end) +{ + return IcuToString(thread, string.tempSubStringBetween(begin, end)); +} + +std::string JSLocale::GetNumberingSystem(const icu::Locale &icuLocale) +{ + UErrorCode status = U_ZERO_ERROR; + std::unique_ptr numberingSystem(icu::NumberingSystem::createInstance(icuLocale, status)); + if (U_SUCCESS(status) != 0) { + return numberingSystem->getName(); + } + return "latn"; +} + +bool JSLocale::IsWellFormedCurrencyCode(const std::string ¤cy) +{ + if (currency.length() != INTL_INDEX_THREE) { + return false; + } + return (IsAToZ(currency[INTL_INDEX_ZERO]) && IsAToZ(currency[INTL_INDEX_ONE]) && IsAToZ(currency[INTL_INDEX_TWO])); +} + +JSHandle JSLocale::PutElement(JSThread *thread, int index, const JSHandle &array, + const JSHandle &fieldTypeString, + const JSHandle &value) +{ + auto ecmaVm = thread->GetEcmaVM(); + ObjectFactory *factory = ecmaVm->GetFactory(); + + // Let record be ! ObjectCreate(%ObjectPrototype%). + JSHandle record = factory->NewEmptyJSObject(); + + auto globalConst = thread->GlobalConstants(); + // obj.type = field_type_string + JSObject::CreateDataPropertyOrThrow(thread, record, globalConst->GetHandledTypeString(), fieldTypeString); + // obj.value = value + JSObject::CreateDataPropertyOrThrow(thread, record, globalConst->GetHandledValueString(), value); + + JSTaggedValue::SetProperty(thread, JSHandle::Cast(array), index, + JSHandle::Cast(record), true); + return record; +} + +// 9.2.11 GetOption ( options, property, type, values, fallback ) +bool JSLocale::GetOptionOfBool(JSThread *thread, const JSHandle &options, + const JSHandle &property, bool fallback, bool *res) +{ + // 1. Let value be ? Get(options, property). + OperationResult operationResult = JSObject::GetProperty(thread, options, property); + RETURN_VALUE_IF_ABRUPT_COMPLETION(thread, false); + JSHandle value = operationResult.GetValue(); + *res = fallback; + // 2. If value is not undefined, then + if (!value->IsUndefined()) { + // b. Let value be ToBoolean(value). + *res = value->ToBoolean(); + return true; + } + // 3. not found + return false; +} + +JSHandle JSLocale::GetNumberFieldType(JSThread *thread, JSTaggedValue x, int32_t fieldId) +{ + ASSERT(x.IsNumber()); + auto globalConst = thread->GlobalConstants(); + switch (static_cast(fieldId)) { + case UNUM_INTEGER_FIELD: + if (std::isfinite(x.GetNumber())) { + return globalConst->GetHandledIntegerString(); + } + if (std::isnan(x.GetNumber())) { + return globalConst->GetHandledNanString(); + } + return globalConst->GetHandledInfinityString(); + + case UNUM_FRACTION_FIELD: + return globalConst->GetHandledFractionString(); + + case UNUM_DECIMAL_SEPARATOR_FIELD: + return globalConst->GetHandledDecimalString(); + + case UNUM_GROUPING_SEPARATOR_FIELD: + return globalConst->GetHandledGroupString(); + + case UNUM_CURRENCY_FIELD: + return globalConst->GetHandledCurrencyString(); + + case UNUM_PERCENT_FIELD: + return globalConst->GetHandledPercentSignString(); + + case UNUM_SIGN_FIELD: + return std::signbit(x.GetNumber()) ? globalConst->GetHandledMinusSignString() + : globalConst->GetHandledPlusSignString(); + + case UNUM_EXPONENT_SYMBOL_FIELD: + return globalConst->GetHandledExponentSeparatorString(); + + case UNUM_EXPONENT_SIGN_FIELD: + return globalConst->GetHandledExponentMinusSignString(); + + case UNUM_EXPONENT_FIELD: + return globalConst->GetHandledExponentIntegerString(); + + case UNUM_COMPACT_FIELD: + return globalConst->GetHandledCompactString(); + + case UNUM_MEASURE_UNIT_FIELD: + return globalConst->GetHandledUnitString(); + + default: + UNREACHABLE(); + } + + UNREACHABLE(); +} + +// 10.1.1 ApplyOptionsToTag( tag, options ) +bool JSLocale::ApplyOptionsToTag(JSThread *thread, const JSHandle &tag, const JSHandle &options, + TagElements &tagElements) +{ + EcmaVM *ecmaVm = thread->GetEcmaVM(); + const GlobalEnvConstants *globalConst = thread->GlobalConstants(); + ObjectFactory *factory = ecmaVm->GetFactory(); + if (*tag == *(factory->GetEmptyString())) { + return false; + } + // 2. If IsStructurallyValidLanguageTag(tag) is false, throw a RangeError exception. + if (!IsStructurallyValidLanguageTag(tag)) { + return false; + } + + tagElements.language = GetOption(thread, options, globalConst->GetHandledLanguageString(), OptionType::STRING, + globalConst->GetHandledUndefined(), globalConst->GetHandledUndefined()); + RETURN_VALUE_IF_ABRUPT_COMPLETION(thread, false); + + // 4. If language is not undefined, then + // a. If language does not match the unicode_language_subtag production, throw a RangeError exception. + if (!tagElements.language->IsUndefined()) { + std::string languageStr = ConvertToStdString(JSHandle::Cast(tagElements.language)); + if (languageStr[INTL_INDEX_ZERO] == '\0' || IsAlpha(languageStr, INTL_INDEX_FOUR, INTL_INDEX_FOUR)) { + return false; + } + } + + // 5. Let script be ? GetOption(options, "script", "string", undefined, undefined). + tagElements.script = GetOption(thread, options, globalConst->GetHandledScriptString(), OptionType::STRING, + globalConst->GetHandledUndefined(), globalConst->GetHandledUndefined()); + RETURN_VALUE_IF_ABRUPT_COMPLETION(thread, false); + + // 6. If script is not undefined, then + // a. If script does not match the unicode_script_subtag production, throw a RangeError exception. + if (!tagElements.script->IsUndefined()) { + std::string scriptStr = JSLocale::ConvertToStdString((JSHandle::Cast(tagElements.script))); + if (scriptStr[INTL_INDEX_ZERO] == '\0') { + return false; + } + } + + // 7. Let region be ? GetOption(options, "region", "string", undefined, undefined). + // 8. If region is not undefined, then + // a. If region does not match the unicode_region_subtag production, throw a RangeError exception. + tagElements.region = GetOption(thread, options, globalConst->GetHandledRegionString(), OptionType::STRING, + globalConst->GetHandledUndefined(), globalConst->GetHandledUndefined()); + RETURN_VALUE_IF_ABRUPT_COMPLETION(thread, false); + + if (!tagElements.region->IsUndefined()) { + std::string regionStr = ConvertToStdString(JSHandle::Cast(tagElements.region)); + if (regionStr[INTL_INDEX_ZERO] == '\0') { + return false; + } + } + return true; +} + +bool BuildOptionsTags(const JSHandle &tag, icu::LocaleBuilder *builder, JSHandle language, + JSHandle script, JSHandle region) +{ + std::string tagStr = JSLocale::ConvertToStdString(tag); + auto len = static_cast(tagStr.length()); + ASSERT(len > 0); + builder->setLanguageTag({tagStr.c_str(), len}); + UErrorCode status = U_ZERO_ERROR; + icu::Locale locale = builder->build(status); + locale.canonicalize(status); + if (U_FAILURE(status) != 0) { + return false; + } + builder->setLocale(locale); + + if (!language->IsUndefined()) { + std::string languageStr = JSLocale::ConvertToStdString(JSHandle::Cast(language)); + builder->setLanguage(languageStr); + builder->build(status); + if ((U_FAILURE(status) != 0)) { + return false; + } + } + + if (!script->IsUndefined()) { + std::string scriptStr = JSLocale::ConvertToStdString((JSHandle::Cast(script))); + builder->setScript(scriptStr); + builder->build(status); + if ((U_FAILURE(status) != 0)) { + return false; + } + } + + if (!region->IsUndefined()) { + std::string regionStr = JSLocale::ConvertToStdString(JSHandle::Cast(region)); + builder->setRegion(regionStr); + builder->build(status); + if ((U_FAILURE(status) != 0)) { + return false; + } + } + return true; +} + +bool InsertOptions(JSThread *thread, const JSHandle &options, icu::LocaleBuilder *builder) +{ + const std::vector hourCycleValues = {"h11", "h12", "h23", "h24"}; + const std::vector caseFirstValues = {"upper", "lower", "false"}; + const std::vector emptyValues = {}; + const GlobalEnvConstants *globalConst = thread->GlobalConstants(); + std::string strResult; + bool findca = + JSLocale::GetOptionOfString(thread, options, globalConst->GetHandledCalendarString(), emptyValues, &strResult); + RETURN_VALUE_IF_ABRUPT_COMPLETION(thread, false); + if (findca) { + if (!uloc_toLegacyType(uloc_toLegacyKey("ca"), strResult.c_str())) { + return false; + } + builder->setUnicodeLocaleKeyword("ca", strResult.c_str()); + } + + bool findco = + JSLocale::GetOptionOfString(thread, options, globalConst->GetHandledCollationString(), emptyValues, &strResult); + RETURN_VALUE_IF_ABRUPT_COMPLETION(thread, false); + if (findco) { + if (!uloc_toLegacyType(uloc_toLegacyKey("co"), strResult.c_str())) { + return false; + } + builder->setUnicodeLocaleKeyword("co", strResult.c_str()); + } + + bool findhc = JSLocale::GetOptionOfString(thread, options, globalConst->GetHandledHourCycleString(), + hourCycleValues, &strResult); + RETURN_VALUE_IF_ABRUPT_COMPLETION(thread, false); + if (findhc) { + if (!uloc_toLegacyType(uloc_toLegacyKey("hc"), strResult.c_str())) { + return false; + } + builder->setUnicodeLocaleKeyword("hc", strResult.c_str()); + } + + bool findkf = JSLocale::GetOptionOfString(thread, options, globalConst->GetHandledCaseFirstString(), + caseFirstValues, &strResult); + RETURN_VALUE_IF_ABRUPT_COMPLETION(thread, false); + if (findkf) { + if (!uloc_toLegacyType(uloc_toLegacyKey("kf"), strResult.c_str())) { + return false; + } + builder->setUnicodeLocaleKeyword("kf", strResult.c_str()); + } + + bool boolResult = false; + bool findkn = + JSLocale::GetOptionOfBool(thread, options, globalConst->GetHandledNumericString(), false, &boolResult); + if (findkn) { + strResult = boolResult ? "true" : "false"; + if (!uloc_toLegacyType(uloc_toLegacyKey("kn"), strResult.c_str())) { + return false; + } + builder->setUnicodeLocaleKeyword("kn", strResult.c_str()); + } + + bool findnu = JSLocale::GetOptionOfString(thread, options, globalConst->GetHandledNumberingSystemString(), + emptyValues, &strResult); + RETURN_VALUE_IF_ABRUPT_COMPLETION(thread, false); + if (findnu) { + if (!uloc_toLegacyType(uloc_toLegacyKey("nu"), strResult.c_str())) { + return false; + } + builder->setUnicodeLocaleKeyword("nu", strResult.c_str()); + } + return true; +} + +JSHandle JSLocale::InitializeLocale(JSThread *thread, const JSHandle &locale, + const JSHandle &localeString, + const JSHandle &options) +{ + icu::LocaleBuilder builder; + TagElements tagElements; + if (!ApplyOptionsToTag(thread, localeString, options, tagElements)) { + THROW_RANGE_ERROR_AND_RETURN(thread, "apply option to tag failed", locale); + } + + bool res = BuildOptionsTags(localeString, &builder, tagElements.language, tagElements.script, tagElements.region); + if (!res) { + THROW_RANGE_ERROR_AND_RETURN(thread, "apply option to tag failed", locale); + } + bool insertResult = InsertOptions(thread, options, &builder); + RETURN_VALUE_IF_ABRUPT_COMPLETION(thread, locale); + UErrorCode status = U_ZERO_ERROR; + icu::Locale icuLocale = builder.build(status); + icuLocale.canonicalize(status); + + if (!insertResult || (U_FAILURE(status) != 0)) { + THROW_RANGE_ERROR_AND_RETURN(thread, "insert or build failed", locale); + } + ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); + factory->NewJSIntlIcuData(locale, icuLocale, JSLocale::FreeIcuLocale); + return locale; +} + +bool JSLocale::DealwithLanguageTag(const std::vector &containers, size_t &address) +{ + // The abstract operation returns true if locale can be generated from the ABNF grammar in section 2.1 of the RFC, + // starting with Language-Tag, and does not contain duplicate variant or singleton subtags + // If language tag is empty, return false. + if (containers.empty()) { + return false; + } + + // a. if the first tag is not language, return false. + if (!IsLanguageSubtag(containers[0])) { + return false; + } + + // if the tag include language only, like "zh" or "de", return true; + if (containers.size() == 1) { + return true; + } + + // Else, then + // if is unique singleton subtag, script and region tag. + if (IsExtensionSingleton(containers[1])) { + return true; + } + + if (IsScriptSubtag(containers[address])) { + address++; + if (containers.size() == address) { + return true; + } + } + + if (IsRegionSubtag(containers[address])) { + address++; + } + + for (size_t i = address; i < containers.size(); i++) { + if (IsExtensionSingleton(containers[i])) { + return true; + } + if (!IsVariantSubtag(containers[i])) { + return false; + } + } + return true; +} + +int ConvertValue(const UErrorCode &status, std::string &value, const std::string &key) +{ + if (status == U_ILLEGAL_ARGUMENT_ERROR || value.empty()) { + return 1; + } + + if (value == "yes") { + value = "true"; + } + + if (key == "kf" && value == "true") { + return 2; + } + return 0; +} + +JSHandle JSLocale::NormalizeKeywordValue(JSThread *thread, const JSHandle &locale, + const std::string &key) +{ + icu::Locale *icuLocale = locale->GetIcuLocale(); + UErrorCode status = U_ZERO_ERROR; + auto value = icuLocale->getUnicodeKeywordValue(key, status); + + EcmaVM *ecmaVm = thread->GetEcmaVM(); + ObjectFactory *factory = ecmaVm->GetFactory(); + + int result = ConvertValue(status, value, key); + if (result == 1) { + return JSHandle::Cast(thread->GlobalConstants()->GetHandledUndefinedString()); + } + if (result == 2) { + return factory->GetEmptyString(); + } + return factory->NewFromStdString(value); +} + +JSHandle JSLocale::ToString(JSThread *thread, const JSHandle &locale) +{ + icu::Locale *icuLocale = locale->GetIcuLocale(); + if (icuLocale == nullptr) { + ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); + return factory->GetEmptyString(); + } + JSHandle result = ToLanguageTag(thread, *icuLocale); + return result; +} + +JSHandle JSLocale::GetAvailableLocales(JSThread *thread, const char *localeKey, const char *localePath) +{ + UErrorCode status = U_ZERO_ERROR; + auto globalConst = thread->GlobalConstants(); + JSHandle specialValue = JSHandle::Cast(globalConst->GetHandledEnUsPosixString()); + std::string specialString = ConvertToStdString(specialValue); + UEnumeration *uenum = uloc_openAvailableByType(ULOC_AVAILABLE_WITH_LEGACY_ALIASES, &status); + std::vector allLocales; + const char *loc = nullptr; + for (loc = uenum_next(uenum, nullptr, &status); loc != nullptr; loc = uenum_next(uenum, nullptr, &status)) { + ASSERT(U_SUCCESS(status)); + std::string locStr(loc); + std::replace(locStr.begin(), locStr.end(), '_', '-'); + if (locStr == specialString) { + locStr = "en-US-u-va-posix"; + } + + if (localePath != nullptr || localeKey != nullptr) { + icu::Locale loc(locStr.c_str()); + bool res = false; + if (!CheckLocales(loc, localeKey, localePath, res)) { + continue; + } + } + bool isScript = false; + allLocales.push_back(locStr); + icu::Locale formalLocale = icu::Locale::createCanonical(locStr.c_str()); + std::string scriptStr = formalLocale.getScript(); + isScript = !scriptStr.empty(); + if (isScript) { + std::string languageStr = formalLocale.getLanguage(); + std::string countryStr = formalLocale.getCountry(); + std::string shortLocale = icu::Locale(languageStr.c_str(), countryStr.c_str()).getName(); + std::replace(shortLocale.begin(), shortLocale.end(), '_', '-'); + allLocales.push_back(shortLocale); + } + } + uenum_close(uenum); + return ConstructLocaleList(thread, allLocales); +} +} // namespace panda::ecmascript diff --git a/runtime/js_locale.h b/runtime/js_locale.h new file mode 100644 index 000000000..47a0be226 --- /dev/null +++ b/runtime/js_locale.h @@ -0,0 +1,701 @@ +/* + * Copyright (c) 2021-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_JSLOCALE_H +#define ECMASCRIPT_JSLOCALE_H + +#include "plugins/ecmascript/runtime/ecma_macros.h" +#include "plugins/ecmascript/runtime/js_array.h" +#include "plugins/ecmascript/runtime/js_object.h" +#include "plugins/ecmascript/runtime/mem/c_containers.h" +#include "ohos/init_data.h" +#include "unicode/basictz.h" +#include "unicode/brkiter.h" +#include "unicode/calendar.h" +#include "unicode/coll.h" +#include "unicode/datefmt.h" +#include "unicode/decimfmt.h" +#include "unicode/dtitvfmt.h" +#include "unicode/dtptngen.h" +#include "unicode/fieldpos.h" +#include "unicode/formattedvalue.h" +#include "unicode/gregocal.h" +#include "unicode/locid.h" +#include "unicode/normalizer2.h" +#include "unicode/numberformatter.h" +#include "unicode/numfmt.h" +#include "unicode/numsys.h" +#include "unicode/smpdtfmt.h" +#include "unicode/timezone.h" +#include "unicode/udat.h" +#include "unicode/unistr.h" +#include "unicode/ures.h" +#include "unicode/ustring.h" +#include "unicode/uvernum.h" +#include "unicode/uversion.h" + +namespace panda::ecmascript { +enum class OptionType : uint8_t { STRING = 0x01, BOOLEAN }; +enum class LocaleMatcherOption : uint8_t { LOOKUP = 0x01, BEST_FIT, EXCEPTION }; +enum class FormatMatcherOption : uint8_t { BASIC = 0x01, BEST_FIT, EXCEPTION }; +enum class LocaleType : uint8_t { + LITERAL = 0x01, + NUMBER, + PLUS_SIGN, + MINUS_SIGN, + PERCENT_SIGN, + UNIT_PREFIX, + UNIT_SUFFIX, + CURRENCY_CODE, + CURRENCY_PREFIX, + CURRENCY_SUFFIX, +}; +enum class RoundingType : uint8_t { FRACTIONDIGITS = 0x01, SIGNIFICANTDIGITS, COMPACTROUNDING, EXCEPTION }; +enum class NotationOption : uint8_t { STANDARD = 0x01, SCIENTIFIC, ENGINEERING, COMPACT, EXCEPTION }; + +constexpr uint32_t MAX_DIGITS = 21; +constexpr uint32_t MAX_FRACTION_DIGITS = 20; +constexpr uint8_t INTL_INDEX_ZERO = 0; +constexpr uint8_t INTL_INDEX_ONE = 1; +constexpr uint8_t INTL_INDEX_TWO = 2; +constexpr uint8_t INTL_INDEX_THREE = 3; +constexpr uint8_t INTL_INDEX_FOUR = 4; +constexpr uint8_t INTL_INDEX_FIVE = 5; +constexpr uint8_t INTL_INDEX_EIGHT = 8; + +class JSIntlIterator : public icu::Locale::Iterator { +public: + JSIntlIterator(const JSHandle &data, uint32_t length) : length_(length), curIdx_(0) + { + for (uint32_t idx = 0; idx < length; idx++) { + std::string str = base::StringHelper::ToStdString(EcmaString::Cast(data->Get(idx).GetTaggedObject())); + data_.emplace_back(str); + } + } + + ~JSIntlIterator() override = default; + DEFAULT_COPY_SEMANTIC(JSIntlIterator); + DEFAULT_MOVE_SEMANTIC(JSIntlIterator); + + UBool hasNext() const override + { + return static_cast(curIdx_ < length_); + } + + const icu::Locale &next() override + { + ASSERT(curIdx_ < length_); + UErrorCode status = U_ZERO_ERROR; + locale_ = icu::Locale::forLanguageTag(data_[curIdx_].c_str(), status); + ASSERT(U_SUCCESS(status)); + curIdx_++; + return locale_; + } + + inline const std::string &operator[](size_t index) const noexcept + { + ASSERT(index < length_); + return data_[index]; + } + +private: + std::vector data_ {}; + uint32_t length_ {0}; + uint32_t curIdx_ {0}; + icu::Locale locale_ {}; +}; + +struct ResolvedLocale { + std::string locale {}; + icu::Locale localeData {}; + std::map extensions {}; +}; + +struct MatcherResult { + std::string locale; + std::string extension; +}; + +struct OptionData { + std::string name; + std::string key; + std::vector possibleValues; + bool isBoolValue = false; +}; + +struct TagElements { + JSHandle language; + JSHandle script; + JSHandle region; +}; + +class JSLocale : public JSObject { +public: + struct ParsedLocale { + std::string base; + std::string extension; + }; + + static JSLocale *Cast(ObjectHeader *object) + { + ASSERT(JSTaggedValue(object).IsJSLocale()); + return static_cast(object); + } + + static constexpr size_t ICU_FIELD_OFFSET = JSObject::SIZE; + // icu::Locale internal slot. + ACCESSORS(IcuField, ICU_FIELD_OFFSET, SIZE) + + DECL_VISIT_OBJECT_FOR_JS_OBJECT(JSObject, ICU_FIELD_OFFSET, SIZE) + DECL_DUMP() + + icu::Locale *GetIcuLocale() const + { + ASSERT(GetIcuField().IsJSNativePointer()); + auto result = JSNativePointer::Cast(GetIcuField().GetTaggedObject())->GetExternalPointer(); + return reinterpret_cast(result); + } + + static void FreeIcuLocale(void *pointer, void *data) + { + if (pointer == nullptr) { + return; + } + auto icuLocale = reinterpret_cast(pointer); + icuLocale->~Locale(); + if (data != nullptr) { + reinterpret_cast(data)->GetRegionFactory()->FreeBuffer(pointer); + } + } + + static std::string ConvertToStdString(const JSHandle &ecmaStr); + + // 6.2.2 IsStructurallyValidLanguageTag ( locale ) + static bool IsStructurallyValidLanguageTag(const JSHandle &tag); + + static bool DealwithLanguageTag(const std::vector &containers, size_t &address); + + // 6.2.3 CanonicalizeUnicodeLocaleId ( locale ) + static JSHandle CanonicalizeUnicodeLocaleId(JSThread *thread, const JSHandle &locale); + + // 6.2.4 DefaultLocale () + static JSHandle DefaultLocale(JSThread *thread); + + // 6.4.1 IsValidTimeZoneName ( timeZone ) + static bool IsValidTimeZoneName(const icu::TimeZone &tz); + + // 9.2.1 CanonicalizeLocaleList ( locales ) + static JSHandle CanonicalizeLocaleList(JSThread *thread, const JSHandle &locales); + + template + static JSHandle CanonicalizeHelper(JSThread *thread, const JSHandle &locales, + JSHandle &obj, JSHandle &seen); + + // 9.2.2 BestAvailableLocale ( availableLocales, locale ) + static std::string BestAvailableLocale(JSThread *thread, const JSHandle &availableLocales, + const std::string &locale); + + // 9.2.3 LookupMatcher ( availableLocales, requestedLocales ) + static JSHandle LookupMatcher(JSThread *thread, const JSHandle &availableLocales, + const JSHandle &requestedLocales); + + // 9.2.4 BestFitMatcher ( availableLocales, requestedLocales ) + static JSHandle BestFitMatcher(JSThread *thread, const JSHandle &availableLocales, + const JSHandle &requestedLocales); + + // 9.2.5 UnicodeExtensionValue ( extension, key ) + static std::string UnicodeExtensionValue(const std::string &extension, const std::string &key); + + // 9.2.7 ResolveLocale ( availableLocales, requestedLocales, options, relevantExtensionKeys, localeData ) + static ResolvedLocale ResolveLocale(JSThread *thread, const JSHandle &availableLocales, + const JSHandle &requestedLocales, LocaleMatcherOption matcher, + const std::set &relevantExtensionKeys); + + // 9.2.8 LookupSupportedLocales ( availableLocales, requestedLocales ) + static JSHandle LookupSupportedLocales(JSThread *thread, const JSHandle &availableLocales, + const JSHandle &requestedLocales); + + // 9.2.9 BestFitSupportedLocales ( availableLocales, requestedLocales ) + static JSHandle BestFitSupportedLocales(JSThread *thread, + const JSHandle &availableLocales, + const JSHandle &requestedLocales); + + // 9.2.10 SupportedLocales ( availableLocales, requestedLocales, options ) + static JSHandle SupportedLocales(JSThread *thread, const JSHandle &availableLocales, + const JSHandle &requestedLocales, + const JSHandle &options); + + // 9.2.11 GetOption ( options, property, type, values, fallback ) + template + static T GetOptionOfString(JSThread *thread, const JSHandle &options, + const JSHandle &property, const std::vector &enumValues, + const std::vector &strValues, T fallback) + { + // 1. Let value be ? Get(options, property). + OperationResult operationResult = JSObject::GetProperty(thread, options, property); + RETURN_VALUE_IF_ABRUPT_COMPLETION(thread, T::EXCEPTION); + JSHandle value = operationResult.GetValue(); + + if (value->IsUndefined()) { + return fallback; + } + + // 2. If value is not undefined, then + // d. If values is not undefined, then + // i. If values does not contain an element equal to value, throw a RangeError exception. + JSHandle valueEStr = JSTaggedValue::ToString(thread, value); + RETURN_VALUE_IF_ABRUPT_COMPLETION(thread, T::EXCEPTION); + std::string valueStr = ConvertToStdString(valueEStr); + int existIdx = -1; + if (!enumValues.empty()) { + int strValuesSize = strValues.size(); + for (int i = 0; i < strValuesSize; i++) { + if (strValues[i] == valueStr) { + existIdx = i; + } + } + if (existIdx == -1) { + THROW_RANGE_ERROR_AND_RETURN(thread, "getStringOption failed", T::EXCEPTION); + } + } + if (existIdx == -1) { + UNREACHABLE(); + } + // e.Return value. + return enumValues[existIdx]; + } + + static bool GetOptionOfBool(JSThread *thread, const JSHandle &options, + const JSHandle &property, bool fallback, bool *res); + + static JSHandle GetOption(JSThread *thread, const JSHandle &options, + const JSHandle &property, OptionType type, + const JSHandle &values, + const JSHandle &fallback); + + static bool GetOptionOfString(JSThread *thread, const JSHandle &options, + const JSHandle &property, const std::vector &values, + std::string *optionValue); + + // 9.2.12 DefaultNumberOption ( value, minimum, maximum, fallback ) + static int DefaultNumberOption(JSThread *thread, const JSHandle &value, int minimum, int maximum, + int fallback); + + // 9.2.13 GetNumberOption ( options, property, minimum, maximum, fallback ) + static int GetNumberOption(JSThread *thread, const JSHandle &options, + const JSHandle &property, int minimum, int maximum, int fallback); + + static bool IsLanguageSubtag(const std::string &value) + { + return IsAlpha(value, INTL_INDEX_TWO, INTL_INDEX_THREE) || IsAlpha(value, INTL_INDEX_FIVE, INTL_INDEX_EIGHT); + } + + static bool IsScriptSubtag(const std::string &value) + { + return IsAlpha(value, INTL_INDEX_FOUR, INTL_INDEX_FOUR); + } + + static bool IsRegionSubtag(const std::string &value) + { + return IsAlpha(value, INTL_INDEX_TWO, INTL_INDEX_TWO) || IsDigit(value, INTL_INDEX_THREE, INTL_INDEX_THREE); + } + + static bool IsVariantSubtag(const std::string &value) + { + return IsThirdDigitAlphanum(value) || IsAlphanum(value, INTL_INDEX_FIVE, INTL_INDEX_EIGHT); + } + + static bool IsThirdDigitAlphanum(const std::string &value) + { + return InRange(value[0], '0', '9') && value.length() == 4 && + IsAlphanum(value.substr(INTL_INDEX_ONE), INTL_INDEX_THREE, INTL_INDEX_THREE); + } + + static bool IsExtensionSingleton(const std::string &value) + { + return IsAlphanum(value, INTL_INDEX_ONE, INTL_INDEX_ONE); + } + + static bool IsNormativeCalendar(const std::string &value) + { + return IsWellAlphaNumList(value); + } + + static bool IsNormativeNumberingSystem(const std::string &value) + { + return IsWellAlphaNumList(value); + } + + static bool IsWellNumberingSystem(const std::string &value) + { + std::set irregularList = {"native", "traditio", "finance"}; + if (irregularList.find(value) != irregularList.end()) { + return false; + } + UErrorCode status = U_ZERO_ERROR; + icu::NumberingSystem *numberingSystem = icu::NumberingSystem::createInstanceByName(value.c_str(), status); + bool result = U_SUCCESS(status) != 0 && numberingSystem != nullptr; + delete numberingSystem; + numberingSystem = nullptr; + return result; + } + + static bool IsWellCollation(const icu::Locale &locale, const std::string &value) + { + std::set irregularList = {"standard", "search"}; + if (irregularList.find(value) != irregularList.end()) { + return false; + } + return IsWellExtension(locale, "collation", value); + } + + static bool IsWellCalendar(const icu::Locale &locale, const std::string &value) + { + return IsWellExtension(locale, "calendar", value); + } + + template + static bool IsWellExtension(const icu::Locale &locale, const char *key, const std::string &value) + { + UErrorCode status = U_ZERO_ERROR; + const char *outdatedType = uloc_toLegacyType(key, value.c_str()); + if (outdatedType == nullptr) { + return false; + } + icu::StringEnumeration *sequence = + T::getKeywordValuesForLocale(key, icu::Locale(locale.getBaseName()), false, status); + if (U_FAILURE(status) != 0) { + delete sequence; + sequence = nullptr; + return false; + } + int32_t size; + const char *element = sequence->next(&size, status); + while (U_SUCCESS(status) && element != nullptr) { + if (strcmp(outdatedType, element) == 0) { + delete sequence; + sequence = nullptr; + return true; + } + element = sequence->next(&size, status); + } + delete sequence; + sequence = nullptr; + return false; + } + + static inline constexpr int AsciiAlphaToLower(uint32_t c) + { + constexpr uint32_t FLAG = 0x20; + return static_cast(c | FLAG); + } + + static bool IsAsciiAlpha(char ch) + { + return InRange(ch, 'A', 'Z') || InRange(ch, 'a', 'z'); + } + + static char LocaleIndependentAsciiToUpper(char ch) + { + return (InRange(ch, 'a', 'z')) ? static_cast((ch - 'a' + 'A')) : ch; + } + + static char LocaleIndependentAsciiToLower(char ch) + { + return (InRange(ch, 'A', 'Z')) ? static_cast((ch - 'A' + 'a')) : ch; + } + + template + static bool InRange(T value, U start, U end) + { + ASSERT(start <= end); + ASSERT(sizeof(T) >= sizeof(U)); + return (value >= static_cast(start)) && (value <= static_cast(end)); + } + + static bool IsWellAlphaNumList(const std::string &value) + { + if (value.length() < 3) { + return false; + } + char lastChar = value[value.length() - 1]; + if (lastChar == '-') { + return false; + } + std::vector items; + std::istringstream input(value); + std::string temp; + while (getline(input, temp, '-')) { + items.push_back(temp); + } + for (auto &item : items) { + if (!IsAlphanum(item, INTL_INDEX_THREE, INTL_INDEX_EIGHT)) { + return false; + } + } + return true; + } + + static bool ValidateOtherTags(const icu::Locale &locale, const char *packageName, const char *key, bool &res) + { + const char *localeCountry = locale.getCountry(); + const char *localeScript = locale.getScript(); + // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-pointer-arithmetic) + if (localeCountry[0] != '\0' && localeScript[0] != '\0') { + std::string removeCountry; + removeCountry = locale.getLanguage(); + removeCountry.append("-"); + removeCountry.append(localeScript); + return CheckLocales(removeCountry.c_str(), key, packageName, res); + } + // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-pointer-arithmetic) + if (localeCountry[0] != '\0' || localeScript[0] != '\0') { + std::string language = locale.getLanguage(); + return CheckLocales(language.c_str(), key, packageName, res); + } + return res; + } + + static bool CheckLocales(const icu::Locale &locale, const char *key, const char *packageName, bool &res) + { + res = false; + UErrorCode status = U_ZERO_ERROR; + const char *formalLocale = locale.getName(); + UResourceBundle *localeRes = ures_open(packageName, formalLocale, &status); + if (localeRes != nullptr && status == U_ZERO_ERROR) { + bool flag = (key == nullptr) ? true : false; + if (flag) { + res = true; + } else { + UResourceBundle *keyRes = ures_getByKey(localeRes, key, nullptr, &status); + if (keyRes != nullptr && status == U_ZERO_ERROR) { + res = true; + } + ures_close(keyRes); + } + } + ures_close(localeRes); + if (res) { + return res; + } + ValidateOtherTags(locale, packageName, key, res); + return res; + } + + static JSHandle IcuToString(JSThread *thread, const icu::UnicodeString &string); + + static JSHandle IcuToString(JSThread *thread, const icu::UnicodeString &string, int32_t begin, + int32_t end); + + static JSHandle GetAvailableLocales(JSThread *thread, const char *key, const char *path); + + static JSHandle PutElement(JSThread *thread, int index, const JSHandle &array, + const JSHandle &fieldTypeString, + const JSHandle &value); + + static JSHandle ToLanguageTag(JSThread *thread, const icu::Locale &locale); + + static std::string GetNumberingSystem(const icu::Locale &icuLocale); + + static bool IsWellFormedCurrencyCode(const std::string ¤cy); + + static JSHandle GetNumberFieldType(JSThread *thread, JSTaggedValue x, int32_t fieldId); + + static bool ApplyOptionsToTag(JSThread *thread, const JSHandle &tag, const JSHandle &options, + TagElements &tagElements); + + static JSHandle InitializeLocale(JSThread *thread, const JSHandle &locale, + const JSHandle &localeString, + const JSHandle &options); + + static JSHandle NormalizeKeywordValue(JSThread *thread, const JSHandle &locale, + const std::string &key); + + static void HandleLocaleExtension(size_t &start, size_t &extensionEnd, const std::string &result, size_t len); + + static ParsedLocale HandleLocale(const JSHandle &locale); + + static JSHandle ToString(JSThread *thread, const JSHandle &locale); + + // 12.1.1 SetNumberFormatDigitOptions ( intlObj, options, mnfdDefault, mxfdDefault, notation ) + template + static void SetNumberFormatDigitOptions(JSThread *thread, const JSHandle &intlObj, + const JSHandle &options, int mnfdDefault, int mxfdDefault, + NotationOption notation) + { + // 1. Assert: Type(intlObj) is Object. + // 2. Assert: Type(options) is Object. + // 3. Assert: Type(mnfdDefault) is Number. + // 4. Assert: Type(mxfdDefault) is Number. + ASSERT(options->IsHeapObject()); + auto globalConst = thread->GlobalConstants(); + // Set intlObj.[[MinimumFractionDigits]] to 0. + intlObj->SetMinimumFractionDigits(thread, JSTaggedValue(0)); + // Set intlObj.[[MaximumFractionDigits]] to 0. + intlObj->SetMaximumFractionDigits(thread, JSTaggedValue(0)); + // Set intlObj.[[MinimumSignificantDigits]] to 0. + intlObj->SetMinimumSignificantDigits(thread, JSTaggedValue(0)); + // Set intlObj.[[MaximumSignificantDigits]] to 0. + intlObj->SetMaximumSignificantDigits(thread, JSTaggedValue(0)); + + // 5. Let mnid be ? GetNumberOption(options, "minimumIntegerDigits,", 1, 21, 1). + JSHandle mnidKey = globalConst->GetHandledMinimumIntegerDigitsString(); + int mnid = GetNumberOption(thread, JSHandle::Cast(options), mnidKey, 1, MAX_DIGITS, 1); + // 6. Let mnfd be ? Get(options, "minimumFractionDigits"). + JSHandle mnfdKey = globalConst->GetHandledMinimumFractionDigitsString(); + JSHandle mnfd = JSTaggedValue::GetProperty(thread, options, mnfdKey).GetValue(); + // 7. Let mxfd be ? Get(options, "maximumFractionDigits"). + JSHandle mxfdKey = globalConst->GetHandledMaximumFractionDigitsString(); + JSHandle mxfd = JSTaggedValue::GetProperty(thread, options, mxfdKey).GetValue(); + // 8. Let mnsd be ? Get(options, "minimumSignificantDigits"). + JSHandle mnsdKey = globalConst->GetHandledMinimumSignificantDigitsString(); + JSHandle mnsd = JSTaggedValue::GetProperty(thread, options, mnsdKey).GetValue(); + // 9. Let mxsd be ? Get(options, "maximumSignificantDigits"). + JSHandle mxsdKey = globalConst->GetHandledMaximumSignificantDigitsString(); + JSHandle mxsd = JSTaggedValue::GetProperty(thread, options, mxsdKey).GetValue(); + + // 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. + intlObj->SetRoundingType(thread, JSTaggedValue(static_cast(RoundingType::SIGNIFICANTDIGITS))); + // b. Let mnsd be ? DefaultNumberOption(mnsd, 1, 21, 1). + mnsd = JSHandle( + thread, JSTaggedValue(JSLocale::DefaultNumberOption(thread, mnsd, 1, MAX_DIGITS, 1))); + // c. Let mxsd be ? DefaultNumberOption(mxsd, mnsd, 21, 21). + mxsd = JSHandle(thread, JSTaggedValue(JSLocale::DefaultNumberOption( + thread, mxsd, mnsd->GetInt(), MAX_DIGITS, MAX_DIGITS))); + // d. Set intlObj.[[MinimumSignificantDigits]] to mnsd. + intlObj->SetMinimumSignificantDigits(thread, mnsd); + // e. Set intlObj.[[MaximumSignificantDigits]] to mxsd. + intlObj->SetMaximumSignificantDigits(thread, mxsd); + } else { + if (!mnfd->IsUndefined() || !mxfd->IsUndefined()) { + // 12. Else if mnfd is not undefined or mxfd is not undefined, then + // a. Set intlObj.[[RoundingType]] to fractionDigits. + intlObj->SetRoundingType(thread, JSTaggedValue(static_cast(RoundingType::FRACTIONDIGITS))); + if (!mxfd->IsUndefined()) { + auto mxfdValue = + JSTaggedValue(JSLocale::DefaultNumberOption(thread, mxfd, 0, MAX_FRACTION_DIGITS, mxfdDefault)); + mxfd = JSHandle(thread, mxfdValue); + mnfdDefault = std::min(mnfdDefault, mxfd->GetInt()); + } + // b. Let mnfd be ? DefaultNumberOption(mnfd, 0, 20, mnfdDefault). + mnfd = JSHandle( + thread, JSTaggedValue(DefaultNumberOption(thread, mnfd, 0, MAX_FRACTION_DIGITS, mnfdDefault))); + // c. Let mxfdActualDefault be max( mnfd, mxfdDefault ). + int mxfdActualDefault = std::max(mnfd->GetInt(), mxfdDefault); + // d. Let mxfd be ? DefaultNumberOption(mxfd, mnfd, 20, mxfdActualDefault). + mxfd = JSHandle( + thread, JSTaggedValue(JSLocale::DefaultNumberOption(thread, mxfd, mnfd->GetInt(), + MAX_FRACTION_DIGITS, mxfdActualDefault))); + // e. Set intlObj.[[MinimumFractionDigits]] to mnfd. + intlObj->SetMinimumFractionDigits(thread, mnfd); + // f. Set intlObj.[[MaximumFractionDigits]] to mxfd. + intlObj->SetMaximumFractionDigits(thread, mxfd); + } else if (notation == NotationOption::COMPACT) { + // 13. Else if notation is "compact", then + // a. Set intlObj.[[RoundingType]] to compactRounding. + intlObj->SetRoundingType(thread, JSTaggedValue(static_cast(RoundingType::COMPACTROUNDING))); + } else { + // 14. else, + // a.Set intlObj.[[RoundingType]] to fractionDigits. + intlObj->SetRoundingType(thread, JSTaggedValue(static_cast(RoundingType::FRACTIONDIGITS))); + // b.Set intlObj.[[MinimumFractionDigits]] to mnfdDefault. + intlObj->SetMinimumFractionDigits(thread, JSTaggedValue(mnfdDefault)); + // c.Set intlObj.[[MaximumFractionDigits]] to mxfdDefault. + intlObj->SetMaximumFractionDigits(thread, JSTaggedValue(mxfdDefault)); + } + } + } + + static JSHandle ConstructLocaleList(JSThread *thread, + const std::vector &icuAvailableLocales); + + static bool CheckLocales(const icu::Locale &locale, const char *path, const char *key); + + static bool IsPrivateSubTag(std::string result, size_t len) + { + if ((len > INTL_INDEX_ONE) && (result[INTL_INDEX_ONE] == '-')) { + ASSERT(result[INTL_INDEX_ZERO] == 'x' || result[INTL_INDEX_ZERO] == 'i'); + return true; + } + return false; + } + +private: + static icu::Locale BuildICULocale(const std::string &bcp47Locale); + + static bool IsCheckRange(const std::string &str, size_t min, size_t max, bool(rangeCheckFunc)(char)) + { + if (!InRange(str.length(), min, max)) { + return false; + } + for (char i : str) { + if (!rangeCheckFunc(i)) { + return false; + } + } + return true; + } + + static bool IsAlpha(const std::string &str, size_t min, size_t max) + { + if (!InRange(str.length(), min, max)) { + return false; + } + for (char c : str) { + if (!IsAsciiAlpha(c)) { + return false; + } + } + return true; + } + + static bool IsDigit(const std::string &str, size_t min, size_t max) + { + if (!InRange(str.length(), min, max)) { + return false; + } + for (char i : str) { + if (!InRange(i, '0', '9')) { + return false; + } + } + return true; + } + + static bool IsAlphanum(const std::string &str, size_t min, size_t max) + { + if (!InRange(str.length(), min, max)) { + return false; + } + for (char i : str) { + if (!IsAsciiAlpha(i) && !InRange(i, '0', '9')) { + return false; + } + } + return true; + } + + static bool IsAToZ(char ch) + { + int lowerCh = JSLocale::AsciiAlphaToLower(ch); + return JSLocale::InRange(lowerCh, 'a', 'z'); + } +}; +} // namespace panda::ecmascript +#endif // ECMASCRIPT_JSLOCALE_H diff --git a/runtime/js_map.cpp b/runtime/js_map.cpp new file mode 100644 index 000000000..9251712f9 --- /dev/null +++ b/runtime/js_map.cpp @@ -0,0 +1,81 @@ +/* + * Copyright (c) 2021-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 "plugins/ecmascript/runtime/js_tagged_value.h" +#include "js_map.h" +#include "linked_hash_table-inl.h" +#include "object_factory.h" +#include "utils/bit_utils.h" + +namespace panda::ecmascript { +void JSMap::Set(JSThread *thread, const JSHandle &map, const JSHandle &key, + const JSHandle &value) +{ + if (!LinkedHashMap::IsKey(key.GetTaggedValue())) { + THROW_TYPE_ERROR(thread, "the value must be Key of JSSet"); + } + JSHandle mapHandle(thread, LinkedHashMap::Cast(map->GetLinkedMap().GetTaggedObject())); + + JSHandle newMap = LinkedHashMap::Set(thread, mapHandle, key, value); + map->SetLinkedMap(thread, newMap); +} + +bool JSMap::Delete(const JSThread *thread, const JSHandle &map, const JSHandle &key) +{ + JSHandle mapHandle(thread, LinkedHashMap::Cast(map->GetLinkedMap().GetTaggedObject())); + int entry = mapHandle->FindElement(key.GetTaggedValue()); + if (entry == -1) { + return false; + } + mapHandle->RemoveEntry(thread, entry); + + JSHandle newMap = LinkedHashMap::Shrink(thread, mapHandle); + map->SetLinkedMap(thread, newMap); + return true; +} + +void JSMap::Clear(const JSThread *thread, const JSHandle &map) +{ + LinkedHashMap *linkedMap = LinkedHashMap::Cast(map->GetLinkedMap().GetTaggedObject()); + linkedMap->Clear(thread); +} + +bool JSMap::Has(JSTaggedValue key) const +{ + return LinkedHashMap::Cast(GetLinkedMap().GetTaggedObject())->Has(key); +} + +JSTaggedValue JSMap::Get(JSTaggedValue key) const +{ + return LinkedHashMap::Cast(GetLinkedMap().GetTaggedObject())->Get(key); +} + +int JSMap::GetSize() const +{ + return LinkedHashMap::Cast(GetLinkedMap().GetTaggedObject())->NumberOfElements(); +} + +JSTaggedValue JSMap::GetKey(int entry) const +{ + ASSERT_PRINT(entry >= 0 && entry < GetSize(), "entry must be non-negative integer less than capacity"); + return LinkedHashMap::Cast(GetLinkedMap().GetTaggedObject())->GetKey(entry); +} + +JSTaggedValue JSMap::GetValue(int entry) const +{ + ASSERT_PRINT(entry >= 0 && entry < GetSize(), "entry must be non-negative integer less than capacity"); + return LinkedHashMap::Cast(GetLinkedMap().GetTaggedObject())->GetValue(entry); +} +} // namespace panda::ecmascript diff --git a/runtime/js_map.h b/runtime/js_map.h new file mode 100644 index 000000000..7ab3d022e --- /dev/null +++ b/runtime/js_map.h @@ -0,0 +1,53 @@ +/* + * Copyright (c) 2021-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_JSMAP_H +#define ECMASCRIPT_JSMAP_H + +#include +#include "js_object.h" +#include "js_tagged_value-inl.h" + +namespace panda::ecmascript { +class JSMap : public JSObject { +public: + CAST_CHECK(JSMap, IsJSMap); + + static bool Delete(const JSThread *thread, const JSHandle &map, const JSHandle &key); + + static void Set(JSThread *thread, const JSHandle &map, const JSHandle &key, + const JSHandle &value); + static void Clear(const JSThread *thread, const JSHandle &map); + + bool Has(JSTaggedValue key) const; + + JSTaggedValue Get(JSTaggedValue key) const; + + int GetSize() const; + + JSTaggedValue GetKey(int entry) const; + + JSTaggedValue GetValue(int entry) const; + + static constexpr size_t LINKED_MAP_OFFSET = JSObject::SIZE; + ACCESSORS(LinkedMap, LINKED_MAP_OFFSET, SIZE) + + DECL_VISIT_OBJECT_FOR_JS_OBJECT(JSObject, LINKED_MAP_OFFSET, SIZE) + + DECL_DUMP() +}; +} // namespace panda::ecmascript + +#endif // ECMASCRIPT_JSMAP_H diff --git a/runtime/js_map_iterator.cpp b/runtime/js_map_iterator.cpp new file mode 100644 index 000000000..1781885f1 --- /dev/null +++ b/runtime/js_map_iterator.cpp @@ -0,0 +1,117 @@ +/* + * Copyright (c) 2021-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 "js_map_iterator.h" +#include "builtins/builtins_errors.h" +#include "js_array.h" +#include "js_map.h" +#include "linked_hash_table-inl.h" +#include "object_factory.h" + +namespace panda::ecmascript { +using BuiltinsBase = base::BuiltinsBase; +JSTaggedValue JSMapIterator::Next(EcmaRuntimeCallInfo *argv) +{ + ASSERT(argv); + JSThread *thread = argv->GetThread(); + [[maybe_unused]] EcmaHandleScope handleScope(thread); + // 1.Let O be the this value + JSHandle input(BuiltinsBase::GetThis(argv)); + + // 3.If O does not have all of the internal slots of a Map Iterator Instance (23.1.5.3), throw a TypeError + // exception. + if (!input->IsJSMapIterator()) { + THROW_TYPE_ERROR_AND_RETURN(thread, "this value is not a map iterator", JSTaggedValue::Exception()); + } + JSHandle iter(input); + iter->Update(thread); + JSHandle undefinedHandle(thread, JSTaggedValue::Undefined()); + // 4.Let m be O.[[IteratedMap]]. + JSHandle iteratedMap(thread, iter->GetIteratedMap()); + + // 5.Let index be O.[[MapNextIndex]]. + int index = iter->GetNextIndex().GetInt(); + IterationKind itemKind = IterationKind(iter->GetIterationKind().GetInt()); + // 7.If m is undefined, return CreateIterResultObject(undefined, true). + if (iteratedMap->IsUndefined()) { + return JSIterator::CreateIterResultObject(thread, undefinedHandle, true).GetTaggedValue(); + }; + JSHandle map(iteratedMap); + int totalElements = map->NumberOfElements() + map->NumberOfDeletedElements(); + + JSMutableHandle keyHandle(thread, JSTaggedValue::Undefined()); + while (index < totalElements) { + JSTaggedValue key = map->GetKey(index); + if (!key.IsHole()) { + iter->SetNextIndex(thread, JSTaggedValue(index + 1)); + keyHandle.Update(key); + // If itemKind is key, let result be e.[[Key]] + if (itemKind == IterationKind::KEY) { + return JSIterator::CreateIterResultObject(thread, keyHandle, false).GetTaggedValue(); + } + JSHandle value(thread, map->GetValue(index)); + // Else if itemKind is value, let result be e.[[Value]]. + if (itemKind == IterationKind::VALUE) { + return JSIterator::CreateIterResultObject(thread, value, false).GetTaggedValue(); + } + // Else + ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); + JSHandle array(factory->NewTaggedArray(2)); // 2 means the length of array + array->Set(thread, 0, keyHandle); + array->Set(thread, 1, value); + JSHandle keyAndValue(JSArray::CreateArrayFromList(thread, array)); + return JSIterator::CreateIterResultObject(thread, keyAndValue, false).GetTaggedValue(); + } + index++; + } + // 13.Set O.[[IteratedMap]] to undefined. + iter->SetIteratedMap(thread, JSTaggedValue::Undefined()); + return JSIterator::CreateIterResultObject(thread, undefinedHandle, true).GetTaggedValue(); +} + +void JSMapIterator::Update(const JSThread *thread) +{ + [[maybe_unused]] DisallowGarbageCollection noGc; + JSTaggedValue iteratedMap = GetIteratedMap(); + if (iteratedMap.IsUndefined()) { + return; + } + LinkedHashMap *map = LinkedHashMap::Cast(iteratedMap.GetTaggedObject()); + if (map->GetNextTable().IsHole()) { + return; + } + int index = GetNextIndex().GetInt(); + JSTaggedValue nextTable = map->GetNextTable(); + while (!nextTable.IsHole()) { + index -= map->GetDeletedElementsAt(index); + map = LinkedHashMap::Cast(nextTable.GetTaggedObject()); + nextTable = map->GetNextTable(); + } + SetIteratedMap(thread, JSTaggedValue(map)); + SetNextIndex(thread, JSTaggedValue(index)); +} + +JSHandle JSMapIterator::CreateMapIterator(JSThread *thread, const JSHandle &obj, + IterationKind kind) +{ + ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); + if (!obj->IsJSMap()) { + JSHandle undefinedHandle(thread, JSTaggedValue::Undefined()); + THROW_TYPE_ERROR_AND_RETURN(thread, "obj is not JSMap", undefinedHandle); + } + JSHandle iter(factory->NewJSMapIterator(JSHandle(obj), kind)); + return iter; +} +} // namespace panda::ecmascript diff --git a/runtime/js_map_iterator.h b/runtime/js_map_iterator.h new file mode 100644 index 000000000..ac21f6410 --- /dev/null +++ b/runtime/js_map_iterator.h @@ -0,0 +1,47 @@ +/* + * Copyright (c) 2021-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_JS_MAP_ITERATOR_H +#define ECMASCRIPT_JS_MAP_ITERATOR_H + +#include "js_iterator.h" +#include "js_object.h" + +namespace panda::ecmascript { +class JSMapIterator : public JSObject { +public: + static JSMapIterator *Cast(ObjectHeader *obj) + { + ASSERT(JSTaggedValue(obj).IsJSMapIterator()); + return static_cast(obj); + } + static JSHandle CreateMapIterator(JSThread *thread, const JSHandle &obj, + IterationKind kind); + + static JSTaggedValue Next(EcmaRuntimeCallInfo *argv); + void Update(const JSThread *thread); + + static constexpr size_t ITERATED_MAP_OFFSET = JSObject::SIZE; + ACCESSORS(IteratedMap, ITERATED_MAP_OFFSET, NEXT_INDEX_OFFSET); + ACCESSORS(NextIndex, NEXT_INDEX_OFFSET, ITERATION_KIND_OFFSET); + ACCESSORS(IterationKind, ITERATION_KIND_OFFSET, SIZE); + + DECL_VISIT_OBJECT_FOR_JS_OBJECT(JSObject, ITERATED_MAP_OFFSET, SIZE) + + DECL_DUMP() +}; +} // namespace panda::ecmascript + +#endif // ECMASCRIPT_JS_MAP_ITERATOR_H diff --git a/runtime/js_method.cpp b/runtime/js_method.cpp new file mode 100644 index 000000000..510c42960 --- /dev/null +++ b/runtime/js_method.cpp @@ -0,0 +1,69 @@ +/* + * Copyright (c) 2021-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 "js_method.h" +#include "plugins/ecmascript/runtime/js_tagged_value-inl.h" +#include "libpandafile/method_data_accessor-inl.h" + +namespace panda::ecmascript { +// It's not allowed '#' token appear in ECMA function(method) name, which discriminates same names in panda methods. +CString JSMethod::ParseFunctionName() const +{ + auto *name = GetStringDataAnnotation(Method::AnnotationField::FUNCTION_NAME).data; + if (name == nullptr) { + name = GetName().data; + } + return utf::Mutf8AsCString(name); +} + +void JSMethod::SetCallTypeFromAnnotation() +{ + const panda_file::File *pandaFile = GetPandaFile(); + panda_file::File::EntityId fieldId = GetFileId(); + panda_file::MethodDataAccessor mda(*pandaFile, fieldId); + mda.EnumerateAnnotations([&](panda_file::File::EntityId annotation_id) { + panda_file::AnnotationDataAccessor ada(*pandaFile, annotation_id); + auto *annotation_name = reinterpret_cast(pandaFile->GetStringData(ada.GetClassId()).data); + if (::strcmp("L_ESCallTypeAnnotation;", annotation_name) == 0) { + uint32_t elem_count = ada.GetCount(); + for (uint32_t i = 0; i < elem_count; i++) { + panda_file::AnnotationDataAccessor::Elem adae = ada.GetElement(i); + auto *elem_name = reinterpret_cast(pandaFile->GetStringData(adae.GetNameId()).data); + if (::strcmp("callType", elem_name) == 0) { + callType_ = adae.GetScalarValue().GetValue(); + } + } + } + }); +} + +JSTaggedValue JSMethod::GetLength() const +{ + JSTaggedValue length = JSTaggedValue::Undefined(); + if (GetPandaFile() != nullptr) { + length = JSTaggedValue(GetNumericalAnnotation(Method::AnnotationField::FUNCTION_LENGTH)); + } + return length; +} + +std::string JSMethod::GetFullName() +{ + auto name = ParseFunctionName(); + if (name.empty()) { + return std::string(Method::GetFullName()); + } + return std::string(GetClass() != nullptr ? PandaString(GetClass()->GetName()) : "") + "::" + std::string(name); +} +} // namespace panda::ecmascript diff --git a/runtime/js_method.h b/runtime/js_method.h new file mode 100644 index 000000000..ab212165c --- /dev/null +++ b/runtime/js_method.h @@ -0,0 +1,127 @@ +/* + * Copyright (c) 2021-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_JS_METHOD_H +#define ECMASCRIPT_JS_METHOD_H + +#include "plugins/ecmascript/runtime/mem/c_string.h" +#include "include/method.h" +#include "libpandafile/file.h" + +namespace panda { +class Class; +} // namespace panda + +static constexpr uint32_t NORMAL_CALL_TYPE = 0; // 0: normal (without this, newTarget, extra, func) +static constexpr uint32_t HAVE_THIS_BIT = 1; // 1: the last bit means this +static constexpr uint32_t HAVE_NEWTARGET_BIT = 2; // 2: the 2nd to last bit means newTarget +static constexpr uint32_t HAVE_EXTRA_BIT = 4; // 4: the 3rd to last bit means extra +static constexpr uint32_t HAVE_FUNC_BIT = 8; // 8: the 4th to last bit means func (for old version, UINT32_MAX) + +namespace panda::ecmascript { +class JSMethod : public Method { +public: + static constexpr uint32_t MAX_SLOT_SIZE = 0xFFFFFFFF; + + static JSMethod *Cast(Method *method) + { + return static_cast(method); + } + + explicit JSMethod(Class *klass, const panda_file::File *pf, panda_file::File::EntityId fileId, + panda_file::File::EntityId codeId, uint32_t accessFlags, uint32_t numArgs, const uint16_t *shorty) + : Method(klass, pf, fileId, codeId, accessFlags, numArgs, shorty) + { + bytecodeArray_ = JSMethod::GetInstructions(); + bytecodeArraySize_ = JSMethod::GetCodeSize(); + } + + explicit JSMethod(const Method *method) : Method(method) + { + bytecodeArray_ = JSMethod::GetInstructions(); + bytecodeArraySize_ = JSMethod::GetCodeSize(); + } + + JSMethod() = delete; + ~JSMethod() = default; + JSMethod(const JSMethod &) = delete; + JSMethod(JSMethod &&) = delete; + JSMethod &operator=(const JSMethod &) = delete; + JSMethod &operator=(JSMethod &&) = delete; + + static constexpr uint32_t GetBytecodeArrayOffset() + { + return MEMBER_OFFSET(JSMethod, bytecodeArray_); + } + + const uint8_t *GetBytecodeArray() const + { + return bytecodeArray_; + } + + uint32_t GetBytecodeArraySize() const + { + return bytecodeArraySize_; + } + + void SetBytecodeArray(const uint8_t *bc) + { + bytecodeArray_ = bc; + } + + uint32_t GetSlotSize() const + { + return slotSize_; + } + + void UpdateSlotSize(uint32_t size) + { + uint64_t end = GetSlotSize() + size; + if (end >= MAX_SLOT_SIZE) { + slotSize_ = MAX_SLOT_SIZE; + return; + } + slotSize_ = static_cast(end); + } + + void SetSlotSize(uint32_t size) + { + if (size >= MAX_SLOT_SIZE) { + slotSize_ = MAX_SLOT_SIZE; + return; + } + slotSize_ = size; + } + + uint32_t GetCallType() const + { + return callType_; + } + + CString ParseFunctionName() const; + std::string GetFullName(); + void SetCallTypeFromAnnotation(); + + JSTaggedValue GetLength() const; + +private: + const uint8_t *bytecodeArray_ {nullptr}; + uint32_t bytecodeArraySize_ {0}; + uint32_t slotSize_ {0}; + uint32_t callType_ {UINT32_MAX}; // UINT32_MAX means not found +}; +} // namespace panda::ecmascript + +#endif // ECMASCRIPT_JS_METHOD_H diff --git a/runtime/js_native_pointer.h b/runtime/js_native_pointer.h new file mode 100644 index 000000000..519ed980a --- /dev/null +++ b/runtime/js_native_pointer.h @@ -0,0 +1,66 @@ +/* + * Copyright (c) 2021-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_JSNATIVEPOINTER_H +#define ECMASCRIPT_JSNATIVEPOINTER_H + +#include "plugins/ecmascript/runtime/ecma_macros.h" +#include "plugins/ecmascript/runtime/mem/tagged_object.h" + +namespace panda::ecmascript { +using DeleteEntryPoint = void (*)(void *, void *); + +// Used for the requirement of ACE that wants to associated a registered C++ resource with a JSObject. +class JSNativePointer : public TaggedObject { +public: + static JSNativePointer *Cast(ObjectHeader *object) + { + ASSERT(JSTaggedValue(object).IsJSNativePointer()); + return reinterpret_cast(object); + } + + inline void ResetExternalPointer(void *externalPointer) + { + DeleteExternalPointer(); + SetExternalPointer(externalPointer); + } + + inline void Destroy() + { + DeleteExternalPointer(); + SetExternalPointer(nullptr); + SetDeleter(nullptr); + SetData(nullptr); + } + + static constexpr size_t POINTER_OFFSET = TaggedObjectSize(); + SET_GET_VOID_FIELD(ExternalPointer, POINTER_OFFSET, DELETER_OFFSET); + SET_GET_PRIMITIVE_FIELD(Deleter, DeleteEntryPoint, DELETER_OFFSET, DATA_OFFSET); + SET_GET_VOID_FIELD(Data, DATA_OFFSET, SIZE); + +private: + inline void DeleteExternalPointer() + { + void *externalPointer = GetExternalPointer(); + if (externalPointer != nullptr) { + DeleteEntryPoint deleter = GetDeleter(); + if (deleter != nullptr) { + deleter(externalPointer, GetData()); + } + } + } +}; +} // namespace panda::ecmascript +#endif // ECMASCRIPT_JSNATIVEPOINTER_H diff --git a/runtime/js_number_format.cpp b/runtime/js_number_format.cpp new file mode 100644 index 000000000..2717db558 --- /dev/null +++ b/runtime/js_number_format.cpp @@ -0,0 +1,1004 @@ +/* + * Copyright (c) 2021-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 "plugins/ecmascript/runtime/js_number_format.h" + +namespace panda::ecmascript { +constexpr uint32_t DEFAULT_FRACTION_DIGITS = 2; +constexpr uint32_t PERUNIT_STRING = 5; + +JSHandle OptionToEcmaString(JSThread *thread, StyleOption style) +{ + JSMutableHandle result(thread, JSTaggedValue::Undefined()); + auto globalConst = thread->GlobalConstants(); + switch (style) { + case StyleOption::DECIMAL: + result.Update(globalConst->GetHandledDecimalString().GetTaggedValue()); + break; + case StyleOption::CURRENCY: + result.Update(globalConst->GetHandledCurrencyString().GetTaggedValue()); + break; + case StyleOption::PERCENT: + result.Update(globalConst->GetHandledPercentString().GetTaggedValue()); + break; + case StyleOption::UNIT: + result.Update(globalConst->GetHandledUnitString().GetTaggedValue()); + break; + default: + UNREACHABLE(); + } + return result; +} + +JSHandle OptionToEcmaString(JSThread *thread, CurrencyDisplayOption currencyDisplay) +{ + JSMutableHandle result(thread, JSTaggedValue::Undefined()); + auto globalConst = thread->GlobalConstants(); + switch (currencyDisplay) { + case CurrencyDisplayOption::CODE: + result.Update(globalConst->GetHandledCodeString().GetTaggedValue()); + break; + case CurrencyDisplayOption::SYMBOL: + result.Update(globalConst->GetHandledSymbolString().GetTaggedValue()); + break; + case CurrencyDisplayOption::NARROWSYMBOL: + result.Update(globalConst->GetHandledNarrowSymbolString().GetTaggedValue()); + break; + case CurrencyDisplayOption::NAME: + result.Update(globalConst->GetHandledNameString().GetTaggedValue()); + break; + default: + UNREACHABLE(); + } + return result; +} + +JSHandle OptionToEcmaString(JSThread *thread, CurrencySignOption currencySign) +{ + auto globalConst = thread->GlobalConstants(); + JSMutableHandle result(thread, JSTaggedValue::Undefined()); + switch (currencySign) { + case CurrencySignOption::STANDARD: + result.Update(globalConst->GetHandledStandardString().GetTaggedValue()); + break; + case CurrencySignOption::ACCOUNTING: + result.Update(globalConst->GetHandledAccountingString().GetTaggedValue()); + break; + default: + UNREACHABLE(); + } + return result; +} + +JSHandle OptionToEcmaString(JSThread *thread, UnitDisplayOption unitDisplay) +{ + JSMutableHandle result(thread, JSTaggedValue::Undefined()); + auto globalConst = thread->GlobalConstants(); + switch (unitDisplay) { + case UnitDisplayOption::SHORT: + result.Update(globalConst->GetHandledShortString().GetTaggedValue()); + break; + case UnitDisplayOption::NARROW: + result.Update(globalConst->GetHandledNarrowString().GetTaggedValue()); + break; + case UnitDisplayOption::LONG: + result.Update(globalConst->GetHandledLongString().GetTaggedValue()); + break; + default: + UNREACHABLE(); + } + return result; +} + +JSHandle OptionToEcmaString(JSThread *thread, NotationOption notation) +{ + JSMutableHandle result(thread, JSTaggedValue::Undefined()); + auto globalConst = thread->GlobalConstants(); + switch (notation) { + case NotationOption::STANDARD: + result.Update(globalConst->GetHandledStandardString().GetTaggedValue()); + break; + case NotationOption::SCIENTIFIC: + result.Update(globalConst->GetHandledScientificString().GetTaggedValue()); + break; + case NotationOption::ENGINEERING: + result.Update(globalConst->GetHandledEngineeringString().GetTaggedValue()); + break; + case NotationOption::COMPACT: + result.Update(globalConst->GetHandledCompactString().GetTaggedValue()); + break; + default: + UNREACHABLE(); + } + return result; +} + +JSHandle OptionToEcmaString(JSThread *thread, CompactDisplayOption compactDisplay) +{ + JSMutableHandle result(thread, JSTaggedValue::Undefined()); + auto globalConst = thread->GlobalConstants(); + switch (compactDisplay) { + case CompactDisplayOption::SHORT: + result.Update(globalConst->GetHandledShortString().GetTaggedValue()); + break; + case CompactDisplayOption::LONG: + result.Update(globalConst->GetHandledLongString().GetTaggedValue()); + break; + default: + UNREACHABLE(); + } + return result; +} + +JSHandle OptionToEcmaString(JSThread *thread, SignDisplayOption signDisplay) +{ + JSMutableHandle result(thread, JSTaggedValue::Undefined()); + auto globalConst = thread->GlobalConstants(); + switch (signDisplay) { + case SignDisplayOption::AUTO: + result.Update(globalConst->GetHandledAutoString().GetTaggedValue()); + break; + case SignDisplayOption::ALWAYS: + result.Update(globalConst->GetHandledAlwaysString().GetTaggedValue()); + break; + case SignDisplayOption::NEVER: + result.Update(globalConst->GetHandledNeverString().GetTaggedValue()); + break; + case SignDisplayOption::EXCEPTZERO: + result.Update(globalConst->GetHandledExceptZeroString().GetTaggedValue()); + break; + default: + UNREACHABLE(); + } + return result; +} + +icu::MeasureUnit ToMeasureUnit(const std::string &sanctionedUnit) +{ + UErrorCode status = U_ZERO_ERROR; + // Get All ICU measure unit + int32_t total = icu::MeasureUnit::getAvailable(nullptr, 0, status); + status = U_ZERO_ERROR; + std::vector units(total); + icu::MeasureUnit::getAvailable(units.data(), total, status); + ASSERT(U_SUCCESS(status)); + + // Find measure unit according to sanctioned unit + // then return measure unit + for (auto &unit : units) { + if (std::strcmp(sanctionedUnit.c_str(), unit.getSubtype()) == 0) { + return unit; + } + } + return icu::MeasureUnit(); +} + +// ecma402 #sec-issanctionedsimpleunitidentifier +bool IsSanctionedSimpleUnitIdentifier(const std::string &unit) +{ + // 1. If unitIdentifier is listed in sanctioned unit set, return true. + // 2. Else, Return false. + auto it = sanctionedUnit.find(unit); + return it != sanctionedUnit.end(); +} + +// 6.5.1 IsWellFormedUnitIdentifier ( unitIdentifier ) +bool IsWellFormedUnitIdentifier(const std::string &unit, icu::MeasureUnit &icuUnit, icu::MeasureUnit &icuPerUnit) +{ + // 1. If the result of IsSanctionedSimpleUnitIdentifier(unitIdentifier) is true, then + // a. Return true. + icu::MeasureUnit result = icu::MeasureUnit(); + icu::MeasureUnit emptyUnit = icu::MeasureUnit(); + auto pos = unit.find("-per-"); + if (IsSanctionedSimpleUnitIdentifier(unit) && pos == std::string::npos) { + result = ToMeasureUnit(unit); + icuUnit = result; + icuPerUnit = emptyUnit; + return true; + } + + // 2. If the substring "-per-" does not occur exactly once in unitIdentifier, + // a. then false + size_t afterPos = pos + PERUNIT_STRING; + if (pos == std::string::npos || unit.find("-per-", afterPos) != std::string::npos) { + return false; + } + + // 3. Let numerator be the substring of unitIdentifier from the beginning to just before "-per-". + std::string numerator = unit.substr(0, pos); + // 4. If the result of IsSanctionedUnitIdentifier(numerator) is false, then + // a. return false + if (IsSanctionedSimpleUnitIdentifier(numerator)) { + result = ToMeasureUnit(numerator); + } else { + return false; + } + + // 5. Let denominator be the substring of unitIdentifier from just after "-per-" to the end. + std::string denominator = unit.substr(pos + PERUNIT_STRING); + + // 6. If the result of IsSanctionedUnitIdentifier(denominator) is false, then + // a. Return false + icu::MeasureUnit perResult = icu::MeasureUnit(); + if (IsSanctionedSimpleUnitIdentifier(denominator)) { + perResult = ToMeasureUnit(denominator); + } else { + return false; + } + + // 7. Return true. + icuUnit = result; + icuPerUnit = perResult; + return true; +} + +// 12.1.13 SetNumberFormatUnitOptions ( intlObj, options ) +FractionDigitsOption SetNumberFormatUnitOptions(JSThread *thread, + const JSHandle &numberFormat, + const JSHandle &optionsObject, + icu::number::LocalizedNumberFormatter *icuNumberFormatter) +{ + auto globalConst = thread->GlobalConstants(); + FractionDigitsOption fractionDigitsOption; + // 3. Let style be ? GetOption(options, "style", "string", « "decimal", "percent", "currency", "unit" », "decimal"). + JSHandle property = globalConst->GetHandledStyleString(); + auto style = JSLocale::GetOptionOfString( + thread, optionsObject, property, + {StyleOption::DECIMAL, StyleOption::PERCENT, StyleOption::CURRENCY, StyleOption::UNIT}, + {"decimal", "percent", "currency", "unit"}, StyleOption::DECIMAL); + RETURN_VALUE_IF_ABRUPT_COMPLETION(thread, fractionDigitsOption); + + // 4. Set intlObj.[[Style]] to style. + JSHandle styleValue(thread, JSTaggedValue(static_cast(style))); + numberFormat->SetStyle(thread, styleValue); + + // 5. Let currency be ? GetOption(options, "currency", "string", undefined, undefined). + property = globalConst->GetHandledCurrencyString(); + JSHandle undefinedValue(thread, JSTaggedValue::Undefined()); + JSHandle currency = + JSLocale::GetOption(thread, optionsObject, property, OptionType::STRING, undefinedValue, undefinedValue); + RETURN_VALUE_IF_ABRUPT_COMPLETION(thread, fractionDigitsOption); + + // 6. If currency is not undefined, then + // a. If the result of IsWellFormedCurrencyCode(currency) is false, throw a RangeError exception. + if (!currency->IsUndefined()) { + JSHandle currencyStr = JSHandle::Cast(currency); + if (currencyStr->IsUtf16()) { + THROW_RANGE_ERROR_AND_RETURN(thread, "not a utf-8", fractionDigitsOption); + } + std::string currencyCStr = JSLocale::ConvertToStdString(currencyStr); + if (!JSLocale::IsWellFormedCurrencyCode(currencyCStr)) { + THROW_RANGE_ERROR_AND_RETURN(thread, "not a wellformed code", fractionDigitsOption); + } + } else { + // 7. If style is "currency" and currency is undefined, throw a TypeError exception. + if (style == StyleOption::CURRENCY) { + THROW_TYPE_ERROR_AND_RETURN(thread, "style is currency but currency is undefined", fractionDigitsOption); + } + } + + // 8. Let currencyDisplay be ? GetOption(options, "currencyDisplay", "string", + // « "code", "symbol", "narrowSymbol", "name" », "symbol"). + property = globalConst->GetHandledCurrencyDisplayString(); + auto currencyDisplay = JSLocale::GetOptionOfString( + thread, optionsObject, property, + {CurrencyDisplayOption::CODE, CurrencyDisplayOption::SYMBOL, CurrencyDisplayOption::NARROWSYMBOL, + CurrencyDisplayOption::NAME}, + {"code", "symbol", "narrowSymbol", "name"}, CurrencyDisplayOption::SYMBOL); + RETURN_VALUE_IF_ABRUPT_COMPLETION(thread, fractionDigitsOption); + JSHandle currencyDisplayValue(thread, JSTaggedValue(static_cast(currencyDisplay))); + numberFormat->SetCurrencyDisplay(thread, currencyDisplayValue); + + // 9. Let currencySign be ? GetOption(options, "currencySign", "string", « "standard", "accounting" », "standard"). + property = globalConst->GetHandledCurrencySignString(); + auto currencySign = JSLocale::GetOptionOfString( + thread, optionsObject, property, {CurrencySignOption::STANDARD, CurrencySignOption::ACCOUNTING}, + {"standard", "accounting"}, CurrencySignOption::STANDARD); + RETURN_VALUE_IF_ABRUPT_COMPLETION(thread, fractionDigitsOption); + JSHandle currencySignValue(thread, JSTaggedValue(static_cast(currencySign))); + numberFormat->SetCurrencySign(thread, currencySignValue); + + // 10. Let unit be ? GetOption(options, "unit", "string", undefined, undefined). + property = globalConst->GetHandledUnitString(); + JSHandle unit = + JSLocale::GetOption(thread, optionsObject, property, OptionType::STRING, undefinedValue, undefinedValue); + RETURN_VALUE_IF_ABRUPT_COMPLETION(thread, fractionDigitsOption); + numberFormat->SetUnit(thread, unit); + + // 11. If unit is not undefined, then + // If the result of IsWellFormedUnitIdentifier(unit) is false, throw a RangeError exception. + icu::MeasureUnit icuUnit; + icu::MeasureUnit icuPerUnit; + if (!unit->IsUndefined()) { + JSHandle unitStr = JSHandle::Cast(unit); + if (unitStr->IsUtf16()) { + THROW_RANGE_ERROR_AND_RETURN(thread, "Unit input is illegal", fractionDigitsOption); + } + std::string str = JSLocale::ConvertToStdString(unitStr); + if (!IsWellFormedUnitIdentifier(str, icuUnit, icuPerUnit)) { + THROW_RANGE_ERROR_AND_RETURN(thread, "Unit input is illegal", fractionDigitsOption); + } + } else { + // 15.12. if style is "unit" and unit is undefined, throw a TypeError exception. + if (style == StyleOption::UNIT) { + THROW_TYPE_ERROR_AND_RETURN(thread, "style is unit but unit is undefined", fractionDigitsOption); + } + } + + // 13. Let unitDisplay be ? GetOption(options, "unitDisplay", "string", « "short", "narrow", "long" », "short"). + property = globalConst->GetHandledUnitDisplayString(); + auto unitDisplay = JSLocale::GetOptionOfString( + thread, optionsObject, property, {UnitDisplayOption::SHORT, UnitDisplayOption::NARROW, UnitDisplayOption::LONG}, + {"short", "narrow", "long"}, UnitDisplayOption::SHORT); + RETURN_VALUE_IF_ABRUPT_COMPLETION(thread, fractionDigitsOption); + JSHandle unitDisplayValue(thread, JSTaggedValue(static_cast(unitDisplay))); + numberFormat->SetUnitDisplay(thread, unitDisplayValue); + + // 14. If style is "currency", then + // a. Let currency be the result of converting currency to upper case as specified in 6.1. + // b. Set intlObj.[[Currency]] to currency. + // c. Set intlObj.[[CurrencyDisplay]] to currencyDisplay. + // d. Set intlObj.[[CurrencySign]] to currencySign. + icu::UnicodeString currencyUStr; + UErrorCode status = U_ZERO_ERROR; + if (style == StyleOption::CURRENCY) { + JSHandle currencyStr = JSHandle::Cast(currency); + std::string currencyCStr = JSLocale::ConvertToStdString(currencyStr); + std::transform(currencyCStr.begin(), currencyCStr.end(), currencyCStr.begin(), toupper); + ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); + JSHandle currencyValue = JSHandle::Cast(factory->NewFromStdString(currencyCStr)); + numberFormat->SetCurrency(thread, currencyValue); + currencyUStr = currencyCStr.c_str(); + if (!currencyUStr.isEmpty()) { // NOLINT(readability-implicit-bool-conversion) + *icuNumberFormatter = icuNumberFormatter->unit(icu::CurrencyUnit(currencyUStr.getBuffer(), status)); + ASSERT(U_SUCCESS(status)); + UNumberUnitWidth uNumberUnitWidth; + // Trans currencyDisplayOption to ICU format number display option + switch (currencyDisplay) { + case CurrencyDisplayOption::CODE: + uNumberUnitWidth = UNumberUnitWidth::UNUM_UNIT_WIDTH_ISO_CODE; + break; + case CurrencyDisplayOption::SYMBOL: + uNumberUnitWidth = UNumberUnitWidth::UNUM_UNIT_WIDTH_SHORT; + break; + case CurrencyDisplayOption::NARROWSYMBOL: + uNumberUnitWidth = UNumberUnitWidth::UNUM_UNIT_WIDTH_NARROW; + break; + case CurrencyDisplayOption::NAME: + uNumberUnitWidth = UNumberUnitWidth::UNUM_UNIT_WIDTH_FULL_NAME; + break; + default: + UNREACHABLE(); + } + *icuNumberFormatter = icuNumberFormatter->unitWidth(uNumberUnitWidth); + } + } + + // 15. If style is "unit", then + // if unit is not undefiend set unit to LocalizedNumberFormatter + // then if perunit is not undefiend set perunit to LocalizedNumberFormatter + if (style == StyleOption::UNIT) { + icu::MeasureUnit emptyUnit = icu::MeasureUnit(); + if (icuUnit != emptyUnit) { // NOLINT(readability-implicit-bool-conversion) + *icuNumberFormatter = icuNumberFormatter->unit(icuUnit); + } + if (icuPerUnit != emptyUnit) { // NOLINT(readability-implicit-bool-conversion) + *icuNumberFormatter = icuNumberFormatter->perUnit(icuPerUnit); + } + } + + // 17. If style is "currency", then + // a. Let cDigits be CurrencyDigits(currency). + // b. Let mnfdDefault be cDigits. + // c. Let mxfdDefault be cDigits. + if (style == StyleOption::CURRENCY) { + int32_t cDigits = JSNumberFormat::CurrencyDigits(currencyUStr); + fractionDigitsOption.mnfdDefault = cDigits; + fractionDigitsOption.mxfdDefault = cDigits; + } else { + // 18. Else, + // a. Let mnfdDefault be 0. + // b. If style is "percent", then + // i. Let mxfdDefault be 0. + // c. else, + // i. Let mxfdDefault be 3. + fractionDigitsOption.mnfdDefault = 0; + if (style == StyleOption::PERCENT) { + fractionDigitsOption.mxfdDefault = 0; + } else { + fractionDigitsOption.mxfdDefault = 3; // Max decimal precision is 3 + } + } + return fractionDigitsOption; +} + +// 12.1.2 InitializeNumberFormat ( numberFormat, locales, options ) +// NOLINTNEXTLINE(readability-function-size) +void JSNumberFormat::InitializeNumberFormat(JSThread *thread, const JSHandle &numberFormat, + const JSHandle &locales, + const JSHandle &options) +{ + ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); + // 1. Let requestedLocales be ? CanonicalizeLocaleList(locales). + JSHandle requestedLocales = JSLocale::CanonicalizeLocaleList(thread, locales); + + // 2. If options is undefined, then + // a. Let options be ObjectCreate(null). + // 3. Else, + // a. Let options be ? ToObject(options). + JSHandle optionsObject; + if (options->IsUndefined()) { + optionsObject = factory->OrdinaryNewJSObjectCreate(JSHandle(thread, JSTaggedValue::Null())); + } else { + optionsObject = JSTaggedValue::ToObject(thread, options); + RETURN_IF_ABRUPT_COMPLETION(thread); + } + + auto globalConst = thread->GlobalConstants(); + // 5. Let matcher be ? GetOption(options, "localeMatcher", "string", « "lookup", "best fit" », "best fit"). + JSHandle property = globalConst->GetHandledLocaleMatcherString(); + auto matcher = JSLocale::GetOptionOfString( + thread, optionsObject, property, {LocaleMatcherOption::LOOKUP, LocaleMatcherOption::BEST_FIT}, + {"lookup", "best fit"}, LocaleMatcherOption::BEST_FIT); + RETURN_IF_ABRUPT_COMPLETION(thread); + + // 7. Let numberingSystem be ? GetOption(options, "numberingSystem", "string", undefined, undefined). + property = globalConst->GetHandledNumberingSystemString(); + JSHandle undefinedValue(thread, JSTaggedValue::Undefined()); + JSHandle numberingSystemTaggedValue = + JSLocale::GetOption(thread, optionsObject, property, OptionType::STRING, undefinedValue, undefinedValue); + RETURN_IF_ABRUPT_COMPLETION(thread); + numberFormat->SetNumberingSystem(thread, numberingSystemTaggedValue); + + // 8. If numberingSystem is not undefined, then + // a. If numberingSystem does not match the Unicode Locale Identifier type nonterminal, + // throw a RangeError exception. `(3*8alphanum) *("-" (3*8alphanum))` + std::string numberingSystemStr; + if (!numberingSystemTaggedValue->IsUndefined()) { + JSHandle numberingSystemEcmaString = JSHandle::Cast(numberingSystemTaggedValue); + if (numberingSystemEcmaString->IsUtf16()) { + THROW_RANGE_ERROR(thread, "invalid numberingSystem"); + } + numberingSystemStr = JSLocale::ConvertToStdString(numberingSystemEcmaString); + if (!JSLocale::IsNormativeNumberingSystem(numberingSystemStr)) { + THROW_RANGE_ERROR(thread, "invalid numberingSystem"); + } + } + + // 10. Let localeData be %NumberFormat%.[[LocaleData]]. + JSHandle availableLocales; + if (requestedLocales->GetLength() == 0) { + availableLocales = factory->EmptyArray(); + } else { + availableLocales = GetAvailableLocales(thread); + } + + // 11. Let r be ResolveLocale( %NumberFormat%.[[AvailableLocales]], requestedLocales, opt, + // %NumberFormat%.[[RelevantExtensionKeys]], localeData). + std::set relevantExtensionKeys{"nu"}; + ResolvedLocale r = + JSLocale::ResolveLocale(thread, availableLocales, requestedLocales, matcher, relevantExtensionKeys); + + // 12. Set numberFormat.[[Locale]] to r.[[locale]]. + icu::Locale icuLocale = r.localeData; + JSHandle localeStr = JSLocale::ToLanguageTag(thread, icuLocale); + numberFormat->SetLocale(thread, localeStr.GetTaggedValue()); + + // Set numberingSystemStr to UnicodeKeyWord "nu" + UErrorCode status = U_ZERO_ERROR; + if (!numberingSystemStr.empty()) { + if (JSLocale::IsWellNumberingSystem(numberingSystemStr)) { + icuLocale.setUnicodeKeywordValue("nu", numberingSystemStr, status); + ASSERT(U_SUCCESS(status)); + } + } + + icu::number::LocalizedNumberFormatter icuNumberFormatter = + icu::number::NumberFormatter::withLocale(icuLocale).roundingMode(UNUM_ROUND_HALFUP); + + // Get default numbering system associated with the specified locale + std::unique_ptr numberingSystemPtr(icu::NumberingSystem::createInstance(icuLocale, status)); + + int32_t mnfdDefault = 0; + int32_t mxfdDefault = 0; + FractionDigitsOption fractionOptions = + SetNumberFormatUnitOptions(thread, numberFormat, optionsObject, &icuNumberFormatter); + RETURN_IF_ABRUPT_COMPLETION(thread); + mnfdDefault = fractionOptions.mnfdDefault; + mxfdDefault = fractionOptions.mxfdDefault; + JSTaggedValue unitDisplayValue = numberFormat->GetUnitDisplay(); + auto unitDisplay = static_cast(unitDisplayValue.GetInt()); + + // Trans unitDisplay option to ICU display option + UNumberUnitWidth uNumberUnitWidth; + switch (unitDisplay) { + case UnitDisplayOption::SHORT: + uNumberUnitWidth = UNumberUnitWidth::UNUM_UNIT_WIDTH_SHORT; + break; + case UnitDisplayOption::NARROW: + uNumberUnitWidth = UNumberUnitWidth::UNUM_UNIT_WIDTH_NARROW; + break; + case UnitDisplayOption::LONG: + // UNUM_UNIT_WIDTH_FULL_NAME Print the full name of the unit, without any abbreviations. + uNumberUnitWidth = UNumberUnitWidth::UNUM_UNIT_WIDTH_FULL_NAME; + break; + default: + UNREACHABLE(); + } + icuNumberFormatter = icuNumberFormatter.unitWidth(uNumberUnitWidth); + + // 16. Let style be numberFormat.[[Style]]. + JSTaggedValue styleValue = numberFormat->GetStyle(); + auto style = static_cast(styleValue.GetInt()); + if (style == StyleOption::PERCENT) { + icuNumberFormatter = icuNumberFormatter.unit(icu::MeasureUnit::getPercent()) + .scale(icu::number::Scale::powerOfTen(2)); // means 10^2 + } + + // 19. Let notation be ? GetOption( + // options, "notation", "string", « "standard", "scientific", "engineering", "compact" », "standard"). + property = globalConst->GetHandledNotationString(); + auto notation = JSLocale::GetOptionOfString( + thread, optionsObject, property, + {NotationOption::STANDARD, NotationOption::SCIENTIFIC, NotationOption::ENGINEERING, NotationOption::COMPACT}, + {"standard", "scientific", "engineering", "compact"}, NotationOption::STANDARD); + RETURN_IF_ABRUPT_COMPLETION(thread); + JSHandle notationValue(thread, JSTaggedValue(static_cast(notation))); + numberFormat->SetNotation(thread, notationValue); + + // 21. Perform ? SetNumberFormatDigitOptions(numberFormat, options, mnfdDefault, mxfdDefault, notation). + JSLocale::SetNumberFormatDigitOptions(thread, numberFormat, JSHandle::Cast(optionsObject), + mnfdDefault, mxfdDefault, notation); + icuNumberFormatter = SetICUFormatterDigitOptions(icuNumberFormatter, numberFormat); + + // 22. Let compactDisplay be ? GetOptionOfString(options, "compactDisplay", "string", « "short", "long" », "short"). + property = globalConst->GetHandledCompactDisplayString(); + auto compactDisplay = JSLocale::GetOptionOfString( + thread, optionsObject, property, {CompactDisplayOption::SHORT, CompactDisplayOption::LONG}, {"short", "long"}, + CompactDisplayOption::SHORT); + JSHandle compactDisplayValue(thread, JSTaggedValue(static_cast(compactDisplay))); + numberFormat->SetCompactDisplay(thread, compactDisplayValue); + + // Trans NotationOption to ICU Noation and set to icuNumberFormatter + if (notation == NotationOption::COMPACT) { + switch (compactDisplay) { + case CompactDisplayOption::SHORT: + icuNumberFormatter = icuNumberFormatter.notation(icu::number::Notation::compactShort()); + break; + case CompactDisplayOption::LONG: + icuNumberFormatter = icuNumberFormatter.notation(icu::number::Notation::compactLong()); + break; + default: + break; + } + } + switch (notation) { + case NotationOption::STANDARD: + icuNumberFormatter = icuNumberFormatter.notation(icu::number::Notation::simple()); + break; + case NotationOption::SCIENTIFIC: + icuNumberFormatter = icuNumberFormatter.notation(icu::number::Notation::scientific()); + break; + case NotationOption::ENGINEERING: + icuNumberFormatter = icuNumberFormatter.notation(icu::number::Notation::engineering()); + break; + default: + break; + } + + // 24. Let useGrouping be ? GetOption(options, "useGrouping", "boolean", undefined, true). + property = globalConst->GetHandledUserGroupingString(); + bool useGrouping = false; + [[maybe_unused]] bool find = JSLocale::GetOptionOfBool(thread, optionsObject, property, true, &useGrouping); + RETURN_IF_ABRUPT_COMPLETION(thread); + JSHandle useGroupingValue(thread, JSTaggedValue(useGrouping)); + numberFormat->SetUseGrouping(thread, useGroupingValue); + + // 25. Set numberFormat.[[UseGrouping]] to useGrouping. + if (!useGrouping) { + icuNumberFormatter = icuNumberFormatter.grouping(UNumberGroupingStrategy::UNUM_GROUPING_OFF); + } + + // 26. Let signDisplay be ? + // GetOption(options, "signDisplay", "string", « "auto", "never", "always", "exceptZero" », "auto"). + property = globalConst->GetHandledSignDisplayString(); + auto signDisplay = JSLocale::GetOptionOfString( + thread, optionsObject, property, + {SignDisplayOption::AUTO, SignDisplayOption::NEVER, SignDisplayOption::ALWAYS, SignDisplayOption::EXCEPTZERO}, + {"auto", "never", "always", "exceptZero"}, SignDisplayOption::AUTO); + RETURN_IF_ABRUPT_COMPLETION(thread); + JSHandle signDisplayValue(thread, JSTaggedValue(static_cast(signDisplay))); + numberFormat->SetSignDisplay(thread, signDisplayValue); + + // 27. Set numberFormat.[[SignDisplay]] to signDisplay. + // The default sign in ICU is UNUM_SIGN_AUTO which is mapped from + // SignDisplay::AUTO and CurrencySign::STANDARD so we can skip setting + // under that values for optimization. + JSTaggedValue currencySignValue = numberFormat->GetCurrencySign(); + auto currencySign = static_cast(currencySignValue.GetInt()); + + // Trans SignDisPlayOption to ICU UNumberSignDisplay and set to icuNumberFormatter + switch (signDisplay) { + case SignDisplayOption::AUTO: + // if CurrencySign is ACCOUNTING, Use the locale-dependent accounting format on negative numbers + if (currencySign == CurrencySignOption::ACCOUNTING) { + icuNumberFormatter = icuNumberFormatter.sign(UNumberSignDisplay::UNUM_SIGN_ACCOUNTING); + } else { + icuNumberFormatter = icuNumberFormatter.sign(UNumberSignDisplay::UNUM_SIGN_AUTO); + } + break; + case SignDisplayOption::NEVER: + icuNumberFormatter = icuNumberFormatter.sign(UNumberSignDisplay::UNUM_SIGN_NEVER); + break; + case SignDisplayOption::ALWAYS: + // if CurrencySign is ACCOUNTING, Use the locale-dependent accounting format on negative numbers + if (currencySign == CurrencySignOption::ACCOUNTING) { + icuNumberFormatter = icuNumberFormatter.sign(UNumberSignDisplay::UNUM_SIGN_ACCOUNTING_ALWAYS); + } else { + icuNumberFormatter = icuNumberFormatter.sign(UNumberSignDisplay::UNUM_SIGN_ALWAYS); + } + break; + case SignDisplayOption::EXCEPTZERO: + // if CurrencySign is ACCOUNTING, Use the locale-dependent accounting format on negative numbers + if (currencySign == CurrencySignOption::ACCOUNTING) { + icuNumberFormatter = icuNumberFormatter.sign(UNumberSignDisplay::UNUM_SIGN_ACCOUNTING_EXCEPT_ZERO); + } else { + icuNumberFormatter = icuNumberFormatter.sign(UNumberSignDisplay::UNUM_SIGN_EXCEPT_ZERO); + } + break; + default: + break; + } + + // Set numberFormat.[[IcuNumberForma]] to handleNumberFormatter + factory->NewJSIntlIcuData(numberFormat, icuNumberFormatter, JSNumberFormat::FreeIcuNumberformat); + // Set numberFormat.[[BoundFormat]] to undefinedValue + numberFormat->SetBoundFormat(thread, undefinedValue); +} + +// 12.1.3 CurrencyDigits ( currency ) +int32_t JSNumberFormat::CurrencyDigits(const icu::UnicodeString ¤cy) +{ + UErrorCode status = U_ZERO_ERROR; + // If the ISO 4217 currency and funds code list contains currency as an alphabetic code, + // return the minor unit value corresponding to the currency from the list; otherwise, return 2. + int32_t fractionDigits = + ucurr_getDefaultFractionDigits(reinterpret_cast(currency.getBuffer()), &status); + if (U_SUCCESS(status) != 0) { + return fractionDigits; + } + return DEFAULT_FRACTION_DIGITS; +} + +// 12.1.8 FormatNumeric( numberFormat, x ) +JSHandle JSNumberFormat::FormatNumeric(JSThread *thread, const JSHandle &numberFormat, + JSTaggedValue x) +{ + icu::number::LocalizedNumberFormatter *icuNumberFormat = numberFormat->GetIcuCallTarget(); + ASSERT(icuNumberFormat != nullptr); + + UErrorCode status = U_ZERO_ERROR; + double number = x.GetNumber(); + icu::number::FormattedNumber formattedNumber = icuNumberFormat->formatDouble(number, status); + if (U_FAILURE(status) != 0) { + JSHandle errorResult(thread, JSTaggedValue::Exception()); + THROW_RANGE_ERROR_AND_RETURN(thread, "icu formatter format failed", errorResult); + } + icu::UnicodeString result = formattedNumber.toString(status); + if (U_FAILURE(status) != 0) { + JSHandle errorResult(thread, JSTaggedValue::Exception()); + THROW_RANGE_ERROR_AND_RETURN(thread, "formatted number toString failed", errorResult); + } + JSHandle stringValue = JSLocale::IcuToString(thread, result); + return JSHandle::Cast(stringValue); +} + +void GroupToParts(JSThread *thread, const icu::number::FormattedNumber &formatted, const JSHandle &receiver, + const JSHandle &numberFormat, JSTaggedValue x) +{ + UErrorCode status = U_ZERO_ERROR; + icu::UnicodeString formattedText = formatted.toString(status); + if (U_FAILURE(status) != 0) { // NOLINT(readability-implicit-bool-conversion) + THROW_TYPE_ERROR(thread, "formattedNumber toString failed"); + } + ASSERT(x.IsNumber()); + + StyleOption styleOption = static_cast(numberFormat->GetStyle().GetInt()); + + icu::ConstrainedFieldPosition cfpo; + // Set constrainCategory to UFIELD_CATEGORY_NUMBER which is specified for UNumberFormatFields + cfpo.constrainCategory(UFIELD_CATEGORY_NUMBER); + auto globalConst = thread->GlobalConstants(); + JSMutableHandle typeString(thread, JSTaggedValue::Undefined()); + int index = 0; + int previousLimit = 0; + /** + * From ICU header file document @unumberformatter.h + * Sets a constraint on the field category. + * + * When this instance of ConstrainedFieldPosition is passed to FormattedValue#nextPosition, + * positions are skipped unless they have the given category. + * + * Any previously set constraints are cleared. + * + * For example, to loop over only the number-related fields: + * + * ConstrainedFieldPosition cfpo; + * cfpo.constrainCategory(UFIELDCATEGORY_NUMBER_FORMAT); + * while (fmtval.nextPosition(cfpo, status)) { + * // handle the number-related field position + * } + */ + bool lastFieldGroup = false; + int groupLeapLength = 0; + while (formatted.nextPosition(cfpo, status) != 0) { + int32_t fieldId = cfpo.getField(); + int32_t start = cfpo.getStart(); + int32_t limit = cfpo.getLimit(); + typeString.Update(globalConst->GetLiteralString()); + // If start greater than previousLimit, means a literal type exists before number fields + // so add a literal type with value of formattedText.sub(0, start) + // Special case when fieldId is UNUM_GROUPING_SEPARATOR_FIELD + if (static_cast(fieldId) == UNUM_GROUPING_SEPARATOR_FIELD) { + JSHandle substring = JSLocale::IcuToString(thread, formattedText, previousLimit, start); + typeString.Update(globalConst->GetIntegerString()); + JSLocale::PutElement(thread, index, receiver, typeString, JSHandle::Cast(substring)); + RETURN_IF_ABRUPT_COMPLETION(thread); + index++; + { + typeString.Update(JSLocale::GetNumberFieldType(thread, x, fieldId).GetTaggedValue()); + substring = JSLocale::IcuToString(thread, formattedText, start, limit); + JSLocale::PutElement(thread, index, receiver, typeString, JSHandle::Cast(substring)); + RETURN_IF_ABRUPT_COMPLETION(thread); + index++; + } + lastFieldGroup = true; + groupLeapLength = start - previousLimit + 1; + previousLimit = limit; + continue; + } + if (start > previousLimit) { + JSHandle substring = JSLocale::IcuToString(thread, formattedText, previousLimit, start); + JSLocale::PutElement(thread, index, receiver, typeString, JSHandle::Cast(substring)); + RETURN_IF_ABRUPT_COMPLETION(thread); + index++; + } + if (lastFieldGroup) { + start = start + groupLeapLength; + lastFieldGroup = false; + } + // Special case in ICU when style is unit and unit is percent + if (styleOption == StyleOption::UNIT && static_cast(fieldId) == UNUM_PERCENT_FIELD) { + typeString.Update(globalConst->GetUnitString()); + } else { + typeString.Update(JSLocale::GetNumberFieldType(thread, x, fieldId).GetTaggedValue()); + } + JSHandle substring = JSLocale::IcuToString(thread, formattedText, start, limit); + JSLocale::PutElement(thread, index, receiver, typeString, JSHandle::Cast(substring)); + RETURN_IF_ABRUPT_COMPLETION(thread); + index++; + previousLimit = limit; + } + // If iterated length is smaller than formattedText.length, means a literal type exists after number fields + // so add a literal type with value of formattedText.sub(previousLimit, formattedText.length) + if (formattedText.length() > previousLimit) { + typeString.Update(globalConst->GetLiteralString()); + JSHandle substring = + JSLocale::IcuToString(thread, formattedText, previousLimit, formattedText.length()); + JSLocale::PutElement(thread, index, receiver, typeString, JSHandle::Cast(substring)); + } +} + +// 12.1.9 FormatNumericToParts( numberFormat, x ) +JSHandle JSNumberFormat::FormatNumericToParts(JSThread *thread, const JSHandle &numberFormat, + JSTaggedValue x) +{ + ASSERT(x.IsNumber()); + icu::number::LocalizedNumberFormatter *icuNumberFormatter = numberFormat->GetIcuCallTarget(); + ASSERT(icuNumberFormatter != nullptr); + + UErrorCode status = U_ZERO_ERROR; + double number = x.GetNumber(); + icu::number::FormattedNumber formattedNumber = icuNumberFormatter->formatDouble(number, status); + if (U_FAILURE(status) != 0) { + ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); + JSHandle emptyArray = factory->NewJSArray(); + THROW_RANGE_ERROR_AND_RETURN(thread, "icu formatter format failed", emptyArray); + } + + JSHandle arr = JSArray::ArrayCreate(thread, JSTaggedNumber(0)); + JSHandle result = JSHandle::Cast(arr); + GroupToParts(thread, formattedNumber, result, numberFormat, x); + RETURN_HANDLE_IF_ABRUPT_COMPLETION(JSArray, thread); + return result; +} + +// 12.1.12 UnwrapNumberFormat( nf ) +JSHandle JSNumberFormat::UnwrapNumberFormat(JSThread *thread, const JSHandle &nf) +{ + // 1. Assert: Type(nf) is Object. + ASSERT(nf->IsJSObject()); + + // 2. If nf does not have an [[InitializedNumberFormat]] internal slot and ? + // InstanceofOperator(nf, %NumberFormat%) is true, then Let nf be ? Get(nf, %Intl%.[[FallbackSymbol]]). + JSHandle env = thread->GetEcmaVM()->GetGlobalEnv(); + bool hasIstance = JSObject::InstanceOf(thread, nf, env->GetNumberFormatFunction()); + RETURN_VALUE_IF_ABRUPT_COMPLETION(thread, JSHandle(thread, JSTaggedValue::Undefined())); + + bool isJSNumberFormat = nf->IsJSNumberFormat(); + // If nf does not have an [[InitializedNumberFormat]] internal slot and ? + // InstanceofOperator(nf, %NumberFormat%) is true, then + // a. Let nf be ? Get(nf, %Intl%.[[FallbackSymbol]]). + if (!isJSNumberFormat && hasIstance) { + JSHandle key(thread, JSHandle::Cast(env->GetIntlFunction())->GetFallbackSymbol()); + OperationResult operationResult = JSTaggedValue::GetProperty(thread, nf, key); + RETURN_VALUE_IF_ABRUPT_COMPLETION(thread, JSHandle(thread, JSTaggedValue::Undefined())); + return operationResult.GetValue(); + } + // 3. Perform ? RequireInternalSlot(nf, [[InitializedNumberFormat]]). + if (!isJSNumberFormat) { + THROW_TYPE_ERROR_AND_RETURN(thread, "this is not object", + JSHandle(thread, JSTaggedValue::Exception())); + } + return nf; +} + +JSHandle JSNumberFormat::GetAvailableLocales(JSThread *thread) +{ + JSHandle env = thread->GetEcmaVM()->GetGlobalEnv(); + JSHandle numberFormatLocales = env->GetNumberFormatLocales(); + if (!numberFormatLocales->IsUndefined()) { + return JSHandle::Cast(numberFormatLocales); + } + const char *key = "NumberElements"; + const char *path = nullptr; + JSHandle availableLocales = JSLocale::GetAvailableLocales(thread, key, path); + env->SetNumberFormatLocales(thread, availableLocales); + return availableLocales; +} + +void JSNumberFormat::ResolvedOptions(JSThread *thread, const JSHandle &numberFormat, + const JSHandle &options) +{ + // Table 5: Resolved Options of NumberFormat Instances + // Internal Slot Property + // [[Locale]] "locale" + // [[NumberingSystem]] "numberingSystem" + // [[Style]] "style" + // [[Currency]] "currency" + // [[CurrencyDisplay]] "currencyDisplay" + // [[CurrencySign]] "currencySign" + // [[Unit]] "unit" + // [[UnitDisplay]] "unitDisplay" + // [[MinimumIntegerDigits]] "minimumIntegerDigits" + // [[MinimumFractionDigits]] "minimumFractionDigits" + // [[MaximumFractionDigits]] "maximumFractionDigits" + // [[MinimumSignificantDigits]] "minimumSignificantDigits" + // [[MaximumSignificantDigits]] "maximumSignificantDigits" + // [[UseGrouping]] "useGrouping" + // [[Notation]] "notation" + // [[CompactDisplay]] "compactDisplay" + // [SignDisplay]] "signDisplay" + // [[Locale]] + auto globalConst = thread->GlobalConstants(); + JSHandle property = globalConst->GetHandledLocaleString(); + JSHandle locale(thread, numberFormat->GetLocale()); + JSObject::CreateDataPropertyOrThrow(thread, options, property, locale); + + // [[NumberingSystem]] + JSHandle numberingSystem(thread, numberFormat->GetNumberingSystem()); + if (numberingSystem->IsUndefined()) { + numberingSystem = globalConst->GetHandledLatnString(); + } + property = globalConst->GetHandledNumberingSystemString(); + JSObject::CreateDataPropertyOrThrow(thread, options, property, numberingSystem); + + // [[Style]] + StyleOption style = static_cast(numberFormat->GetStyle().GetInt()); + property = globalConst->GetHandledStyleString(); + JSHandle styleString = OptionToEcmaString(thread, style); + JSObject::CreateDataPropertyOrThrow(thread, options, property, styleString); + + // [[currency]] + JSHandle currency(thread, JSTaggedValue::Undefined()); + // If style is not currency the currency should be undefined + if (style == StyleOption::CURRENCY) { + currency = JSHandle(thread, numberFormat->GetCurrency()); + } + if (!currency->IsUndefined()) { // NOLINT(readability-implicit-bool-conversion) + property = globalConst->GetHandledCurrencyString(); + JSObject::CreateDataPropertyOrThrow(thread, options, property, currency); + + // [[CurrencyDisplay]] + property = globalConst->GetHandledCurrencyDisplayString(); + CurrencyDisplayOption currencyDisplay = + static_cast(numberFormat->GetCurrencyDisplay().GetInt()); + JSHandle currencyDisplayString = OptionToEcmaString(thread, currencyDisplay); + JSObject::CreateDataPropertyOrThrow(thread, options, property, currencyDisplayString); + + // [[CurrencySign]] + property = globalConst->GetHandledCurrencySignString(); + CurrencySignOption currencySign = static_cast(numberFormat->GetCurrencySign().GetInt()); + JSHandle currencySignString = OptionToEcmaString(thread, currencySign); + JSObject::CreateDataPropertyOrThrow(thread, options, property, currencySignString); + } + + if (style == StyleOption::UNIT) { + JSHandle unit(thread, numberFormat->GetUnit()); + if (!unit->IsUndefined()) { + // [[Unit]] + property = globalConst->GetHandledUnitString(); + JSObject::CreateDataPropertyOrThrow(thread, options, property, unit); + } + // [[UnitDisplay]] + property = globalConst->GetHandledUnitDisplayString(); + UnitDisplayOption unitDisplay = static_cast(numberFormat->GetUnitDisplay().GetInt()); + JSHandle unitDisplayString = OptionToEcmaString(thread, unitDisplay); + JSObject::CreateDataPropertyOrThrow(thread, options, property, unitDisplayString); + } + // [[MinimumIntegerDigits]] + property = globalConst->GetHandledMinimumIntegerDigitsString(); + JSHandle minimumIntegerDigits(thread, numberFormat->GetMinimumIntegerDigits()); + JSObject::CreateDataPropertyOrThrow(thread, options, property, minimumIntegerDigits); + + RoundingType roundingType = static_cast(numberFormat->GetRoundingType().GetInt()); + if (roundingType == RoundingType::SIGNIFICANTDIGITS) { + // [[MinimumSignificantDigits]] + property = globalConst->GetHandledMinimumSignificantDigitsString(); + JSHandle minimumSignificantDigits(thread, numberFormat->GetMinimumSignificantDigits()); + JSObject::CreateDataPropertyOrThrow(thread, options, property, minimumSignificantDigits); + // [[MaximumSignificantDigits]] + property = globalConst->GetHandledMaximumSignificantDigitsString(); + JSHandle maximumSignificantDigits(thread, numberFormat->GetMaximumSignificantDigits()); + JSObject::CreateDataPropertyOrThrow(thread, options, property, maximumSignificantDigits); + } else { + // [[MinimumFractionDigits]] + property = globalConst->GetHandledMinimumFractionDigitsString(); + JSHandle minimumFractionDigits(thread, numberFormat->GetMinimumFractionDigits()); + JSObject::CreateDataPropertyOrThrow(thread, options, property, minimumFractionDigits); + // [[MaximumFractionDigits]] + property = globalConst->GetHandledMaximumFractionDigitsString(); + JSHandle maximumFractionDigits(thread, numberFormat->GetMaximumFractionDigits()); + JSObject::CreateDataPropertyOrThrow(thread, options, property, maximumFractionDigits); + } + + // [[UseGrouping]] + property = globalConst->GetHandledUserGroupingString(); + JSObject::CreateDataPropertyOrThrow(thread, options, property, + JSHandle(thread, numberFormat->GetUseGrouping())); + + // [[Notation]] + property = globalConst->GetHandledNotationString(); + NotationOption notation = static_cast(numberFormat->GetNotation().GetInt()); + JSHandle notationString = OptionToEcmaString(thread, notation); + JSObject::CreateDataPropertyOrThrow(thread, options, property, notationString); + + // Only output compactDisplay when notation is compact. + if (notation == NotationOption::COMPACT) { + // [[CompactDisplay]] + property = globalConst->GetHandledCompactDisplayString(); + CompactDisplayOption compactDisplay = + static_cast(numberFormat->GetCompactDisplay().GetInt()); + JSHandle compactDisplayString = OptionToEcmaString(thread, compactDisplay); + JSObject::CreateDataPropertyOrThrow(thread, options, property, compactDisplayString); + } + + // [[SignDisplay]] + property = globalConst->GetHandledSignDisplayString(); + SignDisplayOption signDisplay = static_cast(numberFormat->GetSignDisplay().GetInt()); + JSHandle signDisplayString = OptionToEcmaString(thread, signDisplay); + JSObject::CreateDataPropertyOrThrow(thread, options, property, signDisplayString); +} +} // namespace panda::ecmascript diff --git a/runtime/js_number_format.h b/runtime/js_number_format.h new file mode 100644 index 000000000..419026c94 --- /dev/null +++ b/runtime/js_number_format.h @@ -0,0 +1,160 @@ +/* + * Copyright (c) 2021-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_JS_NUMBER_FORMAT_H +#define ECMASCRIPT_JS_NUMBER_FORMAT_H + +#include "global_env.h" +#include "js_array.h" +#include "js_hclass.h" +#include "js_intl.h" +#include "js_locale.h" +#include "js_object.h" + +namespace panda::ecmascript { +enum class StyleOption : uint8_t { DECIMAL = 0x01, CURRENCY, PERCENT, UNIT, EXCEPTION }; + +enum class CompactDisplayOption : uint8_t { SHORT = 0x01, LONG, EXCEPTION }; + +enum class SignDisplayOption : uint8_t { AUTO = 0x01, ALWAYS, NEVER, EXCEPTZERO, EXCEPTION }; + +enum class CurrencyDisplayOption : uint8_t { CODE = 0x01, SYMBOL, NARROWSYMBOL, NAME, EXCEPTION }; + +enum class CurrencySignOption : uint8_t { STANDARD = 0x01, ACCOUNTING, EXCEPTION }; + +enum class UnitDisplayOption : uint8_t { SHORT = 0x01, NARROW, LONG, EXCEPTION }; + +struct FractionDigitsOption { + int32_t mnfdDefault = 0; + int32_t mxfdDefault = 0; +}; + +// NOLINTNEXTLINE(fuchsia-statically-constructed-objects) +static const std::set sanctionedUnit({ "acre", "bit", "byte", "celsius", "centimeter", "day", "degree", + "fahrenheit", "fluid-ounce", "foot", "gallon", "gigabit", "gigabyte", + "gram", "hectare", "hour", "inch", "kilobit", "kilobyte", "kilogram", + "kilometer", "liter", "megabit", "megabyte", "meter", "mile", + "mile-scandinavian", "millimeter", "milliliter", "millisecond", + "minute", "month", "ounce", "percent", "petabyte", "pound", "second", + "stone", "terabit", "terabyte", "week", "yard", "year" }); + +class JSNumberFormat : public JSObject { +public: + CAST_CHECK(JSNumberFormat, IsJSNumberFormat); + + static constexpr size_t LOCALE_OFFSET = JSObject::SIZE; + + ACCESSORS(Locale, LOCALE_OFFSET, NUMBER_STRING_SYSTEM_OFFSET) + ACCESSORS(NumberingSystem, NUMBER_STRING_SYSTEM_OFFSET, STYLE_OFFSET) + ACCESSORS(Style, STYLE_OFFSET, CURRENCY_OFFSET) + ACCESSORS(Currency, CURRENCY_OFFSET, CURRENCY_DISPLAY_OFFSET) + ACCESSORS(CurrencyDisplay, CURRENCY_DISPLAY_OFFSET, CURRENCY_SIGN_OFFSET) + ACCESSORS(CurrencySign, CURRENCY_SIGN_OFFSET, UNIT_OFFSET) + ACCESSORS(Unit, UNIT_OFFSET, UNIT_DISPLAY_OFFSET) + ACCESSORS(UnitDisplay, UNIT_DISPLAY_OFFSET, MINIMUM_INTEGER_DIGITS_OFFSET) + ACCESSORS(MinimumIntegerDigits, MINIMUM_INTEGER_DIGITS_OFFSET, MINIMUM_FRACTION_DIGITS_OFFSET) + ACCESSORS(MinimumFractionDigits, MINIMUM_FRACTION_DIGITS_OFFSET, MAXIMUM_FRACTION_DIGITS_OFFSET) + ACCESSORS(MaximumFractionDigits, MAXIMUM_FRACTION_DIGITS_OFFSET, MININUM_SIGNIFICANT_DIGITS_OFFSET) + ACCESSORS(MinimumSignificantDigits, MININUM_SIGNIFICANT_DIGITS_OFFSET, MAXINUM_SIGNIFICANT_DIGITS_OFFSET) + ACCESSORS(MaximumSignificantDigits, MAXINUM_SIGNIFICANT_DIGITS_OFFSET, USER_GROUPING_OFFSET) + ACCESSORS(UseGrouping, USER_GROUPING_OFFSET, ROUNDING_TYPE_OFFSET) + ACCESSORS(RoundingType, ROUNDING_TYPE_OFFSET, NOTATION_OFFSET) + ACCESSORS(Notation, NOTATION_OFFSET, COMPACT_DISPLAY_OFFSET) + ACCESSORS(CompactDisplay, COMPACT_DISPLAY_OFFSET, SIGN_DISPLAY_OFFSET) + ACCESSORS(SignDisplay, SIGN_DISPLAY_OFFSET, ICU_NUMBER_FORMAT_OFFSET) + ACCESSORS(BoundFormat, ICU_NUMBER_FORMAT_OFFSET, BOUND_FORMAT_OFFSET) + + // icu field. + ACCESSORS(IcuField, BOUND_FORMAT_OFFSET, SIZE) + + DECL_VISIT_OBJECT_FOR_JS_OBJECT(JSObject, LOCALE_OFFSET, SIZE) + DECL_DUMP() + + icu::number::LocalizedNumberFormatter *GetIcuCallTarget() const + { + ASSERT(GetIcuField().IsJSNativePointer()); + auto result = JSNativePointer::Cast(GetIcuField().GetTaggedObject())->GetExternalPointer(); + return reinterpret_cast(result); + } + + static void FreeIcuNumberformat(void *pointer, void *data) + { + if (pointer == nullptr) { + return; + } + auto icuNumberformat = reinterpret_cast(pointer); + icuNumberformat->~LocalizedNumberFormatter(); + if (data != nullptr) { + reinterpret_cast(data)->GetRegionFactory()->FreeBuffer(pointer); + } + } + + // 12.1.2 InitializeNumberFormat ( numberFormat, locales, options ) + static void InitializeNumberFormat(JSThread *thread, const JSHandle &numberFormat, + const JSHandle &locales, const JSHandle &options); + + // 12.1.3 CurrencyDigits ( currency ) + static int32_t CurrencyDigits(const icu::UnicodeString ¤cy); + + // 12.1.8 FormatNumeric( numberFormat, x ) + static JSHandle FormatNumeric(JSThread *thread, const JSHandle &numberFormat, + JSTaggedValue x); + + // 12.1.9 FormatNumericToParts( numberFormat, x ) + static JSHandle FormatNumericToParts(JSThread *thread, const JSHandle &numberFormat, + JSTaggedValue x); + + // 12.1.12 UnwrapNumberFormat( nf ) + static JSHandle UnwrapNumberFormat(JSThread *thread, const JSHandle &nf); + + static JSHandle GetAvailableLocales(JSThread *thread); + static void ResolvedOptions(JSThread *thread, const JSHandle &numberFormat, + const JSHandle &options); + + template + static icu::number::LocalizedNumberFormatter SetICUFormatterDigitOptions( + icu::number::LocalizedNumberFormatter &icuNumberformatter, const JSHandle &formatter) + { + int minimumIntegerDigits = formatter->GetMinimumIntegerDigits().GetInt(); + // Set ICU formatter IntegerWidth to MinimumIntegerDigits + icuNumberformatter = + icuNumberformatter.integerWidth(icu::number::IntegerWidth::zeroFillTo(minimumIntegerDigits)); + + int minimumSignificantDigits = formatter->GetMinimumSignificantDigits().GetInt(); + int maximumSignificantDigits = formatter->GetMaximumSignificantDigits().GetInt(); + int minimumFractionDigits = formatter->GetMinimumFractionDigits().GetInt(); + int maximumFractionDigits = formatter->GetMaximumFractionDigits().GetInt(); + + // If roundingtype is "compact-rounding" return ICU formatter + auto roundingType = static_cast(formatter->GetRoundingType().GetInt()); + if (roundingType == RoundingType::COMPACTROUNDING) { + return icuNumberformatter; + } + // Else, Set ICU formatter FractionDigits and SignificantDigits + // a. Set ICU formatter minFraction, maxFraction to MinimumFractionDigits, MaximumFractionDigits + icu::number::Precision precision = + icu::number::Precision::minMaxFraction(minimumFractionDigits, maximumFractionDigits); + // b. if MinimumSignificantDigits is not 0, + // Set ICU formatter minSignificantDigits, maxSignificantDigits to MinimumSignificantDigits, + // MaximumSignificantDigits + if (minimumSignificantDigits != 0) { + precision = + icu::number::Precision::minMaxSignificantDigits(minimumSignificantDigits, maximumSignificantDigits); + } + return icuNumberformatter.precision(precision); + } +}; +} // namespace panda::ecmascript +#endif // ECMASCRIPT_JS_NUMBER_FORMAT_H \ No newline at end of file diff --git a/runtime/js_object-inl.h b/runtime/js_object-inl.h new file mode 100644 index 000000000..ba5797fae --- /dev/null +++ b/runtime/js_object-inl.h @@ -0,0 +1,309 @@ +/* + * Copyright (c) 2021-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_JSOBJECT_INL_H +#define ECMASCRIPT_JSOBJECT_INL_H + +#include "plugins/ecmascript/runtime/js_hclass-inl.h" +#include "plugins/ecmascript/runtime/js_object.h" +#include "plugins/ecmascript/runtime/js_tagged_value-inl.h" +#include "plugins/ecmascript/runtime/js_typed_array.h" +#include "plugins/ecmascript/runtime/tagged_array-inl.h" + +namespace panda::ecmascript { +inline bool ECMAObject::IsBuiltinsConstructor() const +{ + return GetClass()->IsBuiltinsCtor(); +} + +inline bool ECMAObject::IsCallable() const +{ + return GetClass()->IsCallable(); +} + +// JSObject +inline bool JSObject::IsExtensible() const +{ + return GetJSHClass()->IsExtensible(); +} + +inline void JSObject::FillElementsWithHoles(const JSThread *thread, uint32_t start, uint32_t end) +{ + if (start >= end) { + return; + } + + TaggedArray *elements = TaggedArray::Cast(GetElements().GetTaggedObject()); + for (uint32_t i = start; i < end; i++) { + elements->Set(thread, i, JSTaggedValue::Hole()); + } +} + +inline JSHClass *JSObject::GetJSHClass() const +{ + return GetClass(); +} + +inline bool JSObject::IsJSGlobalObject() const +{ + return GetJSHClass()->IsJSGlobalObject(); +} + +inline bool JSObject::IsConstructor() const +{ + return GetJSHClass()->IsConstructor(); +} + +inline bool JSObject::IsECMAObject() const +{ + return GetJSHClass()->IsECMAObject(); +} + +inline bool JSObject::IsJSError() const +{ + return GetJSHClass()->IsJSError(); +} + +inline bool JSObject::IsArguments() const +{ + return GetJSHClass()->IsArguments(); +} + +inline bool JSObject::IsDate() const +{ + return GetJSHClass()->IsDate(); +} + +inline bool JSObject::IsJSArray() const +{ + return GetJSHClass()->IsJSArray(); +} + +inline bool JSObject::IsJSMap() const +{ + return GetJSHClass()->IsJSMap(); +} + +inline bool JSObject::IsJSSet() const +{ + return GetJSHClass()->IsJSSet(); +} + +inline bool JSObject::IsJSRegExp() const +{ + return GetJSHClass()->IsJSRegExp(); +} + +inline bool JSObject::IsJSFunction() const +{ + return GetJSHClass()->IsJSFunction(); +} + +inline bool JSObject::IsBoundFunction() const +{ + return GetJSHClass()->IsJsBoundFunction(); +} + +inline bool JSObject::IsJSIntlBoundFunction() const +{ + return GetJSHClass()->IsJSIntlBoundFunction(); +} + +inline bool JSObject::IsProxyRevocFunction() const +{ + return GetJSHClass()->IsJSProxyRevocFunction(); +} + +inline bool JSObject::IsAccessorData() const +{ + return GetJSHClass()->IsAccessorData(); +} + +inline bool JSObject::IsJSGlobalEnv() const +{ + return GetJSHClass()->IsJsGlobalEnv(); +} + +inline bool JSObject::IsJSProxy() const +{ + return GetJSHClass()->IsJSProxy(); +} + +inline bool JSObject::IsGeneratorObject() const +{ + return GetJSHClass()->IsGeneratorObject(); +} + +inline bool JSObject::IsForinIterator() const +{ + return GetJSHClass()->IsForinIterator(); +} + +inline bool JSObject::IsJSSetIterator() const +{ + return GetJSHClass()->IsJSSetIterator(); +} + +inline bool JSObject::IsJSMapIterator() const +{ + return GetJSHClass()->IsJSMapIterator(); +} + +inline bool JSObject::IsJSArrayIterator() const +{ + return GetJSHClass()->IsJSArrayIterator(); +} + +inline bool JSObject::IsJSPrimitiveRef() const +{ + return GetJSHClass()->IsJsPrimitiveRef(); +} + +inline bool JSObject::IsElementDict() const +{ + return TaggedArray::Cast(GetElements().GetTaggedObject())->IsDictionaryMode(); +} + +inline bool JSObject::IsPropertiesDict() const +{ + return TaggedArray::Cast(GetProperties().GetTaggedObject())->IsDictionaryMode(); +} + +inline bool JSObject::IsTypedArray() const +{ + return GetJSHClass()->IsTypedArray(); +} + +void JSObject::SetPropertyInlinedProps(const JSThread *thread, uint32_t index, JSTaggedValue value) +{ + SetPropertyInlinedProps(thread, GetJSHClass(), index, value); +} + +JSTaggedValue JSObject::GetPropertyInlinedProps(uint32_t index) const +{ + return GetPropertyInlinedProps(GetJSHClass(), index); +} + +void JSObject::SetPropertyInlinedProps(const JSThread *thread, const JSHClass *hclass, uint32_t index, + JSTaggedValue value) +{ + uint32_t offset = hclass->GetInlinedPropertiesOffset(index); + SET_VALUE_WITH_BARRIER(thread, this, offset, value); +} + +JSTaggedValue JSObject::GetPropertyInlinedProps(const JSHClass *hclass, uint32_t index) const +{ + uint32_t offset = hclass->GetInlinedPropertiesOffset(index); + return JSTaggedValue(GET_VALUE(this, offset)); +} + +JSTaggedValue JSObject::GetProperty(const JSHClass *hclass, PropertyAttributes attr) const +{ + if (attr.IsInlinedProps()) { + return GetPropertyInlinedProps(hclass, attr.GetOffset()); + } + TaggedArray *array = TaggedArray::Cast(GetProperties().GetTaggedObject()); + return array->Get(attr.GetOffset() - hclass->GetInlinedProperties()); +} + +void JSObject::SetProperty(const JSThread *thread, const JSHClass *hclass, PropertyAttributes attr, JSTaggedValue value) +{ + if (attr.IsInlinedProps()) { + SetPropertyInlinedProps(thread, hclass, attr.GetOffset(), value); + } else { + TaggedArray *array = TaggedArray::Cast(GetProperties().GetTaggedObject()); + array->Set(thread, attr.GetOffset() - hclass->GetInlinedProperties(), value); + } +} + +inline bool JSObject::ShouldTransToDict(uint32_t capacity, uint32_t index) +{ + if (index < capacity) { + return false; + } + if (index - capacity > MAX_GAP) { + return true; + } + + if (capacity >= MIN_GAP) { + return index > capacity * FAST_ELEMENTS_FACTOR; + } + + return false; +} + +inline uint32_t JSObject::ComputeElementCapacity(uint32_t oldCapacity) +{ + uint32_t newCapacity = oldCapacity + (oldCapacity >> 1U); + return newCapacity > MIN_ELEMENTS_LENGTH ? newCapacity : MIN_ELEMENTS_LENGTH; +} + +inline uint32_t JSObject::ComputePropertyCapacity(uint32_t oldCapacity) +{ + uint32_t newCapacity = oldCapacity + PROPERTIES_GROW_SIZE; + return newCapacity > JSHClass::MAX_CAPACITY_OF_OUT_OBJECTS ? JSHClass::MAX_CAPACITY_OF_OUT_OBJECTS : newCapacity; +} + +// static +template +JSHandle JSObject::CreateListFromArrayLike(JSThread *thread, const JSHandle &obj) +{ + // 3. If Type(obj) is not Object, throw a TypeError exception. + if (!obj->IsECMAObject()) { + THROW_TYPE_ERROR_AND_RETURN(thread, "CreateListFromArrayLike must accept object", + JSHandle(thread, JSTaggedValue::Exception())); + } + // 4. Let len be ToLength(Get(obj, "length")). + JSHandle lengthKeyHandle = thread->GlobalConstants()->GetHandledLengthString(); + + JSHandle value = GetProperty(thread, obj, lengthKeyHandle).GetValue(); + JSTaggedNumber number = JSTaggedValue::ToLength(thread, value); + // 5. ReturnIfAbrupt(len). + RETURN_HANDLE_IF_ABRUPT_COMPLETION(JSTaggedValue, thread); + if (number.GetNumber() > MAX_ELEMENT_INDEX) { + THROW_TYPE_ERROR_AND_RETURN(thread, "len is bigger than 2^32 - 1", + JSHandle(thread, JSTaggedValue::Exception())); + } + + uint32_t len = number.ToUint32(); + // 6. Let list be an empty List. + JSHandle array = thread->GetEcmaVM()->GetFactory()->NewTaggedArray(len); + + if (obj->IsTypedArray()) { + JSTypedArray::FastCopyElementToArray(thread, obj, array); + // c. ReturnIfAbrupt(next). + RETURN_HANDLE_IF_ABRUPT_COMPLETION(JSTaggedValue, thread); + return JSHandle(array); + } + // 8. Repeat while index < len + for (uint32_t i = 0; i < len; i++) { + JSTaggedValue next = JSTaggedValue::GetProperty(thread, obj, i).GetValue().GetTaggedValue(); + // c. ReturnIfAbrupt(next). + RETURN_HANDLE_IF_ABRUPT_COMPLETION(JSTaggedValue, thread); + + // NOLINTNEXTLINE(readability-braces-around-statements, bugprone-suspicious-semicolon) + if constexpr (types == ElementTypes::STRING_AND_SYMBOL) { + if (!next.IsString() && !next.IsSymbol()) { + THROW_TYPE_ERROR_AND_RETURN(thread, "CreateListFromArrayLike: not an element of elementTypes", + JSHandle(thread, JSTaggedValue::Exception())); + } + } + + array->Set(thread, i, next); + } + return JSHandle(array); +} +} // namespace panda::ecmascript +#endif // ECMASCRIPT_JSOBJECT_INL_H diff --git a/runtime/js_object.cpp b/runtime/js_object.cpp new file mode 100644 index 000000000..57e922705 --- /dev/null +++ b/runtime/js_object.cpp @@ -0,0 +1,1973 @@ +/* + * Copyright (c) 2021-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 "accessor_data.h" +#include "plugins/ecmascript/runtime/ecma_macros.h" +#include "plugins/ecmascript/runtime/ecma_vm.h" +#include "plugins/ecmascript/runtime/global_env.h" +#include "plugins/ecmascript/runtime/internal_call_params.h" +#include "plugins/ecmascript/runtime/interpreter/fast_runtime_stub-inl.h" +#include "plugins/ecmascript/runtime/js_primitive_ref.h" +#include "plugins/ecmascript/runtime/js_thread.h" +#include "global_dictionary-inl.h" +#include "js_array.h" +#include "js_for_in_iterator.h" +#include "js_hclass.h" +#include "js_invoker.h" +#include "js_iterator.h" +#include "object_factory.h" +#include "property_attributes.h" +#include "tagged_array-inl.h" + +namespace panda::ecmascript { +PropertyAttributes::PropertyAttributes(const PropertyDescriptor &desc) +{ + DISALLOW_GARBAGE_COLLECTION; + if (desc.HasWritable()) { + SetWritable(desc.IsWritable()); + } + + if (desc.HasEnumerable()) { + SetEnumerable(desc.IsEnumerable()); + } + + if (desc.HasConfigurable()) { + SetConfigurable(desc.IsConfigurable()); + } + + if (desc.IsAccessorDescriptor()) { + SetIsAccessor(true); + } + // internal accessor + if (desc.HasValue() && desc.GetValue()->IsAccessor()) { + SetIsAccessor(true); + } +} + +JSMethod *ECMAObject::GetCallTarget() const +{ + const TaggedObject *obj = this; + ASSERT(JSTaggedValue(obj).IsJSFunctionBase() || JSTaggedValue(obj).IsJSProxy()); + if (JSTaggedValue(obj).IsJSFunctionBase()) { + return JSFunctionBase::ConstCast(obj)->GetMethod(); + } + return JSProxy::ConstCast(obj)->GetMethod(); +} + +JSHandle JSObject::GrowElementsCapacity(const JSThread *thread, const JSHandle &obj, + uint32_t capacity) +{ + uint32_t newCapacity = ComputeElementCapacity(capacity); + ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); + JSHandle oldElements(thread, obj->GetElements()); + uint32_t oldLength = oldElements->GetLength(); + JSHandle newElements = factory->CopyArray(oldElements, oldLength, newCapacity); + + obj->SetElements(thread, newElements); + return newElements; +} + +bool JSObject::IsRegExp(JSThread *thread, const JSHandle &argument) +{ + if (!argument->IsECMAObject()) { + return false; + } + JSHandle matchSymbol = thread->GetEcmaVM()->GetGlobalEnv()->GetMatchSymbol(); + JSHandle isRegexp = JSObject::GetProperty(thread, argument, matchSymbol).GetValue(); + RETURN_VALUE_IF_ABRUPT_COMPLETION(thread, false); + if (!isRegexp->IsUndefined()) { + return isRegexp->ToBoolean(); + } + JSHandle argumentObj = JSHandle::Cast(argument); + return argumentObj->IsJSRegExp(); +} + +JSHandle JSObject::TransitionToDictionary(const JSThread *thread, const JSHandle &receiver) +{ + JSHandle array(thread, receiver->GetProperties()); + JSHandle jshclass(thread, receiver->GetJSHClass()); + ASSERT(!jshclass->IsDictionaryMode()); + uint32_t propNumber = jshclass->NumberOfProps(); + + ASSERT(!jshclass->GetLayout().IsNull()); + JSHandle layoutInfoHandle(thread, jshclass->GetLayout()); + ASSERT(layoutInfoHandle->GetLength() != 0); + JSMutableHandle dict( + thread, NameDictionary::Create(thread, NameDictionary::ComputeHashTableSize(propNumber))); + JSMutableHandle valueHandle(thread, JSTaggedValue::Undefined()); + JSMutableHandle keyHandle(thread, JSTaggedValue::Undefined()); + uint32_t numberInlinedProps = jshclass->GetInlinedProperties(); + for (uint32_t i = 0; i < propNumber; i++) { + JSTaggedValue key = layoutInfoHandle->GetKey(i); + PropertyAttributes attr = layoutInfoHandle->GetAttr(i); + ASSERT(i == attr.GetOffset()); + JSTaggedValue value; + + if (i < numberInlinedProps) { + value = receiver->GetPropertyInlinedProps(i); + } else { + value = array->Get(i - numberInlinedProps); + } + + attr.SetBoxType(PropertyBoxType::UNDEFINED); + valueHandle.Update(value); + keyHandle.Update(key); + JSHandle newDict = NameDictionary::PutIfAbsent(thread, dict, keyHandle, valueHandle, attr); + dict.Update(newDict); + } + + receiver->SetProperties(thread, dict); + // change HClass + JSHClass::TransitionToDictionary(thread, receiver); + + // trim in-obj properties space + if (numberInlinedProps > 0) { + ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); + uint32_t newSize = receiver->GetClass()->GetObjectSize(); + size_t trimBytes = numberInlinedProps * JSTaggedValue::TaggedTypeSize(); + factory->FillFreeObject(ToUintPtr(*receiver) + newSize, trimBytes, RemoveSlots::YES); + } + + return dict; +} + +void JSObject::ElementsToDictionary(const JSThread *thread, JSHandle obj) +{ + JSHandle elements(thread, obj->GetElements()); + ASSERT(!obj->GetJSHClass()->IsDictionaryElement()); + int length = elements->GetLength(); + JSMutableHandle dict(thread, NumberDictionary::Create(thread)); + auto attr = PropertyAttributes(PropertyAttributes::GetDefaultAttributes()); + JSMutableHandle key(thread, JSTaggedValue::Undefined()); + JSMutableHandle valueHandle(thread, JSTaggedValue ::Undefined()); + for (int i = 0; i < length; i++) { + JSTaggedValue value = elements->Get(i); + if (value.IsHole()) { + continue; + } + key.Update(JSTaggedValue(i)); + valueHandle.Update(value); + JSHandle newDict = NumberDictionary::PutIfAbsent(thread, dict, key, valueHandle, attr); + dict.Update(newDict); + } + obj->SetElements(thread, dict); + + JSHClass::TransitionElementsToDictionary(thread, obj); +} + +bool JSObject::IsArrayLengthWritable(JSThread *thread, const JSHandle &receiver) +{ + auto *hclass = receiver->GetJSHClass(); + if (!hclass->IsDictionaryMode()) { + LayoutInfo *layoutInfo = LayoutInfo::Cast(hclass->GetLayout().GetTaggedObject()); + PropertyAttributes attr(layoutInfo->GetAttr(JSArray::LENGTH_INLINE_PROPERTY_INDEX)); + return attr.IsWritable(); + } + JSHandle lengthKey = thread->GlobalConstants()->GetHandledLengthString(); + ObjectOperator op(thread, receiver, lengthKey, OperatorType::OWN); + return op.GetAttr().IsWritable(); +} + +bool JSObject::AddElementInternal(JSThread *thread, const JSHandle &receiver, uint32_t index, + const JSHandle &value, PropertyAttributes attr) +{ + bool isDictionary = receiver->GetJSHClass()->IsDictionaryElement(); + if (receiver->IsJSArray()) { + DISALLOW_GARBAGE_COLLECTION; + JSHandle arr(receiver); + uint32_t oldLength = arr->GetArrayLength(); + if (index >= oldLength) { + if (!IsArrayLengthWritable(thread, receiver)) { + return false; + } + arr->SetArrayLength(thread, index + 1); + } + } + thread->NotifyStableArrayElementsGuardians(receiver); + + TaggedArray *elements = TaggedArray::Cast(receiver->GetElements().GetTaggedObject()); + if (isDictionary) { + ASSERT(elements->IsDictionaryMode()); + JSHandle keyHandle(thread, JSTaggedValue(static_cast(index))); + JSHandle newDict = + NumberDictionary::Put(thread, JSHandle(thread, elements), keyHandle, value, attr); + receiver->SetElements(thread, newDict); + return true; + } + + uint32_t capacity = elements->GetLength(); + if (index >= capacity || !attr.IsDefaultAttributes()) { + if (ShouldTransToDict(capacity, index) || !attr.IsDefaultAttributes()) { + JSObject::ElementsToDictionary(thread, receiver); + JSHandle keyHandle(thread, JSTaggedValue(static_cast(index))); + JSHandle dict(thread, receiver->GetElements()); + JSHandle newKey = NumberDictionary::Put(thread, dict, keyHandle, value, attr); + receiver->SetElements(thread, newKey); + return true; + } + elements = *JSObject::GrowElementsCapacity(thread, receiver, index + 1); + } + elements->Set(thread, index, value); + receiver->GetJSHClass()->UpdateRepresentation(value.GetTaggedValue()); + return true; +} + +void JSObject::DeletePropertyInternal(JSThread *thread, const JSHandle &obj, + const JSHandle &key, uint32_t index) +{ + JSHandle array(thread, obj->GetProperties()); + + if (obj->IsJSGlobalObject()) { + JSHandle dictHandle(thread, obj->GetProperties()); + JSHandle newDict = GlobalDictionary::Remove(thread, dictHandle, index); + obj->SetProperties(thread, newDict); + return; + } + + if (!array->IsDictionaryMode()) { + JSHandle dictHandle(TransitionToDictionary(thread, obj)); + int entry = dictHandle->FindEntry(key.GetTaggedValue()); + ASSERT(entry != -1); + JSHandle newDict = NameDictionary::Remove(thread, dictHandle, entry); + obj->SetProperties(thread, newDict); + return; + } + + JSHandle dictHandle(array); + JSHandle newDict = NameDictionary::Remove(thread, dictHandle, index); + obj->SetProperties(thread, newDict); +} + +void JSObject::GetAllKeys(const JSThread *thread, const JSHandle &obj, int offset, + const JSHandle &keyArray) + +{ + TaggedArray *array = TaggedArray::Cast(obj->GetProperties().GetTaggedObject()); + if (!array->IsDictionaryMode()) { + int end = obj->GetJSHClass()->NumberOfProps(); + if (end > 0) { + LayoutInfo::Cast(obj->GetJSHClass()->GetLayout().GetTaggedObject()) + ->GetAllKeys(thread, end, offset, *keyArray); + } + return; + } + + if (obj->IsJSGlobalObject()) { + GlobalDictionary *dict = GlobalDictionary::Cast(array); + return dict->GetAllKeys(thread, offset, *keyArray); + } + + NameDictionary *dict = NameDictionary::Cast(obj->GetProperties().GetTaggedObject()); + dict->GetAllKeys(thread, offset, *keyArray); +} + +// For Serialization use. Does not support JSGlobalObject +void JSObject::GetAllKeys(const JSThread *thread, const JSHandle &obj, std::vector &keyVector) +{ + DISALLOW_GARBAGE_COLLECTION; + ASSERT_PRINT(!obj->IsJSGlobalObject(), "Do not support get key of JSGlobal Object"); + TaggedArray *array = TaggedArray::Cast(obj->GetProperties().GetTaggedObject()); + if (!array->IsDictionaryMode()) { + int end = obj->GetJSHClass()->NumberOfProps(); + if (end > 0) { + LayoutInfo::Cast(obj->GetJSHClass()->GetLayout().GetTaggedObject())->GetAllKeys(thread, end, keyVector); + } + } else { + NameDictionary *dict = NameDictionary::Cast(obj->GetProperties().GetTaggedObject()); + dict->GetAllKeysIntoVector(thread, keyVector); + } +} + +JSHandle JSObject::GetAllEnumKeys(const JSThread *thread, const JSHandle &obj, int offset, + uint32_t numOfKeys, uint32_t *keys) +{ + ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); + if (obj->IsJSGlobalObject()) { + JSHandle keyArray = factory->NewTaggedArray(numOfKeys); + GlobalDictionary *dict = GlobalDictionary::Cast(obj->GetProperties().GetTaggedObject()); + dict->GetEnumAllKeys(thread, offset, *keyArray, keys); + return keyArray; + } + + TaggedArray *array = TaggedArray::Cast(obj->GetProperties().GetTaggedObject()); + if (!array->IsDictionaryMode()) { + JSTaggedValue enumCache = obj->GetJSHClass()->GetEnumCache(); + if (!enumCache.IsNull()) { + auto keyArray = JSHandle(thread, enumCache); + *keys = keyArray->GetLength(); + return keyArray; + } + JSHandle keyArray = factory->NewTaggedArray(numOfKeys); + int end = obj->GetJSHClass()->NumberOfProps(); + if (end > 0) { + LayoutInfo::Cast(obj->GetJSHClass()->GetLayout().GetTaggedObject()) + ->GetAllEnumKeys(thread, end, offset, *keyArray, keys); + if (*keys == keyArray->GetLength()) { + obj->GetJSHClass()->SetEnumCache(thread, keyArray.GetTaggedValue()); + } + } + return keyArray; + } + + JSHandle keyArray = factory->NewTaggedArray(numOfKeys); + NameDictionary *dict = NameDictionary::Cast(obj->GetProperties().GetTaggedObject()); + dict->GetAllEnumKeys(thread, offset, *keyArray, keys); + return keyArray; +} + +void JSObject::GetAllElementKeys(JSThread *thread, const JSHandle &obj, int offset, + const JSHandle &keyArray) +{ + uint32_t elementIndex = 0; + + if (obj->IsJSPrimitiveRef() && JSPrimitiveRef::Cast(*obj)->IsString()) { + elementIndex = JSPrimitiveRef::Cast(*obj)->GetStringLength() + offset; + for (uint32_t i = offset; i < elementIndex; ++i) { + auto key = base::NumberHelper::NumberToString(thread, JSTaggedValue(i)); + keyArray->Set(thread, i, key); + } + } + + JSHandle elements(thread, obj->GetElements()); + if (!elements->IsDictionaryMode()) { + uint32_t elementsLen = elements->GetLength(); + for (uint32_t i = 0, j = elementIndex; i < elementsLen; ++i) { + if (!elements->Get(i).IsHole()) { + auto key = base::NumberHelper::NumberToString(thread, JSTaggedValue(i)); + keyArray->Set(thread, j++, key); + } + } + } else { + NumberDictionary::GetAllKeys(thread, JSHandle(elements), elementIndex, keyArray); + } +} + +void JSObject::GetALLElementKeysIntoVector(const JSThread *thread, const JSHandle &obj, + std::vector &keyVector) +{ + JSHandle elements(thread, obj->GetElements()); + if (!elements->IsDictionaryMode()) { + uint32_t elementsLen = elements->GetLength(); + for (uint32_t i = 0; i < elementsLen; ++i) { + if (!elements->Get(i).IsHole()) { + keyVector.emplace_back(JSTaggedValue(i)); + } + } + } else { + JSHandle dict = JSHandle::Cast(elements); + dict->GetAllKeysIntoVector(thread, keyVector); + } +} + +JSHandle JSObject::GetEnumElementKeys(JSThread *thread, const JSHandle &obj, int offset, + uint32_t numOfElements, uint32_t *keys) +{ + ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); + JSHandle elementArray = factory->NewTaggedArray(numOfElements); + uint32_t elementIndex = 0; + JSMutableHandle keyHandle(thread, JSTaggedValue::Undefined()); + + if (obj->IsJSPrimitiveRef() && JSPrimitiveRef::Cast(*obj)->IsString()) { + elementIndex = JSPrimitiveRef::Cast(*obj)->GetStringLength(); + *keys += elementIndex; + elementIndex += offset; + for (uint32_t i = offset; i < elementIndex; ++i) { + keyHandle.Update(JSTaggedValue(i)); + auto key = JSTaggedValue::ToString(thread, keyHandle); + elementArray->Set(thread, i, key); + } + } + + JSHandle arr(thread, obj->GetElements()); + if (!arr->IsDictionaryMode()) { + uint32_t elementsLen = arr->GetLength(); + uint32_t preElementIndex = elementIndex; + for (uint32_t i = 0; i < elementsLen; ++i) { + if (!arr->Get(i).IsHole()) { + keyHandle.Update(JSTaggedValue(i)); + auto key = JSTaggedValue::ToString(thread, keyHandle); + elementArray->Set(thread, elementIndex++, key); + } + } + *keys += (elementIndex - preElementIndex); + } else { + NumberDictionary::GetAllEnumKeys(thread, JSHandle(arr), elementIndex, elementArray, keys); + } + return elementArray; +} + +uint32_t JSObject::GetNumberOfKeys() +{ + DISALLOW_GARBAGE_COLLECTION; + TaggedArray *array = TaggedArray::Cast(GetProperties().GetTaggedObject()); + + if (!array->IsDictionaryMode()) { + return GetJSHClass()->NumberOfProps(); + } + + return NameDictionary::Cast(array)->EntriesCount(); +} + +bool JSObject::GlobalSetProperty(JSThread *thread, const JSHandle &key, + const JSHandle &value, bool mayThrow) +{ + ASSERT_PRINT(JSTaggedValue::IsPropertyKey(key), "Key is not a property key"); + + ObjectOperator op(thread, key); + if (!op.IsFound()) { + PropertyAttributes attr = PropertyAttributes::Default(true, true, false); + op.SetAttr(attr); + } + return SetProperty(&op, value, mayThrow); +} + +uint32_t JSObject::GetNumberOfElements() +{ + DISALLOW_GARBAGE_COLLECTION; + uint32_t numOfElements = 0; + if (IsJSPrimitiveRef() && JSPrimitiveRef::Cast(this)->IsString()) { + numOfElements = JSPrimitiveRef::Cast(this)->GetStringLength(); + } + + TaggedArray *elements = TaggedArray::Cast(GetElements().GetTaggedObject()); + if (!elements->IsDictionaryMode()) { + uint32_t elementsLen = elements->GetLength(); + for (uint32_t i = 0; i < elementsLen; ++i) { + if (!elements->Get(i).IsHole()) { + numOfElements++; + } + } + } else { + numOfElements += NumberDictionary::Cast(elements)->EntriesCount(); + } + + return numOfElements; +} + +// 9.1.9 [[Set]] ( P, V, Receiver) +bool JSObject::SetProperty(JSThread *thread, const JSHandle &obj, const JSHandle &key, + const JSHandle &value, const JSHandle &receiver, bool mayThrow) +{ + ASSERT_PRINT(!(obj->IsUndefined() || obj->IsNull() || obj->IsHole()), "Obj is not a valid object"); + ASSERT_PRINT(JSTaggedValue::IsPropertyKey(key), "Key is not a property key"); + + // 2 ~ 4 findProperty in Receiver, Obj and its parents + ObjectOperator op(thread, obj, receiver, key); + return SetProperty(&op, value, mayThrow); +} + +bool JSObject::SetProperty(JSThread *thread, const JSHandle &obj, const JSHandle &key, + const JSHandle &value, bool mayThrow) +{ + ASSERT_PRINT(obj->IsECMAObject(), "Obj is not a valid JSObject"); + ASSERT_PRINT(JSTaggedValue::IsPropertyKey(key), "Key is not a property key"); + + ObjectOperator op(thread, obj, key); + return SetProperty(&op, value, mayThrow); +} + +bool JSObject::SetProperty(JSThread *thread, const JSHandle &obj, const JSHandle &key, + const JSHandle &value, bool mayThrow) +{ + ASSERT_PRINT(!(obj->IsUndefined() || obj->IsNull() || obj->IsHole()), "Obj is not a valid object"); + ASSERT_PRINT(JSTaggedValue::IsPropertyKey(key), "Key is not a property key"); + + // 2 ~ 4 findProperty in Receiver, Obj and its parents + ObjectOperator op(thread, obj, key); + return SetProperty(&op, value, mayThrow); +} + +bool JSObject::SetProperty(JSThread *thread, const JSHandle &obj, uint32_t index, + const JSHandle &value, bool mayThrow) +{ + ASSERT_PRINT(!(obj->IsUndefined() || obj->IsNull() || obj->IsHole()), "Obj is not a valid object"); + + ObjectOperator op(thread, obj, index); + return SetProperty(&op, value, mayThrow); +} + +bool JSObject::SetProperty(ObjectOperator *op, const JSHandle &value, bool mayThrow) +{ + JSThread *thread = op->GetThread(); + + JSHandle receiver = op->GetReceiver(); + JSHandle holder = op->GetHolder(); + if (holder->IsJSProxy()) { + if (op->IsElement()) { + JSHandle key(thread, JSTaggedValue(op->GetElementIndex())); + return JSProxy::SetProperty(thread, JSHandle::Cast(holder), key, value, receiver, mayThrow); + } + return JSProxy::SetProperty(thread, JSHandle::Cast(holder), op->GetKey(), value, receiver, mayThrow); + } + + // When op is not found and is not set extra attributes + if (!op->IsFound() && op->IsPrimitiveAttr()) { + op->SetAsDefaultAttr(); + } + + bool isInternalAccessor = false; + if (op->IsAccessorDescriptor()) { + isInternalAccessor = AccessorData::Cast(op->GetValue().GetTaggedObject())->IsInternal(); + } + + // 5. If IsDataDescriptor(ownDesc) is true, then + if (!op->IsAccessorDescriptor() || isInternalAccessor) { + bool isSuccess = true; + if (!op->IsWritable()) { + if (mayThrow) { + THROW_TYPE_ERROR_AND_RETURN(thread, "Cannot assign to read only property", false); + } + return false; + } + + if (!receiver->IsECMAObject()) { + if (mayThrow) { + THROW_TYPE_ERROR_AND_RETURN(thread, "Receiver is not a JSObject", false); + } + return false; + } + + if (receiver->IsJSProxy()) { + JSMutableHandle key(thread, JSTaggedValue::Undefined()); + if (op->IsElement()) { + key.Update(JSTaggedValue(op->GetElementIndex())); + } else { + key.Update(op->GetKey().GetTaggedValue()); + } + + PropertyDescriptor existDesc(thread); + JSProxy::GetOwnProperty(thread, JSHandle::Cast(receiver), key, existDesc); + RETURN_VALUE_IF_ABRUPT_COMPLETION(thread, false); + if (!existDesc.IsEmpty()) { + if (existDesc.IsAccessorDescriptor()) { + return false; + } + + if (!existDesc.IsWritable()) { + return false; + } + + PropertyDescriptor valueDesc(thread, value); + return JSProxy::DefineOwnProperty(thread, JSHandle::Cast(receiver), key, valueDesc); + } + + isSuccess = CreateDataProperty(thread, JSHandle(receiver), key, value); + + if (!isSuccess && mayThrow) { + THROW_TYPE_ERROR_AND_RETURN(thread, "", false); + } + return isSuccess; + } + + // 5e. If existingDescriptor is not undefined, then + bool hasReceiver = false; + if (op->HasReceiver()) { + op->ReLookupPropertyInReceiver(); + hasReceiver = true; + } + + if (op->IsFound() && !op->IsOnPrototype()) { + // i. If IsAccessorDescriptor(existingDescriptor) is true, return false. + if (op->IsAccessorDescriptor() && !isInternalAccessor) { + return false; + } + + // ii. If existingDescriptor.[[Writable]] is false, return false. + if (!op->IsWritable()) { + if (mayThrow) { + THROW_TYPE_ERROR_AND_RETURN(thread, "Cannot assign to read only property", false); + } + return false; + } + isSuccess = op->UpdateDataValue(JSHandle(receiver), value, isInternalAccessor, mayThrow); + RETURN_VALUE_IF_ABRUPT_COMPLETION(thread, isSuccess); + } else { + // 5f. Else if Receiver does not currently have a property P, Return CreateDataProperty(Receiver, P, V). + if (!receiver->IsExtensible(thread)) { + if (mayThrow) { + THROW_TYPE_ERROR_AND_RETURN(thread, "receiver is not Extensible", false); + } + return false; + } + if (LIKELY(!hasReceiver)) { + return op->AddProperty(JSHandle(receiver), value, op->GetAttr()); + } + PropertyAttributes attr; + attr.SetDefaultAttributes(); + return op->AddProperty(JSHandle(receiver), value, attr); + } + return isSuccess; + } + // 6. Assert: IsAccessorDescriptor(ownDesc) is true. + ASSERT(op->IsAccessorDescriptor()); + // 8. If setter is undefined, return false. + AccessorData *accessor = AccessorData::Cast(op->GetValue().GetTaggedObject()); + return CallSetter(thread, *accessor, receiver, value, mayThrow); +} + +bool JSObject::CallSetter(JSThread *thread, const AccessorData &accessor, const JSHandle &receiver, + const JSHandle &value, bool mayThrow) +{ + if (UNLIKELY(accessor.IsInternal())) { + return accessor.CallInternalSet(thread, JSHandle::Cast(receiver), value, mayThrow); + } + JSTaggedValue setter = accessor.GetSetter(); + // 8. If setter is undefined, return false. + if (setter.IsUndefined()) { + if (mayThrow) { + THROW_TYPE_ERROR_AND_RETURN(thread, "Cannot set property when setter is undefined", false); + } + return false; + } + + JSHandle func(thread, setter); + InternalCallParams *arguments = thread->GetInternalCallParams(); + arguments->MakeArgv(value); + JSFunction::Call(thread, func, receiver, 1, arguments->GetArgv()); + + // 10. ReturnIfAbrupt(setterResult). + RETURN_VALUE_IF_ABRUPT_COMPLETION(thread, false); + + return true; +} + +JSTaggedValue JSObject::CallGetter(JSThread *thread, const AccessorData *accessor, + const JSHandle &receiver) +{ + JSTaggedValue getter = accessor->GetGetter(); + // 7. If getter is undefined, return undefined. + if (getter.IsUndefined()) { + return JSTaggedValue::Undefined(); + } + + JSHandle func(thread, getter); + JSTaggedValue res = JSFunction::Call(thread, func, receiver, 0, nullptr); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + return res; +} + +// 9.1.8 [[Get]] (P, Receiver) +OperationResult JSObject::GetProperty(JSThread *thread, const JSHandle &obj, + const JSHandle &key, const JSHandle &receiver) +{ + ASSERT_PRINT(!(obj->IsUndefined() || obj->IsNull() || obj->IsHole()), "Obj is not a valid object"); + ASSERT_PRINT(JSTaggedValue::IsPropertyKey(key), "Key is not a property key"); + + ObjectOperator op(thread, obj, receiver, key); + return OperationResult(thread, GetProperty(thread, &op), PropertyMetaData(op.IsFound())); +} + +OperationResult JSObject::GetProperty(JSThread *thread, const JSHandle &obj, + const JSHandle &key) +{ + ASSERT_PRINT(obj->IsECMAObject(), "Obj is not a valid JSObject"); + ASSERT_PRINT(JSTaggedValue::IsPropertyKey(key), "Key is not a property key"); + + ObjectOperator op(thread, obj, key); + return OperationResult(thread, GetProperty(thread, &op), PropertyMetaData(op.IsFound())); +} + +OperationResult JSObject::GetProperty(JSThread *thread, const JSHandle &obj, + const JSHandle &key) +{ + ASSERT_PRINT(!(obj->IsUndefined() || obj->IsNull() || obj->IsHole()), "Obj is not a valid object"); + ASSERT_PRINT(JSTaggedValue::IsPropertyKey(key), "Key is not a property key"); + + ObjectOperator op(thread, obj, key); + return OperationResult(thread, GetProperty(thread, &op), PropertyMetaData(op.IsFound())); +} + +OperationResult JSObject::GetProperty(JSThread *thread, const JSHandle &obj, uint32_t index) +{ + ASSERT_PRINT(!(obj->IsUndefined() || obj->IsNull() || obj->IsHole()), "Obj is not a valid object"); + + ObjectOperator op(thread, obj, index); + return OperationResult(thread, GetProperty(thread, &op), PropertyMetaData(op.IsFound())); +} + +OperationResult JSObject::GetPropertyFromGlobal(JSThread *thread, const JSHandle &key) +{ + ASSERT_PRINT(JSTaggedValue::IsPropertyKey(key), "Key is not a property key"); + + ObjectOperator op(thread, key); + return OperationResult(thread, GetProperty(thread, &op), PropertyMetaData(op.IsFound())); +} + +JSTaggedValue JSObject::GetProperty(JSThread *thread, ObjectOperator *op) +{ + JSHandle receiver = op->GetReceiver(); + JSHandle holder = op->GetHolder(); + if (holder->IsJSProxy()) { + if (op->IsElement()) { + JSHandle key(thread, JSTaggedValue(op->GetElementIndex())); + return JSProxy::GetProperty(thread, JSHandle::Cast(holder), key, receiver) + .GetValue() + .GetTaggedValue(); + } + return JSProxy::GetProperty(thread, JSHandle::Cast(holder), op->GetKey(), receiver) + .GetValue() + .GetTaggedValue(); + } + + // 4. If desc is undefined, then + if (!op->IsFound()) { + // 4c. If obj and parent is null, return undefined. + return JSTaggedValue::Undefined(); + } + // 5. If IsDataDescriptor(desc) is true, return desc.[[Value]] + if (!op->IsAccessorDescriptor()) { + JSTaggedValue ret = op->GetValue(); + if (ret.IsPropertyBox()) { + ret = PropertyBox::Cast(ret.GetTaggedObject())->GetValue(); + } + return ret; + } + // 6. Otherwise, IsAccessorDescriptor(desc) must be true so, let getter be desc.[[Get]]. + JSTaggedValue value = op->GetValue(); + if (value.IsPropertyBox()) { + value = PropertyBox::Cast(value.GetTaggedObject())->GetValue(); + } + + AccessorData *accessor = AccessorData::Cast(value.GetTaggedObject()); + // 8. Return Call(getter, Receiver). + if (UNLIKELY(accessor->IsInternal())) { + return accessor->CallInternalGet(thread, JSHandle::Cast(holder)); + } + return CallGetter(thread, accessor, receiver); +} + +bool JSObject::DeleteProperty(JSThread *thread, const JSHandle &obj, const JSHandle &key) +{ + // 1. Assert: IsPropertyKey(P) is true. + ASSERT_PRINT(JSTaggedValue::IsPropertyKey(key), "Key is not a property key"); + // 2. Let desc be O.[[GetOwnProperty]](P). + + ObjectOperator op(thread, JSHandle(obj), key, OperatorType::OWN); + + // 4. If desc is undefined, return true. + if (!op.IsFound()) { + return true; + } + // 5. If desc.[[Configurable]] is true, then + // a. Remove the own property with name P from O. + // b. Return true. + // 6. Return false. + if (op.IsConfigurable()) { + op.DeletePropertyInHolder(); + return true; + } + return false; +} + +bool JSObject::GetOwnProperty(JSThread *thread, const JSHandle &obj, const JSHandle &key, + PropertyDescriptor &desc) +{ + return OrdinaryGetOwnProperty(thread, obj, key, desc); +} + +bool JSObject::GlobalGetOwnProperty(JSThread *thread, const JSHandle &key, PropertyDescriptor &desc) +{ + ASSERT_PRINT(JSTaggedValue::IsPropertyKey(key), "Key is not a property key"); + ObjectOperator op(thread, key, OperatorType::OWN); + + if (!op.IsFound()) { + return false; + } + + op.ToPropertyDescriptor(desc); + + if (desc.HasValue()) { + PropertyBox *cell = PropertyBox::Cast(desc.GetValue().GetTaggedValue().GetTaggedObject()); + JSHandle valueHandle(thread, cell->GetValue()); + desc.SetValue(valueHandle); + } + ASSERT(!desc.GetValue()->IsInternalAccessor()); + return true; +} + +bool JSObject::OrdinaryGetOwnProperty(JSThread *thread, const JSHandle &obj, + const JSHandle &key, PropertyDescriptor &desc) +{ + ASSERT_PRINT(JSTaggedValue::IsPropertyKey(key), "Key is not a property key"); + ObjectOperator op(thread, JSHandle(obj), key, OperatorType::OWN); + + if (!op.IsFound()) { + return false; + } + + op.ToPropertyDescriptor(desc); + + if (desc.HasValue() && obj->IsJSGlobalObject()) { + PropertyBox *cell = PropertyBox::Cast(desc.GetValue().GetTaggedValue().GetTaggedObject()); + JSHandle valueHandle(thread, cell->GetValue()); + desc.SetValue(valueHandle); + } + + return true; +} + +bool JSObject::DefineOwnProperty(JSThread *thread, const JSHandle &obj, const JSHandle &key, + const PropertyDescriptor &desc) +{ + return OrdinaryDefineOwnProperty(thread, obj, key, desc); +} + +bool JSObject::DefineOwnProperty(JSThread *thread, const JSHandle &obj, uint32_t index, + const PropertyDescriptor &desc) +{ + return OrdinaryDefineOwnProperty(thread, obj, index, desc); +} + +// 9.1.6.1 OrdinaryDefineOwnProperty (O, P, Desc) +bool JSObject::OrdinaryDefineOwnProperty(JSThread *thread, const JSHandle &obj, + const JSHandle &key, const PropertyDescriptor &desc) +{ + ASSERT_PRINT(JSTaggedValue::IsPropertyKey(key), "Key is not a property key"); + // 1. Let current be O.[[GetOwnProperty]](P). + JSHandle objValue(obj); + ObjectOperator op(thread, objValue, key, OperatorType::OWN); + + bool extensible = obj->IsExtensible(); + PropertyDescriptor current(thread); + op.ToPropertyDescriptor(current); + // 4. Return ValidateAndApplyPropertyDescriptor(O, P, extensible, Desc, current). + return ValidateAndApplyPropertyDescriptor(&op, extensible, desc, current); +} + +bool JSObject::OrdinaryDefineOwnProperty(JSThread *thread, const JSHandle &obj, uint32_t index, + const PropertyDescriptor &desc) +{ + JSHandle objValue(obj); + ObjectOperator op(thread, objValue, index, OperatorType::OWN); + + bool extensible = obj->IsExtensible(); + PropertyDescriptor current(thread); + op.ToPropertyDescriptor(current); + return ValidateAndApplyPropertyDescriptor(&op, extensible, desc, current); +} + +// 9.1.6.3 ValidateAndApplyPropertyDescriptor (O, P, extensible, Desc, current) +bool JSObject::ValidateAndApplyPropertyDescriptor(ObjectOperator *op, bool extensible, const PropertyDescriptor &desc, + const PropertyDescriptor ¤t) +{ + // 2. If current is undefined, then + if (current.IsEmpty()) { + // 2a. If extensible is false, return false. + if (!extensible) { + return false; + } + if (!op->HasHolder()) { + return true; + } + + // 2c. If IsGenericDescriptor(Desc) or IsDataDescriptor(Desc) is true, then + PropertyAttributes attr(desc); + bool success = false; + if (!desc.IsAccessorDescriptor()) { + success = op->AddPropertyInHolder(desc.GetValue(), attr); + } else { // is AccessorDescriptor + // may GC in NewAccessorData, so we need to handle getter and setter. + JSThread *thread = op->GetThread(); + JSHandle accessor = thread->GetEcmaVM()->GetFactory()->NewAccessorData(); + if (desc.HasGetter()) { + accessor->SetGetter(thread, desc.GetGetter()); + } + + if (desc.HasSetter()) { + accessor->SetSetter(thread, desc.GetSetter()); + } + success = op->AddPropertyInHolder(JSHandle::Cast(accessor), attr); + } + + return success; + } + + // 3. Return true, if every field in Desc is absent + // 4. Return true, if every field in Desc also occurs in current and the value of every field in Desc is the + // same value as the corresponding field in current when compared using the SameValue algorithm. + if ((!desc.HasEnumerable() || desc.IsEnumerable() == current.IsEnumerable()) && + (!desc.HasConfigurable() || desc.IsConfigurable() == current.IsConfigurable()) && + (!desc.HasValue() || JSTaggedValue::SameValue(current.GetValue(), desc.GetValue())) && + (!desc.HasWritable() || (current.IsWritable() == desc.IsWritable())) && + (!desc.HasGetter() || + (current.HasGetter() && JSTaggedValue::SameValue(current.GetGetter(), desc.GetGetter()))) && + (!desc.HasSetter() || + (current.HasSetter() && JSTaggedValue::SameValue(current.GetSetter(), desc.GetSetter())))) { + return true; + } + + // 5. If the [[Configurable]] field of current is false, then + if (!current.IsConfigurable()) { + // 5a. Return false, if the [[Configurable]] field of Desc is true. + if (desc.HasConfigurable() && desc.IsConfigurable()) { + return false; + } + // b. Return false, if the [[Enumerable]] field of Desc is present and the [[Enumerable]] fields of current + // and Desc are the Boolean negation of each other. + if (desc.HasEnumerable() && (desc.IsEnumerable() != current.IsEnumerable())) { + return false; + } + } + + // 6. If IsGenericDescriptor(Desc) is true, no further validation is required. + if (desc.IsGenericDescriptor()) { + // 7. Else if IsDataDescriptor(current) and IsDataDescriptor(Desc) have different results, then + } else if (current.IsDataDescriptor() != desc.IsDataDescriptor()) { + // 7a. Return false, if the [[Configurable]] field of current is false. + if (!current.IsConfigurable()) { + return false; + } + // 7b. If IsDataDescriptor(current) is true, then + if (current.IsDataDescriptor()) { + // 7bi. If O is not undefined, convert the property named P of object O from a data property to an + // accessor property. Preserve the existing values of the converted property’s [[Configurable]] and + // [[Enumerable]] attributes and set the rest of the property’s attributes to their default values. + } else { + // 7ci. If O is not undefined, convert the property named P of object O from an accessor property to a + // data property. Preserve the existing values of the converted property’s [[Configurable]] and + // [[Enumerable]] attributes and set the rest of the property’s attributes to their default values. + } + // 8. Else if IsDataDescriptor(current) and IsDataDescriptor(Desc) are both true, then + } else if (current.IsDataDescriptor() && desc.IsDataDescriptor()) { + // 8a. If the [[Configurable]] field of current is false, then + if (!current.IsConfigurable()) { + // 8a i. Return false, if the [[Writable]] field of current is false and the [[Writable]] field of Desc + // is true. + if (!current.IsWritable() && desc.HasWritable() && desc.IsWritable()) { + return false; + } + // 8a ii. If the [[Writable]] field of current is false, then + if (!current.IsWritable()) { + if (desc.HasValue() && !JSTaggedValue::SameValue(current.GetValue(), desc.GetValue())) { + return false; + } + } + } + // 8b. Else the [[Configurable]] field of current is true, so any change is acceptable. + } else { // 9. Else IsAccessorDescriptor(current) and IsAccessorDescriptor(Desc) are both true, + // 9a. If the [[Configurable]] field of current is false, then + if (!current.IsConfigurable()) { + // i. Return false, if the [[Set]] field of Desc is present and SameValue(Desc.[[Set]], current.[[Set]]) + // is false. + if (desc.HasSetter() && !JSTaggedValue::SameValue(current.GetSetter(), desc.GetSetter())) { + return false; + } + // ii. Return false, if the [[Get]] field of Desc is present and SameValue(Desc.[[Get]], + // current.[[Get]]) is false. + if (desc.HasGetter() && !JSTaggedValue::SameValue(current.GetGetter(), desc.GetGetter())) { + return false; + } + } + } + + if (op->HasHolder()) { + // 10. If O is not undefined, then + // a. For each field of Desc that is present, set the corresponding attribute of the property named P of object + // O to the value of the field. + return op->WriteDataPropertyInHolder(desc); + } + return true; +} + +// 9.1.6.2 IsCompatiblePropertyDescriptor (Extensible, Desc, Current) +bool JSObject::IsCompatiblePropertyDescriptor(bool extensible, const PropertyDescriptor &desc, + const PropertyDescriptor ¤t) +{ + // 1. Return ValidateAndApplyPropertyDescriptor(undefined, undefined, Extensible, Desc, Current). + ObjectOperator op; + return ValidateAndApplyPropertyDescriptor(&op, extensible, desc, current); +} + +JSTaggedValue JSObject::GetPrototype(JSThread *thread) const +{ + JSHandle obj(thread, JSTaggedValue(this)); + if (obj->IsJSProxy()) { + return JSProxy::GetPrototype(thread, JSHandle(obj)); + } + JSHClass *hclass = GetJSHClass(); + return hclass->GetPrototype(); +} + +bool JSObject::SetPrototype(JSThread *thread, const JSHandle &obj, const JSHandle &proto) +{ + ASSERT_PRINT(proto->IsECMAObject() || proto->IsNull(), "proto must be object or null"); + JSTaggedValue current = obj->GetPrototype(thread); + if (current == proto.GetTaggedValue()) { + return true; + } + if (!obj->IsExtensible()) { + return false; + } + bool done = false; + JSTaggedValue tempProto = proto.GetTaggedValue(); + while (!done) { + if (tempProto.IsNull() || !tempProto.IsECMAObject()) { + done = true; + } else if (JSTaggedValue::SameValue(tempProto, obj.GetTaggedValue())) { + return false; + } else { + if (tempProto.IsJSProxy()) { + break; + } + tempProto = JSObject::Cast(tempProto.GetTaggedObject())->GetPrototype(thread); + } + } + // map transition + JSHandle dynclass(thread, obj->GetJSHClass()); + JSHandle newDynclass = JSHClass::TransitionProto(thread, dynclass, proto); + JSHClass::NotifyHclassChanged(thread, dynclass, newDynclass); + obj->SetClass(newDynclass); + thread->NotifyStableArrayElementsGuardians(obj); + return true; +} + +bool JSObject::HasProperty(JSThread *thread, const JSHandle &obj, const JSHandle &key) +{ + ASSERT_PRINT(JSTaggedValue::IsPropertyKey(key), "Key is not a property key"); + JSHandle objValue(obj); + ObjectOperator op(thread, objValue, key); + + JSHandle holder = op.GetHolder(); + if (holder->IsJSProxy()) { + return JSProxy::HasProperty(thread, JSHandle::Cast(holder), key); + } + + return op.IsFound(); +} + +bool JSObject::HasProperty(JSThread *thread, const JSHandle &obj, uint32_t index) +{ + JSHandle objValue(obj); + ObjectOperator op(thread, objValue, index); + + JSHandle holder = op.GetHolder(); + if (holder->IsJSProxy()) { + JSHandle key(thread, JSTaggedValue(index)); + return JSProxy::HasProperty(thread, JSHandle::Cast(holder), key); + } + + return op.IsFound(); +} + +bool JSObject::PreventExtensions(JSThread *thread, const JSHandle &obj) +{ + if (obj->IsExtensible()) { + JSHandle jshclass(thread, obj->GetJSHClass()); + JSHandle newHclass = JSHClass::TransitionExtension(thread, jshclass); + obj->SetClass(newHclass); + } + + return true; +} + +// 9.1.12 [[OwnPropertyKeys]] ( ) +JSHandle JSObject::GetOwnPropertyKeys(JSThread *thread, const JSHandle &obj) +{ + [[maybe_unused]] uint32_t elementIndex = 0; + uint32_t numOfElements = obj->GetNumberOfElements(); + uint32_t keyLen = numOfElements + obj->GetNumberOfKeys(); + + JSHandle keyArray = thread->GetEcmaVM()->GetFactory()->NewTaggedArray(keyLen); + + if (numOfElements > 0) { + GetAllElementKeys(thread, obj, 0, keyArray); + } + GetAllKeys(thread, obj, static_cast(numOfElements), keyArray); + return keyArray; +} + +JSHandle JSObject::ObjectCreate(JSThread *thread, const JSHandle &proto) +{ + JSHandle env = thread->GetEcmaVM()->GetGlobalEnv(); + JSHandle constructor = env->GetObjectFunction(); + JSHandle objHandle = + thread->GetEcmaVM()->GetFactory()->NewJSObjectByConstructor(JSHandle(constructor), constructor); + RETURN_HANDLE_IF_ABRUPT_COMPLETION(JSObject, thread); + SetPrototype(thread, objHandle, JSHandle(proto)); + return objHandle; +} + +// 7.3.4 CreateDataProperty (O, P, V) +bool JSObject::CreateDataProperty(JSThread *thread, const JSHandle &obj, const JSHandle &key, + const JSHandle &value) +{ + ASSERT_PRINT(obj->IsECMAObject(), "Obj is not a valid object"); + ASSERT_PRINT(JSTaggedValue::IsPropertyKey(key), "Key is not a property key"); + auto result = FastRuntimeStub::SetPropertyByValue(thread, obj.GetTaggedValue(), key.GetTaggedValue(), + value.GetTaggedValue()); + if (!result.IsHole()) { + return result != JSTaggedValue::Exception(); + } + PropertyDescriptor desc(thread, value, true, true, true); + return JSTaggedValue::DefineOwnProperty(thread, JSHandle::Cast(obj), key, desc); +} + +bool JSObject::CreateDataProperty(JSThread *thread, const JSHandle &obj, uint32_t index, + const JSHandle &value) +{ + ASSERT_PRINT(obj->IsECMAObject(), "Obj is not a valid object"); + auto result = + FastRuntimeStub::SetPropertyByIndex(thread, obj.GetTaggedValue(), index, value.GetTaggedValue()); + if (!result.IsHole()) { + return result != JSTaggedValue::Exception(); + } + PropertyDescriptor desc(thread, value, true, true, true); + return DefineOwnProperty(thread, obj, index, desc); +} + +// 7.3.5 CreateMethodProperty (O, P, V) +bool JSObject::CreateDataPropertyOrThrow(JSThread *thread, const JSHandle &obj, + const JSHandle &key, const JSHandle &value) +{ + ASSERT_PRINT(obj->IsECMAObject(), "Obj is not a valid object"); + ASSERT_PRINT(JSTaggedValue::IsPropertyKey(key), "Key is not a property key"); + + bool success = CreateDataProperty(thread, obj, key, value); + if (!success) { + THROW_TYPE_ERROR_AND_RETURN(thread, "failed to create data property", success); + } + return success; +} + +bool JSObject::CreateDataPropertyOrThrow(JSThread *thread, const JSHandle &obj, uint32_t index, + const JSHandle &value) +{ + ASSERT_PRINT(obj->IsECMAObject(), "Obj is not a valid object"); + + bool success = CreateDataProperty(thread, obj, index, value); + if (!success) { + THROW_TYPE_ERROR_AND_RETURN(thread, "failed to create data property", success); + } + return success; +} +// 7.3.6 CreateDataPropertyOrThrow (O, P, V) +bool JSObject::CreateMethodProperty(JSThread *thread, const JSHandle &obj, const JSHandle &key, + const JSHandle &value) +{ + ASSERT_PRINT(obj->IsECMAObject(), "Obj is not a valid object"); + ASSERT_PRINT(JSTaggedValue::IsPropertyKey(key), "Key is not a property key"); + + PropertyDescriptor desc(thread, value, true, false, true); + return DefineOwnProperty(thread, obj, key, desc); +} + +// 7.3.9 GetMethod (O, P) +JSHandle JSObject::GetMethod(JSThread *thread, const JSHandle &obj, + const JSHandle &key) +{ + JSTaggedValue func = FastRuntimeStub::FastGetProperty(thread, obj.GetTaggedValue(), key.GetTaggedValue()); + if (func.IsUndefined() || func.IsNull()) { + return JSHandle(thread, JSTaggedValue::Undefined()); + } + + JSHandle result(thread, func); + if (!result->IsCallable()) { + THROW_TYPE_ERROR_AND_RETURN(thread, "obj is not Callable", result); + } + return result; +} + +// 7.3.14 SetIntegrityLevel (O, level) +bool JSObject::SetIntegrityLevel(JSThread *thread, const JSHandle &obj, IntegrityLevel level) +{ + ASSERT_PRINT(obj->IsECMAObject(), "Obj is not a valid object"); + ASSERT_PRINT((level == IntegrityLevel::SEALED || level == IntegrityLevel::FROZEN), + "level is not a valid IntegrityLevel"); + + bool status = PreventExtensions(thread, obj); + RETURN_VALUE_IF_ABRUPT_COMPLETION(thread, false); + if (!status) { + return false; + } + + JSHandle jshandleKeys = JSTaggedValue::GetOwnPropertyKeys(thread, JSHandle(obj)); + RETURN_VALUE_IF_ABRUPT_COMPLETION(thread, false); + PropertyDescriptor descNoConf(thread); + descNoConf.SetConfigurable(false); + PropertyDescriptor descNoConfWrite(thread); + descNoConfWrite.SetWritable(false); + descNoConfWrite.SetConfigurable(false); + + if (level == IntegrityLevel::SEALED) { + uint32_t length = jshandleKeys->GetLength(); + if (length == 0) { + return true; + } + auto key = jshandleKeys->Get(0); + JSMutableHandle handleKey(thread, key); + for (uint32_t i = 0; i < length; i++) { + auto taggedKey = JSTaggedValue(jshandleKeys->Get(i)); + handleKey.Update(taggedKey); + [[maybe_unused]] bool success = + JSTaggedValue::DefinePropertyOrThrow(thread, JSHandle(obj), handleKey, descNoConf); + RETURN_VALUE_IF_ABRUPT_COMPLETION(thread, false); + } + } else { + uint32_t length = jshandleKeys->GetLength(); + if (length == 0) { + return true; + } + auto key = jshandleKeys->Get(0); + JSMutableHandle handleKey(thread, key); + for (uint32_t i = 0; i < length; i++) { + auto taggedKey = JSTaggedValue(jshandleKeys->Get(i)); + handleKey.Update(taggedKey); + PropertyDescriptor currentDesc(thread); + bool curDescStatus = + JSTaggedValue::GetOwnProperty(thread, JSHandle(obj), handleKey, currentDesc); + RETURN_VALUE_IF_ABRUPT_COMPLETION(thread, false); + if (curDescStatus) { + PropertyDescriptor desc = currentDesc.IsAccessorDescriptor() ? descNoConf : descNoConfWrite; + [[maybe_unused]] bool success = + JSTaggedValue::DefinePropertyOrThrow(thread, JSHandle(obj), handleKey, desc); + RETURN_VALUE_IF_ABRUPT_COMPLETION(thread, false); + } + } + } + return true; +} + +// 7.3.15 TestIntegrityLevel (O, level) +bool JSObject::TestIntegrityLevel(JSThread *thread, const JSHandle &obj, IntegrityLevel level) +{ + ASSERT_PRINT(obj->IsECMAObject(), "Obj is not a valid object"); + ASSERT_PRINT((level == IntegrityLevel::SEALED || level == IntegrityLevel::FROZEN), + "level is not a valid IntegrityLevel"); + + auto objVal = JSHandle(obj); + bool status = objVal->IsExtensible(thread); + RETURN_VALUE_IF_ABRUPT_COMPLETION(thread, false); + if (status) { + return false; + } + + JSHandle jshandleKeys = JSTaggedValue::GetOwnPropertyKeys(thread, objVal); + RETURN_VALUE_IF_ABRUPT_COMPLETION(thread, false); + uint32_t length = jshandleKeys->GetLength(); + if (length == 0) { + return true; + } + auto key = jshandleKeys->Get(0); + JSMutableHandle handleKey(thread, key); + for (uint32_t i = 0; i < length; i++) { + auto taggedKey = JSTaggedValue(jshandleKeys->Get(i)); + handleKey.Update(taggedKey); + PropertyDescriptor currentDesc(thread); + bool curDescStatus = JSTaggedValue::GetOwnProperty(thread, objVal, handleKey, currentDesc); + RETURN_VALUE_IF_ABRUPT_COMPLETION(thread, false); + if (curDescStatus) { + if (currentDesc.IsConfigurable()) { + return false; + } + if (level == IntegrityLevel::FROZEN && currentDesc.IsDataDescriptor() && currentDesc.IsWritable()) { + return false; + } + } + } + return true; +} + +// 7.3.21 EnumerableOwnNames (O) +JSHandle JSObject::EnumerableOwnNames(JSThread *thread, const JSHandle &obj) +{ + ASSERT_PRINT(obj->IsECMAObject(), "obj is not object"); + ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); + JSHandle keys; + JSHandle tagObj(obj); + uint32_t copyLength = 0; + // fast mode + if (tagObj->IsJSObject() && !tagObj->IsTypedArray()) { + uint32_t numOfKeys = obj->GetNumberOfKeys(); + uint32_t numOfElements = obj->GetNumberOfElements(); + JSHandle elementArray; + if (numOfElements > 0) { + elementArray = JSObject::GetEnumElementKeys(thread, obj, 0, numOfElements, ©Length); + } + + JSHandle keyArray; + if (numOfKeys > 0) { + keyArray = JSObject::GetAllEnumKeys(thread, obj, 0, numOfKeys, ©Length); + } + + if (numOfKeys != 0 && numOfElements != 0) { + keys = TaggedArray::AppendSkipHole(thread, elementArray, keyArray, copyLength); + } else if (numOfKeys != 0) { + keys = factory->CopyArray(keyArray, copyLength, copyLength); + } else if (numOfElements != 0) { + keys = factory->CopyArray(elementArray, copyLength, copyLength); + } else { + keys = factory->EmptyArray(); + } + return keys; + } + + keys = JSTaggedValue::GetOwnPropertyKeys(thread, tagObj); + RETURN_HANDLE_IF_ABRUPT_COMPLETION(TaggedArray, thread); + uint32_t length = keys->GetLength(); + + JSHandle names = factory->NewTaggedArray(length); + for (uint32_t i = 0; i < length; i++) { + JSTaggedValue key(keys->Get(i)); + if (key.IsString()) { + PropertyDescriptor desc(thread); + bool status = JSTaggedValue::GetOwnProperty(thread, JSHandle(obj), + JSHandle(thread, key), desc); + RETURN_HANDLE_IF_ABRUPT_COMPLETION(TaggedArray, thread); + + if (status && desc.IsEnumerable()) { + names->Set(thread, copyLength, key); + copyLength++; + } + } + } + + return factory->CopyArray(names, length, copyLength); +} + +JSHandle JSObject::EnumerableOwnPropertyNames(JSThread *thread, const JSHandle &obj, + PropertyKind kind, array_size_t *length_out) +{ + // 1. Assert: Type(O) is Object. + ASSERT_PRINT(obj->IsECMAObject(), "obj is not object"); + + // 2. Let ownKeys be ? O.[[OwnPropertyKeys]](). + JSHandle ownKeys = JSTaggedValue::GetOwnPropertyKeys(thread, JSHandle(obj)); + RETURN_HANDLE_IF_ABRUPT_COMPLETION(TaggedArray, thread); + + // 3. Let properties be a new empty List. + uint32_t length = ownKeys->GetLength(); + ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); + JSHandle properties = factory->NewTaggedArray(length); + + // 4. For each element key of ownKeys, do + // a. If Type(key) is String, then + // i. Let desc be ? O.[[GetOwnProperty]](key). + // ii. If desc is not undefined and desc.[[Enumerable]] is true, then + // 1. If kind is key, append key to properties. + // 2. Else, + // a. Let value be ? Get(O, key). + // b. If kind is value, append value to properties. + // c. Else, + // i. Assert: kind is key+value. + // ii. Let entry be ! CreateArrayFromList(« key, value »). + // iii. Append entry to properties. + JSMutableHandle key(thread, JSTaggedValue::Undefined()); + uint32_t index = 0; + for (uint32_t i = 0; i < length; i++) { + key.Update(ownKeys->Get(thread, i)); + if (key->IsString()) { + PropertyDescriptor desc(thread); + bool status = + JSTaggedValue::GetOwnProperty(thread, JSHandle(obj), JSHandle(key), desc); + RETURN_HANDLE_IF_ABRUPT_COMPLETION(TaggedArray, thread); + if (status && desc.IsEnumerable()) { + if (kind == PropertyKind::KEY) { + properties->Set(thread, index++, key); + } else { + OperationResult result = + JSTaggedValue::GetProperty(thread, JSHandle::Cast(obj), key); + RETURN_HANDLE_IF_ABRUPT_COMPLETION(TaggedArray, thread); + JSHandle value = result.GetValue(); + if (kind == PropertyKind::VALUE) { + properties->Set(thread, index++, value); + } else { + ASSERT_PRINT(kind == PropertyKind::KEY_VALUE, "kind is invalid"); + JSHandle keyValue = factory->NewTaggedArray(2); // 2: key-value pair + keyValue->Set(thread, 0, key.GetTaggedValue()); + keyValue->Set(thread, 1, value.GetTaggedValue()); + JSHandle entry = JSArray::CreateArrayFromList(thread, keyValue); + properties->Set(thread, index++, entry.GetTaggedValue()); + } + } + } + } + } + + *length_out = index; + + // 5. Return properties. + return properties; +} + +JSHandle JSObject::GetFunctionRealm(JSThread *thread, const JSHandle &object) +{ + // 1. Assert: obj is a callable object. + ASSERT(object->IsCallable()); + // 2. If obj has a [[Realm]] internal slot, then return obj’s [[Realm]] internal slot. + // 3. If obj is a Bound Function exotic object, then + if (object->IsBoundFunction()) { + // a. Let target be obj’s [[BoundTargetFunction]] internal slot. + JSHandle target(thread, JSHandle(object)->GetBoundTarget()); + // b. Return GetFunctionRealm(target). + return GetFunctionRealm(thread, target); + } + // 4. If obj is a Proxy exotic object, then + if (object->IsJSProxy()) { + // a. If the value of the [[ProxyHandler]] internal slot of obj is null, throw a TypeError exception. + if (JSHandle(object)->GetHandler().IsNull()) { + THROW_TYPE_ERROR_AND_RETURN(thread, "JSObject::GetFunctionRealm: handler is null", + JSHandle(thread, JSTaggedValue::Exception())); + } + // b. Let proxyTarget be the value of obj’s [[ProxyTarget]] internal slot. + JSHandle proxyTarget(thread, JSHandle(object)->GetTarget()); + return GetFunctionRealm(thread, proxyTarget); + } + JSTaggedValue maybeGlobalEnv = JSHandle(object)->GetLexicalEnv(); + if (maybeGlobalEnv.IsUndefined()) { + return thread->GetEcmaVM()->GetGlobalEnv(); + } + while (!maybeGlobalEnv.IsJSGlobalEnv()) { + maybeGlobalEnv = LexicalEnv::Cast(maybeGlobalEnv.GetTaggedObject())->GetParentEnv(); + } + return JSHandle(thread, maybeGlobalEnv); +} + +bool JSObject::InstanceOf(JSThread *thread, const JSHandle &object, + const JSHandle &target) +{ + // 1. If Type(target) is not Object, throw a TypeError exception. + if (!target->IsECMAObject()) { + THROW_TYPE_ERROR_AND_RETURN(thread, "InstanceOf error when type of target is not Object", false); + } + + EcmaVM *vm = thread->GetEcmaVM(); + // 2. Let instOfHandler be GetMethod(target, @@hasInstance). + JSHandle instOfHandler = GetMethod(thread, target, vm->GetGlobalEnv()->GetHasInstanceSymbol()); + + // 3. ReturnIfAbrupt(instOfHandler). + RETURN_VALUE_IF_ABRUPT_COMPLETION(thread, false); + + // 4. If instOfHandler is not undefined, then + if (!instOfHandler->IsUndefined()) { + // a. Return ! ToBoolean(? Call(instOfHandler, target, «object»)). + InternalCallParams *arguments = thread->GetInternalCallParams(); + arguments->MakeArgv(object); + JSTaggedValue tagged = JSFunction::Call(thread, instOfHandler, target, 1, arguments->GetArgv()); + + return tagged.ToBoolean(); + } + + // 5. If IsCallable(target) is false, throw a TypeError exception. + if (!target->IsCallable()) { + THROW_TYPE_ERROR_AND_RETURN(thread, "InstanceOf error when target is not Callable", false); + } + + // 6. Return ? OrdinaryHasInstance(target, object). + return JSFunction::OrdinaryHasInstance(thread, target, object); +} + +// ecma6.0 6.2.4.4 +JSHandle JSObject::FromPropertyDescriptor(JSThread *thread, const PropertyDescriptor &desc) +{ + // 1. If Desc is undefined, return undefined + if (desc.IsEmpty()) { + return JSHandle(thread, JSTaggedValue::Undefined()); + } + + // 2. Let obj be ObjectCreate(%ObjectPrototype%). + JSHandle env = thread->GetEcmaVM()->GetGlobalEnv(); + JSHandle objFunc = env->GetObjectFunction(); + JSHandle objHandle = + thread->GetEcmaVM()->GetFactory()->NewJSObjectByConstructor(JSHandle(objFunc), objFunc); + + auto globalConst = thread->GlobalConstants(); + // 4. If Desc has a [[Value]] field, then Perform CreateDataProperty(obj, "value", Desc.[[Value]]). + if (desc.HasValue()) { + JSHandle valueStr = globalConst->GetHandledValueString(); + bool success = CreateDataProperty(thread, objHandle, valueStr, desc.GetValue()); + RASSERT_PRINT(success, "CreateDataProperty must be success"); + } + // 5. If Desc has a [[Writable]] field, then Perform CreateDataProperty(obj, "writable", Desc.[[Writable]]). + if (desc.HasWritable()) { + JSHandle writableStr = globalConst->GetHandledWritableString(); + JSHandle writable(thread, JSTaggedValue(desc.IsWritable())); + [[maybe_unused]] bool success = CreateDataProperty(thread, objHandle, writableStr, writable); + ASSERT_PRINT(success, "CreateDataProperty must be success"); + } + // 6. If Desc has a [[Get]] field, then Perform CreateDataProperty(obj, "get", Desc.[[Get]]). + if (desc.HasGetter()) { + JSHandle getStr = globalConst->GetHandledGetString(); + bool success = CreateDataProperty(thread, objHandle, getStr, desc.GetGetter()); + RASSERT_PRINT(success, "CreateDataProperty must be success"); + } + // 7. If Desc has a [[Set]] field, then Perform CreateDataProperty(obj, "set", Desc.[[Set]]) + if (desc.HasSetter()) { + JSHandle setStr = globalConst->GetHandledSetString(); + bool success = CreateDataProperty(thread, objHandle, setStr, desc.GetSetter()); + RASSERT_PRINT(success, "CreateDataProperty must be success"); + } + // 8. If Desc has an [[Enumerable]] field, then Perform CreateDataProperty(obj, "enumerable", + // Desc.[[Enumerable]]). + if (desc.HasEnumerable()) { + JSHandle enumerableStr = globalConst->GetHandledEnumerableString(); + JSHandle enumerable(thread, JSTaggedValue(desc.IsEnumerable())); + [[maybe_unused]] bool success = CreateDataProperty(thread, objHandle, enumerableStr, enumerable); + ASSERT_PRINT(success, "CreateDataProperty must be success"); + } + // 9. If Desc has a [[Configurable]] field, then Perform CreateDataProperty(obj , "configurable", + // Desc.[[Configurable]]). + if (desc.HasConfigurable()) { + JSHandle configurableStr = globalConst->GetHandledConfigurableString(); + JSHandle configurable(thread, JSTaggedValue(desc.IsConfigurable())); + [[maybe_unused]] bool success = CreateDataProperty(thread, objHandle, configurableStr, configurable); + ASSERT_PRINT(success, "CreateDataProperty must be success"); + } + return JSHandle(objHandle); +} + +bool JSObject::ToPropertyDescriptorFast(JSThread *thread, const JSHandle &obj, PropertyDescriptor &desc) +{ + auto *hclass = obj->GetTaggedObject()->GetClass(); + JSType jsType = hclass->GetObjectType(); + if (jsType != JSType::JS_OBJECT) { + return false; + } + if (hclass->IsDictionaryMode()) { + return false; + } + auto env = thread->GetEcmaVM()->GetGlobalEnv(); + auto globalConst = thread->GlobalConstants(); + if (hclass->GetPrototype() != env->GetObjectFunctionPrototype().GetTaggedValue()) { + return false; + } + if (JSObject::Cast(hclass->GetPrototype().GetTaggedObject())->GetClass() != + env->GetObjectFunctionPrototypeClass().GetObject()) { + return false; + } + LayoutInfo *layoutInfo = LayoutInfo::Cast(hclass->GetLayout().GetTaggedObject()); + int propsNumber = hclass->NumberOfProps(); + for (int i = 0; i < propsNumber; i++) { + auto attr = layoutInfo->GetAttr(i); + if (attr.IsAccessor()) { + return false; + } + auto key = layoutInfo->GetKey(i); + auto value = JSObject::Cast(obj->GetTaggedObject())->GetProperty(hclass, attr); + if (key == globalConst->GetEnumerableString()) { + bool enumerable = value.ToBoolean(); + desc.SetEnumerable(enumerable); + } else if (key == globalConst->GetConfigurableString()) { + bool configurable = value.ToBoolean(); + desc.SetConfigurable(configurable); + } else if (key == globalConst->GetValueString()) { + auto handleValue = JSHandle(thread, value); + desc.SetValue(handleValue); + } else if (key == globalConst->GetWritableString()) { + bool writable = value.ToBoolean(); + desc.SetWritable(writable); + } else if (key == globalConst->GetGetString()) { + if (!value.IsCallable()) { + return false; + } + auto getter = JSHandle(thread, value); + desc.SetGetter(getter); + } else if (key == globalConst->GetSetString()) { + if (!value.IsCallable()) { + return false; + } + auto setter = JSHandle(thread, value); + desc.SetSetter(setter); + } + } + + if (desc.IsAccessorDescriptor()) { + // 22a. If either desc.[[Value]] or desc.[[Writable]] is present, throw a TypeError exception. + if (desc.HasValue() || desc.HasWritable()) { + THROW_TYPE_ERROR_AND_RETURN(thread, "either Value or Writable is present", true); + } + } + return true; +} + +// ecma6.0 6.2.4.5 ToPropertyDescriptor ( Obj ) +void JSObject::ToPropertyDescriptor(JSThread *thread, const JSHandle &obj, PropertyDescriptor &desc) +{ + if (!obj->IsECMAObject()) { + // 2. If Type(Obj) is not Object, throw a TypeError exception. + THROW_TYPE_ERROR(thread, "ToPropertyDescriptor error obj is not Object"); + } + + if (ToPropertyDescriptorFast(thread, obj, desc)) { + return; + } + auto globalConst = thread->GlobalConstants(); + // 3. Let desc be a new Property Descriptor that initially has no fields. + // 4. Let hasEnumerable be HasProperty(Obj, "enumerable") + JSHandle enumerableStr = globalConst->GetHandledEnumerableString(); + { + ObjectOperator op(thread, obj.GetTaggedValue(), enumerableStr.GetTaggedValue()); + if (op.IsFound()) { + auto value = op.FastGetValue(); + bool enumerable = value->IsException() ? false : value->ToBoolean(); + desc.SetEnumerable(enumerable); + } + } + // 7. Let hasConfigurable be HasProperty(Obj, "configurable"). + JSHandle configurableStr = globalConst->GetHandledConfigurableString(); + { + ObjectOperator op(thread, obj.GetTaggedValue(), configurableStr.GetTaggedValue()); + if (op.IsFound()) { + auto value = op.FastGetValue(); + bool conf = value->IsException() ? false : value->ToBoolean(); + desc.SetConfigurable(conf); + } + } + // 10. Let hasValue be HasProperty(Obj, "value"). + JSHandle valueStr = globalConst->GetHandledValueString(); + { + ObjectOperator op(thread, obj.GetTaggedValue(), valueStr.GetTaggedValue()); + if (op.IsFound()) { + JSHandle prop = op.FastGetValue(); + desc.SetValue(prop); + } + } + // 13. Let hasWritable be HasProperty(Obj, "writable"). + JSHandle writableStr = globalConst->GetHandledWritableString(); + { + ObjectOperator op(thread, obj.GetTaggedValue(), writableStr.GetTaggedValue()); + if (op.IsFound()) { + auto value = op.FastGetValue(); + bool writable = value->IsException() ? false : value->ToBoolean(); + desc.SetWritable(writable); + } + } + // 16. Let hasGet be HasProperty(Obj, "get"). + JSHandle getStr = globalConst->GetHandledGetString(); + { + ObjectOperator op(thread, obj.GetTaggedValue(), getStr.GetTaggedValue()); + if (op.IsFound()) { + JSHandle getter = op.FastGetValue(); + if (!getter->IsCallable() && !getter->IsUndefined()) { + THROW_TYPE_ERROR(thread, "getter not callable or undefined"); + } + desc.SetGetter(getter); + } + } + + // 19. Let hasSet be HasProperty(Obj, "set"). + JSHandle setStr = globalConst->GetHandledSetString(); + { + ObjectOperator op(thread, obj.GetTaggedValue(), setStr.GetTaggedValue()); + if (op.IsFound()) { + JSHandle setter = op.FastGetValue(); + if (!setter->IsCallable() && !setter->IsUndefined()) { + THROW_TYPE_ERROR(thread, "setter not callable or undefined"); + } + desc.SetSetter(setter); + } + } + + // 22. If either desc.[[Get]] or desc.[[Set]] is present, then + if (desc.IsAccessorDescriptor()) { + // 22a. If either desc.[[Value]] or desc.[[Writable]] is present, throw a TypeError exception. + if (desc.HasValue() || desc.HasWritable()) { + THROW_TYPE_ERROR(thread, "either desc.[[Value]] or desc.[[Writable]] is present"); + } + } + // 23. Return desc. +} + +JSHandle JSObject::SpeciesConstructor(JSThread *thread, const JSHandle &obj, + const JSHandle &defaultConstructort) +{ + JSHandle env = thread->GetEcmaVM()->GetGlobalEnv(); + const GlobalEnvConstants *globalConst = thread->GlobalConstants(); + // Assert: Type(O) is Object. + ASSERT_PRINT(obj->IsECMAObject(), "obj must be js object"); + + // Let C be Get(O, "constructor"). + JSHandle contructorKey = globalConst->GetHandledConstructorString(); + JSHandle objConstructor(GetProperty(thread, JSHandle(obj), contructorKey).GetValue()); + // ReturnIfAbrupt(C). + RETURN_HANDLE_IF_ABRUPT_COMPLETION(JSTaggedValue, thread); + // If C is undefined, return defaultConstructor. + if (objConstructor->IsUndefined()) { + return defaultConstructort; + } + // If Type(C) is not Object, throw a TypeError exception. + if (!objConstructor->IsECMAObject()) { + THROW_TYPE_ERROR_AND_RETURN(thread, "Constructor is not Object", + JSHandle(thread, JSTaggedValue::Exception())); + } + // Let S be Get(C, @@species). + JSHandle speciesSymbol = env->GetSpeciesSymbol(); + JSHandle speciesConstructor(GetProperty(thread, objConstructor, speciesSymbol).GetValue()); + // ReturnIfAbrupt(S). + RETURN_HANDLE_IF_ABRUPT_COMPLETION(JSTaggedValue, thread); + // If S is either undefined or null, return defaultConstructor. + if (speciesConstructor->IsUndefined() || speciesConstructor->IsNull()) { + return defaultConstructort; + } + // If IsConstructor(S) is true, return S. + if (speciesConstructor->IsConstructor()) { + return speciesConstructor; + } + // Throw a TypeError exception. + THROW_TYPE_ERROR_AND_RETURN(thread, "Is not Constructor", + JSHandle(thread, JSTaggedValue::Exception())); + return JSHandle(thread, JSTaggedValue::Exception()); +} + +// 6.2.4.6 CompletePropertyDescriptor ( Desc ) +void PropertyDescriptor::CompletePropertyDescriptor(const JSThread *thread, PropertyDescriptor &desc) +{ + // 1. ReturnIfAbrupt(Desc). + // 2. Assert: Desc is a Property Descriptor + // 3. Let like be Record{[[Value]]: undefined, [[Writable]]: false, [[Get]]: undefined, [[Set]]: undefined, + // [[Enumerable]]: false, [[Configurable]]: false}. + // 4. If either IsGenericDescriptor(Desc) or IsDataDescriptor(Desc) is true, then + if (!desc.IsAccessorDescriptor()) { + // a. If Desc does not have a [[Value]] field, set Desc.[[Value]] to like.[[Value]]. + // b. If Desc does not have a [[Writable]] field, set Desc.[[Writable]] to like.[[Writable]]. + if (!desc.HasValue()) { + desc.SetValue(JSHandle(thread, JSTaggedValue::Undefined())); + } + if (!desc.HasWritable()) { + desc.SetWritable(false); + } + } else { + // a. If Desc does not have a [[Get]] field, set Desc.[[Get]] to like.[[Get]]. + // b. If Desc does not have a [[Set]] field, set Desc.[[Set]] to like.[[Set]]. + // Default value of Get and Set is undefined. + } + // 6. If Desc does not have an [[Enumerable]] field, set Desc.[[Enumerable]] to like.[[Enumerable]]. + // 7. If Desc does not have a [[Configurable]] field, set Desc.[[Configurable]] to like.[[Configurable]]. + if (!desc.HasEnumerable()) { + desc.SetEnumerable(false); + } + if (!desc.HasConfigurable()) { + desc.SetConfigurable(false); + } +} + +// 13.7.5.15 EnumerateObjectProperties ( O ) +JSHandle JSObject::EnumerateObjectProperties(JSThread *thread, const JSHandle &obj) +{ + // 1. Return an Iterator object (25.1.1.2) whose next method iterates over all the String-valued keys of + // enumerable properties of O. The Iterator object must inherit from %IteratorPrototype% (25.1.2). The + // mechanics and order of enumerating the properties is not specified but must conform to the rules specified + // below. + JSHandle object; + if (obj->IsString()) { + object = JSHandle::Cast(JSPrimitiveRef::StringCreate(thread, obj)); + } else { + object = JSTaggedValue::ToPrototypeOrObj(thread, obj); + } + + return thread->GetEcmaVM()->GetFactory()->NewJSForinIterator(object); +} + +void JSObject::DefinePropertyByLiteral(JSThread *thread, const JSHandle &obj, + const JSHandle &key, const JSHandle &value, + bool useForClass) +{ + ASSERT_PRINT(obj->IsECMAObject(), "Obj is not a valid object"); + ASSERT_PRINT(JSTaggedValue::IsPropertyKey(key), "Key is not a property key"); + PropertyAttributes attr = + useForClass ? PropertyAttributes::Default(true, false, true) : PropertyAttributes::Default(); + + if (value->IsAccessorData()) { + attr.SetIsAccessor(true); + } + + uint32_t index = 0; + if (UNLIKELY(JSTaggedValue::ToElementIndex(key.GetTaggedValue(), &index))) { + AddElementInternal(thread, obj, index, value, attr); + return; + } + UNREACHABLE(); +} + +void JSObject::DefineSetter(JSThread *thread, const JSHandle &obj, const JSHandle &key, + const JSHandle &value) +{ + ASSERT_PRINT(!(obj->IsUndefined() || obj->IsNull() || obj->IsHole()), "Obj is not a valid object"); + ASSERT_PRINT(JSTaggedValue::IsPropertyKey(key), "Key is not a property key"); + ObjectOperator op(thread, obj, key, OperatorType::OWN); + ASSERT(op.IsFound()); + op.DefineSetter(value); +} + +void JSObject::DefineGetter(JSThread *thread, const JSHandle &obj, const JSHandle &key, + const JSHandle &value) +{ + ASSERT_PRINT(!(obj->IsUndefined() || obj->IsNull() || obj->IsHole()), "Obj is not a valid object"); + ASSERT_PRINT(JSTaggedValue::IsPropertyKey(key), "Key is not a property key"); + ObjectOperator op(thread, obj, key, OperatorType::OWN); + ASSERT(op.IsFound()); + op.DefineGetter(value); +} + +JSHandle JSObject::CreateObjectFromProperties(const JSThread *thread, const JSHandle &properties) +{ + ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); + size_t length = properties->GetLength(); + uint32_t propsLen = 0; + for (size_t i = 0; i < length; i += 2) { // 2: skip a pair of key and value + if (properties->Get(i).IsHole()) { + break; + } + propsLen++; + } + if (propsLen <= PropertyAttributes::MAX_CAPACITY_OF_PROPERTIES) { + JSHandle obj = factory->NewJSObjectByClass(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)); + } + return obj; + } + JSHandle obj = factory->NewEmptyJSObject(); + JSHClass::TransitionToDictionary(thread, obj); + + JSMutableHandle dict( + thread, NameDictionary::Create(thread, NameDictionary::ComputeHashTableSize(propsLen))); + JSMutableHandle valueHandle(thread, JSTaggedValue::Undefined()); + JSMutableHandle keyHandle(thread, JSTaggedValue::Undefined()); + for (size_t i = 0; i < propsLen; i++) { + PropertyAttributes attr = PropertyAttributes::Default(); + // 2: literal contains a pair of key-value + valueHandle.Update(properties->Get(i * 2 + 1)); + // 2: literal contains a pair of key-value + keyHandle.Update(properties->Get(i * 2)); + JSHandle newDict = NameDictionary::PutIfAbsent(thread, dict, keyHandle, valueHandle, attr); + dict.Update(newDict); + } + obj->SetProperties(thread, dict); + return obj; +} + +void JSObject::AddAccessor(JSThread *thread, const JSHandle &obj, const JSHandle &key, + const JSHandle &value, PropertyAttributes attr) +{ + ASSERT_PRINT(!(obj->IsUndefined() || obj->IsNull() || obj->IsHole()), "Obj is not a valid object"); + ASSERT_PRINT(JSTaggedValue::IsPropertyKey(key), "Key is not a property key"); + ASSERT_PRINT(attr.IsAccessor(), "Attr is not AccessorData"); + ObjectOperator op(thread, obj, key, OperatorType::OWN); + ASSERT(!op.IsFound()); + op.AddProperty(JSHandle::Cast(obj), JSHandle(value), attr); +} + +bool JSObject::UpdatePropertyInDictionary(const JSThread *thread, JSTaggedValue key, JSTaggedValue value) +{ + [[maybe_unused]] DisallowGarbageCollection noGc; + NameDictionary *dict = NameDictionary::Cast(GetProperties().GetTaggedObject()); + int entry = dict->FindEntry(key); + if (entry == -1) { + return false; + } + dict->UpdateValue(thread, entry, value); + return true; +} + +void ECMAObject::SetHash(int32_t hash) +{ + auto hashField = Barriers::GetDynValue(this, HASH_OFFSET); + JSTaggedValue value(hashField); + JSThread *thread = this->GetJSThread(); + if (value.IsHeapObject()) { + ASSERT(value.IsTaggedArray()); + TaggedArray *array = TaggedArray::Cast(value.GetHeapObject()); + array->Set(thread, 0, JSTaggedValue(hash)); + } else { + ObjectAccessor::SetDynPrimitive(thread, this, HASH_OFFSET, JSTaggedValue(hash).GetRawData()); + } +} + +int32_t ECMAObject::GetHash() const +{ + auto hashField = Barriers::GetDynValue(this, HASH_OFFSET); + JSTaggedValue value(hashField); + if (value.IsHeapObject()) { + TaggedArray *array = TaggedArray::Cast(value.GetHeapObject()); + return array->Get(0).GetInt(); + } + JSThread *thread = this->GetJSThread(); + JSHandle valueHandle(thread, value); + return JSTaggedValue::ToInt32(thread, valueHandle); +} + +void *ECMAObject::GetNativePointerField(int32_t index) const +{ + auto hashField = Barriers::GetDynValue(this, HASH_OFFSET); + JSTaggedValue value(hashField); + if (value.IsHeapObject()) { + JSThread *thread = this->GetJSThread(); + JSHandle array(thread, value); + if (static_cast(array->GetLength()) > index + 1) { + JSHandle pointer(thread, array->Get(index + 1)); + return pointer->GetExternalPointer(); + } + } + return nullptr; +} + +void ECMAObject::SetNativePointerField(int32_t index, void *nativePointer, const DeleteEntryPoint &callBack, void *data) +{ + auto hashField = Barriers::GetDynValue(this, HASH_OFFSET); + JSTaggedValue value(hashField); + if (!value.IsHeapObject()) { + return; + } + + JSThread *thread = this->GetJSThread(); + JSHandle array(thread, value); + auto len = static_cast(array->GetLength()); + if (len <= index + 1) { + return; + } + + EcmaVM *vm = thread->GetEcmaVM(); + JSHandle current = JSHandle(thread, array->Get(thread, index + 1)); + if (!current->IsHole() && nativePointer == nullptr) { + // Try to remove native pointer if exists. + vm->RemoveArrayDataList(*JSHandle(current)); + array->Set(thread, index + 1, JSTaggedValue::Hole()); + return; + } + + JSHandle pointer = vm->GetFactory()->NewJSNativePointer(nativePointer, callBack, data); + if (callBack != nullptr) { + vm->PushToArrayDataList(*pointer); + } + array->Set(thread, index + 1, pointer.GetTaggedValue()); +} + +int32_t ECMAObject::GetNativePointerFieldCount() const +{ + int32_t len = 0; + auto hashField = Barriers::GetDynValue(this, HASH_OFFSET); + JSTaggedValue value(hashField); + if (value.IsHeapObject()) { + TaggedArray *array = TaggedArray::Cast(value.GetHeapObject()); + len = array->GetLength() - 1; + } + return len; +} + +void ECMAObject::SetNativePointerFieldCount(int32_t count) +{ + auto hashField = Barriers::GetDynValue(this, HASH_OFFSET); + JSTaggedValue value(hashField); + if (!value.IsHeapObject()) { + JSThread *thread = this->GetJSThread(); + JSHandle obj(thread, this); + JSHandle newArray = thread->GetEcmaVM()->GetFactory()->NewTaggedArray(count + 1); + newArray->Set(thread, 0, value); + ObjectAccessor::SetDynValue(thread, *obj, HASH_OFFSET, newArray.GetTaggedValue().GetRawData()); + } +} +} // namespace panda::ecmascript diff --git a/runtime/js_object.h b/runtime/js_object.h new file mode 100644 index 000000000..3385e81f7 --- /dev/null +++ b/runtime/js_object.h @@ -0,0 +1,632 @@ +/* + * Copyright (c) 2021-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_JSOBJECT_H +#define ECMASCRIPT_JSOBJECT_H + +#include + +#include "plugins/ecmascript/runtime/ecma_macros.h" +#include "plugins/ecmascript/runtime/ecma_runtime_call_info.h" +#include "plugins/ecmascript/runtime/ecma_string.h" +#include "plugins/ecmascript/runtime/ic/property_box.h" +#include "plugins/ecmascript/runtime/js_handle.h" +#include "plugins/ecmascript/runtime/js_hclass.h" +#include "plugins/ecmascript/runtime/js_native_pointer.h" +#include "plugins/ecmascript/runtime/js_tagged_value.h" +#include "plugins/ecmascript/runtime/mem/object_xray.h" +#include "plugins/ecmascript/runtime/mem/slots.h" +#include "plugins/ecmascript/runtime/object_operator.h" +#include "plugins/ecmascript/runtime/property_attributes.h" +#include "plugins/ecmascript/runtime/tagged_array.h" + +namespace panda { +namespace ecmascript { +class ObjectOperator; + +class AccessorData; +class JSFunction; +class AccessorData; +class JSArray; + +class JSForInIterator; + +class LexicalEnv; + +// Integrity level for objects +enum IntegrityLevel { SEALED, FROZEN }; + +enum PositionKind { UNKNOWN = 0, INDEXED_PROPERTY = 1, INLINE_NAMED_PROPERTY = 2, OUT_NAMED_PROPERTY = 3 }; +enum PropertyKind { KEY = 0, VALUE, KEY_VALUE }; + +// ecma6.0 6.2.4 The Property Descriptor Specification Type +class PropertyDescriptor final { +public: + explicit PropertyDescriptor() = delete; + + ~PropertyDescriptor() = default; + DEFAULT_NOEXCEPT_MOVE_SEMANTIC(PropertyDescriptor); + DEFAULT_COPY_SEMANTIC(PropertyDescriptor); + + explicit PropertyDescriptor(const JSThread *thread) : thread_(thread) {} + + explicit PropertyDescriptor(const JSThread *thread, JSHandle v) : thread_(thread), value_(v) {} + + explicit PropertyDescriptor(const JSThread *thread, JSHandle v, bool w, bool e, bool c) + : thread_(thread), + writable_(w), + enumerable_(e), + configurable_(c), + hasWritable_(true), + hasEnumerable_(true), + hasConfigurable_(true), + value_(v) + { + } + + explicit PropertyDescriptor(const JSThread *thread, bool w, bool e, bool c) + : PropertyDescriptor(thread, JSHandle(), w, e, c) + { + } + + inline JSHandle GetValue() const + { + if (value_.IsEmpty()) { + return JSHandle(thread_, JSTaggedValue::Undefined()); + } + return value_; + } + + inline void SetValue(JSHandle value) + { + value_ = value; + } + + inline bool IsWritable() const + { + return writable_; + } + + inline void SetWritable(bool flag) + { + writable_ = flag; + hasWritable_ = true; + } + + inline bool IsEnumerable() const + { + return enumerable_; + } + + inline void SetEnumerable(bool flag) + { + enumerable_ = flag; + hasEnumerable_ = true; + } + + inline bool IsConfigurable() const + { + return configurable_; + } + + inline void SetConfigurable(bool flag) + { + configurable_ = flag; + hasConfigurable_ = true; + } + + inline bool HasValue() const + { + return !value_.IsEmpty(); + } + + inline bool HasWritable() const + { + return hasWritable_; + } + + inline bool HasConfigurable() const + { + return hasConfigurable_; + } + + inline bool HasEnumerable() const + { + return hasEnumerable_; + } + + inline bool HasGetter() const + { + return !getter_.IsEmpty(); + } + + inline bool HasSetter() const + { + return !setter_.IsEmpty(); + } + + inline JSHandle GetGetter() const + { + if (getter_->IsNull()) { + return JSHandle(thread_, JSTaggedValue::Undefined()); + } + return getter_; + } + + inline JSHandle GetSetter() const + { + if (setter_->IsNull()) { + return JSHandle(thread_, JSTaggedValue::Undefined()); + } + return setter_; + } + + inline void SetGetter(JSHandle value) + { + getter_ = value; + } + + inline void SetSetter(JSHandle value) + { + setter_ = value; + } + + // 6.2.4.1 + inline bool IsAccessorDescriptor() const + { + // 2. If both Desc.[[Get]] and Desc.[[Set]] are absent, return false. + return !(getter_.IsEmpty() && setter_.IsEmpty()); + } + + inline bool IsDataDescriptor() const + { + // 2. If both Desc.[[Value]] and Desc.[[Writable]] are absent, return false. + return !(value_.IsEmpty() && !hasWritable_); + } + + inline bool IsGenericDescriptor() const + { + // 2. If IsAccessorDescriptor(Desc) and IsDataDescriptor(Desc) are both false, return true + return !IsAccessorDescriptor() && !IsDataDescriptor(); + } + + inline bool IsEmpty() const + { + return !hasWritable_ && !hasEnumerable_ && !hasConfigurable_ && !HasValue() && !HasGetter() && !HasSetter(); + } + + static void CompletePropertyDescriptor(const JSThread *thread, PropertyDescriptor &desc); + +private: + const JSThread *thread_{nullptr}; + + bool writable_ {false}; + bool enumerable_ {false}; + bool configurable_ {false}; + bool hasWritable_ {false}; + bool hasEnumerable_ {false}; + bool hasConfigurable_ {false}; + + JSHandle value_ {}; + JSHandle getter_ {}; + JSHandle setter_ {}; +}; + +enum class ElementTypes { ALLTYPES, STRING_AND_SYMBOL }; + +class PropertyMetaData { +public: + using IsFoundField = BitField; + using IsInlinedPropsField = IsFoundField::NextFlag; + using RepresentationField = IsInlinedPropsField::NextField; + using OffsetField = RepresentationField::NextField; + + explicit PropertyMetaData(uint32_t metaData) : metaData_(metaData) {} + + ~PropertyMetaData() = default; + DEFAULT_NOEXCEPT_MOVE_SEMANTIC(PropertyMetaData); + DEFAULT_COPY_SEMANTIC(PropertyMetaData); + + explicit PropertyMetaData(bool isFound) + { + SetFound(isFound); + } + + inline bool IsFound() const + { + return IsFoundField::Get(metaData_); + } + + inline void SetFound(bool flag) + { + IsFoundField::Set(flag, &metaData_); + } + + inline bool GetIsInlinedProps() const + { + return IsInlinedPropsField::Get(metaData_); + } + + inline void SetIsInlinedProps(bool flag) + { + IsInlinedPropsField::Set(flag, &metaData_); + } + + inline Representation GetRepresentation() const + { + return RepresentationField::Get(metaData_); + } + + inline void SetRepresentation(Representation representation) + { + RepresentationField::Set(representation, &metaData_); + } + + inline void SetOffset(uint32_t offset) + { + OffsetField::Set(offset, &metaData_); + } + + inline uint32_t GetOffset() const + { + return OffsetField::Get(metaData_); + } + +private: + uint32_t metaData_{0}; +}; + +class OperationResult { +public: + explicit OperationResult(const JSThread *thread, JSTaggedValue value, PropertyMetaData metaData) + : metaData_(metaData) + { + thread_ = thread; + value_ = JSHandle(thread_, value); + } + + ~OperationResult() = default; + DEFAULT_NOEXCEPT_MOVE_SEMANTIC(OperationResult); + DEFAULT_COPY_SEMANTIC(OperationResult); + + JSHandle GetValue() const + { + if (value_->IsPropertyBox()) { + return JSHandle(thread_, + PropertyBox::Cast(value_.GetTaggedValue().GetTaggedObject())->GetValue()); + } + return value_; + } + + JSHandle GetRawValue() const + { + return value_; + } + + const PropertyMetaData &GetPropertyMetaData() const + { + return metaData_; + } + +private: + const JSThread *thread_ {nullptr}; + JSHandle value_ {}; + PropertyMetaData metaData_{0U}; +}; + +class ECMAObject : public TaggedObject { +public: + CAST_CHECK(ECMAObject, IsECMAObject); + + void SetBuiltinsCtorMode(); + bool IsBuiltinsConstructor() const; + void SetCallable(bool flag); + bool IsCallable() const; + JSMethod *GetCallTarget() const; + + static constexpr size_t HASH_OFFSET = TaggedObjectSize(); + static constexpr size_t SIZE = HASH_OFFSET + sizeof(JSTaggedType); + + void SetHash(int32_t hash); + int32_t GetHash() const; + void InitializeHash() + { + // Without barrier because 'this' is a new object and the field by HASH_OFFSET doesn't contain an object + ObjectAccessor::SetDynValueWithoutBarrier(this, ECMAObject::HASH_OFFSET, JSTaggedValue(0).GetRawData()); + } + + void* GetNativePointerField(int32_t index) const; + void SetNativePointerField(int32_t index, void *nativePointer, + const DeleteEntryPoint &callBack, void *data); + int32_t GetNativePointerFieldCount() const; + void SetNativePointerFieldCount(int32_t count); + + DECL_VISIT_OBJECT(HASH_OFFSET, SIZE); + + void VisitObjects(const EcmaObjectRangeVisitor &visitor) + { + // no field in this object + VisitRangeSlot(visitor); + } +}; + +class JSObject : public ECMAObject { +public: + static constexpr int MIN_ELEMENTS_LENGTH = 3; + static constexpr int MIN_PROPERTIES_LENGTH = JSHClass::DEFAULT_CAPACITY_OF_IN_OBJECTS; + static constexpr int PROPERTIES_GROW_SIZE = 4; + static constexpr int FAST_ELEMENTS_FACTOR = 3; + static constexpr int MIN_GAP = 256; + static constexpr int MAX_GAP = 1024; + static constexpr uint32_t MAX_ELEMENT_INDEX = std::numeric_limits::max(); + + CAST_CHECK(JSObject, IsECMAObject); + CAST_CHECK_TAGGEDVALUE(JSObject, IsECMAObject); + + // ecma6.0 6.2.4.4 + static JSHandle FromPropertyDescriptor(JSThread *thread, const PropertyDescriptor &desc); + + // ecma6.0 6.2.4.5 ToPropertyDescriptor ( Obj ) + static void ToPropertyDescriptor(JSThread *thread, const JSHandle &obj, PropertyDescriptor &desc); + static bool ToPropertyDescriptorFast(JSThread *thread, const JSHandle &obj, + PropertyDescriptor &desc); + + // ecma6 7.3 Operations on Objects + static JSHandle GetMethod(JSThread *thread, const JSHandle &obj, + const JSHandle &key); + + static bool CreateDataProperty(JSThread *thread, const JSHandle &obj, const JSHandle &key, + const JSHandle &value); + + static bool CreateDataProperty(JSThread *thread, const JSHandle &obj, uint32_t index, + const JSHandle &value); + + static bool CreateMethodProperty(JSThread *thread, const JSHandle &obj, + const JSHandle &key, const JSHandle &value); + + static bool CreateDataPropertyOrThrow(JSThread *thread, const JSHandle &obj, + const JSHandle &key, const JSHandle &value); + + static bool CreateDataPropertyOrThrow(JSThread *thread, const JSHandle &obj, uint32_t index, + const JSHandle &value); + + static JSHandle EnumerableOwnNames(JSThread *thread, const JSHandle &obj); + + // 7.3.23 EnumerableOwnPropertyNames ( O, kind ) + static JSHandle EnumerableOwnPropertyNames(JSThread *thread, const JSHandle &obj, + PropertyKind kind, array_size_t *length_out); + + static JSHandle GetFunctionRealm(JSThread *thread, const JSHandle &object); + + static bool SetIntegrityLevel(JSThread *thread, const JSHandle &obj, IntegrityLevel level); + + static bool TestIntegrityLevel(JSThread *thread, const JSHandle &obj, IntegrityLevel level); + + static JSHandle SpeciesConstructor(JSThread *thread, const JSHandle &obj, + const JSHandle &defaultConstructort); + // 7.3.17 + template + static JSHandle CreateListFromArrayLike(JSThread *thread, const JSHandle &obj); + + // emca6 9.1 + // [[GetPrototypeOf]] + JSTaggedValue GetPrototype(JSThread *thread) const; + + // [[SetPrototypeOf]] + static bool SetPrototype(JSThread *thread, const JSHandle &obj, const JSHandle &proto); + + // [[IsExtensible]] + bool IsExtensible() const; + + // [[PreventExtensions]] + static bool PreventExtensions(JSThread *thread, const JSHandle &obj); + + // [[GetOwnProperty]] -> Undefined | Property Descriptor + static bool GetOwnProperty(JSThread *thread, const JSHandle &obj, const JSHandle &key, + PropertyDescriptor &desc); + + static bool GlobalGetOwnProperty(JSThread *thread, const JSHandle &key, PropertyDescriptor &desc); + + static bool OrdinaryGetOwnProperty(JSThread *thread, const JSHandle &obj, + const JSHandle &key, PropertyDescriptor &desc); + + // [[DefineOwnProperty]] + static bool DefineOwnProperty(JSThread *thread, const JSHandle &obj, const JSHandle &key, + const PropertyDescriptor &desc); + + static bool DefineOwnProperty(JSThread *thread, const JSHandle &obj, uint32_t index, + const PropertyDescriptor &desc); + + static bool OrdinaryDefineOwnProperty(JSThread *thread, const JSHandle &obj, + const JSHandle &key, const PropertyDescriptor &desc); + + static bool OrdinaryDefineOwnProperty(JSThread *thread, const JSHandle &obj, uint32_t index, + const PropertyDescriptor &desc); + + static bool IsCompatiblePropertyDescriptor(bool extensible, const PropertyDescriptor &desc, + const PropertyDescriptor ¤t); + + static bool ValidateAndApplyPropertyDescriptor(ObjectOperator *op, bool extensible, const PropertyDescriptor &desc, + const PropertyDescriptor ¤t); + + static OperationResult GetProperty(JSThread *thread, const JSHandle &obj, + const JSHandle &key); + + static OperationResult GetProperty(JSThread *thread, const JSHandle &obj, + const JSHandle &key, const JSHandle &receiver); + + static OperationResult GetProperty(JSThread *thread, const JSHandle &obj, + const JSHandle &key); + + static OperationResult GetProperty(JSThread *thread, const JSHandle &obj, uint32_t index); + + static OperationResult GetPropertyFromGlobal(JSThread *thread, const JSHandle &key); + + static bool SetProperty(JSThread *thread, const JSHandle &obj, const JSHandle &key, + const JSHandle &value, bool mayThrow = false); + + static bool SetProperty(JSThread *thread, const JSHandle &obj, const JSHandle &key, + const JSHandle &value, bool mayThrow = false); + + static bool SetProperty(JSThread *thread, const JSHandle &obj, const JSHandle &key, + const JSHandle &value, const JSHandle &receiver, + bool mayThrow = false); + + static bool SetProperty(JSThread *thread, const JSHandle &obj, uint32_t index, + const JSHandle &value, bool mayThrow = false); + + static bool GlobalSetProperty(JSThread *thread, const JSHandle &key, + const JSHandle &value, bool mayThrow); + + // [[HasProperty]] + static bool HasProperty(JSThread *thread, const JSHandle &obj, const JSHandle &key); + + static bool HasProperty(JSThread *thread, const JSHandle &obj, uint32_t index); + + // 9.1.10 [[Delete]] + static bool DeleteProperty(JSThread *thread, const JSHandle &obj, const JSHandle &key); + + // [[OwnPropertyKeys]] + static JSHandle GetOwnPropertyKeys(JSThread *thread, const JSHandle &obj); + + // 9.1.13 ObjectCreate + static JSHandle ObjectCreate(JSThread *thread, const JSHandle &proto); + + // 12.9.4 Runtime Semantics: InstanceofOperator(O, C) + static bool InstanceOf(JSThread *thread, const JSHandle &object, + const JSHandle &target); + + // 13.7.5.15 EnumerateObjectProperties ( O ); same as [[Enumerate]] + static JSHandle EnumerateObjectProperties(JSThread *thread, const JSHandle &obj); + + static bool IsRegExp(JSThread *thread, const JSHandle &argument); + + static JSTaggedValue CallGetter(JSThread *thread, const AccessorData *accessor, + const JSHandle &receiver); + static bool CallSetter(JSThread *thread, const AccessorData &accessor, const JSHandle &receiver, + const JSHandle &value, bool mayThrow = false); + + void FillElementsWithHoles(const JSThread *thread, uint32_t start, uint32_t end); + + JSHClass *GetJSHClass() const; + bool IsJSGlobalObject() const; + bool IsConstructor() const; + bool IsECMAObject() const; + bool IsJSError() const; + bool IsArguments() const; + bool IsDate() const; + bool IsJSArray() const; + bool IsJSMap() const; + bool IsJSSet() const; + bool IsJSRegExp() const; + bool IsJSFunction() const; + bool IsBoundFunction() const; + bool IsJSIntlBoundFunction() const; + bool IsProxyRevocFunction() const; + bool IsAccessorData() const; + bool IsJSGlobalEnv() const; + bool IsJSProxy() const; + bool IsGeneratorObject() const; + bool IsForinIterator() const; + bool IsJSSetIterator() const; + bool IsJSMapIterator() const; + bool IsJSArrayIterator() const; + bool IsJSPrimitiveRef() const; + bool IsElementDict() const; + bool IsPropertiesDict() const; + bool IsTypedArray() const; + + static void DefinePropertyByLiteral(JSThread *thread, const JSHandle &obj, + const JSHandle &key, const JSHandle &value, + bool useForClass = false); + static void DefineSetter(JSThread *thread, const JSHandle &obj, const JSHandle &key, + const JSHandle &value); + static void DefineGetter(JSThread *thread, const JSHandle &obj, const JSHandle &key, + const JSHandle &value); + static JSHandle CreateObjectFromProperties(const JSThread *thread, + const JSHandle &properties); + static void GetAllKeys(const JSThread *thread, const JSHandle &obj, int offset, + const JSHandle &keyArray); + static void GetAllKeys(const JSThread *thread, const JSHandle &obj, + std::vector &keyVector); + static void GetAllElementKeys(JSThread *thread, const JSHandle &obj, int offset, + const JSHandle &keyArray); + static void GetALLElementKeysIntoVector(const JSThread *thread, const JSHandle &obj, + std::vector &keyVector); + uint32_t GetNumberOfKeys(); + uint32_t GetNumberOfElements(); + + static JSHandle GetEnumElementKeys(JSThread *thread, const JSHandle &obj, int offset, + uint32_t numOfElements, uint32_t *keys); + static JSHandle GetAllEnumKeys(const JSThread *thread, const JSHandle &obj, int offset, + uint32_t numOfKeys, uint32_t *keys); + + static void AddAccessor(JSThread *thread, const JSHandle &obj, const JSHandle &key, + const JSHandle &value, PropertyAttributes attr); + + static constexpr size_t PROPERTIES_OFFSET = ECMAObject::SIZE; + + ACCESSORS(Properties, PROPERTIES_OFFSET, ELEMENTS_OFFSET); + ACCESSORS(Elements, ELEMENTS_OFFSET, SIZE); + + DECL_VISIT_OBJECT_FOR_JS_OBJECT(ECMAObject, PROPERTIES_OFFSET, SIZE) + + DECL_DUMP() + + static JSHandle TransitionToDictionary(const JSThread *thread, const JSHandle &receiver); + + inline void SetPropertyInlinedProps(const JSThread *thread, uint32_t index, JSTaggedValue value); + inline void SetPropertyInlinedProps(const JSThread *thread, const JSHClass *hclass, uint32_t index, + JSTaggedValue value); + inline JSTaggedValue GetPropertyInlinedProps(uint32_t index) const; + inline JSTaggedValue GetPropertyInlinedProps(const JSHClass *hclass, uint32_t index) const; + inline JSTaggedValue GetProperty(const JSHClass *hclass, PropertyAttributes attr) const; + inline void SetProperty(const JSThread *thread, const JSHClass *hclass, PropertyAttributes attr, + JSTaggedValue value); + + static bool IsArrayLengthWritable(JSThread *thread, const JSHandle &receiver); + bool UpdatePropertyInDictionary(const JSThread *thread, JSTaggedValue key, JSTaggedValue value); + static bool ShouldTransToDict(uint32_t capacity, uint32_t index); + static JSHandle GrowElementsCapacity(const JSThread *thread, const JSHandle &obj, + uint32_t capacity); + +protected: + static void ElementsToDictionary(const JSThread *thread, JSHandle obj); + +private: + friend class ObjectOperator; + friend class LoadICRuntime; + friend class StoreICRuntime; + friend class FastRuntimeStub; + friend class ICRuntimeStub; + friend class RuntimeTrampolines; + + static bool AddElementInternal( + JSThread *thread, const JSHandle &receiver, uint32_t index, const JSHandle &value, + PropertyAttributes attr = PropertyAttributes(PropertyAttributes::GetDefaultAttributes())); + + static JSTaggedValue GetProperty(JSThread *thread, ObjectOperator *op); + static bool SetProperty(ObjectOperator *op, const JSHandle &value, bool mayThrow); + static void DeletePropertyInternal(JSThread *thread, const JSHandle &obj, + const JSHandle &key, uint32_t index); + int FindProperty(const JSHandle &key); + + static uint32_t ComputeElementCapacity(uint32_t oldCapacity); + static uint32_t ComputePropertyCapacity(uint32_t oldCapacity); +}; +} // namespace ecmascript +} // namespace panda + +#endif diff --git a/runtime/js_plural_rules.cpp b/runtime/js_plural_rules.cpp new file mode 100644 index 000000000..80614179f --- /dev/null +++ b/runtime/js_plural_rules.cpp @@ -0,0 +1,370 @@ +/* + * Copyright (c) 2021-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 "plugins/ecmascript/runtime/js_plural_rules.h" + +#include "plugins/ecmascript/runtime/ecma_macros.h" +#include "plugins/ecmascript/runtime/global_env.h" +#include "plugins/ecmascript/runtime/global_env_constants.h" +#include "plugins/ecmascript/runtime/js_number_format.h" + +namespace panda::ecmascript { +enum class TypeOption : uint8_t { CARDINAL = 0x01, ORDINAL, EXCEPTION }; + +constexpr int32_t STRING_SEPARATOR_LENGTH = 4; + +icu::number::LocalizedNumberFormatter *JSPluralRules::GetIcuNumberFormatter() const +{ + ASSERT(GetIcuNF().IsJSNativePointer()); + auto result = JSNativePointer::Cast(GetIcuNF().GetTaggedObject())->GetExternalPointer(); + return reinterpret_cast(result); +} + +void JSPluralRules::FreeIcuNumberFormatter(void *pointer, [[maybe_unused]] void* hint) +{ + if (pointer == nullptr) { + return; + } + auto icuNumberFormatter = reinterpret_cast(pointer); + icuNumberFormatter->~LocalizedNumberFormatter(); +} + +void JSPluralRules::SetIcuNumberFormatter(JSThread *thread, const JSHandle &pluralRules, + const icu::number::LocalizedNumberFormatter &icuNF, const DeleteEntryPoint &callback) +{ + EcmaVM *ecmaVm = thread->GetEcmaVM(); + ObjectFactory *factory = ecmaVm->GetFactory(); + + icu::number::LocalizedNumberFormatter *icuPointer = + ecmaVm->GetRegionFactory()->New(icuNF); + ASSERT(icuPointer != nullptr); + JSTaggedValue data = pluralRules->GetIcuNF(); + if (data.IsHeapObject() && data.IsJSNativePointer()) { + JSNativePointer *native = JSNativePointer::Cast(data.GetTaggedObject()); + native->ResetExternalPointer(icuPointer); + return; + } + JSHandle pointer = factory->NewJSNativePointer(icuPointer); + pointer->SetDeleter(callback); + pluralRules->SetIcuNF(thread, pointer.GetTaggedValue()); + ecmaVm->PushToArrayDataList(*pointer); +} + +icu::PluralRules *JSPluralRules::GetIcuPluralRules() const +{ + ASSERT(GetIcuPR().IsJSNativePointer()); + auto result = JSNativePointer::Cast(GetIcuPR().GetTaggedObject())->GetExternalPointer(); + return reinterpret_cast(result); +} + +void JSPluralRules::FreeIcuPluralRules(void *pointer, [[maybe_unused]] void* hint) +{ + if (pointer == nullptr) { + return; + } + auto icuPluralRules = reinterpret_cast(pointer); + icuPluralRules->~PluralRules(); +} + +void JSPluralRules::SetIcuPluralRules(JSThread *thread, const JSHandle &pluralRules, + const icu::PluralRules &icuPR, const DeleteEntryPoint &callback) +{ + [[maybe_unused]] EcmaHandleScope scope(thread); + EcmaVM *ecmaVm = thread->GetEcmaVM(); + ObjectFactory *factory = ecmaVm->GetFactory(); + + icu::PluralRules *icuPointer = ecmaVm->GetRegionFactory()->New(icuPR); + ASSERT(icuPointer != nullptr); + JSTaggedValue data = pluralRules->GetIcuPR(); + if (data.IsHeapObject() && data.IsJSNativePointer()) { + JSNativePointer *native = JSNativePointer::Cast(data.GetTaggedObject()); + native->ResetExternalPointer(icuPointer); + return; + } + JSHandle pointer = factory->NewJSNativePointer(icuPointer); + pointer->SetDeleter(callback); + pluralRules->SetIcuPR(thread, pointer.GetTaggedValue()); + ecmaVm->PushToArrayDataList(*pointer); +} + +JSHandle JSPluralRules::BuildLocaleSet(JSThread *thread, const std::set &icuAvailableLocales) +{ + EcmaVM *ecmaVm = thread->GetEcmaVM(); + ObjectFactory *factory = ecmaVm->GetFactory(); + JSHandle locales = factory->NewTaggedArray(icuAvailableLocales.size()); + int32_t index = 0; + + for (const std::string &locale : icuAvailableLocales) { + JSHandle localeStr = factory->NewFromStdString(locale); + locales->Set(thread, index++, localeStr); + } + return locales; +} + +bool GetNextLocale(icu::StringEnumeration *locales, std::string &localeStr, int32_t *len) +{ + UErrorCode status = U_ZERO_ERROR; + const char *locale = nullptr; + locale = locales->next(len, status); + if ((U_SUCCESS(status) == 0) || locale == nullptr) { + localeStr = ""; + return false; + } + localeStr = std::string(locale); + return true; +} + +JSHandle JSPluralRules::GetAvailableLocales(JSThread *thread) +{ + UErrorCode status = U_ZERO_ERROR; + std::unique_ptr locales(icu::PluralRules::getAvailableLocales(status)); + ASSERT(U_SUCCESS(status)); + std::set set; + std::string localeStr; + int32_t len = 0; + while (GetNextLocale(locales.get(), localeStr, &len)) { + if (len >= STRING_SEPARATOR_LENGTH) { + std::replace(localeStr.begin(), localeStr.end(), '_', '-'); + } + set.insert(localeStr); + } + return BuildLocaleSet(thread, set); +} + +// InitializePluralRules ( pluralRules, locales, options ) +JSHandle JSPluralRules::InitializePluralRules(JSThread *thread, + const JSHandle &pluralRules, + const JSHandle &locales, + const JSHandle &options) +{ + EcmaVM *ecmaVm = thread->GetEcmaVM(); + ObjectFactory *factory = ecmaVm->GetFactory(); + auto globalConst = thread->GlobalConstants(); + + // 1. Let requestedLocales be ? CanonicalizeLocaleList(locales). + JSHandle requestedLocales = JSLocale::CanonicalizeLocaleList(thread, locales); + RETURN_HANDLE_IF_ABRUPT_COMPLETION(JSPluralRules, thread); + + // 2&3. If options is undefined, then Let options be ObjectCreate(null). else Let options be ? ToObject(options). + JSHandle prOptions; + if (!options->IsUndefined()) { + prOptions = JSTaggedValue::ToObject(thread, options); + RETURN_HANDLE_IF_ABRUPT_COMPLETION(JSPluralRules, thread); + } else { + prOptions = factory->OrdinaryNewJSObjectCreate(JSHandle(thread, JSTaggedValue::Null())); + } + + // 5. Let matcher be ? GetOption(options, "localeMatcher", "string", « "lookup", "best fit" », "best fit"). + LocaleMatcherOption matcher = + JSLocale::GetOptionOfString(thread, prOptions, globalConst->GetHandledLocaleMatcherString(), + {LocaleMatcherOption::LOOKUP, LocaleMatcherOption::BEST_FIT}, + {"lookup", "best fit"}, LocaleMatcherOption::BEST_FIT); + RETURN_HANDLE_IF_ABRUPT_COMPLETION(JSPluralRules, thread); + + // 7. Let t be ? GetOption(options, "type", "string", « "cardinal", "ordinal" », "cardinal"). + JSHandle property = JSHandle::Cast(globalConst->GetHandledTypeString()); + TypeOption type = + JSLocale::GetOptionOfString(thread, prOptions, property, { TypeOption::CARDINAL, TypeOption::ORDINAL }, + { "cardinal", "ordinal" }, TypeOption::CARDINAL); + RETURN_HANDLE_IF_ABRUPT_COMPLETION(JSPluralRules, thread); + + // set pluralRules.[[type]] to type + JSHandle typeValue; + if (type == TypeOption::CARDINAL) { + typeValue = globalConst->GetHandledCardinalString(); + } else { + typeValue = globalConst->GetHandledOrdinalString(); + } + pluralRules->SetType(thread, typeValue.GetTaggedValue()); + + // Let r be ResolveLocale(%PluralRules%.[[AvailableLocales]], requestedLocales, opt, + // %PluralRules%.[[RelevantExtensionKeys]], localeData). + JSHandle availableLocales; + if (requestedLocales->GetLength() == 0) { + availableLocales = factory->EmptyArray(); + } else { + availableLocales = GetAvailableLocales(thread); + } + std::set relevantExtensionKeys{""}; + ResolvedLocale r = + JSLocale::ResolveLocale(thread, availableLocales, requestedLocales, matcher, relevantExtensionKeys); + RETURN_HANDLE_IF_ABRUPT_COMPLETION(JSPluralRules, thread); + icu::Locale icuLocale = r.localeData; + + // Get ICU numberFormatter with given locale + icu::number::LocalizedNumberFormatter icuNumberFormatter = + icu::number::NumberFormatter::withLocale(icuLocale).roundingMode(UNUM_ROUND_HALFUP); + + bool sucess = true; + UErrorCode status = U_ZERO_ERROR; + UPluralType icuType = UPLURAL_TYPE_CARDINAL; + // Trans typeOption to ICU typeOption + switch (type) { + case TypeOption::ORDINAL: + icuType = UPLURAL_TYPE_ORDINAL; + break; + case TypeOption::CARDINAL: + icuType = UPLURAL_TYPE_CARDINAL; + break; + default: + UNREACHABLE(); + } + std::unique_ptr icuPluralRules(icu::PluralRules::forLocale(icuLocale, icuType, status)); + if (U_FAILURE(status) != 0) { // NOLINT(readability-implicit-bool-conversion) + sucess = false; + } + + // Trans typeOption to ICU typeOption + if (!sucess || icuPluralRules == nullptr) { + icu::Locale noExtensionLocale(icuLocale.getBaseName()); + status = U_ZERO_ERROR; + switch (type) { + case TypeOption::ORDINAL: + icuType = UPLURAL_TYPE_ORDINAL; + break; + case TypeOption::CARDINAL: + icuType = UPLURAL_TYPE_CARDINAL; + break; + default: + UNREACHABLE(); + } + icuPluralRules.reset(icu::PluralRules::forLocale(icuLocale, icuType, status)); + } + if (U_FAILURE(status) || icuPluralRules == nullptr) { // NOLINT(readability-implicit-bool-conversion) + THROW_RANGE_ERROR_AND_RETURN(thread, "cannot create icuPluralRules", pluralRules); + } + + // 9. Perform ? SetNumberFormatDigitOptions(pluralRules, options, 0, 3, "standard"). + JSLocale::SetNumberFormatDigitOptions(thread, pluralRules, JSHandle::Cast(prOptions), MNFD_DEFAULT, + MXFD_DEFAULT, NotationOption::STANDARD); + icuNumberFormatter = JSNumberFormat::SetICUFormatterDigitOptions(icuNumberFormatter, pluralRules); + + // Set pluralRules.[[IcuPluralRules]] to icuPluralRules + SetIcuPluralRules(thread, pluralRules, *icuPluralRules, JSPluralRules::FreeIcuPluralRules); + + // Set pluralRules.[[IcuNumberFormat]] to icuNumberFormatter + SetIcuNumberFormatter(thread, pluralRules, icuNumberFormatter, JSPluralRules::FreeIcuNumberFormatter); + + // 12. Set pluralRules.[[Locale]] to the value of r.[[locale]]. + JSHandle localeStr = JSLocale::ToLanguageTag(thread, icuLocale); + pluralRules->SetLocale(thread, localeStr.GetTaggedValue()); + + // 13. Return pluralRules. + return pluralRules; +} + +JSHandle FormatNumericToString(JSThread *thread, const icu::number::LocalizedNumberFormatter *icuFormatter, + const icu::PluralRules *icuPluralRules, double n) +{ + UErrorCode status = U_ZERO_ERROR; + icu::number::FormattedNumber formatted = icuFormatter->formatDouble(n, status); + if (U_FAILURE(status) != 0) { // NOLINT(readability-implicit-bool-conversion) + JSHandle exception(thread, JSTaggedValue::Exception()); + THROW_RANGE_ERROR_AND_RETURN(thread, "invalid resolve number", JSHandle::Cast(exception)); + } + + icu::UnicodeString uString = icuPluralRules->select(formatted, status); + if (U_FAILURE(status) != 0) { // NOLINT(readability-implicit-bool-conversion) + JSHandle exception(thread, JSTaggedValue::Exception()); + THROW_RANGE_ERROR_AND_RETURN(thread, "invalid resolve number", JSHandle::Cast(exception)); + } + + ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); + JSHandle result = + factory->NewFromUtf16(reinterpret_cast(uString.getBuffer()), uString.length()); + return result; +} +JSHandle JSPluralRules::ResolvePlural(JSThread *thread, const JSHandle &pluralRules, + double n) +{ + icu::PluralRules *icuPluralRules = pluralRules->GetIcuPluralRules(); + icu::number::LocalizedNumberFormatter *icuFormatter = pluralRules->GetIcuNumberFormatter(); + if (icuPluralRules == nullptr || icuFormatter == nullptr) { + return JSHandle(thread, JSTaggedValue::Undefined()); + } + + JSHandle result = FormatNumericToString(thread, icuFormatter, icuPluralRules, n); + RETURN_HANDLE_IF_ABRUPT_COMPLETION(EcmaString, thread); + return result; +} + +void JSPluralRules::ResolvedOptions(JSThread *thread, const JSHandle &pluralRules, + const JSHandle &options) +{ + EcmaVM *ecmaVm = thread->GetEcmaVM(); + ObjectFactory *factory = ecmaVm->GetFactory(); + auto globalConst = thread->GlobalConstants(); + + // [[Locale]] + JSHandle property = JSHandle::Cast(globalConst->GetHandledLocaleString()); + JSHandle locale(thread, pluralRules->GetLocale()); + PropertyDescriptor localeDesc(thread, JSHandle::Cast(locale), true, true, true); + JSObject::DefineOwnProperty(thread, options, property, localeDesc); + + // [[type]] + property = JSHandle::Cast(globalConst->GetHandledTypeString()); + PropertyDescriptor typeDesc(thread, JSHandle(thread, pluralRules->GetType()), true, true, true); + JSObject::DefineOwnProperty(thread, options, property, typeDesc); + + // [[MinimumIntegerDigits]] + property = JSHandle::Cast(globalConst->GetHandledMinimumIntegerDigitsString()); + JSHandle minimumIntegerDigits(thread, pluralRules->GetMinimumIntegerDigits()); + JSObject::CreateDataPropertyOrThrow(thread, options, property, minimumIntegerDigits); + + RoundingType roundingType = static_cast(pluralRules->GetRoundingType().GetInt()); + if (roundingType == RoundingType::SIGNIFICANTDIGITS) { + // [[MinimumSignificantDigits]] + property = globalConst->GetHandledMinimumSignificantDigitsString(); + JSHandle minimumSignificantDigits(thread, pluralRules->GetMinimumSignificantDigits()); + JSObject::CreateDataPropertyOrThrow(thread, options, property, minimumSignificantDigits); + // [[MaximumSignificantDigits]] + property = globalConst->GetHandledMaximumSignificantDigitsString(); + JSHandle maximumSignificantDigits(thread, pluralRules->GetMaximumSignificantDigits()); + JSObject::CreateDataPropertyOrThrow(thread, options, property, maximumSignificantDigits); + } else { + // [[MinimumFractionDigits]] + property = globalConst->GetHandledMinimumFractionDigitsString(); + JSHandle minimumFractionDigits(thread, pluralRules->GetMinimumFractionDigits()); + JSObject::CreateDataPropertyOrThrow(thread, options, property, minimumFractionDigits); + // [[MaximumFractionDigits]] + property = globalConst->GetHandledMaximumFractionDigitsString(); + JSHandle maximumFractionDigits(thread, pluralRules->GetMaximumFractionDigits()); + JSObject::CreateDataPropertyOrThrow(thread, options, property, maximumFractionDigits); + } + + // 5. Let pluralCategories be a List of Strings representing the possible results of PluralRuleSelect + // for the selected locale pr.[[Locale]]. This List consists of unique String values, + // from the the list "zero", "one", "two", "few", "many" and "other", + // that are relevant for the locale whose localization is specified in LDML Language Plural Rules. + UErrorCode status = U_ZERO_ERROR; + icu::PluralRules *icuPluralRules = pluralRules->GetIcuPluralRules(); + ASSERT(icuPluralRules != nullptr); + std::unique_ptr categories(icuPluralRules->getKeywords(status)); + int32_t count = categories->count(status); + ASSERT(U_SUCCESS(status)); + JSHandle pluralCategories = factory->NewTaggedArray(count); + for (int32_t i = 0; i < count; i++) { + const icu::UnicodeString *category = categories->snext(status); + ASSERT(U_SUCCESS(status)); + JSHandle value = JSLocale::IcuToString(thread, *category); + pluralCategories->Set(thread, i, value); + } + + // 6. Perform ! CreateDataProperty(options, "pluralCategories", CreateArrayFromList(pluralCategories)). + property = globalConst->GetHandledPluralCategoriesString(); + JSHandle jsPluralCategories = JSArray::CreateArrayFromList(thread, pluralCategories); + JSObject::CreateDataPropertyOrThrow(thread, options, property, JSHandle::Cast(jsPluralCategories)); +} +} // namespace panda::ecmascript diff --git a/runtime/js_plural_rules.h b/runtime/js_plural_rules.h new file mode 100644 index 000000000..3ee3b9480 --- /dev/null +++ b/runtime/js_plural_rules.h @@ -0,0 +1,81 @@ +/* + * Copyright (c) 2021-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_JS_PLURAL_RULES_H +#define ECMASCRIPT_JS_PLURAL_RULES_H + +#include "js_array.h" +#include "js_hclass.h" +#include "js_intl.h" +#include "js_locale.h" +#include "js_object.h" +#include "js_tagged_value.h" + +namespace panda::ecmascript { +constexpr int MNFD_DEFAULT = 0; +constexpr int MXFD_DEFAULT = 3; + +class JSPluralRules : public JSObject { +public: + CAST_CHECK(JSPluralRules, IsJSPluralRules); + + static constexpr size_t LOCALE_OFFSET = JSObject::SIZE; + + ACCESSORS(Locale, LOCALE_OFFSET, INITIALIZED_PLURAL_RULES) + ACCESSORS(InitializedPluralRules, INITIALIZED_PLURAL_RULES, TYPE_OFFSET) + ACCESSORS(Type, TYPE_OFFSET, MININUM_INTEGER_DIGITS_OFFSET) + ACCESSORS(MinimumIntegerDigits, MININUM_INTEGER_DIGITS_OFFSET, MINIMUM_FRACTION_DIGITS_OFFSET) + ACCESSORS(MinimumFractionDigits, MINIMUM_FRACTION_DIGITS_OFFSET, MAXIMUM_FRACTION_DIGITS_OFFSET) + ACCESSORS(MaximumFractionDigits, MAXIMUM_FRACTION_DIGITS_OFFSET, MININUM_SIGNIFICANT_DIGITS_OFFSET) + ACCESSORS(MinimumSignificantDigits, MININUM_SIGNIFICANT_DIGITS_OFFSET, MAXINUM_SIGNIFICANT_DIGITS_OFFSET) + ACCESSORS(MaximumSignificantDigits, MAXINUM_SIGNIFICANT_DIGITS_OFFSET, ROUDING_TYPE_OFFSET) + ACCESSORS(RoundingType, ROUDING_TYPE_OFFSET, ICU_PLURAL_RULES_OFFSET) + ACCESSORS(IcuPR, ICU_PLURAL_RULES_OFFSET, ICU_NUMBER_FORMAT_OFFSET) + ACCESSORS(IcuNF, ICU_NUMBER_FORMAT_OFFSET, SIZE) + + DECL_VISIT_OBJECT_FOR_JS_OBJECT(JSObject, LOCALE_OFFSET, SIZE) + DECL_DUMP() + + icu::number::LocalizedNumberFormatter *GetIcuNumberFormatter() const; + + static void SetIcuNumberFormatter(JSThread *thread, const JSHandle &pluralRules, + const icu::number::LocalizedNumberFormatter &icuNF, const DeleteEntryPoint &callback); + + static void FreeIcuNumberFormatter(void *pointer, [[maybe_unused]] void* hint = nullptr); + + icu::PluralRules *GetIcuPluralRules() const; + + static void SetIcuPluralRules(JSThread *thread, const JSHandle &pluralRules, + const icu::PluralRules &icuPR, const DeleteEntryPoint &callback); + + static void FreeIcuPluralRules(void *pointer, [[maybe_unused]] void* hint = nullptr); + + static JSHandle BuildLocaleSet(JSThread *thread, const std::set &icuAvailableLocales); + + static JSHandle GetAvailableLocales(JSThread *thread); + + // 15.1.1 InitializePluralRules ( pluralRules, locales, options ) + static JSHandle InitializePluralRules(JSThread *thread, const JSHandle &pluralRules, + const JSHandle &locales, + const JSHandle &options); + + // 15.1.4 ResolvePlural ( pluralRules, n ) + static JSHandle ResolvePlural(JSThread *thread, const JSHandle &pluralRules, double n); + + static void ResolvedOptions(JSThread *thread, const JSHandle &pluralRules, + const JSHandle &options); +}; +} // namespace panda::ecmascript +#endif // ECMASCRIPT_JS_PLURAL_RULES_H \ No newline at end of file diff --git a/runtime/js_primitive_ref.cpp b/runtime/js_primitive_ref.cpp new file mode 100644 index 000000000..2e1566a5b --- /dev/null +++ b/runtime/js_primitive_ref.cpp @@ -0,0 +1,72 @@ +/* + * Copyright (c) 2021-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 "js_primitive_ref.h" +#include "plugins/ecmascript/runtime/ecma_string-inl.h" +#include "plugins/ecmascript/runtime/ecma_vm.h" +#include "plugins/ecmascript/runtime/global_env.h" +#include "plugins/ecmascript/runtime/mem/assert_scope-inl.h" +#include "plugins/ecmascript/runtime/object_factory.h" + +namespace panda::ecmascript { +// ES6 9.4.3.4 StringCreate( value, prototype) +JSHandle JSPrimitiveRef::StringCreate(JSThread *thread, const JSHandle &value) +{ + // 1. ReturnIfAbrupt(prototype). + // 2. Assert: Type(value) is String. + ASSERT(value->IsString()); + // 3. Let S be a newly created String exotic object. + // 4. Set the [[StringData]] internal slot of S to value. + ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); + JSHandle str(factory->NewJSString(value)); + // 11. Let length be the number of code unit elements in value. + // 12. Let status be DefinePropertyOrThrow(S, "length", PropertyDescriptor{[[Value]]: length, [[Writable]]: + // false, [[Enumerable]]: false, [[Configurable]]: false }). + JSHandle lengthStr = thread->GlobalConstants()->GetHandledLengthString(); + + int32_t length = EcmaString::Cast(value->GetTaggedObject())->GetLength(); + PropertyDescriptor desc(thread, JSHandle(thread, JSTaggedValue(length)), false, false, false); + [[maybe_unused]] bool status = JSTaggedValue::DefinePropertyOrThrow(thread, str, lengthStr, desc); + // 13. Assert: status is not an abrupt completion. + // 14. Return S. + ASSERT(status); + return JSHandle(str); +} + +bool JSPrimitiveRef::StringGetIndexProperty(const JSThread *thread, const JSHandle &obj, uint32_t index, + PropertyDescriptor *desc) +{ + uint16_t tmpChar; + { + DISALLOW_GARBAGE_COLLECTION; + EcmaString *str = EcmaString::Cast(JSPrimitiveRef::Cast(*obj)->GetValue().GetTaggedObject()); + if (str->GetLength() <= index) { + return false; + } + // 10. Let resultStr be a String value of length 1, containing one code unit from str, specifically the code + // unit at index index + tmpChar = str->At(index); + } + JSHandle value(thread->GetEcmaVM()->GetFactory()->NewFromUtf16(&tmpChar, 1)); + // 11. Return a PropertyDescriptor{ [[Value]]: resultStr, [[Enumerable]]: true, [[Writable]]: false, + // [[Configurable]]: false }. + desc->SetValue(value); + desc->SetEnumerable(true); + desc->SetWritable(false); + desc->SetConfigurable(false); + return true; +} +// ES6 9.4.3.3 [[OwnPropertyKeys]] ( ) +} // namespace panda::ecmascript diff --git a/runtime/js_primitive_ref.h b/runtime/js_primitive_ref.h new file mode 100644 index 000000000..9436c53c6 --- /dev/null +++ b/runtime/js_primitive_ref.h @@ -0,0 +1,84 @@ +/* + * Copyright (c) 2021-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_JSPRIMITIVEREF_H +#define ECMASCRIPT_JSPRIMITIVEREF_H + +#include "plugins/ecmascript/runtime/js_hclass.h" +#include "plugins/ecmascript/runtime/js_object.h" +#include "plugins/ecmascript/runtime/js_tagged_value-inl.h" +#include "plugins/ecmascript/runtime/tagged_array.h" + +namespace panda::ecmascript { +enum class PrimitiveType : uint8_t { + PRIMITIVE_BOOLEAN = 0, + PRIMITIVE_NUMBER, + PRIMITIVE_STRING, + PRIMITIVE_SYMBOL, +}; + +class JSPrimitiveRef : public JSObject { +public: + CAST_CHECK(JSPrimitiveRef, IsJSPrimitiveRef); + + JSPrimitiveRef() = delete; + + bool IsNumber() const + { + return GetValue().IsNumber(); + } + + bool IsInt() const + { + return GetValue().IsInt(); + } + + bool IsBoolean() const + { + return GetValue().IsBoolean(); + } + + bool IsString() const + { + return GetValue().IsString(); + } + + bool IsSymbol() const + { + return GetValue().IsSymbol(); + } + + uint32_t GetStringLength() const + { + ASSERT(IsString()); + return EcmaString::Cast(GetValue().GetTaggedObject())->GetLength(); + } + + // ES6 9.4.3 String Exotic Objects + // ES6 9.4.3.4 StringCreate( value, prototype)// proto always be %StringPrototype% + static JSHandle StringCreate(JSThread *thread, const JSHandle &value); + static bool StringGetIndexProperty(const JSThread *thread, const JSHandle &obj, uint32_t index, + PropertyDescriptor *desc); + + static constexpr size_t VALUE_OFFSET = JSObject::SIZE; + ACCESSORS(Value, VALUE_OFFSET, SIZE); + + DECL_VISIT_OBJECT_FOR_JS_OBJECT(JSObject, VALUE_OFFSET, SIZE) + + DECL_DUMP() +}; +} // namespace panda::ecmascript + +#endif // ECMASCRIPT_JSPRIMITIVEREF_H diff --git a/runtime/js_promise.cpp b/runtime/js_promise.cpp new file mode 100644 index 000000000..ae7587aaa --- /dev/null +++ b/runtime/js_promise.cpp @@ -0,0 +1,240 @@ +/* + * Copyright (c) 2021-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 "plugins/ecmascript/runtime/js_promise.h" +#include "plugins/ecmascript/runtime/base/error_type.h" +#include "plugins/ecmascript/runtime/builtins/builtins_promise_handler.h" +#include "plugins/ecmascript/runtime/ecma_macros.h" +#include "plugins/ecmascript/runtime/global_env.h" +#include "plugins/ecmascript/runtime/internal_call_params.h" +#include "plugins/ecmascript/runtime/jobs/micro_job_queue.h" +#include "plugins/ecmascript/runtime/js_function.h" +#include "plugins/ecmascript/runtime/js_handle.h" +#include "plugins/ecmascript/runtime/js_tagged_value-inl.h" +#include "plugins/ecmascript/runtime/js_thread.h" +#include "plugins/ecmascript/runtime/object_factory.h" + +namespace panda::ecmascript { +using BuiltinsPromiseHandler = builtins::BuiltinsPromiseHandler; + +JSHandle JSPromise::CreateResolvingFunctions(JSThread *thread, + const JSHandle &promise) +{ + ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); + // 1. Let alreadyResolved be a new Record { [[value]]: false }. + JSHandle record = factory->NewPromiseRecord(); + record->SetValue(thread, JSTaggedValue::False()); + + // 2. Let resolve be a new built-in function object as defined in Promise Resolve Functions (25.4.1.3.2). + JSHandle resolve = + factory->CreateJSPromiseReactionsFunction(reinterpret_cast(BuiltinsPromiseHandler::Resolve)); + // 3. Set the [[Promise]] internal slot of resolve to promise. + resolve->SetPromise(thread, promise); + // 4. Set the [[AlreadyResolved]] internal slot of resolve to alreadyResolved. + resolve->SetAlreadyResolved(thread, record); + // 5. Let reject be a new built-in function object as defined in Promise Reject Functions (25.4.1.3.1). + JSHandle reject = + factory->CreateJSPromiseReactionsFunction(reinterpret_cast(BuiltinsPromiseHandler::Reject)); + // 6. Set the [[Promise]] internal slot of reject to promise. + reject->SetPromise(thread, promise); + // 7. Set the [[AlreadyResolved]] internal slot of reject to alreadyResolved. + reject->SetAlreadyResolved(thread, record); + // 8. Return a new Record { [[Resolve]]: resolve, [[Reject]]: reject }. + JSHandle reactions = factory->NewResolvingFunctionsRecord(); + reactions->SetResolveFunction(thread, resolve.GetTaggedValue()); + reactions->SetRejectFunction(thread, reject.GetTaggedValue()); + return reactions; +} + +JSTaggedValue JSPromise::FulfillPromise(JSThread *thread, const JSHandle &promise, + const JSHandle &value) +{ + const GlobalEnvConstants *globalConst = thread->GlobalConstants(); + // 1. Assert: the value of promise's [[PromiseState]] internal slot is "pending". + JSHandle handleStatus(thread, promise->GetPromiseState()); + ASSERT_PRINT(JSTaggedValue::SameValue(handleStatus.GetTaggedValue(), + JSTaggedValue(static_cast(PromiseStatus::PENDING))), + "FulfillPromise: state must be pending"); + // 2. Let reactions be the value of promise's [[PromiseFulfillReactions]] internal slot. + JSHandle reactions(thread, promise->GetPromiseFulfillReactions()); + // 3. Set the value of promise's [[PromiseResult]] internal slot to value. + promise->SetPromiseResult(thread, value); + // 4. Set the value of promise's [[PromiseFulfillReactions]] internal slot to undefined. + promise->SetPromiseFulfillReactions(thread, globalConst->GetHandledUndefined()); + // 5. Set the value of promise's [[PromiseRejectReactions]] internal slot to undefined. + promise->SetPromiseRejectReactions(thread, globalConst->GetHandledUndefined()); + // 6. Set the value of promise's [[PromiseState]] internal slot to "fulfilled". + promise->SetPromiseState(thread, JSTaggedValue(static_cast(PromiseStatus::FULFILLED))); + // 7. Return TriggerPromiseReactions(reactions, reason). + return TriggerPromiseReactions(thread, reactions, value); +} + +JSHandle JSPromise::NewPromiseCapability(JSThread *thread, const JSHandle &obj) +{ + ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); + // 1. If IsConstructor(C) is false, throw a TypeError exception. + if (!obj->IsConstructor()) { + THROW_TYPE_ERROR_AND_RETURN(thread, "NewPromiseCapability: obj is not constructor!", + factory->NewPromiseCapability()); + } + // 2. NOTE C is assumed to be a constructor function that supports the parameter conventions of the Promise + // constructor (see 25.4.3.1). + // 3. Let promiseCapability be a new PromiseCapability { [[Promise]]: undefined, [[Resolve]]: undefined, + // [[Reject]]: undefined }. + JSHandle promiseCapability = factory->NewPromiseCapability(); + // 4. Let executor be a new built-in function object as defined in GetCapabilitiesExecutor Functions + // (25.4.1.5.1). + JSHandle executor = + factory->CreateJSPromiseExecutorFunction(reinterpret_cast(BuiltinsPromiseHandler::Executor)); + // 5. Set the [[Capability]] internal slot of executor to promiseCapability. + executor->SetCapability(thread, promiseCapability.GetTaggedValue()); + // 6. Let promise be Construct(C, «executor»). + // 7. ReturnIfAbrupt(promise). + JSHandle newTarget(thread, JSTaggedValue::Undefined()); + InternalCallParams *arguments = thread->GetInternalCallParams(); + arguments->MakeArgv(executor); + JSTaggedValue result = JSFunction::Construct(thread, obj, 1, arguments->GetArgv(), newTarget); + JSHandle promise(thread, result); + RETURN_VALUE_IF_ABRUPT_COMPLETION(thread, factory->NewPromiseCapability()); + // 8. If IsCallable(promiseCapability.[[Resolve]]) is false, throw a TypeError exception. + if (!promiseCapability->GetResolve().IsCallable()) { + THROW_TYPE_ERROR_AND_RETURN(thread, "NewPromiseCapability: resolve is not a callable function!", + factory->NewPromiseCapability()); + } + // 9. If IsCallable(promiseCapability.[[Reject]]) is false, throw a TypeError exception. + if (!promiseCapability->GetReject().IsCallable()) { + THROW_TYPE_ERROR_AND_RETURN(thread, "NewPromiseCapability: reject is not a callable function!", + factory->NewPromiseCapability()); + } + // 10. Set promiseCapability.[[Promise]] to promise. + promiseCapability->SetPromise(thread, promise); + // 11. Return promiseCapability. + return promiseCapability; +} + +bool JSPromise::IsPromise(const JSHandle &value) +{ + // 1. If Type(x) is not Object, return false. + if (!value->IsECMAObject()) { + return false; + } + // 2. If x does not have a [[PromiseState]] internal slot, return false. + if (!value->IsJSPromise()) { + return false; + } + // 3. Return true + return true; +} + +// 27.2.4.7.1 PromiseResolve ( C, x ) +JSTaggedValue JSPromise::PromiseResolve(JSThread *thread, const JSHandle &constructor, + const JSHandle &xValue) +{ + const GlobalEnvConstants *globalConst = thread->GlobalConstants(); + + // 1. Let C be the this value. + ASSERT_PRINT(constructor->IsECMAObject(), "Constructor must is not object"); + + // 2. If IsPromise(x) is true, + if (xValue->IsJSPromise()) { + // a. Let xConstructor be Get(x, "constructor"). + JSHandle ctorKey(globalConst->GetHandledConstructorString()); + JSHandle xConstructor = JSObject::GetProperty(thread, xValue, ctorKey).GetValue(); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + + // b. If SameValue(xConstructor, C) is true, return x. + if (JSTaggedValue::SameValue(constructor.GetTaggedValue(), xConstructor.GetTaggedValue())) { + JSHandle value = JSHandle::Cast(xValue); + return value.GetTaggedValue(); + } + } + + // 3. Let promiseCapability be NewPromiseCapability(C). + JSHandle promiseCapability = JSPromise::NewPromiseCapability(thread, constructor); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + + // 4. Perform ? Call(promiseCapability.[[Resolve]], undefined, « x »). + JSHandle thisArg = globalConst->GetHandledUndefined(); + InternalCallParams *arguments = thread->GetInternalCallParams(); + arguments->MakeArgv(xValue); + JSHandle resolve(thread, promiseCapability->GetResolve()); + + [[maybe_unused]] JSTaggedValue res = JSFunction::Call(thread, resolve, thisArg, 1, arguments->GetArgv()); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + + // 5. Return promiseCapability.[[Promise]]. + return promiseCapability->GetPromise(); +} + +JSTaggedValue JSPromise::RejectPromise(JSThread *thread, const JSHandle &promise, + const JSHandle &reason) +{ + const GlobalEnvConstants *globalConst = thread->GlobalConstants(); + // 1. Assert: the value of promise's [[PromiseState]] internal slot is "pending". + JSHandle handleStatus(thread, promise->GetPromiseState()); + + ASSERT_PRINT(JSTaggedValue::SameValue(handleStatus.GetTaggedValue(), + JSTaggedValue(static_cast(PromiseStatus::PENDING))), + "RejectPromise: state must be pending"); + // 2. Let reactions be the value of promise's [[PromiseRejectReactions]] internal slot. + JSHandle reactions(thread, TaggedQueue::Cast(promise->GetPromiseRejectReactions().GetTaggedObject())); + // 3. Set the value of promise's [[PromiseResult]] internal slot to reason. + promise->SetPromiseResult(thread, reason); + // 4. Set the value of promise's [[PromiseFulfillReactions]] internal slot to undefined. + promise->SetPromiseFulfillReactions(thread, globalConst->GetHandledUndefined()); + // 5. Set the value of promise's [[PromiseRejectReactions]] internal slot to undefined. + promise->SetPromiseRejectReactions(thread, globalConst->GetHandledUndefined()); + // 6. Set the value of promise's [[PromiseState]] internal slot to "rejected". + promise->SetPromiseState(thread, JSTaggedValue(static_cast(PromiseStatus::REJECTED))); + // 7. When a promise is rejected without any handlers, it is called with its operation argument set to "reject". + if (!promise->GetPromiseIsHandled().ToBoolean()) { + thread->GetEcmaVM()->PromiseRejectionTracker(promise, reason, PromiseRejectionEvent::REJECT); + } + // 8. Return TriggerPromiseReactions(reactions, reason). + return TriggerPromiseReactions(thread, reactions, reason); +} + +JSTaggedValue JSPromise::TriggerPromiseReactions(JSThread *thread, const JSHandle &reactions, + const JSHandle &argument) +{ + // 1. Repeat for each reaction in reactions, in original insertion order + // a. Perform EnqueueJob("PromiseJobs", PromiseReactionJob, «reaction, argument»). + JSHandle job = thread->GetEcmaVM()->GetMicroJobQueue(); + JSHandle globalEnv = thread->GetEcmaVM()->GetGlobalEnv(); + const GlobalEnvConstants *globalConst = thread->GlobalConstants(); + ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); + JSHandle promiseReactionsJob(globalEnv->GetPromiseReactionJob()); + JSMutableHandle reaction(thread, JSTaggedValue::Undefined()); + while (!reactions->Empty()) { + reaction.Update(reactions->Pop(thread)); + JSHandle arguments = factory->NewTaggedArray(2); // 2 means the length of new array + arguments->Set(thread, 0, reaction); + arguments->Set(thread, 1, argument); + job::MicroJobQueue::EnqueueJob(thread, job, job::QueueType::QUEUE_PROMISE, promiseReactionsJob, arguments); + } + // 2. Return undefined. + return globalConst->GetUndefined(); +} + +JSHandle JSPromise::IfThrowGetThrowValue(JSThread *thread) +{ + if (thread->GetException().IsObjectWrapper()) { + JSHandle wrapperValue(thread, thread->GetException()); + JSHandle throwValue(thread, wrapperValue->GetValue()); + return throwValue; + } + return JSHandle(thread, thread->GetException()); +} +} // namespace panda::ecmascript diff --git a/runtime/js_promise.h b/runtime/js_promise.h new file mode 100644 index 000000000..3a9cb1032 --- /dev/null +++ b/runtime/js_promise.h @@ -0,0 +1,142 @@ +/* + * Copyright (c) 2021-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_PROMISE_H +#define ECMASCRIPT_PROMISE_H + +#include "js_object.h" +#include "plugins/ecmascript/runtime/js_function.h" +#include "plugins/ecmascript/runtime/tagged_queue.h" +#include "plugins/ecmascript/runtime/tagged_queue-inl.h" +#include "plugins/ecmascript/runtime/js_tagged_value.h" +#include "plugins/ecmascript/runtime/accessor_data.h" +#include "plugins/ecmascript/runtime/js_tagged_value-inl.h" + +namespace panda::ecmascript { +enum class PromiseStatus : uint32_t { PENDING = 0, FULFILLED, REJECTED }; +enum class PromiseType : uint32_t { RESOLVE = 0, REJECT }; +enum class PromiseRejectionEvent : uint32_t { REJECT = 0, HANDLE }; + +class PromiseReaction final : public Record { +public: + CAST_CHECK(PromiseReaction, IsPromiseReaction); + + static constexpr size_t PROMISE_CAPABILITY_OFFSET = Record::SIZE; + ACCESSORS(PromiseCapability, PROMISE_CAPABILITY_OFFSET, TYPE_OFFSET); + ACCESSORS(Type, TYPE_OFFSET, HANDLER_OFFSET); + ACCESSORS(Handler, HANDLER_OFFSET, SIZE); + + DECL_DUMP() + + DECL_VISIT_OBJECT(PROMISE_CAPABILITY_OFFSET, SIZE) +}; + +class PromiseCapability final : public Record { +public: + CAST_CHECK(PromiseCapability, IsPromiseCapability); + + static constexpr size_t PROMISE_OFFSET = Record::SIZE; + ACCESSORS(Promise, PROMISE_OFFSET, RESOLVE_OFFSET); + ACCESSORS(Resolve, RESOLVE_OFFSET, REJECT_OFFSET); + ACCESSORS(Reject, REJECT_OFFSET, SIZE); + + DECL_DUMP() + + DECL_VISIT_OBJECT(PROMISE_OFFSET, SIZE) +}; + +class PromiseIteratorRecord final : public Record { +public: + CAST_CHECK(PromiseIteratorRecord, IsPromiseIteratorRecord); + + static constexpr size_t ITERATOR_OFFSET = Record::SIZE; + ACCESSORS(Iterator, ITERATOR_OFFSET, DONE_OFFSET); + ACCESSORS(Done, DONE_OFFSET, SIZE); + + DECL_DUMP() + + DECL_VISIT_OBJECT(ITERATOR_OFFSET, SIZE) +}; + +class PromiseRecord final : public Record { +public: + CAST_CHECK(PromiseRecord, IsPromiseRecord); + + static constexpr size_t VALUE_OFFSET = Record::SIZE; + ACCESSORS(Value, VALUE_OFFSET, SIZE); + DECL_DUMP() + + DECL_VISIT_OBJECT(VALUE_OFFSET, SIZE) +}; + +class ResolvingFunctionsRecord final : public Record { +public: + CAST_CHECK(ResolvingFunctionsRecord, IsResolvingFunctionsRecord); + + static constexpr size_t RESOLVE_FUNCTION_OFFSET = Record::SIZE; + ACCESSORS(ResolveFunction, RESOLVE_FUNCTION_OFFSET, REJECT_FUNCTION_OFFSET); + ACCESSORS(RejectFunction, REJECT_FUNCTION_OFFSET, SIZE); + + DECL_DUMP() + + DECL_VISIT_OBJECT(RESOLVE_FUNCTION_OFFSET, SIZE) +}; + +class JSPromise final : public JSObject { +public: + CAST_CHECK(JSPromise, IsJSPromise); + + // ES6 25.4.1.3 CreateResolvingFunctions ( promise ) + static JSHandle CreateResolvingFunctions(JSThread *thread, + const JSHandle &promise); + + // ES6 25.4.1.4 FulfillPromise ( promise, value) + static JSTaggedValue FulfillPromise(JSThread *thread, const JSHandle &promise, + const JSHandle &value); + + // 25.4.1.5 NewPromiseCapability ( C ) + static JSHandle NewPromiseCapability(JSThread *thread, const JSHandle &obj); + + // ES6 24.4.1.6 IsPromise (x) + static bool IsPromise(const JSHandle &value); + + // ES6 25.4.1.7 RejectPromise (promise, reason) + static JSTaggedValue RejectPromise(JSThread *thread, const JSHandle &promise, + const JSHandle &reason); + + // 25.4.1.8 TriggerPromiseReactions (reactions, argument) + static JSTaggedValue TriggerPromiseReactions(JSThread *thread, const JSHandle &reactions, + const JSHandle &argument); + + static JSHandle IfThrowGetThrowValue(JSThread *thread); + + // 27.2.4.7.1 PromiseResolve ( C, x ) + static JSTaggedValue PromiseResolve(JSThread *thread, const JSHandle &constructor, + const JSHandle &value); + + static constexpr size_t PROMISE_STATE_OFFSET = JSObject::SIZE; + ACCESSORS(PromiseState, PROMISE_STATE_OFFSET, PROMISE_RESULT_OFFSET); + ACCESSORS(PromiseResult, PROMISE_RESULT_OFFSET, PROMISE_FULFILL_REACTIONS_OFFSET); + ACCESSORS(PromiseFulfillReactions, PROMISE_FULFILL_REACTIONS_OFFSET, PROMISE_REJECT_REACTIONS_OFFSET); + ACCESSORS(PromiseRejectReactions, PROMISE_REJECT_REACTIONS_OFFSET, PROMISE_IS_HANDLED_OFFSET); + ACCESSORS(PromiseIsHandled, PROMISE_IS_HANDLED_OFFSET, SIZE); + + DECL_DUMP() + + DECL_VISIT_OBJECT_FOR_JS_OBJECT(JSObject, PROMISE_STATE_OFFSET, SIZE) +}; +} // namespace panda::ecmascript + +#endif // ECMASCRIPT_PROMISE_H diff --git a/runtime/js_proxy.cpp b/runtime/js_proxy.cpp new file mode 100644 index 000000000..f330f909b --- /dev/null +++ b/runtime/js_proxy.cpp @@ -0,0 +1,945 @@ +/* + * Copyright (c) 2021-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 "js_proxy.h" +#include "plugins/ecmascript/runtime/ecma_macros.h" +#include "plugins/ecmascript/runtime/global_env.h" +#include "plugins/ecmascript/runtime/internal_call_params.h" +#include "plugins/ecmascript/runtime/js_array.h" +#include "plugins/ecmascript/runtime/js_handle.h" +#include "plugins/ecmascript/runtime/object_factory.h" + +namespace panda::ecmascript { +static JSHandle ToPropertyKey(JSThread *thread, const JSHandle &key) +{ + if (key->IsNumber()) { + return JSHandle::Cast(base::NumberHelper::NumberToString(thread, key.GetTaggedValue())); + } + + return key; +} + +// ES6 9.5.15 ProxyCreate(target, handler) +JSHandle JSProxy::ProxyCreate(JSThread *thread, const JSHandle &target, + const JSHandle &handler) +{ + // 1. If Type(target) is not Object, throw a TypeError exception. + if (!target->IsECMAObject()) { + THROW_TYPE_ERROR_AND_RETURN(thread, "ProxyCreate: target is not Object", + JSHandle(thread, JSTaggedValue::Exception())); + } + + // 2. If Type(handler) is not Object, throw a TypeError exception. + if (!handler->IsECMAObject()) { + THROW_TYPE_ERROR_AND_RETURN(thread, "ProxyCreate: handler is not Object", + JSHandle(thread, JSTaggedValue::Exception())); + } + // 3. Let P be ! MakeBasicObject(« [[ProxyHandler]], [[ProxyTarget]] »). + // 6. If IsCallable(target) is true, then P.[[Call]] as specified in 9.5.12. + + // 8. Set the [[ProxyTarget]] internal slot of P to target. + // 9. Set the [[ProxyHandler]] internal slot of P to handler. + return thread->GetEcmaVM()->GetFactory()->NewJSProxy(target, handler); +} + +// ES6 9.5.1 [[GetPrototypeOf]] ( ) +JSTaggedValue JSProxy::GetPrototype(JSThread *thread, const JSHandle &proxy) +{ + // 1. Let handler be the value of the [[ProxyHandler]] internal slot of O. + JSHandle handler(thread, proxy->GetHandler()); + // 2. If handler is null, throw a TypeError exception. + if (handler->IsNull()) { + THROW_TYPE_ERROR_AND_RETURN(thread, "JSProxy::GetPrototype: handler is null", JSTaggedValue::Exception()); + } + // 3. Assert: Type(handler) is Object. + ASSERT(handler->IsECMAObject()); + // 4. Let target be the value of the [[ProxyTarget]] internal slot of O. + JSHandle targetHandle(thread, proxy->GetTarget()); + // 5. Let trap be GetMethod(handler, "getPrototypeOf"). + JSHandle name(thread->GlobalConstants()->GetHandledGetPrototypeOfString()); + JSHandle trap = JSObject::GetMethod(thread, handler, name); + // 6. ReturnIfAbrupt(trap). + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + + // 7. If trap is undefined, then Return target.[[GetPrototypeOf]](). + if (trap->IsUndefined()) { + return JSHandle(targetHandle)->GetPrototype(thread); + } + // 8. Let handlerProto be Call(trap, handler, «target»). + InternalCallParams *arguments = thread->GetInternalCallParams(); + arguments->MakeArgv(targetHandle); + JSTaggedValue handlerProto = JSFunction::Call(thread, trap, handler, 1, arguments->GetArgv()); + + // 9. ReturnIfAbrupt(handlerProto). + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + // 10. If Type(handlerProto) is neither Object nor Null, throw a TypeError exception. + if (!handlerProto.IsECMAObject() && !handlerProto.IsNull()) { + THROW_TYPE_ERROR_AND_RETURN(thread, "JSProxy::GetPrototype: Type(handlerProto) is neither Object nor Null", + JSTaggedValue::Exception()); + } + // 11. Let extensibleTarget be IsExtensible(target). + // 12. ReturnIfAbrupt(extensibleTarget). + // 13. If extensibleTarget is true, return handlerProto. + if (targetHandle->IsExtensible(thread)) { + return handlerProto; + } + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + + // 14. Let targetProto be target.[[GetPrototypeOf]](). + JSTaggedValue targetProto = JSHandle(targetHandle)->GetPrototype(thread); + // 15. ReturnIfAbrupt(targetProto). + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + // 16. If SameValue(handlerProto, targetProto) is false, throw a TypeError exception. + if (!JSTaggedValue::SameValue(handlerProto, targetProto)) { + THROW_TYPE_ERROR_AND_RETURN(thread, "JSProxy::GetPrototype: SameValue(handlerProto, targetProto) is false", + JSTaggedValue::Exception()); + } + // 17. Return handlerProto. + return handlerProto; +} + +// ES6 9.5.2 [[SetPrototypeOf]] (V) +bool JSProxy::SetPrototype(JSThread *thread, const JSHandle &proxy, const JSHandle &proto) +{ + // 1. Assert: Either Type(V) is Object or Type(V) is Null. + ASSERT(proto->IsECMAObject() || proto->IsNull()); + // 2. Let handler be the value of the [[ProxyHandler]] internal slot of O. + JSTaggedValue handler = proxy->GetHandler(); + // 3. If handler is null, throw a TypeError exception. + if (handler.IsNull()) { + THROW_TYPE_ERROR_AND_RETURN(thread, "JSProxy::SetPrototype: handler is null", false); + } + // 4. Assert: Type(handler) is Object. + ASSERT(handler.IsECMAObject()); + // 5. Let target be the value of the [[ProxyTarget]] internal slot of O. + JSHandle targetHandle(thread, proxy->GetTarget()); + // 6. Let trap be GetMethod(handler, "setPrototypeOf"). + JSHandle name = thread->GlobalConstants()->GetHandledSetPrototypeOfString(); + JSHandle trap(JSObject::GetMethod(thread, JSHandle(thread, handler), name)); + // 7. ReturnIfAbrupt(trap). + RETURN_VALUE_IF_ABRUPT_COMPLETION(thread, false); + + // 7. If trap is undefined, then Return target.[[SetPrototypeOf]](V). + if (trap->IsUndefined()) { + return JSTaggedValue::SetPrototype(thread, targetHandle, proto); + }; + JSHandle handlerTag(thread, proxy->GetHandler()); + InternalCallParams *arguments = thread->GetInternalCallParams(); + arguments->MakeArgv(targetHandle, proto); + JSTaggedValue trapResult = + JSFunction::Call(thread, trap, handlerTag, 2, arguments->GetArgv()); // 2: target and proto + + // 9. Let booleanTrapResult be ToBoolean(Call(trap, handler, «target, V»)). + // If booleanTrapResult is false, return false + bool booleanTrapResult = trapResult.ToBoolean(); + if (!booleanTrapResult) { + return false; + } + // 10. ReturnIfAbrupt(booleanTrapResult). + // 11. Let extensibleTarget be IsExtensible(target). + // 12. ReturnIfAbrupt(extensibleTarget). + // 13. If extensibleTarget is true, return booleanTrapResult + if (targetHandle->IsExtensible(thread)) { + return booleanTrapResult; + } + RETURN_VALUE_IF_ABRUPT_COMPLETION(thread, false); + + // 14. Let targetProto be target.[[GetPrototypeOf]](). + JSTaggedValue targetProto = JSHandle(targetHandle)->GetPrototype(thread); + // 15. ReturnIfAbrupt(targetProto). + RETURN_VALUE_IF_ABRUPT_COMPLETION(thread, false); + + // 16. If booleanTrapResult is true and SameValue(V, targetProto) is false, throw a TypeError exception. + if (booleanTrapResult && !JSTaggedValue::SameValue(proto.GetTaggedValue(), targetProto)) { + THROW_TYPE_ERROR_AND_RETURN(thread, "JSProxy::SetPrototype: TypeError of targetProto and Result", false); + } + // 17. Return handlerProto. + return booleanTrapResult; +} + +// ES6 9.5.3 [[IsExtensible]] ( ) +bool JSProxy::IsExtensible(JSThread *thread, const JSHandle &proxy) +{ + // 1. Let handler be the value of the [[ProxyHandler]] internal slot of O. + JSTaggedValue handler = proxy->GetHandler(); + // 2. If handler is null, throw a TypeError exception. + if (handler.IsNull()) { + THROW_TYPE_ERROR_AND_RETURN(thread, "JSProxy::IsExtensible: handler is null", false); + } + // 3. Assert: Type(handler) is Object. + ASSERT(handler.IsECMAObject()); + // 4. Let target be the value of the [[ProxyTarget]] internal slot of O. + JSHandle targetHandle(thread, proxy->GetTarget()); + // 5. Let trap be GetMethod(handler, "isExtensible"). + JSHandle name = thread->GlobalConstants()->GetHandledIsExtensibleString(); + JSHandle trap(JSObject::GetMethod(thread, JSHandle(thread, handler), name)); + // 6. ReturnIfAbrupt(trap). + RETURN_VALUE_IF_ABRUPT_COMPLETION(thread, false); + + // 7. If trap is undefined, then Return target.[[IsExtensible]](). + if (trap->IsUndefined()) { + return targetHandle->IsExtensible(thread); + } + // 8. Let booleanTrapResult be ToBoolean(Call(trap, handler, «target»)). + JSHandle newTgt(thread, JSTaggedValue::Undefined()); + JSHandle handlerTag(thread, proxy->GetHandler()); + InternalCallParams *arguments = thread->GetInternalCallParams(); + arguments->MakeArgv(targetHandle); + JSTaggedValue trapResult = JSFunction::Call(thread, trap, handlerTag, 1, arguments->GetArgv()); + + bool booleanTrapResult = trapResult.ToBoolean(); + // 9. ReturnIfAbrupt(booleanTrapResult). + RETURN_VALUE_IF_ABRUPT_COMPLETION(thread, false); + + // 10. Let targetResult be target.[[IsExtensible]](). + // 11. ReturnIfAbrupt(targetResult). + // 12. If SameValue(booleanTrapResult, targetResult) is false, throw a TypeError exception. + // 13. Return booleanTrapResult. + if (targetHandle->IsExtensible(thread) != booleanTrapResult) { + THROW_TYPE_ERROR_AND_RETURN(thread, "JSProxy::IsExtensible: TypeError of targetResult", false); + } + + return booleanTrapResult; +} + +// ES6 9.5.4 [[PreventExtensions]] ( ) +bool JSProxy::PreventExtensions(JSThread *thread, const JSHandle &proxy) +{ + // 1. Let handler be the value of the [[ProxyHandler]] internal slot of O. + // 2. If handler is null, throw a TypeError exception. + // 3. Assert: Type(handler) is Object. + // 4. Let target be the value of the [[ProxyTarget]] internal slot of O. + // 5. Let trap be GetMethod(handler, "preventExtensions"). + // 6. ReturnIfAbrupt(trap). + // 7. If trap is undefined, then + // a. Return target.[[PreventExtensions]](). + // 8. Let booleanTrapResult be ToBoolean(Call(trap, handler, «target»)). + // 9. ReturnIfAbrupt(booleanTrapResult). + JSTaggedValue handler = proxy->GetHandler(); + if (handler.IsNull()) { + THROW_TYPE_ERROR_AND_RETURN(thread, "JSProxy::PreventExtensions: handler is null", false); + } + ASSERT(handler.IsECMAObject()); + JSHandle targetHandle(thread, proxy->GetTarget()); + JSHandle name = thread->GlobalConstants()->GetHandledPreventExtensionsString(); + JSHandle trap(JSObject::GetMethod(thread, JSHandle(thread, handler), name)); + // 6. ReturnIfAbrupt(trap). + RETURN_VALUE_IF_ABRUPT_COMPLETION(thread, false); + + if (trap->IsUndefined()) { + return JSTaggedValue::PreventExtensions(thread, targetHandle); + } + JSHandle handlerTag(thread, proxy->GetHandler()); + InternalCallParams *arguments = thread->GetInternalCallParams(); + arguments->MakeArgv(targetHandle); + JSTaggedValue trapResult = JSFunction::Call(thread, trap, handlerTag, 1, arguments->GetArgv()); + + bool booleanTrapResult = trapResult.ToBoolean(); + // 9. ReturnIfAbrupt(booleanTrapResult). + RETURN_VALUE_IF_ABRUPT_COMPLETION(thread, false); + // 10. If booleanTrapResult is true, then + // a. Let targetIsExtensible be target.[[IsExtensible]](). + // b. ReturnIfAbrupt(targetIsExtensible). + // c. If targetIsExtensible is true, throw a TypeError exception. + if (booleanTrapResult && targetHandle->IsExtensible(thread)) { + THROW_TYPE_ERROR_AND_RETURN(thread, "JSProxy::PreventExtensions: targetIsExtensible is true", false); + } + // 11. Return booleanTrapResult. + return booleanTrapResult; +} + +// ES6 9.5.5 [[GetOwnProperty]] (P) +bool JSProxy::GetOwnProperty(JSThread *thread, const JSHandle &proxy, const JSHandle &key, + PropertyDescriptor &desc) +{ + // 1. Assert: IsPropertyKey(P) is true. + ASSERT(JSTaggedValue::IsPropertyKey(key)); + const auto propKey = ToPropertyKey(thread, key); + // 2. Let handler be the value of the [[ProxyHandler]] internal slot of O. + // 3. If handler is null, throw a TypeError exception. + // 4. Assert: Type(handler) is Object. + // 5. Let target be the value of the [[ProxyTarget]] internal slot of O. + // 6. Let trap be GetMethod(handler, "getOwnPropertyDescriptor"). + // 7. ReturnIfAbrupt(trap). + // 8. If trap is undefined, then + // a. Return target.[[GetOwnProperty]](P). + // 9. Let trapResultObj be Call(trap, handler, «target, P»). + // 10. ReturnIfAbrupt(trapResultObj). + // 11. If Type(trapResultObj) is neither Object nor Undefined, throw a TypeError exception + JSTaggedValue handler = proxy->GetHandler(); + if (handler.IsNull()) { + THROW_TYPE_ERROR_AND_RETURN(thread, "JSProxy::GetOwnProperty: handler is null", false); + } + ASSERT(handler.IsECMAObject()); + JSHandle targetHandle(thread, proxy->GetTarget()); + JSHandle name = thread->GlobalConstants()->GetHandledGetOwnPropertyDescriptorString(); + JSHandle trap(JSObject::GetMethod(thread, JSHandle(thread, handler), name)); + // 7. ReturnIfAbrupt(trap). + RETURN_VALUE_IF_ABRUPT_COMPLETION(thread, false); + + if (trap->IsUndefined()) { + return JSTaggedValue::GetOwnProperty(thread, targetHandle, propKey, desc); + } + JSHandle handlerTag(thread, proxy->GetHandler()); + InternalCallParams *arguments = thread->GetInternalCallParams(); + arguments->MakeArgv(targetHandle, propKey); + JSTaggedValue trapResultObj = + JSFunction::Call(thread, trap, handlerTag, 2, arguments->GetArgv()); // 2: target and key + + JSHandle resultHandle(thread, trapResultObj); + + // 11. If Type(trapResultObj) is neither Object nor Undefined, throw a TypeError exception. + if (!trapResultObj.IsECMAObject() && !trapResultObj.IsUndefined()) { + THROW_TYPE_ERROR_AND_RETURN(thread, "JSProxy::GetOwnProperty: TypeError of trapResultObj", false); + } + // 12. Let targetDesc be target.[[GetOwnProperty]](P). + PropertyDescriptor targetDesc(thread); + bool found = JSTaggedValue::GetOwnProperty(thread, targetHandle, propKey, targetDesc); + // 13. ReturnIfAbrupt(targetDesc). + RETURN_VALUE_IF_ABRUPT_COMPLETION(thread, false); + + // 14. If trapResultObj is undefined, then + if (resultHandle->IsUndefined()) { + // a. If targetDesc is undefined, return undefined. + if (!found) { + return false; + } + // b. If targetDesc.[[Configurable]] is false, throw a TypeError exception. + if (!targetDesc.IsConfigurable()) { + THROW_TYPE_ERROR_AND_RETURN(thread, "JSProxy::GetOwnProperty: targetDesc.[[Configurable]] is false", false); + } + // c. Let extensibleTarget be IsExtensible(target). + // d. ReturnIfAbrupt(extensibleTarget). + // e. Assert: Type(extensibleTarget) is Boolean. + // f. If extensibleTarget is false, throw a TypeError exception. + if (!targetHandle->IsExtensible(thread)) { + THROW_TYPE_ERROR_AND_RETURN(thread, "JSProxy::GetOwnProperty: extensibleTarget is false", false); + } + // g. Return undefined. + return false; + } + // 15. Let extensibleTarget be IsExtensible(target). + // 16. ReturnIfAbrupt(extensibleTarget). + // 17. Let resultDesc be ToPropertyDescriptor(trapResultObj). + PropertyDescriptor &resultDesc = desc; + JSObject::ToPropertyDescriptor(thread, resultHandle, resultDesc); + // 18. ReturnIfAbrupt(resultDesc) + RETURN_VALUE_IF_ABRUPT_COMPLETION(thread, false); + // 19. Call CompletePropertyDescriptor(resultDesc). + PropertyDescriptor::CompletePropertyDescriptor(thread, resultDesc); + // 20. Let valid be IsCompatiblePropertyDescriptor (extensibleTarget, resultDesc, targetDesc). + bool valid = JSObject::IsCompatiblePropertyDescriptor(targetHandle->IsExtensible(thread), resultDesc, targetDesc); + if (!valid) { + THROW_TYPE_ERROR_AND_RETURN(thread, "JSProxy::GetOwnProperty: TypeError of valid", false); + } + // 22. If resultDesc.[[Configurable]] is false, then + if (!resultDesc.IsConfigurable()) { + // a. If targetDesc is undefined or targetDesc.[[Configurable]] is true, then + if (!found || targetDesc.IsConfigurable()) { + THROW_TYPE_ERROR_AND_RETURN(thread, "JSProxy::GetOwnProperty: TypeError of targetDesc configurable", false); + } + // b. If resultDesc has a [[Writable]] field and resultDesc.[[Writable]] is false, then + // If targetDesc.[[Writable]] is true, throw a TypeError exception. + if (resultDesc.HasWritable() && !resultDesc.IsWritable() && targetDesc.IsWritable()) { + THROW_TYPE_ERROR_AND_RETURN(thread, "JSProxy::GetOwnProperty: TypeError of targetDesc writable", false); + } + // b. If resultDesc has a [[Writable]] field and resultDesc.[[Writable]] is false, then + // If targetDesc.[[Writable]] is true, throw a TypeError exception. + if (resultDesc.HasWritable() && !resultDesc.IsWritable() && targetDesc.IsWritable()) { + THROW_TYPE_ERROR_AND_RETURN(thread, "", false); + } + } + // 23. Return resultDesc. + return true; +} + +// ES6 9.5.6 [[DefineOwnProperty]] (P, Desc) +bool JSProxy::DefineOwnProperty(JSThread *thread, const JSHandle &proxy, const JSHandle &key, + const PropertyDescriptor &desc) +{ + // step 1 ~ 10 are almost same as GetOwnProperty + ASSERT(JSTaggedValue::IsPropertyKey(key)); + const auto propKey = ToPropertyKey(thread, key); + JSTaggedValue handler = proxy->GetHandler(); + if (handler.IsNull()) { + THROW_TYPE_ERROR_AND_RETURN(thread, "JSProxy::DefineOwnProperty: handler is Null", false); + } + ASSERT(handler.IsECMAObject()); + JSHandle targetHandle(thread, proxy->GetTarget()); + JSHandle name = thread->GlobalConstants()->GetHandledDefinePropertyString(); + JSHandle trap(JSObject::GetMethod(thread, JSHandle(thread, handler), name)); + // 7. ReturnIfAbrupt(trap). + RETURN_VALUE_IF_ABRUPT_COMPLETION(thread, false); + if (trap->IsUndefined()) { + return JSTaggedValue::DefineOwnProperty(thread, targetHandle, propKey, desc); + } + + // 9. Let descObj be FromPropertyDescriptor(Desc). + JSHandle descObj = JSObject::FromPropertyDescriptor(thread, desc); + JSHandle handlerTag(thread, proxy->GetHandler()); + InternalCallParams *arguments = thread->GetInternalCallParams(); + arguments->MakeArgv(targetHandle, propKey, descObj); + JSTaggedValue trapResult = + JSFunction::Call(thread, trap, handlerTag, 3, arguments->GetArgv()); // 3: target, key and desc + + bool booleanTrapResult = trapResult.ToBoolean(); + // 11. ReturnIfAbrupt(booleanTrapResult). + RETURN_VALUE_IF_ABRUPT_COMPLETION(thread, false); + if (!booleanTrapResult) { + return false; + } + // 13. Let targetDesc be target.[[GetOwnProperty]](P). + PropertyDescriptor targetDesc(thread); + bool found = JSTaggedValue::GetOwnProperty(thread, targetHandle, propKey, targetDesc); + // 14. ReturnIfAbrupt(targetDesc). + RETURN_VALUE_IF_ABRUPT_COMPLETION(thread, false); + + // 15. Let extensibleTarget be IsExtensible(target). + // 16. ReturnIfAbrupt(extensibleTarget). + // 17. If Desc has a [[Configurable]] field and if Desc.[[Configurable]] is false, then Let settingConfigFalse be + // true. + // 18. Else let settingConfigFalse be false. + bool settingConfigFalse = false; + if (desc.HasConfigurable() && !desc.IsConfigurable()) { + settingConfigFalse = true; + } + // 19. If targetDesc is undefined, then + if (!found) { + // a. If extensibleTarget is false, throw a TypeError exception. + if (!targetHandle->IsExtensible(thread)) { + THROW_TYPE_ERROR_AND_RETURN(thread, "JSProxy::DefineOwnProperty: extensibleTarget is false", false); + } + // b. If settingConfigFalse is true, throw a TypeError exception. + if (settingConfigFalse) { + THROW_TYPE_ERROR_AND_RETURN(thread, "JSProxy::DefineOwnProperty: settingConfigFalse is true", false); + } + } else { + // a. If IsCompatiblePropertyDescriptor(extensibleTarget, Desc , targetDesc) is false, throw a TypeError + // exception. + if (!JSObject::IsCompatiblePropertyDescriptor(targetHandle->IsExtensible(thread), desc, targetDesc)) { + THROW_TYPE_ERROR_AND_RETURN(thread, "JSProxy::DefineOwnProperty: CompatiblePropertyDescriptor err", false); + } + // b. If settingConfigFalse is true and targetDesc.[[Configurable]] is true, throw a TypeError exception. + if (settingConfigFalse && targetDesc.IsConfigurable()) { + THROW_TYPE_ERROR_AND_RETURN(thread, "JSProxy::DefineOwnProperty: TypeError of settingConfigFalse", false); + } + // c. If IsDataDescriptor(targetDesc) is true, targetDesc.[[Configurable]] is false, and targetDesc.[[Writable]] + // is true, then If Desc has a [[Writable]] field and Desc.[[Writable]] is false, throw a TypeError exception. + if (targetDesc.IsDataDescriptor() && !targetDesc.IsConfigurable() && targetDesc.IsWritable() && + desc.HasWritable() && !desc.IsWritable()) { + THROW_TYPE_ERROR_AND_RETURN(thread, "JSProxy::DefineOwnProperty: TypeError of DataDescriptor", false); + } + // c. If IsDataDescriptor(targetDesc) is true, targetDesc.[[Configurable]] is false, and targetDesc.[[Writable]] + // is true, then If Desc has a [[Writable]] field and Desc.[[Writable]] is false, throw a TypeError exception. + if (targetDesc.IsDataDescriptor() && !targetDesc.IsConfigurable() && targetDesc.IsWritable() && + desc.HasWritable() && !desc.IsWritable()) { + THROW_TYPE_ERROR_AND_RETURN(thread, "", false); + } + } + // 21. Return true. + return true; +} + +// ES6 9.5.7 [[HasProperty]] (P) +bool JSProxy::HasProperty(JSThread *thread, const JSHandle &proxy, const JSHandle &key) +{ + // step 1 ~ 10 are almost same as GetOwnProperty + ASSERT(JSTaggedValue::IsPropertyKey(key)); + const auto propKey = ToPropertyKey(thread, key); + JSTaggedValue handler = proxy->GetHandler(); + if (handler.IsNull()) { + THROW_TYPE_ERROR_AND_RETURN(thread, "JSProxy::HasProperty: handler is Null", false); + } + ASSERT(handler.IsECMAObject()); + JSHandle targetHandle(thread, proxy->GetTarget()); + JSHandle name = thread->GlobalConstants()->GetHandledHasString(); + JSHandle trap(JSObject::GetMethod(thread, JSHandle(thread, handler), name)); + // 7. ReturnIfAbrupt(trap). + RETURN_VALUE_IF_ABRUPT_COMPLETION(thread, false); + if (trap->IsUndefined()) { + return JSTaggedValue::HasProperty(thread, targetHandle, propKey); + } + + // 9. Let booleanTrapResult be ToBoolean(Call(trap, handler, «target, P»)). + JSHandle handlerTag(thread, proxy->GetHandler()); + + InternalCallParams *arguments = thread->GetInternalCallParams(); + arguments->MakeArgv(targetHandle, propKey); + JSTaggedValue trapResult = + JSFunction::Call(thread, trap, handlerTag, 2, arguments->GetArgv()); // 2: target and key + + bool booleanTrapResult = trapResult.ToBoolean(); + // 10. ReturnIfAbrupt(booleanTrapResult). + RETURN_VALUE_IF_ABRUPT_COMPLETION(thread, false); + + // 11. If booleanTrapResult is false, then + if (!booleanTrapResult) { + // a. Let targetDesc be target.[[GetOwnProperty]](P). + PropertyDescriptor targetDesc(thread); + bool found = JSTaggedValue::GetOwnProperty(thread, targetHandle, propKey, targetDesc); + // b. ReturnIfAbrupt(targetDesc). + RETURN_VALUE_IF_ABRUPT_COMPLETION(thread, false); + // c. If targetDesc is not undefined, then + if (found) { + // i. If targetDesc.[[Configurable]] is false, throw a TypeError exception. + if (!targetDesc.IsConfigurable()) { + THROW_TYPE_ERROR_AND_RETURN(thread, "JSProxy::HasProperty: TypeError of targetDesc", false); + } + // ii. Let extensibleTarget be IsExtensible(target). + // iii. ReturnIfAbrupt(extensibleTarget). + // iv. If extensibleTarget is false, throw a TypeError exception. + if (!targetHandle->IsExtensible(thread)) { + THROW_TYPE_ERROR_AND_RETURN(thread, "JSProxy::HasProperty: extensibleTarget is false", false); + } + } + } + return booleanTrapResult; +} + +// ES6 9.5.8 [[Get]] (P, Receiver) +OperationResult JSProxy::GetProperty(JSThread *thread, const JSHandle &proxy, + const JSHandle &key, const JSHandle &receiver) +{ + // step 1 ~ 10 are almost same as GetOwnProperty + ASSERT(JSTaggedValue::IsPropertyKey(key)); + const auto propKey = ToPropertyKey(thread, key); + JSTaggedValue handler = proxy->GetHandler(); + JSHandle exceptionHandle(thread, JSTaggedValue::Exception()); + if (handler.IsNull()) { + THROW_TYPE_ERROR_AND_RETURN(thread, "JSProxy::GetProperty: handler is Null", + OperationResult(thread, exceptionHandle.GetTaggedValue(), PropertyMetaData(false))); + } + ASSERT(handler.IsECMAObject()); + JSHandle targetHandle(thread, proxy->GetTarget()); + JSHandle name = thread->GlobalConstants()->GetHandledGetString(); + JSHandle trap(JSObject::GetMethod(thread, JSHandle(thread, handler), name)); + // 7. ReturnIfAbrupt(trap). + RETURN_VALUE_IF_ABRUPT_COMPLETION( + thread, OperationResult(thread, exceptionHandle.GetTaggedValue(), PropertyMetaData(false))); + + if (trap->IsUndefined()) { + return JSTaggedValue::GetProperty(thread, targetHandle, propKey, receiver); + } + // 9. Let trapResult be Call(trap, handler, «target, P, Receiver»). + JSHandle handlerTag(thread, proxy->GetHandler()); + InternalCallParams *arguments = thread->GetInternalCallParams(); + arguments->MakeArgv(targetHandle, propKey, receiver); + JSTaggedValue trapResult = + JSFunction::Call(thread, trap, handlerTag, 3, arguments->GetArgv()); // 3: «target, P, Receiver» + JSHandle resultHandle(thread, trapResult); + + // 10. ReturnIfAbrupt(trapResult). + RETURN_VALUE_IF_ABRUPT_COMPLETION( + thread, OperationResult(thread, exceptionHandle.GetTaggedValue(), PropertyMetaData(false))); + + // 11. Let targetDesc be target.[[GetOwnProperty]](P). + PropertyDescriptor targetDesc(thread); + bool found = JSTaggedValue::GetOwnProperty(thread, targetHandle, propKey, targetDesc); + // 12. ReturnIfAbrupt(targetDesc). + RETURN_VALUE_IF_ABRUPT_COMPLETION( + thread, OperationResult(thread, exceptionHandle.GetTaggedValue(), PropertyMetaData(false))); + + // 13. If targetDesc is not undefined, then + if (found) { + // a. If IsDataDescriptor(targetDesc) and targetDesc.[[Configurable]] is false and targetDesc.[[Writable]] is + // false, then + if (targetDesc.IsDataDescriptor() && !targetDesc.IsConfigurable() && !targetDesc.IsWritable()) { + // i. If SameValue(trapResult, targetDesc.[[Value]]) is false, throw a TypeError exception. + if (!JSTaggedValue::SameValue(resultHandle.GetTaggedValue(), targetDesc.GetValue().GetTaggedValue())) { + THROW_TYPE_ERROR_AND_RETURN( + thread, "JSProxy::GetProperty: TypeError of trapResult", + OperationResult(thread, exceptionHandle.GetTaggedValue(), PropertyMetaData(false))); + } + } + // b. If IsAccessorDescriptor(targetDesc) and targetDesc.[[Configurable]] is false and targetDesc.[[Get]] is + // undefined, then + if (targetDesc.IsAccessorDescriptor() && !targetDesc.IsConfigurable() && + targetDesc.GetGetter()->IsUndefined()) { + // i. If trapResult is not undefined, throw a TypeError exception. + if (!resultHandle.GetTaggedValue().IsUndefined()) { + THROW_TYPE_ERROR_AND_RETURN( + thread, "JSProxy::GetProperty: trapResult is not undefined", + OperationResult(thread, exceptionHandle.GetTaggedValue(), PropertyMetaData(false))); + } + } + } + // 14. Return trapResult. + return OperationResult(thread, resultHandle.GetTaggedValue(), PropertyMetaData(true)); +} + +// ES6 9.5.9 [[Set]] ( P, V, Receiver) +bool JSProxy::SetProperty(JSThread *thread, const JSHandle &proxy, const JSHandle &key, + const JSHandle &value, const JSHandle &receiver, bool mayThrow) +{ + // step 1 ~ 10 are almost same as GetOwnProperty + ASSERT(JSTaggedValue::IsPropertyKey(key)); + const auto propKey = ToPropertyKey(thread, key); + JSTaggedValue handler = proxy->GetHandler(); + if (handler.IsNull()) { + THROW_TYPE_ERROR_AND_RETURN(thread, "JSProxy::SetProperty: handler is Null", false); + } + ASSERT(handler.IsECMAObject()); + JSHandle targetHandle(thread, proxy->GetTarget()); + JSHandle name = thread->GlobalConstants()->GetHandledSetString(); + JSHandle trap(JSObject::GetMethod(thread, JSHandle(thread, handler), name)); + // 7. ReturnIfAbrupt(trap). + RETURN_VALUE_IF_ABRUPT_COMPLETION(thread, false); + if (trap->IsUndefined()) { + return JSTaggedValue::SetProperty(thread, targetHandle, propKey, value, receiver, mayThrow); + } + + // 9. Let booleanTrapResult be ToBoolean(Call(trap, handler, «target, P, V, Receiver»)) + JSHandle handlerTag(thread, proxy->GetHandler()); + InternalCallParams *arguments = thread->GetInternalCallParams(); + arguments->MakeArgv(targetHandle, propKey, value, receiver); + JSTaggedValue trapResult = + JSFunction::Call(thread, trap, handlerTag, 4, arguments->GetArgv()); // 4: «target, P, V, Receiver» + + bool booleanTrapResult = trapResult.ToBoolean(); + // 11. ReturnIfAbrupt(booleanTrapResult). + RETURN_VALUE_IF_ABRUPT_COMPLETION(thread, false); + if (!booleanTrapResult) { + return false; + } + // 13. Let targetDesc be target.[[GetOwnProperty]](P). + PropertyDescriptor targetDesc(thread); + bool found = JSTaggedValue::GetOwnProperty(thread, targetHandle, propKey, targetDesc); + // 14. If targetDesc is not undefined, then + if (found) { + // a. If IsDataDescriptor(targetDesc) and targetDesc.[[Configurable]] is false and targetDesc.[[Writable]] is + // false, then + if (targetDesc.IsDataDescriptor() && !targetDesc.IsConfigurable() && !targetDesc.IsWritable()) { + // i. If SameValue(trapResult, targetDesc.[[Value]]) is false, throw a TypeError exception. + if (!JSTaggedValue::SameValue(value, targetDesc.GetValue())) { + THROW_TYPE_ERROR_AND_RETURN(thread, "JSProxy::SetProperty: TypeError of trapResult", false); + } + } + // b. If IsAccessorDescriptor(targetDesc) and targetDesc.[[Configurable]] is false, then + // i. If targetDesc.[[Set]] is undefined, throw a TypeError exception. + if (targetDesc.IsAccessorDescriptor() && !targetDesc.IsConfigurable() && + targetDesc.GetSetter()->IsUndefined()) { + THROW_TYPE_ERROR_AND_RETURN(thread, "JSProxy::SetProperty: TypeError of AccessorDescriptor", false); + } + } + return true; +} + +// ES6 9.5.10 [[Delete]] (P) +bool JSProxy::DeleteProperty(JSThread *thread, const JSHandle &proxy, const JSHandle &key) +{ + // step 1 ~ 13 are almost same as GetOwnProperty + ASSERT(JSTaggedValue::IsPropertyKey(key)); + JSTaggedValue handler = proxy->GetHandler(); + if (handler.IsNull()) { + THROW_TYPE_ERROR_AND_RETURN(thread, "JSProxy::DeleteProperty: handler is Null", false); + } + ASSERT(handler.IsECMAObject()); + JSHandle targetHandle(thread, proxy->GetTarget()); + JSHandle name = thread->GlobalConstants()->GetHandledDeletePropertyString(); + JSHandle trap(JSObject::GetMethod(thread, JSHandle(thread, handler), name)); + // 7. ReturnIfAbrupt(trap). + RETURN_VALUE_IF_ABRUPT_COMPLETION(thread, false); + if (trap->IsUndefined()) { + return JSTaggedValue::DeleteProperty(thread, targetHandle, key); + } + + // 9. Let booleanTrapResult be ToBoolean(Call(trap, handler, «target, P»)). + JSHandle newTgt(thread, JSTaggedValue::Undefined()); + JSHandle handlerTag(thread, proxy->GetHandler()); + InternalCallParams *arguments = thread->GetInternalCallParams(); + arguments->MakeArgv(targetHandle, key); + JSTaggedValue trapResult = + JSFunction::Call(thread, trap, handlerTag, 2, arguments->GetArgv()); // 2: target and key + + bool booleanTrapResult = trapResult.ToBoolean(); + // 11. ReturnIfAbrupt(booleanTrapResult). + RETURN_VALUE_IF_ABRUPT_COMPLETION(thread, false); + if (!booleanTrapResult) { + return false; + } + // 13. Let targetDesc be target.[[GetOwnProperty]](P). + PropertyDescriptor targetDesc(thread); + bool found = JSTaggedValue::GetOwnProperty(thread, targetHandle, key, targetDesc); + // 14. If targetDesc is undefined, return true. + if (!found) { + return true; + } + // 15. If targetDesc.[[Configurable]] is false, throw a TypeError exception. + if (!targetDesc.IsConfigurable()) { + THROW_TYPE_ERROR_AND_RETURN(thread, "JSProxy::DeleteProperty: targetDesc is not Configurable", false); + } + if (!targetHandle->IsExtensible(thread)) { + THROW_TYPE_ERROR_AND_RETURN(thread, "JSProxy::DeleteProperty: targetHandle is not Extensible", false); + } + if (!targetHandle->IsExtensible(thread)) { + THROW_TYPE_ERROR_AND_RETURN(thread, "", false); + } + // 16. Return true. + return true; +} + +// ES6 9.5.12 [[OwnPropertyKeys]] () +JSHandle JSProxy::OwnPropertyKeys(JSThread *thread, const JSHandle &proxy) +{ + // step 1 ~ 4 get ProxyHandler and ProxyTarget + JSTaggedValue handler = proxy->GetHandler(); + if (handler.IsNull()) { + THROW_TYPE_ERROR_AND_RETURN(thread, "OwnPropertyKeys: handler is null", + JSHandle(thread, JSTaggedValue::Exception())); + } + + ASSERT(handler.IsECMAObject()); + JSHandle targetHandle(thread, proxy->GetTarget()); + + // 5.Let trap be GetMethod(handler, "ownKeys"). + JSHandle key = thread->GlobalConstants()->GetHandledOwnKeysString(); + JSHandle handlerHandle(thread, handler); + JSHandle trap(JSObject::GetMethod(thread, handlerHandle, key)); + + // 6.ReturnIfAbrupt(trap). + RETURN_HANDLE_IF_ABRUPT_COMPLETION(TaggedArray, thread); + + // 7.If trap is undefined, then + // a.Return target.[[OwnPropertyKeys]](). + if (trap->IsUndefined()) { + return JSTaggedValue::GetOwnPropertyKeys(thread, targetHandle); + } + + // 8.Let trapResultArray be Call(trap, handler, «target»). + JSHandle tagFunc(targetHandle); + InternalCallParams *arguments = thread->GetInternalCallParams(); + arguments->MakeArgv(targetHandle); + JSTaggedValue res = JSFunction::Call(thread, trap, handlerHandle, 1, arguments->GetArgv()); + JSHandle trap_res_arr(thread, res); + + // 9.Let trapResult be CreateListFromArrayLike(trapResultArray, «String, Symbol»). + // 10.ReturnIfAbrupt(trapResult) + // If trapResult contains any duplicate entries, throw a TypeError exception. + JSHandle trapRes( + JSObject::CreateListFromArrayLike(thread, trap_res_arr)); + RETURN_HANDLE_IF_ABRUPT_COMPLETION(TaggedArray, thread); + + if (trapRes->HasDuplicateEntry()) { + THROW_TYPE_ERROR_AND_RETURN(thread, "OwnPropertyKeys: contains duplicate entries", + JSHandle(thread, JSTaggedValue::Exception())); + } + + // 11.Let extensibleTarget be IsExtensible(target). + bool extensibleTarget = targetHandle->IsExtensible(thread); + + // 12.ReturnIfAbrupt(extensibleTarget). + RETURN_HANDLE_IF_ABRUPT_COMPLETION(TaggedArray, thread); + + // 13.Let targetKeys be target.[[OwnPropertyKeys]](). + JSHandle targetKeys = JSTaggedValue::GetOwnPropertyKeys(thread, targetHandle); + + // 14.ReturnIfAbrupt(targetKeys). + RETURN_HANDLE_IF_ABRUPT_COMPLETION(TaggedArray, thread); + + // 15.Assert: targetKeys is a List containing only String and Symbol values. + // 16.Let targetConfigurableKeys be an empty List. + // 17.Let targetNonconfigurableKeys be an empty List. + // 18.Repeat, for each element key of targetKeys, + // a.Let desc be target.[[GetOwnProperty]](key). + // b.ReturnIfAbrupt(desc). + // c.If desc is not undefined and desc.[[Configurable]] is false, then + // i.Append key as an element of targetNonconfigurableKeys. + // d.Else, + // i.Append key as an element of targetConfigurableKeys. + uint32_t length = targetKeys->GetLength(); + JSHandle tgtCfigKeys = thread->GetEcmaVM()->GetFactory()->NewTaggedArray(length); + JSHandle tgtNoCfigKeys = thread->GetEcmaVM()->GetFactory()->NewTaggedArray(length); + + uint32_t cfigLength = 0; + uint32_t noCfigLength = 0; + for (uint32_t i = 0; i < length; i++) { + JSHandle targetKey(thread, targetKeys->Get(i)); + ASSERT(targetKey->IsStringOrSymbol()); + + PropertyDescriptor desc(thread); + JSTaggedValue::GetOwnProperty(thread, targetHandle, targetKey, desc); + RETURN_HANDLE_IF_ABRUPT_COMPLETION(TaggedArray, thread); + + if (!desc.IsEmpty() && !desc.IsConfigurable()) { + tgtNoCfigKeys->Set(thread, noCfigLength, targetKey); + noCfigLength++; + } else { + tgtCfigKeys->Set(thread, cfigLength, targetKey); + cfigLength++; + } + } + + // 19.If extensibleTarget is true and targetNonconfigurableKeys is empty, then + // a.Return trapResult. + if (extensibleTarget && (noCfigLength == 0)) { + return trapRes; + } + + // 20.Let uncheckedResultKeys be a new List which is a copy of trapResult. + JSHandle uncheckFesKeys = + thread->GetEcmaVM()->GetFactory()->CopyArray(trapRes, trapRes->GetLength(), trapRes->GetLength()); + uint32_t uncheckLength = uncheckFesKeys->GetLength(); + + // 21.Repeat, for each key that is an element of targetNonconfigurableKeys, + // a.If key is not an element of uncheckedResultKeys, throw a TypeError exception. + // b.Remove key from uncheckedResultKeys + for (uint32_t i = 0; i < noCfigLength; i++) { + uint32_t idx = uncheckFesKeys->GetIdx(tgtNoCfigKeys->Get(i)); + if (idx == TaggedArray::MAX_ARRAY_INDEX) { + THROW_TYPE_ERROR_AND_RETURN(thread, "OwnPropertyKeys: key is not an element of uncheckedResultKeys", + JSHandle(thread, JSTaggedValue::Exception())); + } + uncheckFesKeys->Set(thread, idx, JSTaggedValue::Hole()); + uncheckLength--; + } + + // 22.If extensibleTarget is true, return trapResult. + if (extensibleTarget) { + return trapRes; + } + + // 23.Repeat, for each key that is an element of targetConfigurableKeys, + // a.If key is not an element of uncheckedResultKeys, throw a TypeError exception. + // b.Remove key from uncheckedResultKeys + for (uint32_t i = 0; i < cfigLength; i++) { + uint32_t idx = uncheckFesKeys->GetIdx(tgtCfigKeys->Get(i)); + if (idx == TaggedArray::MAX_ARRAY_INDEX) { + THROW_TYPE_ERROR_AND_RETURN(thread, "OwnPropertyKeys: key is not an element of uncheckedResultKeys", + JSHandle(thread, JSTaggedValue::Exception())); + } + uncheckFesKeys->Set(thread, idx, JSTaggedValue::Hole()); + uncheckLength--; + } + + // 24.If uncheckedResultKeys is not empty, throw a TypeError exception. + if (uncheckLength != 0) { + THROW_TYPE_ERROR_AND_RETURN(thread, "OwnPropertyKeys: uncheckedResultKeys is not empty", + JSHandle(thread, JSTaggedValue::Exception())); + } + + // 25.Return trapResult. + return trapRes; +} + +// ES6 9.5.13 [[Call]] (thisArgument, argumentsList) +JSTaggedValue JSProxy::CallInternal(JSThread *thread, const JSHandle &proxy, + const JSHandle &thisArg, uint32_t argc, + const JSTaggedType argv[] // NOLINTNEXTLINE(modernize-avoid-c-arrays) +) +{ + // step 1 ~ 4 get ProxyHandler and ProxyTarget + JSHandle handler(thread, proxy->GetHandler()); + if (handler->IsNull()) { + THROW_TYPE_ERROR_AND_RETURN(thread, "Call: handler is null", JSTaggedValue::Exception()); + } + ASSERT(handler->IsECMAObject()); + JSHandle target(thread, proxy->GetTarget()); + + // 5.Let trap be GetMethod(handler, "apply"). + JSHandle key(thread->GlobalConstants()->GetHandledApplyString()); + JSHandle method = JSObject::GetMethod(thread, handler, key); + + // 6.ReturnIfAbrupt(trap). + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + // 7.If trap is undefined, then + // a.Return Call(target, thisArgument, argumentsList). + if (method->IsUndefined()) { + return JSFunction::Call(thread, target, thisArg, argc, argv); + } + // 8.Let argArray be CreateArrayFromList(argumentsList). + ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); + JSHandle taggedArray = factory->NewTaggedArray(argc); + for (uint32_t index = 0; index < argc; ++index) { + // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-pointer-arithmetic) + taggedArray->Set(thread, index, JSTaggedValue(argv[index])); + } + JSHandle arrHandle = JSArray::CreateArrayFromList(thread, taggedArray); + + // 9.Return Call(trap, handler, «target, thisArgument, argArray»). + InternalCallParams *proxyArgv = thread->GetInternalCallParams(); + proxyArgv->MakeArgv(target, thisArg, arrHandle); + return JSFunction::Call(thread, method, handler, 3, proxyArgv->GetArgv()); // 3: «target, thisArgument, argArray» +} + +// ES6 9.5.14 [[Construct]] ( argumentsList, newTarget) +JSTaggedValue JSProxy::ConstructInternal(JSThread *thread, const JSHandle &proxy, uint32_t argc, + const JSTaggedType argv[], // NOLINTNEXTLINE(modernize-avoid-c-arrays) + const JSHandle &newTarget) +{ + // step 1 ~ 4 get ProxyHandler and ProxyTarget + JSHandle handler(thread, proxy->GetHandler()); + if (handler->IsNull()) { + THROW_TYPE_ERROR_AND_RETURN(thread, "Constructor: handler is null", JSTaggedValue::Exception()); + } + ASSERT(handler->IsECMAObject()); + JSHandle target(thread, proxy->GetTarget()); + + // 5.Let trap be GetMethod(handler, "construct"). + JSHandle key(thread->GlobalConstants()->GetHandledProxyConstructString()); + JSHandle method = JSObject::GetMethod(thread, handler, key); + + // 6.ReturnIfAbrupt(trap). + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + // 7.If trap is undefined, then + // a.Assert: target has a [[Construct]] internal method. + // b.Return Construct(target, argumentsList, newTarget). + if (method->IsUndefined()) { + ASSERT(target->IsConstructor()); + return JSFunction::Construct(thread, target, argc, argv, newTarget); + } + + // 8.Let argArray be CreateArrayFromList(argumentsList). + ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); + JSHandle taggedArray = factory->NewTaggedArray(argc); + for (uint32_t index = 0; index < argc; ++index) { + // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-pointer-arithmetic) + taggedArray->Set(thread, index, JSTaggedValue(argv[index])); + } + JSHandle arrHandle = JSArray::CreateArrayFromList(thread, taggedArray); + + // step 8 ~ 9 Call(trap, handler, «target, argArray, newTarget »). + InternalCallParams *arguments = thread->GetInternalCallParams(); + arguments->MakeArgv(target, arrHandle, newTarget); + JSTaggedValue newObj = + JSFunction::Call(thread, method, handler, 3, arguments->GetArgv()); // 3: «target, argArray, newTarget » + // 10.ReturnIfAbrupt(newObj). + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + // 11.If Type(newObj) is not Object, throw a TypeError exception. + if (!newObj.IsObject()) { + THROW_TYPE_ERROR_AND_RETURN(thread, "new object is not object", JSTaggedValue::Exception()); + } + // 12.Return newObj. + return newObj; +} + +bool JSProxy::IsArray(JSThread *thread) const +{ + if (GetHandler().IsNull()) { + THROW_TYPE_ERROR_AND_RETURN(thread, "", false); + } + return GetTarget().IsArray(thread); +} + +JSHandle JSProxy::GetSourceTarget(JSThread *thread) const +{ + JSMutableHandle proxy(thread, JSTaggedValue(this)); + JSMutableHandle target(thread, proxy->GetTarget()); + while (target->IsJSProxy()) { + proxy.Update(target.GetTaggedValue()); + target.Update(proxy->GetTarget()); + } + return target; +} +} // namespace panda::ecmascript diff --git a/runtime/js_proxy.h b/runtime/js_proxy.h new file mode 100644 index 000000000..8dee8cc15 --- /dev/null +++ b/runtime/js_proxy.h @@ -0,0 +1,103 @@ +/* + * Copyright (c) 2021-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_JSPROXY_H +#define ECMASCRIPT_JSPROXY_H + +#include "plugins/ecmascript/runtime/tagged_array.h" +#include "js_object.h" + +namespace panda::ecmascript { +class JSProxy final : public ECMAObject { +public: + CAST_CHECK(JSProxy, IsJSProxy); + + // ES6 9.5.15 ProxyCreate(target, handler) + static JSHandle ProxyCreate(JSThread *thread, const JSHandle &target, + const JSHandle &handler); + // ES6 9.5.1 [[GetPrototypeOf]] ( ) + static JSTaggedValue GetPrototype(JSThread *thread, const JSHandle &proxy); + // ES6 9.5.2 [[SetPrototypeOf]] (V) + static bool SetPrototype(JSThread *thread, const JSHandle &proxy, const JSHandle &proto); + // ES6 9.5.3 [[IsExtensible]] ( ) + static bool IsExtensible(JSThread *thread, const JSHandle &proxy); + // ES6 9.5.4 [[PreventExtensions]] ( ) + static bool PreventExtensions(JSThread *thread, const JSHandle &proxy); + // ES6 9.5.5 [[GetOwnProperty]] (P) + static bool GetOwnProperty(JSThread *thread, const JSHandle &proxy, const JSHandle &key, + PropertyDescriptor &desc); + // ES6 9.5.6 [[DefineOwnProperty]] (P, Desc) + static bool DefineOwnProperty(JSThread *thread, const JSHandle &proxy, const JSHandle &key, + const PropertyDescriptor &desc); + // ES6 9.5.7 [[HasProperty]] (P) + static bool HasProperty(JSThread *thread, const JSHandle &proxy, const JSHandle &key); + // ES6 9.5.8 [[Get]] (P, Receiver) + static inline OperationResult GetProperty(JSThread *thread, const JSHandle &proxy, + const JSHandle &key) + { + return GetProperty(thread, proxy, key, JSHandle::Cast(proxy)); + } + static OperationResult GetProperty(JSThread *thread, const JSHandle &proxy, + const JSHandle &key, const JSHandle &receiver); + // ES6 9.5.9 [[Set]] ( P, V, Receiver) + static inline bool SetProperty(JSThread *thread, const JSHandle &proxy, const JSHandle &key, + const JSHandle &value, bool mayThrow = false) + { + return SetProperty(thread, proxy, key, value, JSHandle::Cast(proxy), mayThrow); + } + static bool SetProperty(JSThread *thread, const JSHandle &proxy, const JSHandle &key, + const JSHandle &value, const JSHandle &receiver, + bool mayThrow = false); + // ES6 9.5.10 [[Delete]] (P) + static bool DeleteProperty(JSThread *thread, const JSHandle &proxy, const JSHandle &key); + + // ES6 9.5.12 [[OwnPropertyKeys]] () + static JSHandle OwnPropertyKeys(JSThread *thread, const JSHandle &proxy); + + void SetConstructor(bool constructor) const + { + GetClass()->SetConstructor(constructor); + } + + void SetCallTarget([[maybe_unused]] const JSThread *thread, JSMethod *p) + { + SetMethod(p); + } + + JSHandle GetSourceTarget(JSThread *thread) const; + + // ES6 9.5.13 [[Call]] (thisArgument, argumentsList) + static JSTaggedValue CallInternal(JSThread *thread, const JSHandle &proxy, + const JSHandle &thisArg, uint32_t argc, + const JSTaggedType argv[]); // NOLINT(modernize-avoid-c-arrays) + // ES6 9.5.14 [[Construct]] ( argumentsList, newTarget) + static JSTaggedValue ConstructInternal( + // NOLINTNEXTLINE(modernize-avoid-c-arrays) + JSThread *thread, const JSHandle &proxy, uint32_t argc, const JSTaggedType argv[], + const JSHandle &newTarget); + + static constexpr size_t METHOD_OFFSET = ECMAObject::SIZE; + SET_GET_NATIVE_FIELD(Method, JSMethod, METHOD_OFFSET, TARGET_OFFSET) + ACCESSORS(Target, TARGET_OFFSET, HANDLER_OFFSET) + + ACCESSORS(Handler, HANDLER_OFFSET, SIZE) + bool IsArray(JSThread *thread) const; + DECL_DUMP() + + DECL_VISIT_OBJECT(TARGET_OFFSET, SIZE) +}; +} // namespace panda::ecmascript + +#endif // ECMASCRIPT_JSPROXY_H diff --git a/runtime/js_realm.h b/runtime/js_realm.h new file mode 100644 index 000000000..7387045a1 --- /dev/null +++ b/runtime/js_realm.h @@ -0,0 +1,38 @@ +/* + * Copyright (c) 2021-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_JSREALM_H +#define ECMASCRIPT_JSREALM_H + +#include "js_global_object.h" + +namespace panda::ecmascript { +class JSRealm : public JSObject { +public: + static JSRealm *Cast(ObjectHeader *object) + { + return static_cast(object); + } + + static constexpr size_t VALUE_OFFSET = JSObject::SIZE; + ACCESSORS(Value, VALUE_OFFSET, GLOBAL_ENV_OFFSET) + ACCESSORS(GlobalEnv, GLOBAL_ENV_OFFSET, SIZE) + + DECL_VISIT_OBJECT_FOR_JS_OBJECT(JSObject, VALUE_OFFSET, SIZE) + DECL_DUMP() +}; +} // namespace panda::ecmascript + +#endif // ECMASCRIPT_JSREALM_H \ No newline at end of file diff --git a/runtime/js_regexp.h b/runtime/js_regexp.h new file mode 100644 index 000000000..b8bba30d0 --- /dev/null +++ b/runtime/js_regexp.h @@ -0,0 +1,42 @@ +/* + * Copyright (c) 2021-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_JSREGEXP_H +#define ECMASCRIPT_JSREGEXP_H + +#include "plugins/ecmascript/runtime/js_object.h" +#include "plugins/ecmascript/runtime/js_hclass.h" +#include "plugins/ecmascript/runtime/js_tagged_value-inl.h" +#include "plugins/ecmascript/runtime/ecma_macros.h" + +namespace panda::ecmascript { +class JSRegExp : public JSObject { +public: + CAST_CHECK(JSRegExp, IsJSRegExp); + + static constexpr size_t LAST_INDEX_OFFSET = JSObject::SIZE; + ACCESSORS(LastIndex, LAST_INDEX_OFFSET, REGEXP_BYTE_CODE_OFFSET); + ACCESSORS(ByteCodeBuffer, REGEXP_BYTE_CODE_OFFSET, ORIGINAL_SOURCE_OFFSET) + ACCESSORS(OriginalSource, ORIGINAL_SOURCE_OFFSET, ORIGINAL_FLAGS_OFFSET) + ACCESSORS(OriginalFlags, ORIGINAL_FLAGS_OFFSET, LENGTH_OFFSET) + ACCESSORS(Length, LENGTH_OFFSET, SIZE) + + DECL_VISIT_OBJECT_FOR_JS_OBJECT(JSObject, LAST_INDEX_OFFSET, SIZE) + + DECL_DUMP() +}; +} // namespace panda::ecmascript + +#endif // ECMASCRIPT_JSPRIMITIVEREF_H diff --git a/runtime/js_relative_time_format.cpp b/runtime/js_relative_time_format.cpp new file mode 100644 index 000000000..65a52d3de --- /dev/null +++ b/runtime/js_relative_time_format.cpp @@ -0,0 +1,523 @@ +/* + * Copyright (c) 2021-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 "js_relative_time_format.h" + +#include "unicode/decimfmt.h" +#include "unicode/numfmt.h" +#include "unicode/unum.h" + +namespace panda::ecmascript { +enum class StyleOption : uint8_t { LONG = 0x01, SHORT, NARROW, EXCEPTION }; +enum class NumericOption : uint8_t { ALWAYS = 0x01, AUTO, EXCEPTION }; + +// 14.1.1 InitializeRelativeTimeFormat ( relativeTimeFormat, locales, options ) +JSHandle JSRelativeTimeFormat::InitializeRelativeTimeFormat( + JSThread *thread, const JSHandle &relativeTimeFormat, const JSHandle &locales, + const JSHandle &options) +{ + EcmaVM *ecmaVm = thread->GetEcmaVM(); + ObjectFactory *factory = ecmaVm->GetFactory(); + + // 1.Let requestedLocales be ? CanonicalizeLocaleList(locales). + JSHandle requestedLocales = JSLocale::CanonicalizeLocaleList(thread, locales); + RETURN_HANDLE_IF_ABRUPT_COMPLETION(JSRelativeTimeFormat, thread); + + // 2&3. If options is undefined, then Let options be ObjectCreate(null). else Let options be ? ToObject(options). + JSHandle rtfOptions; + if (!options->IsUndefined()) { + rtfOptions = JSTaggedValue::ToObject(thread, options); + RETURN_HANDLE_IF_ABRUPT_COMPLETION(JSRelativeTimeFormat, thread); + } else { + rtfOptions = factory->OrdinaryNewJSObjectCreate(JSHandle(thread, JSTaggedValue::Null())); + } + + // 5. Let matcher be ? GetOption(options, "localeMatcher", "string", «"lookup", "best fit"», "best fit"). + auto globalConst = thread->GlobalConstants(); + LocaleMatcherOption matcher = + JSLocale::GetOptionOfString(thread, rtfOptions, globalConst->GetHandledLocaleMatcherString(), + {LocaleMatcherOption::LOOKUP, LocaleMatcherOption::BEST_FIT}, + {"lookup", "best fit"}, LocaleMatcherOption::BEST_FIT); + RETURN_HANDLE_IF_ABRUPT_COMPLETION(JSRelativeTimeFormat, thread); + + // 7. Let numberingSystem be ? GetOption(options, "numberingSystem", "string", undefined, undefined). + JSHandle property = JSHandle::Cast(globalConst->GetHandledNumberingSystemString()); + JSHandle undefinedValue(thread, JSTaggedValue::Undefined()); + JSHandle numberingSystemValue = + JSLocale::GetOption(thread, rtfOptions, property, OptionType::STRING, undefinedValue, undefinedValue); + RETURN_HANDLE_IF_ABRUPT_COMPLETION(JSRelativeTimeFormat, thread); + + // Check whether numberingSystem is well formed and set to %RelativeTimeFormat%.[[numberingSystem]] + std::string numberingSystemStdStr; + if (!numberingSystemValue->IsUndefined()) { + JSHandle numberingSystemString = JSHandle::Cast(numberingSystemValue); + if (numberingSystemString->IsUtf16()) { + THROW_RANGE_ERROR_AND_RETURN(thread, "invalid numberingSystem", relativeTimeFormat); + } + numberingSystemStdStr = JSLocale::ConvertToStdString(numberingSystemString); + if (!JSLocale::IsNormativeNumberingSystem(numberingSystemStdStr)) { + THROW_RANGE_ERROR_AND_RETURN(thread, "invalid numberingSystem", relativeTimeFormat); + } + } else { + relativeTimeFormat->SetNumberingSystem(thread, globalConst->GetHandledLatnString()); + } + + // 10. Let localeData be %RelativeTimeFormat%.[[LocaleData]]. + // 11. Let r be ResolveLocale(%RelativeTimeFormat%.[[AvailableLocales]], requestedLocales, opt, + // %RelativeTimeFormat%.[[RelevantExtensionKeys]], localeData). + JSHandle availableLocales; + if (requestedLocales->GetLength() == 0) { + availableLocales = factory->EmptyArray(); + } else { + availableLocales = JSLocale::GetAvailableLocales(thread, "calendar", nullptr); + } + std::set relevantExtensionKeys {"nu"}; + ResolvedLocale r = + JSLocale::ResolveLocale(thread, availableLocales, requestedLocales, matcher, relevantExtensionKeys); + RETURN_HANDLE_IF_ABRUPT_COMPLETION(JSRelativeTimeFormat, thread); + icu::Locale icuLocale = r.localeData; + + // 12. Let locale be r.[[Locale]]. + JSHandle localeStr = JSLocale::ToLanguageTag(thread, icuLocale); + + // 13. Set relativeTimeFormat.[[Locale]] to locale. + relativeTimeFormat->SetLocale(thread, localeStr.GetTaggedValue()); + + // 15. Set relativeTimeFormat.[[NumberingSystem]] to r.[[nu]]. + UErrorCode status = U_ZERO_ERROR; + if (!numberingSystemStdStr.empty()) { + if (JSLocale::IsWellNumberingSystem(numberingSystemStdStr)) { + icuLocale.setUnicodeKeywordValue("nu", numberingSystemStdStr, status); + ASSERT(U_SUCCESS(status)); + } + } + + // 16. Let s be ? GetOption(options, "style", "string", «"long", "short", "narrow"», "long"). + property = JSHandle::Cast(globalConst->GetHandledStyleString()); + StyleOption styleOption = JSLocale::GetOptionOfString(thread, rtfOptions, property, + {StyleOption::LONG, StyleOption::SHORT, StyleOption::NARROW}, + {"long", "short", "narrow"}, StyleOption::LONG); + RETURN_HANDLE_IF_ABRUPT_COMPLETION(JSRelativeTimeFormat, thread); + + // 17. Set relativeTimeFormat.[[Style]] to s. + JSTaggedValue styleValue(static_cast(styleOption)); + relativeTimeFormat->SetStyle(thread, JSHandle(thread, styleValue)); + + // 18. Let numeric be ? GetOption(options, "numeric", "string", ?"always", "auto"?, "always"). + property = JSHandle::Cast(globalConst->GetHandledNumericString()); + NumericOption numericOption = + JSLocale::GetOptionOfString(thread, rtfOptions, property, {NumericOption::ALWAYS, NumericOption::AUTO}, + {"always", "auto"}, NumericOption::ALWAYS); + RETURN_HANDLE_IF_ABRUPT_COMPLETION(JSRelativeTimeFormat, thread); + + // 19. Set relativeTimeFormat.[[Numeric]] to numeric. + JSTaggedValue numericValue(static_cast(numericOption)); + relativeTimeFormat->SetNumeric(thread, JSHandle(thread, numericValue)); + + // 20. Let relativeTimeFormat.[[NumberFormat]] be ! Construct(%NumberFormat%, « locale »). + icu::NumberFormat *icuNumberFormat = icu::NumberFormat::createInstance(icuLocale, UNUM_DECIMAL, status); + if (U_FAILURE(status) != 0) { + delete icuNumberFormat; + THROW_RANGE_ERROR_AND_RETURN(thread, "icu Number Format Error", relativeTimeFormat); + } + + // Trans StyleOption to ICU Style + UDateRelativeDateTimeFormatterStyle uStyle; + switch (styleOption) { + case StyleOption::LONG: + uStyle = UDAT_STYLE_LONG; + break; + case StyleOption::SHORT: + uStyle = UDAT_STYLE_SHORT; + break; + case StyleOption::NARROW: + uStyle = UDAT_STYLE_NARROW; + break; + default: + UNREACHABLE(); + } + icu::RelativeDateTimeFormatter rtfFormatter(icuLocale, icuNumberFormat, uStyle, UDISPCTX_CAPITALIZATION_NONE, + status); + if (U_FAILURE(status) != 0) { + THROW_RANGE_ERROR_AND_RETURN(thread, "icu Formatter Error", relativeTimeFormat); + } + + std::string numberingSystem = JSLocale::GetNumberingSystem(icuLocale); + auto result = factory->NewFromStdString(numberingSystem); + relativeTimeFormat->SetNumberingSystem(thread, result); + + // Set RelativeTimeFormat.[[IcuRelativeTimeFormatter]] + factory->NewJSIntlIcuData(relativeTimeFormat, rtfFormatter, JSRelativeTimeFormat::FreeIcuRTFFormatter); + + // 22. Return relativeTimeFormat. + return relativeTimeFormat; +} + +// 14.1.2 SingularRelativeTimeUnit ( unit ) +bool SingularUnitToIcuUnit(JSThread *thread, const JSHandle &unit, URelativeDateTimeUnit *unitEnum) +{ + // 1. Assert: Type(unit) is String. + ASSERT(JSHandle::Cast(unit)->IsString()); + + // 2. If unit is "seconds" or "second", return "second". + // 3. If unit is "minutes" or "minute", return "minute". + // 4. If unit is "hours" or "hour", return "hour". + // 5. If unit is "days" or "day", return "day". + // 6. If unit is "weeks" or "week", return "week". + // 7. If unit is "months" or "month", return "month". + // 8. If unit is "quarters" or "quarter", return "quarter". + // 9. If unit is "years" or "year", return "year". + auto globalConst = thread->GlobalConstants(); + JSHandle second = JSHandle::Cast(globalConst->GetHandledSecondString()); + JSHandle minute = JSHandle::Cast(globalConst->GetHandledMinuteString()); + JSHandle hour = JSHandle::Cast(globalConst->GetHandledHourString()); + JSHandle day = JSHandle::Cast(globalConst->GetHandledDayString()); + JSHandle week = JSHandle::Cast(globalConst->GetHandledWeekString()); + JSHandle month = JSHandle::Cast(globalConst->GetHandledMonthString()); + JSHandle quarter = JSHandle::Cast(globalConst->GetHandledQuarterString()); + JSHandle year = JSHandle::Cast(globalConst->GetHandledYearString()); + + JSHandle seconds = JSHandle::Cast(globalConst->GetHandledSecondsString()); + JSHandle minutes = JSHandle::Cast(globalConst->GetHandledMinutesString()); + JSHandle hours = JSHandle::Cast(globalConst->GetHandledHoursString()); + JSHandle days = JSHandle::Cast(globalConst->GetHandledDaysString()); + JSHandle weeks = JSHandle::Cast(globalConst->GetHandledWeeksString()); + JSHandle months = JSHandle::Cast(globalConst->GetHandledMonthsString()); + JSHandle quarters = JSHandle::Cast(globalConst->GetHandledQuartersString()); + JSHandle years = JSHandle::Cast(globalConst->GetHandledYearsString()); + + if (EcmaString::StringsAreEqual(*second, *unit) || EcmaString::StringsAreEqual(*seconds, *unit)) { + *unitEnum = UDAT_REL_UNIT_SECOND; + } else if (EcmaString::StringsAreEqual(*minute, *unit) || EcmaString::StringsAreEqual(*minutes, *unit)) { + *unitEnum = UDAT_REL_UNIT_MINUTE; + } else if (EcmaString::StringsAreEqual(*hour, *unit) || EcmaString::StringsAreEqual(*hours, *unit)) { + *unitEnum = UDAT_REL_UNIT_HOUR; + } else if (EcmaString::StringsAreEqual(*day, *unit) || EcmaString::StringsAreEqual(*days, *unit)) { + *unitEnum = UDAT_REL_UNIT_DAY; + } else if (EcmaString::StringsAreEqual(*week, *unit) || EcmaString::StringsAreEqual(*weeks, *unit)) { + *unitEnum = UDAT_REL_UNIT_WEEK; + } else if (EcmaString::StringsAreEqual(*month, *unit) || EcmaString::StringsAreEqual(*months, *unit)) { + *unitEnum = UDAT_REL_UNIT_MONTH; + } else if (EcmaString::StringsAreEqual(*quarter, *unit) || EcmaString::StringsAreEqual(*quarters, *unit)) { + *unitEnum = UDAT_REL_UNIT_QUARTER; + } else if (EcmaString::StringsAreEqual(*year, *unit) || EcmaString::StringsAreEqual(*years, *unit)) { + *unitEnum = UDAT_REL_UNIT_YEAR; + } else { + return false; + } + // 11. else return unit. + return true; +} + +// Unwrap RelativeTimeFormat +JSHandle JSRelativeTimeFormat::UnwrapRelativeTimeFormat(JSThread *thread, + const JSHandle &rtf) +{ + ASSERT_PRINT(rtf->IsJSObject(), "rtf is not a JSObject"); + + JSHandle env = thread->GetEcmaVM()->GetGlobalEnv(); + bool isInstanceOf = JSFunction::InstanceOf(thread, rtf, env->GetRelativeTimeFormatFunction()); + RETURN_VALUE_IF_ABRUPT_COMPLETION(thread, rtf); + if (!rtf->IsJSRelativeTimeFormat() && isInstanceOf) { + JSHandle key(thread, JSHandle::Cast(env->GetIntlFunction())->GetFallbackSymbol()); + OperationResult operationResult = JSTaggedValue::GetProperty(thread, rtf, key); + RETURN_VALUE_IF_ABRUPT_COMPLETION(thread, rtf); + return operationResult.GetValue(); + } + + // Perform ? RequireInternalSlot(relativeTimeFormat, [[InitializedRelativeTimeFormat]]). + if (!rtf->IsJSRelativeTimeFormat()) { + RETURN_VALUE_IF_ABRUPT_COMPLETION(thread, rtf); + } + return rtf; +} + +// CommonFormat +icu::FormattedRelativeDateTime GetIcuFormatted(JSThread *thread, + const JSHandle &relativeTimeFormat, double value, + const JSHandle &unit) +{ + icu::RelativeDateTimeFormatter *formatter = relativeTimeFormat->GetIcuRTFFormatter(); + ASSERT_PRINT(formatter != nullptr, "rtfFormatter is null"); + + // If isFinite(value) is false, then throw a RangeError exception. + if (!std::isfinite(value)) { + THROW_RANGE_ERROR_AND_RETURN(thread, "", icu::FormattedRelativeDateTime()); + } + + // 10. If unit is not one of "second", "minute", "hour", "day", "week", "month", "quarter", or "year", throw a + // RangeError exception. + URelativeDateTimeUnit unitEnum; + if (!SingularUnitToIcuUnit(thread, unit, &unitEnum)) { + THROW_RANGE_ERROR_AND_RETURN(thread, "", icu::FormattedRelativeDateTime()); + } + UErrorCode status = U_ZERO_ERROR; + NumericOption numeric = static_cast(relativeTimeFormat->GetNumeric().GetNumber()); + + icu::FormattedRelativeDateTime formatted; + switch (numeric) { + case NumericOption::ALWAYS: + formatted = formatter->formatNumericToValue(value, unitEnum, status); + ASSERT_PRINT(U_SUCCESS(status), "icu format to value error"); + break; + case NumericOption::AUTO: + formatted = formatter->formatToValue(value, unitEnum, status); + ASSERT_PRINT(U_SUCCESS(status), "icu format to value error"); + break; + default: + UNREACHABLE(); + } + return formatted; +} + +// 14.1.2 SingularRelativeTimeUnit ( unit ) +JSHandle SingularUnitString(JSThread *thread, const JSHandle &unit) +{ + auto globalConst = thread->GlobalConstants(); + JSHandle second = JSHandle::Cast(globalConst->GetHandledSecondString()); + JSHandle minute = JSHandle::Cast(globalConst->GetHandledMinuteString()); + JSHandle hour = JSHandle::Cast(globalConst->GetHandledHourString()); + JSHandle day = JSHandle::Cast(globalConst->GetHandledDayString()); + JSHandle week = JSHandle::Cast(globalConst->GetHandledWeekString()); + JSHandle month = JSHandle::Cast(globalConst->GetHandledMonthString()); + JSHandle quarter = JSHandle::Cast(globalConst->GetHandledQuarterString()); + JSHandle year = JSHandle::Cast(globalConst->GetHandledYearString()); + JSHandle seconds = JSHandle::Cast(globalConst->GetHandledSecondsString()); + JSHandle minutes = JSHandle::Cast(globalConst->GetHandledMinutesString()); + JSHandle hours = JSHandle::Cast(globalConst->GetHandledHoursString()); + JSHandle days = JSHandle::Cast(globalConst->GetHandledDaysString()); + JSHandle weeks = JSHandle::Cast(globalConst->GetHandledWeeksString()); + JSHandle months = JSHandle::Cast(globalConst->GetHandledMonthsString()); + JSHandle quarters = JSHandle::Cast(globalConst->GetHandledQuartersString()); + JSHandle years = JSHandle::Cast(globalConst->GetHandledYearsString()); + + // 2. If unit is "seconds" or "second", return "second". + if (EcmaString::StringsAreEqual(*second, *unit) || EcmaString::StringsAreEqual(*seconds, *unit)) { + return second; + } + // 3. If unit is "minutes" or "minute", return "minute". + if (EcmaString::StringsAreEqual(*minute, *unit) || EcmaString::StringsAreEqual(*minutes, *unit)) { + return minute; + } + // 4. If unit is "hours" or "hour", return "hour". + if (EcmaString::StringsAreEqual(*hour, *unit) || EcmaString::StringsAreEqual(*hours, *unit)) { + return hour; + } + // 5. If unit is "days" or "day", return "day". + if (EcmaString::StringsAreEqual(*day, *unit) || EcmaString::StringsAreEqual(*days, *unit)) { + return day; + } + // 6. If unit is "weeks" or "week", return "week". + if (EcmaString::StringsAreEqual(*week, *unit) || EcmaString::StringsAreEqual(*weeks, *unit)) { + return week; + } + // 7. If unit is "months" or "month", return "month". + if (EcmaString::StringsAreEqual(*month, *unit) || EcmaString::StringsAreEqual(*months, *unit)) { + return month; + } + // 8. If unit is "quarters" or "quarter", return "quarter". + if (EcmaString::StringsAreEqual(*quarter, *unit) || EcmaString::StringsAreEqual(*quarters, *unit)) { + return quarter; + } + // 9. If unit is "years" or "year", return "year". + if (EcmaString::StringsAreEqual(*year, *unit) || EcmaString::StringsAreEqual(*years, *unit)) { + return year; + } + + JSHandle undefinedValue(thread, JSTaggedValue::Undefined()); + return JSHandle::Cast(undefinedValue); +} + +// 14.1.5 FormatRelativeTime ( relativeTimeFormat, value, unit ) +JSHandle JSRelativeTimeFormat::Format(JSThread *thread, double value, const JSHandle &unit, + const JSHandle &relativeTimeFormat) +{ + ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); + icu::FormattedRelativeDateTime formatted = GetIcuFormatted(thread, relativeTimeFormat, value, unit); + RETURN_HANDLE_IF_ABRUPT_COMPLETION(EcmaString, thread); + UErrorCode status = U_ZERO_ERROR; + icu::UnicodeString uString = formatted.toString(status); + if (U_FAILURE(status) != 0) { + THROW_RANGE_ERROR_AND_RETURN(thread, "icu formatted toString error", factory->GetEmptyString()); + } + JSHandle string = + factory->NewFromUtf16(reinterpret_cast(uString.getBuffer()), uString.length()); + return string; +} + +void FormatToArray(JSThread *thread, const JSHandle &array, const icu::FormattedRelativeDateTime &formatted, + double value, const JSHandle &unit) +{ + UErrorCode status = U_ZERO_ERROR; + icu::UnicodeString formattedText = formatted.toString(status); + if (U_FAILURE(status) != 0) { + THROW_TYPE_ERROR(thread, "formattedRelativeDateTime toString failed"); + } + + icu::ConstrainedFieldPosition cfpo; + // Set constrainCategory to UFIELD_CATEGORY_NUMBER which is specified for UNumberFormatFields + cfpo.constrainCategory(UFIELD_CATEGORY_NUMBER); + int32_t index = 0; + int32_t previousLimit = 0; + auto globalConst = thread->GlobalConstants(); + JSHandle taggedValue(thread, JSTaggedValue(value)); + JSMutableHandle typeString(thread, JSTaggedValue::Undefined()); + JSHandle unitString = globalConst->GetHandledUnitString(); + std::vector> separatorFields; + /** + * From ICU header file document @unumberformatter.h + * Sets a constraint on the field category. + * + * When this instance of ConstrainedFieldPosition is passed to FormattedValue#nextPosition, + * positions are skipped unless they have the given category. + * + * Any previously set constraints are cleared. + * + * For example, to loop over only the number-related fields: + * + * ConstrainedFieldPosition cfpo; + * cfpo.constrainCategory(UFIELDCATEGORY_NUMBER_FORMAT); + * while (fmtval.nextPosition(cfpo, status)) { + * // handle the number-related field position + * } + */ + while ((formatted.nextPosition(cfpo, status) != 0)) { + int32_t fieldId = cfpo.getField(); + int32_t start = cfpo.getStart(); + int32_t limit = cfpo.getLimit(); + // Special case when fieldId is UNUM_GROUPING_SEPARATOR_FIELD + if (static_cast(fieldId) == UNUM_GROUPING_SEPARATOR_FIELD) { + separatorFields.emplace_back(std::pair(start, limit)); + continue; + } + // If start greater than previousLimit, means a literal type exists before number fields + // so add a literal type with value of formattedText.sub(0, start) + if (start > previousLimit) { + typeString.Update(globalConst->GetLiteralString()); + JSHandle substring = JSLocale::IcuToString(thread, formattedText, previousLimit, start); + JSLocale::PutElement(thread, index++, array, typeString, JSHandle::Cast(substring)); + RETURN_IF_ABRUPT_COMPLETION(thread); + } + // Add part when type is unit + // Iterate former grouping separator vector and add unit element to array + for (auto &separatorField : separatorFields) { + if (separatorField.first > start) { + // Add Integer type element + JSHandle resString = + JSLocale::IcuToString(thread, formattedText, start, separatorField.first); + typeString.Update( + JSLocale::GetNumberFieldType(thread, taggedValue.GetTaggedValue(), fieldId).GetTaggedValue()); + JSHandle record = + JSLocale::PutElement(thread, index++, array, typeString, JSHandle::Cast(resString)); + RETURN_IF_ABRUPT_COMPLETION(thread); + JSObject::CreateDataPropertyOrThrow(thread, record, unitString, JSHandle::Cast(unit)); + RETURN_IF_ABRUPT_COMPLETION(thread); + // Add Group type element + resString = JSLocale::IcuToString(thread, formattedText, separatorField.first, separatorField.second); + typeString.Update( + JSLocale::GetNumberFieldType(thread, taggedValue.GetTaggedValue(), UNUM_GROUPING_SEPARATOR_FIELD) + .GetTaggedValue()); + record = + JSLocale::PutElement(thread, index++, array, typeString, JSHandle::Cast(resString)); + RETURN_IF_ABRUPT_COMPLETION(thread); + JSObject::CreateDataPropertyOrThrow(thread, record, unitString, JSHandle::Cast(unit)); + RETURN_IF_ABRUPT_COMPLETION(thread); + start = separatorField.second; + } + } + // Add current field unit + JSHandle subString = JSLocale::IcuToString(thread, formattedText, start, limit); + typeString.Update(JSLocale::GetNumberFieldType(thread, taggedValue.GetTaggedValue(), fieldId).GetTaggedValue()); + JSHandle record = + JSLocale::PutElement(thread, index++, array, typeString, JSHandle::Cast(subString)); + RETURN_IF_ABRUPT_COMPLETION(thread); + JSObject::CreateDataPropertyOrThrow(thread, record, unitString, JSHandle::Cast(unit)); + RETURN_IF_ABRUPT_COMPLETION(thread); + previousLimit = limit; + } + // If iterated length is smaller than formattedText.length, means a literal type exists after number fields + // so add a literal type with value of formattedText.sub(previousLimit, formattedText.length) + if (formattedText.length() > previousLimit) { + typeString.Update(globalConst->GetLiteralString()); + JSHandle substring = + JSLocale::IcuToString(thread, formattedText, previousLimit, formattedText.length()); + JSLocale::PutElement(thread, index, array, typeString, JSHandle::Cast(substring)); + RETURN_IF_ABRUPT_COMPLETION(thread); + } +} + +// 14.1.6 FormatRelativeTimeToParts ( relativeTimeFormat, value, unit ) +JSHandle JSRelativeTimeFormat::FormatToParts(JSThread *thread, double value, const JSHandle &unit, + const JSHandle &relativeTimeFormat) +{ + icu::FormattedRelativeDateTime formatted = GetIcuFormatted(thread, relativeTimeFormat, value, unit); + RETURN_HANDLE_IF_ABRUPT_COMPLETION(JSArray, thread); + JSHandle singularUnit = SingularUnitString(thread, unit); + JSHandle array = JSHandle::Cast(JSArray::ArrayCreate(thread, JSTaggedNumber(0))); + FormatToArray(thread, array, formatted, value, singularUnit); + return array; +} + +void JSRelativeTimeFormat::ResolvedOptions(JSThread *thread, const JSHandle &relativeTimeFormat, + const JSHandle &options) +{ + if (relativeTimeFormat->GetIcuRTFFormatter() != nullptr) { + [[maybe_unused]] icu::RelativeDateTimeFormatter *formatter = relativeTimeFormat->GetIcuRTFFormatter(); + } else { + THROW_RANGE_ERROR(thread, "rtf is not initialized"); + } + + auto globalConst = thread->GlobalConstants(); + // [[locale]] + JSHandle property = JSHandle::Cast(globalConst->GetHandledLocaleString()); + JSHandle locale(thread, relativeTimeFormat->GetLocale()); + PropertyDescriptor localeDesc(thread, JSHandle::Cast(locale), true, true, true); + JSObject::DefineOwnProperty(thread, options, property, localeDesc); + + // [[Style]] + property = JSHandle::Cast(globalConst->GetHandledStyleString()); + StyleOption style = static_cast(relativeTimeFormat->GetStyle().GetNumber()); + JSHandle styleValue; + if (style == StyleOption::LONG) { + styleValue = globalConst->GetHandledLongString(); + } else if (style == StyleOption::SHORT) { + styleValue = globalConst->GetHandledShortString(); + } else if (style == StyleOption::NARROW) { + styleValue = globalConst->GetHandledNarrowString(); + } + PropertyDescriptor styleDesc(thread, styleValue, true, true, true); + JSObject::DefineOwnProperty(thread, options, property, styleDesc); + + // [[Numeric]] + property = JSHandle::Cast(globalConst->GetHandledNumericString()); + NumericOption numeric = static_cast(relativeTimeFormat->GetNumeric().GetNumber()); + JSHandle numericValue; + if (numeric == NumericOption::ALWAYS) { + numericValue = globalConst->GetHandledAlwaysString(); + } else if (numeric == NumericOption::AUTO) { + numericValue = globalConst->GetHandledAutoString(); + } else { + THROW_RANGE_ERROR(thread, "numeric is exception"); + } + PropertyDescriptor numericDesc(thread, numericValue, true, true, true); + JSObject::DefineOwnProperty(thread, options, property, numericDesc); + + // [[NumberingSystem]] + property = JSHandle::Cast(globalConst->GetHandledNumberingSystemString()); + JSHandle numberingSystem(thread, relativeTimeFormat->GetNumberingSystem()); + PropertyDescriptor numberingSystemDesc(thread, numberingSystem, true, true, true); + JSObject::DefineOwnProperty(thread, options, property, numberingSystemDesc); +} +} // namespace panda::ecmascript \ No newline at end of file diff --git a/runtime/js_relative_time_format.h b/runtime/js_relative_time_format.h new file mode 100644 index 000000000..ba92496c7 --- /dev/null +++ b/runtime/js_relative_time_format.h @@ -0,0 +1,95 @@ +/* + * Copyright (c) 2021-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_JS_RELATIVE_TIME_FORMAT_H +#define ECMASCRIPT_JS_RELATIVE_TIME_FORMAT_H + +#include "unicode/reldatefmt.h" + +#include "plugins/ecmascript/runtime/base/number_helper.h" +#include "plugins/ecmascript/runtime/common.h" +#include "plugins/ecmascript/runtime/ecma_macros.h" +#include "plugins/ecmascript/runtime/ecma_string.h" +#include "plugins/ecmascript/runtime/ecma_vm.h" +#include "plugins/ecmascript/runtime/global_env.h" +#include "plugins/ecmascript/runtime/js_array.h" +#include "plugins/ecmascript/runtime/js_date.h" +#include "plugins/ecmascript/runtime/js_intl.h" +#include "plugins/ecmascript/runtime/js_locale.h" +#include "plugins/ecmascript/runtime/js_object.h" +#include "plugins/ecmascript/runtime/js_tagged_number.h" +#include "plugins/ecmascript/runtime/js_tagged_value.h" +#include "plugins/ecmascript/runtime/object_factory.h" + +namespace panda::ecmascript { +class JSRelativeTimeFormat : public JSObject { +public: + CAST_CHECK(JSRelativeTimeFormat, IsJSRelativeTimeFormat); + + static constexpr size_t LOCALE_OFFSET = JSObject::SIZE; + + ACCESSORS(Locale, LOCALE_OFFSET, INITIALIZED_RELATIVE_TIME_FORMAT) + ACCESSORS(InitializedRelativeTimeFormat, INITIALIZED_RELATIVE_TIME_FORMAT, NUMBERING_SYSTEM_OFFSET) + ACCESSORS(NumberingSystem, NUMBERING_SYSTEM_OFFSET, STYLE_OFFSET) + ACCESSORS(Style, STYLE_OFFSET, NUMERIC_OFFSET) + ACCESSORS(Numeric, NUMERIC_OFFSET, AVAILABLE_LOCALES_OFFSET) + ACCESSORS(AvailableLocales, AVAILABLE_LOCALES_OFFSET, ICU_FIELD_OFFSET) + + // icu field + ACCESSORS(IcuField, ICU_FIELD_OFFSET, SIZE) + + DECL_VISIT_OBJECT_FOR_JS_OBJECT(JSObject, LOCALE_OFFSET, SIZE) + DECL_DUMP() + + // 14.1.1 InitializeRelativeTimeFormat ( relativeTimeFormat, locales, options ) + static JSHandle InitializeRelativeTimeFormat( + JSThread *thread, const JSHandle &relativeTimeFormat, + const JSHandle &locales, const JSHandle &options); + + // UnwrapRelativeTimeFormat + static JSHandle UnwrapRelativeTimeFormat(JSThread *thread, const JSHandle &rtf); + + // Get icu formatter from icu field + icu::RelativeDateTimeFormatter *GetIcuRTFFormatter() const + { + ASSERT(GetIcuField().IsJSNativePointer()); + auto result = JSNativePointer::Cast(GetIcuField().GetTaggedObject())->GetExternalPointer(); + return reinterpret_cast(result); + } + + static void FreeIcuRTFFormatter(void *pointer, void *data) + { + if (pointer == nullptr) { + return; + } + auto icuFormatter = reinterpret_cast(pointer); + icuFormatter->~RelativeDateTimeFormatter(); + if (data != nullptr) { + reinterpret_cast(data)->GetRegionFactory()->FreeBuffer(pointer); + } + } + + static void ResolvedOptions(JSThread *thread, const JSHandle &relativeTimeFormat, + const JSHandle &options); + + static JSHandle Format(JSThread *thread, double value, const JSHandle &unit, + const JSHandle &relativeTimeFormat); + + static JSHandle FormatToParts(JSThread *thread, double value, const JSHandle &unit, + const JSHandle &relativeTimeFormat); +}; +} // namespace panda::ecmascript + +#endif // ECMASCRIPT_JS_RELATIVE_TIME_FORMAT_H diff --git a/runtime/js_runtime_options.h b/runtime/js_runtime_options.h new file mode 100644 index 000000000..bfe74fc1d --- /dev/null +++ b/runtime/js_runtime_options.h @@ -0,0 +1,180 @@ +/* + * Copyright (c) 2021-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_JS_RUNTIME_OPTIONS_H_ +#define ECMASCRIPT_JS_RUNTIME_OPTIONS_H_ + +#include "runtime/include/runtime_options.h" +#include "utils/logger.h" + +// namespace panda { +namespace panda::ecmascript { +enum ArkProperties { + DEFAULT = -1, + OPTIONAL_LOG = 1, + GC_STATS_PRINT = 1 << 1, // NOLINT(hicpp-signed-bitwise) + PARALLEL_GC = 1 << 2, // NOLINT(hicpp-signed-bitwise) + CONCURRENT_MARK = 1 << 3, // NOLINT(hicpp-signed-bitwise) + CONCURRENT_SWEEP = 1 << 4, // NOLINT(hicpp-signed-bitwise) +}; + +class JSRuntimeOptions : public RuntimeOptions { +public: + explicit JSRuntimeOptions(const std::string &exe_path = "") : RuntimeOptions(exe_path) {} + explicit JSRuntimeOptions(RuntimeOptions options) : RuntimeOptions(std::move(options)) {} + ~JSRuntimeOptions() = default; + DEFAULT_COPY_SEMANTIC(JSRuntimeOptions); + DEFAULT_MOVE_SEMANTIC(JSRuntimeOptions); + + inline static JSRuntimeOptions Cast(RuntimeOptions runtime_options) + { + return static_cast(std::move(runtime_options)); + } + + void AddOptions(PandArgParser *parser) + { + RuntimeOptions::AddOptions(parser); + parser->Add(&enable_ark_tools_); + parser->Add(&enable_stub_aot_); + parser->Add(&stub_module_file_); + parser->Add(&ark_properties_); + } + + bool IsEnableArkTools() const + { + return enable_ark_tools_.GetValue(); + } + + void SetEnableArkTools(bool value) + { + enable_ark_tools_.SetValue(value); + } + + bool WasSetEnableArkTools() const + { + return enable_ark_tools_.WasSet(); + } + + bool IsEnableStubAot() const + { + return enable_stub_aot_.GetValue(); + } + + void SetEnableStubAot(bool value) + { + enable_stub_aot_.SetValue(value); + } + + bool WasSetEnableStubAot() const + { + return enable_stub_aot_.WasSet(); + } + + std::string GetStubModuleFile() const + { + return stub_module_file_.GetValue(); + } + + void SetStubModuleFile(std::string value) + { + stub_module_file_.SetValue(std::move(value)); + } + + bool WasSetStubModuleFile() const + { + return stub_module_file_.WasSet(); + } + + bool IsEnableForceGC() const + { + return enable_force_gc_.GetValue(); + } + + void SetEnableForceGC(bool value) + { + enable_force_gc_.SetValue(value); + } + + bool IsForceCompressGC() const + { + return force_compress_gc_.GetValue(); + } + + void SetForceCompressGC(bool value) + { + force_compress_gc_.SetValue(value); + } + + void SetArkProperties(int prop) + { + if (prop != ArkProperties::DEFAULT) { + ark_properties_.SetValue(prop); + } + } + + int GetDefaultProperties() + { + // NOLINTNEXTLINE(hicpp-signed-bitwise) + return ArkProperties::PARALLEL_GC | ArkProperties::CONCURRENT_MARK | ArkProperties::CONCURRENT_SWEEP; + } + + int GetArkProperties() + { + return ark_properties_.GetValue(); + } + + bool IsEnableOptionalLog() const + { + // NOLINTNEXTLINE(hicpp-signed-bitwise) + return (ark_properties_.GetValue() & ArkProperties::OPTIONAL_LOG) != 0; + } + + bool IsEnableGCStatsPrint() const + { + // NOLINTNEXTLINE(hicpp-signed-bitwise) + return (ark_properties_.GetValue() & ArkProperties::GC_STATS_PRINT) != 0; + } + + bool IsEnableParallelGC() const + { + // NOLINTNEXTLINE(hicpp-signed-bitwise) + return (ark_properties_.GetValue() & ArkProperties::PARALLEL_GC) != 0; + } + + bool IsEnableConcurrentMark() const + { + // NOLINTNEXTLINE(hicpp-signed-bitwise) + return (ark_properties_.GetValue() & ArkProperties::CONCURRENT_MARK) != 0; + } + + bool IsEnableConcurrentSweep() const + { + // NOLINTNEXTLINE(hicpp-signed-bitwise) + return (ark_properties_.GetValue() & ArkProperties::CONCURRENT_SWEEP) != 0; + } + +private: + PandArg enable_ark_tools_ {"enable-ark-tools", false, R"(Enable ark tools to debug. Default: false)"}; + PandArg enable_stub_aot_ {"enable-stub-aot", false, R"(enable aot of fast stub. Default: false)"}; + PandArg stub_module_file_ {"stub-module-file", R"(stub.m)", + R"(Path to stub module file. Default: "stub.m")"}; + PandArg enable_force_gc_ {"enable-force-gc", true, R"(enable force gc when allocating object)"}; + PandArg force_compress_gc_ {"force-compress-gc", true, + R"(if true trigger compress gc, else trigger semi and old gc)"}; + PandArg ark_properties_ {"ark-properties", GetDefaultProperties(), R"(set ark properties)"}; +}; +} // namespace panda::ecmascript + +#endif // ECMASCRIPT_JS_RUNTIME_OPTIONS_H_ diff --git a/runtime/js_serializer.cpp b/runtime/js_serializer.cpp new file mode 100644 index 000000000..7685ba83f --- /dev/null +++ b/runtime/js_serializer.cpp @@ -0,0 +1,1464 @@ +/* + * Copyright (c) 2021-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 "plugins/ecmascript/runtime/js_serializer.h" + +#include +#include + +#include "plugins/ecmascript/runtime/base/array_helper.h" +#include "plugins/ecmascript/runtime/global_env.h" +#include "plugins/ecmascript/runtime/js_array.h" +#include "plugins/ecmascript/runtime/js_arraybuffer.h" +#include "plugins/ecmascript/runtime/js_hclass.h" +#include "plugins/ecmascript/runtime/js_regexp.h" +#include "plugins/ecmascript/runtime/js_set.h" +#include "plugins/ecmascript/runtime/js_typed_array.h" +#include "plugins/ecmascript/runtime/linked_hash_table-inl.h" +#include "libpandabase/mem/mem.h" +#include "securec.h" + +namespace panda::ecmascript { +constexpr size_t INITIAL_CAPACITY = 64; +constexpr int CAPACITY_INCREASE_RATE = 2; + +bool JSSerializer::WriteType(SerializationUID id) +{ + auto rawId = static_cast(id); + return WriteRawData(&rawId, sizeof(rawId)); +} + +// Write JSTaggedValue could be either a pointer to a HeapObject or a value +bool JSSerializer::SerializeJSTaggedValue(const JSHandle &value) +{ + [[maybe_unused]] EcmaHandleScope scope(thread_); + if (!value->IsHeapObject()) { + if (!WritePrimitiveValue(value)) { + return false; + } + } else { + if (!WriteTaggedObject(value)) { + return false; + } + } + return true; +} + +// Write JSTaggedValue that is pure value +bool JSSerializer::WritePrimitiveValue(const JSHandle &value) +{ + if (value->IsNull()) { + return WriteType(SerializationUID::JS_NULL); + } + if (value->IsUndefined()) { + return WriteType(SerializationUID::JS_UNDEFINED); + } + if (value->IsTrue()) { + return WriteType(SerializationUID::JS_TRUE); + } + if (value->IsFalse()) { + return WriteType(SerializationUID::JS_FALSE); + } + if (value->IsInt()) { + return WriteInt(value->GetInt()); + } + if (value->IsDouble()) { + return WriteDouble(value->GetDouble()); + } + if (value->IsHole()) { + return WriteType(SerializationUID::HOLE); + } + return false; +} + +bool JSSerializer::WriteInt(int32_t value) +{ + size_t oldSize = bufferSize_; + if (!WriteType(SerializationUID::INT32)) { + return false; + } + if (!WriteRawData(&value, sizeof(value))) { + bufferSize_ = oldSize; + return false; + } + return true; +} + +bool JSSerializer::WriteInt(uint32_t value) +{ + size_t oldSize = bufferSize_; + if (!WriteType(SerializationUID::UINT32)) { + return false; + } + if (!WriteRawData(&value, sizeof(value))) { + bufferSize_ = oldSize; + return false; + } + return true; +} + +bool JSSerializer::WriteDouble(double value) +{ + size_t oldSize = bufferSize_; + if (!WriteType(SerializationUID::DOUBLE)) { + return false; + } + if (!WriteRawData(&value, sizeof(value))) { + bufferSize_ = oldSize; + return false; + } + return true; +} + +bool JSSerializer::WriteBoolean(bool value) +{ + if (value) { + return WriteType(SerializationUID::C_TRUE); + } + return WriteType(SerializationUID::C_FALSE); +} + +// Write length for marking how many bytes should be read +bool JSSerializer::WriteLength(uint32_t length) +{ + return WriteRawData(&length, sizeof(length)); +} + +bool JSSerializer::WriteRawData(const void *data, size_t length) +{ + if (length <= 0) { + return false; + } + if ((bufferSize_ + length) > bufferCapacity_) { + if (!AllocateBuffer(length)) { + return false; + } + } + errno_t rc; + // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-pointer-arithmetic) + rc = memcpy_s(buffer_ + bufferSize_, bufferCapacity_ - bufferSize_, data, length); + if (rc != EOK) { + LOG(ERROR, RUNTIME) << "Failed to memcpy_s Data"; + return false; + } + bufferSize_ += length; + return true; +} + +bool JSSerializer::AllocateBuffer(size_t bytes) +{ + // Get internal heap size + if (sizeLimit_ == 0) { + uint64_t heapSize = thread_->GetEcmaVM()->GetJSOptions().GetInternalMemorySizeLimit(); + sizeLimit_ = heapSize; + } + size_t oldSize = bufferSize_; + size_t newSize = oldSize + bytes; + if (newSize > sizeLimit_) { + return false; + } + if (bufferCapacity_ == 0) { + if (bytes < INITIAL_CAPACITY) { + // NOLINTNEXTLINE(cppcoreguidelines-no-malloc) + buffer_ = reinterpret_cast(malloc(INITIAL_CAPACITY)); + if (buffer_ != nullptr) { + bufferCapacity_ = INITIAL_CAPACITY; + return true; + } + return false; + } + // NOLINTNEXTLINE(cppcoreguidelines-no-malloc) + buffer_ = reinterpret_cast(malloc(bytes)); + if (buffer_ != nullptr) { + bufferCapacity_ = bytes; + return true; + } + return false; + } + if (newSize > bufferCapacity_) { + if (!ExpandBuffer(newSize)) { + return false; + } + } + return true; +} + +bool JSSerializer::ExpandBuffer([[maybe_unused]] size_t requestedSize) +{ + size_t newCapacity = bufferCapacity_ * CAPACITY_INCREASE_RATE; + newCapacity = std::max(newCapacity, requestedSize); + if (newCapacity > sizeLimit_) { + return false; + } + // NOLINTNEXTLINE(cppcoreguidelines-no-malloc) + auto *newBuffer = reinterpret_cast(malloc(newCapacity)); + if (newBuffer == nullptr) { + return false; + } + errno_t rc; + rc = memcpy_s(newBuffer, newCapacity, buffer_, bufferSize_); + if (rc != EOK) { + LOG(ERROR, RUNTIME) << "Failed to memcpy_s Data"; + // NOLINTNEXTLINE(cppcoreguidelines-no-malloc) + free(newBuffer); + return false; + } + // NOLINTNEXTLINE(cppcoreguidelines-no-malloc) + free(buffer_); + buffer_ = newBuffer; + bufferCapacity_ = newCapacity; + return true; +} + +// Transfer ownership of buffer, should not use this Serializer after release +std::pair JSSerializer::ReleaseBuffer() +{ + auto res = std::make_pair(buffer_, bufferSize_); + buffer_ = nullptr; + bufferSize_ = 0; + bufferCapacity_ = 0; + objectId_ = 0; + return res; +} + +bool JSSerializer::IsSerialized(uintptr_t addr) const +{ + return referenceMap_.find(addr) != referenceMap_.end(); +} + +bool JSSerializer::WriteIfSerialized(uintptr_t addr) +{ + size_t oldSize = bufferSize_; + auto iter = referenceMap_.find(addr); + if (iter == referenceMap_.end()) { + return false; + } + uint64_t id = iter->second; + if (!WriteType(SerializationUID::TAGGED_OBJECT_REFERNCE)) { + return false; + } + if (!WriteRawData(&id, sizeof(uint64_t))) { + bufferSize_ = oldSize; + return false; + } + return true; +} + +// Write HeapObject +bool JSSerializer::WriteTaggedObject(const JSHandle &value) +{ + uintptr_t addr = reinterpret_cast(value.GetTaggedValue().GetTaggedObject()); + bool serialized = IsSerialized(addr); + if (serialized) { + return WriteIfSerialized(addr); + } + referenceMap_.insert(std::pair(addr, objectId_)); + objectId_++; + + TaggedObject *taggedObject = value->GetTaggedObject(); + JSType type = taggedObject->GetClass()->GetObjectType(); + switch (type) { + case JSType::JS_ERROR: + case JSType::JS_EVAL_ERROR: + case JSType::JS_RANGE_ERROR: + case JSType::JS_REFERENCE_ERROR: + case JSType::JS_TYPE_ERROR: + case JSType::JS_URI_ERROR: + case JSType::JS_SYNTAX_ERROR: + return WriteJSError(value); + case JSType::JS_DATE: + return WriteJSDate(value); + case JSType::JS_ARRAY: + return WriteJSArray(value); + case JSType::JS_MAP: + return WriteJSMap(value); + case JSType::JS_SET: + return WriteJSSet(value); + case JSType::JS_REG_EXP: + return WriteJSRegExp(value); + case JSType::JS_INT8_ARRAY: + return WriteJSTypedArray(value, SerializationUID::JS_INT8_ARRAY); + case JSType::JS_UINT8_ARRAY: + return WriteJSTypedArray(value, SerializationUID::JS_UINT8_ARRAY); + case JSType::JS_UINT8_CLAMPED_ARRAY: + return WriteJSTypedArray(value, SerializationUID::JS_UINT8_CLAMPED_ARRAY); + case JSType::JS_INT16_ARRAY: + return WriteJSTypedArray(value, SerializationUID::JS_INT16_ARRAY); + case JSType::JS_UINT16_ARRAY: + return WriteJSTypedArray(value, SerializationUID::JS_UINT16_ARRAY); + case JSType::JS_INT32_ARRAY: + return WriteJSTypedArray(value, SerializationUID::JS_INT32_ARRAY); + case JSType::JS_UINT32_ARRAY: + return WriteJSTypedArray(value, SerializationUID::JS_UINT32_ARRAY); + case JSType::JS_FLOAT32_ARRAY: + return WriteJSTypedArray(value, SerializationUID::JS_FLOAT32_ARRAY); + case JSType::JS_FLOAT64_ARRAY: + return WriteJSTypedArray(value, SerializationUID::JS_FLOAT64_ARRAY); + case JSType::JS_ARRAY_BUFFER: + return WriteJSArrayBuffer(value); + case JSType::STRING: + return WriteEcmaString(value); + case JSType::JS_OBJECT: + return WritePlainObject(value); + default: + break; + } + return false; +} + +bool JSSerializer::WriteJSError(const JSHandle &value) +{ + size_t oldSize = bufferSize_; + TaggedObject *taggedObject = value->GetTaggedObject(); + JSType errorType = taggedObject->GetClass()->GetObjectType(); + if (!WriteJSErrorHeader(errorType)) { + return false; + } + auto globalConst = thread_->GlobalConstants(); + JSHandle handleMsg = globalConst->GetHandledMessageString(); + JSHandle msg = JSObject::GetProperty(thread_, value, handleMsg).GetValue(); + // Write error message + if (!SerializeJSTaggedValue(msg)) { + bufferSize_ = oldSize; + return false; + } + return true; +} + +bool JSSerializer::WriteJSErrorHeader(JSType type) +{ + switch (type) { + case JSType::JS_ERROR: + return WriteType(SerializationUID::JS_ERROR); + case JSType::JS_EVAL_ERROR: + return WriteType(SerializationUID::EVAL_ERROR); + case JSType::JS_RANGE_ERROR: + return WriteType(SerializationUID::RANGE_ERROR); + case JSType::JS_REFERENCE_ERROR: + return WriteType(SerializationUID::REFERENCE_ERROR); + case JSType::JS_TYPE_ERROR: + return WriteType(SerializationUID::TYPE_ERROR); + case JSType::JS_URI_ERROR: + return WriteType(SerializationUID::URI_ERROR); + case JSType::JS_SYNTAX_ERROR: + return WriteType(SerializationUID::SYNTAX_ERROR); + default: + UNREACHABLE(); + } + return false; +} + +bool JSSerializer::WriteJSDate(const JSHandle &value) +{ + JSHandle date = JSHandle::Cast(value); + size_t oldSize = bufferSize_; + if (!WriteType(SerializationUID::JS_DATE)) { + return false; + } + if (!WritePlainObject(value)) { + bufferSize_ = oldSize; + return false; + } + double timeValue = date->GetTimeValue().GetDouble(); + if (!WriteDouble(timeValue)) { + bufferSize_ = oldSize; + return false; + } + double localOffset = date->GetLocalOffset().GetDouble(); + if (!WriteDouble(localOffset)) { + bufferSize_ = oldSize; + return false; + } + return true; +} + +bool JSSerializer::WriteJSArray(const JSHandle &value) +{ + JSHandle array = JSHandle::Cast(value); + size_t oldSize = bufferSize_; + if (!WriteType(SerializationUID::JS_ARRAY)) { + return false; + } + if (!WritePlainObject(value)) { + bufferSize_ = oldSize; + return false; + } + uint32_t arrayLength = static_cast(array->GetLength().GetInt()); + if (!WriteInt(arrayLength)) { + bufferSize_ = oldSize; + return false; + } + return true; +} + +bool JSSerializer::WriteEcmaString(const JSHandle &value) +{ + JSHandle string = JSHandle::Cast(value); + size_t oldSize = bufferSize_; + if (!WriteType(SerializationUID::ECMASTRING)) { + return false; + } + size_t length = string->GetLength(); + if (!WriteLength(static_cast(length))) { + bufferSize_ = oldSize; + return false; + } + const uint8_t *data = string->GetDataUtf8(); + const uint8_t strEnd = '\0'; + if (!WriteRawData(data, length) || !WriteRawData(&strEnd, sizeof(uint8_t))) { + bufferSize_ = oldSize; + return false; + } + return true; +} + +bool JSSerializer::WriteJSMap(const JSHandle &value) +{ + JSHandle map = JSHandle::Cast(value); + size_t oldSize = bufferSize_; + if (!WriteType(SerializationUID::JS_MAP)) { + return false; + } + if (!WritePlainObject(value)) { + bufferSize_ = oldSize; + return false; + } + int size = map->GetSize(); + if (!WriteLength(static_cast(size))) { + bufferSize_ = oldSize; + return false; + } + for (int i = 0; i < size; i++) { + JSHandle key(thread_, map->GetKey(i)); + if (!SerializeJSTaggedValue(key)) { + bufferSize_ = oldSize; + return false; + } + JSHandle val(thread_, map->GetValue(i)); + if (!SerializeJSTaggedValue(val)) { + bufferSize_ = oldSize; + return false; + } + } + return true; +} + +bool JSSerializer::WriteJSSet(const JSHandle &value) +{ + JSHandle set = JSHandle::Cast(value); + size_t oldSize = bufferSize_; + if (!WriteType(SerializationUID::JS_SET)) { + return false; + } + if (!WritePlainObject(value)) { + bufferSize_ = oldSize; + return false; + } + int size = set->GetSize(); + if (!WriteLength(static_cast(size))) { + bufferSize_ = oldSize; + return false; + } + for (int i = 0; i < size; i++) { + JSHandle val(thread_, set->GetValue(i)); + if (!SerializeJSTaggedValue(val)) { + bufferSize_ = oldSize; + return false; + } + } + return true; +} + +bool JSSerializer::WriteJSRegExp(const JSHandle &value) +{ + JSHandle regExp = JSHandle::Cast(value); + size_t oldSize = bufferSize_; + if (!WriteType(SerializationUID::JS_REG_EXP)) { + return false; + } + if (!WritePlainObject(value)) { + bufferSize_ = oldSize; + return false; + } + uint32_t bufferSize = static_cast(regExp->GetLength().GetInt()); + if (!WriteLength(bufferSize)) { + bufferSize_ = oldSize; + return false; + } + // Write Accessor(ByteCodeBuffer) which is a pointer to a Dynbuffer + JSHandle bufferValue(thread_, regExp->GetByteCodeBuffer()); + JSHandle np = JSHandle::Cast(bufferValue); + void *dynBuffer = np->GetExternalPointer(); + if (!WriteRawData(dynBuffer, bufferSize)) { + bufferSize_ = oldSize; + return false; + } + // Write Accessor(OriginalSource) + JSHandle originalSource(thread_, regExp->GetOriginalSource()); + if (!SerializeJSTaggedValue(originalSource)) { + bufferSize_ = oldSize; + return false; + } + // Write Accessor(OriginalFlags) + JSHandle originalFlags(thread_, regExp->GetOriginalFlags()); + if (!SerializeJSTaggedValue(originalFlags)) { + bufferSize_ = oldSize; + return false; + } + return true; +} + +bool JSSerializer::WriteJSTypedArray(const JSHandle &value, SerializationUID uId) +{ + JSHandle typedArray = JSHandle::Cast(value); + size_t oldSize = bufferSize_; + if (!WriteType(uId)) { + return false; + } + if (!WritePlainObject(value)) { + bufferSize_ = oldSize; + return false; + } + // Write ACCESSORS(ViewedArrayBuffer) which is a pointer to an ArrayBuffer + JSHandle viewedArrayBuffer(thread_, typedArray->GetViewedArrayBuffer()); + if (!WriteJSArrayBuffer(viewedArrayBuffer)) { + bufferSize_ = oldSize; + return false; + } + // Write ACCESSORS(TypedArrayName) + JSHandle typedArrayName(thread_, typedArray->GetTypedArrayName()); + if (!SerializeJSTaggedValue(typedArrayName)) { + bufferSize_ = oldSize; + return false; + } + // Write ACCESSORS(ByteLength) + JSTaggedValue byteLength = typedArray->GetByteLength(); + if (!WriteRawData(&byteLength, sizeof(JSTaggedValue))) { + bufferSize_ = oldSize; + return false; + } + // Write ACCESSORS(ByteOffset) + JSTaggedValue byteOffset = typedArray->GetByteOffset(); + if (!WriteRawData(&byteOffset, sizeof(JSTaggedValue))) { + bufferSize_ = oldSize; + return false; + } + // Write ACCESSORS(ArrayLength) + JSTaggedValue arrayLength = typedArray->GetArrayLength(); + if (!WriteRawData(&arrayLength, sizeof(JSTaggedValue))) { + bufferSize_ = oldSize; + return false; + } + return true; +} + +bool JSSerializer::WriteNativeFunctionPointer(const JSHandle &value) +{ + size_t oldSize = bufferSize_; + if (!WriteType(SerializationUID::NATIVE_FUNCTION_POINTER)) { + return false; + } + JSTaggedValue pointer = value.GetTaggedValue(); + if (!WriteRawData(&pointer, sizeof(JSTaggedValue))) { + bufferSize_ = oldSize; + return false; + } + return true; +} + +bool JSSerializer::WriteJSArrayBuffer(const JSHandle &value) +{ + size_t oldSize = bufferSize_; + JSHandle arrayBuffer = JSHandle::Cast(value); + + if (arrayBuffer->IsDetach()) { + return false; + } + + if (!WriteType(SerializationUID::JS_ARRAY_BUFFER)) { + return false; + } + + // Write Accessors(ArrayBufferByteLength) + JSTaggedValue taggedLength = arrayBuffer->GetArrayBufferByteLength(); + if (!WriteRawData(&taggedLength, sizeof(JSTaggedValue))) { + bufferSize_ = oldSize; + return false; + } + + // write Accessor shared which indicate the C memeory is shared + bool shared = arrayBuffer->GetShared().ToBoolean(); + if (!WriteBoolean(shared)) { + bufferSize_ = oldSize; + return false; + } + + if (shared) { + JSHandle np(thread_, arrayBuffer->GetArrayBufferData()); + void *buffer = np->GetExternalPointer(); + auto bufferAddr = reinterpret_cast(buffer); + if (!WriteRawData(&bufferAddr, sizeof(uint64_t))) { + bufferSize_ = oldSize; + return false; + } + } else { + uint32_t byteLength = JSTaggedNumber(taggedLength).ToUint32(); + // Write Accessors(ArrayBufferData) which is a pointer to a DynBuffer + JSHandle np(thread_, arrayBuffer->GetArrayBufferData()); + void *buffer = np->GetExternalPointer(); + if (!WriteRawData(buffer, byteLength)) { + bufferSize_ = oldSize; + return false; + } + } + + // write obj properties + if (!WritePlainObject(value)) { + bufferSize_ = oldSize; + return false; + } + + return true; +} + +bool JSSerializer::WritePlainObject(const JSHandle &objValue) +{ + JSHandle obj = JSHandle::Cast(objValue); + size_t oldSize = bufferSize_; + if (!WriteType(SerializationUID::JS_PLAIN_OBJECT)) { + return false; + } + // Get the number of elements stored in obj + uint32_t elementsLength = obj->GetNumberOfElements(); + if (!WriteLength(elementsLength)) { + bufferSize_ = oldSize; + return false; + } + std::vector keyVector; + JSObject::GetALLElementKeysIntoVector(thread_, obj, keyVector); + // Write elements' description attributes and value + if (keyVector.size() != elementsLength) { + bufferSize_ = oldSize; + return false; + } + for (uint32_t i = 0; i < elementsLength; i++) { + JSHandle key(thread_, keyVector[i]); + if (!SerializeJSTaggedValue(key)) { + bufferSize_ = oldSize; + return false; + } + PropertyDescriptor desc(thread_); + JSObject::OrdinaryGetOwnProperty(thread_, obj, key, desc); + if (!WriteDesc(desc)) { + bufferSize_ = oldSize; + return false; + } + JSHandle value = desc.GetValue(); + if (!SerializeJSTaggedValue(value)) { + bufferSize_ = oldSize; + return false; + } + } + // Get the number of k-v form properties stored in obj + keyVector.clear(); + uint32_t propertiesLength = obj->GetNumberOfKeys(); + if (!WriteLength(propertiesLength)) { + bufferSize_ = oldSize; + return false; + } + JSObject::GetAllKeys(thread_, obj, keyVector); + if (keyVector.size() != propertiesLength) { + bufferSize_ = oldSize; + return false; + } + // Write keys' description attributes and related values + for (uint32_t i = 0; i < propertiesLength; i++) { + if (keyVector.empty()) { + bufferSize_ = oldSize; + return false; + } + JSHandle key(thread_, keyVector[i]); + if (!SerializeJSTaggedValue(key)) { + bufferSize_ = oldSize; + return false; + } + PropertyDescriptor desc(thread_); + JSObject::OrdinaryGetOwnProperty(thread_, obj, key, desc); + if (!WriteDesc(desc)) { + bufferSize_ = oldSize; + return false; + } + JSHandle value = desc.GetValue(); + if (!SerializeJSTaggedValue(value)) { + bufferSize_ = oldSize; + return false; + } + } + return true; +} + +bool JSSerializer::WriteDesc(const PropertyDescriptor &desc) +{ + size_t oldSize = bufferSize_; + bool isWritable = desc.IsWritable(); + if (!WriteBoolean(isWritable)) { + bufferSize_ = oldSize; + return false; + } + bool isEnumerable = desc.IsEnumerable(); + if (!WriteBoolean(isEnumerable)) { + bufferSize_ = oldSize; + return false; + } + bool isConfigurable = desc.IsConfigurable(); + if (!WriteBoolean(isConfigurable)) { + bufferSize_ = oldSize; + return false; + } + bool hasWritable = desc.HasWritable(); + if (!WriteBoolean(hasWritable)) { + bufferSize_ = oldSize; + return false; + } + bool hasEnumerable = desc.HasEnumerable(); + if (!WriteBoolean(hasEnumerable)) { + bufferSize_ = oldSize; + return false; + } + bool hasConfigurable = desc.HasConfigurable(); + if (!WriteBoolean(hasConfigurable)) { + bufferSize_ = oldSize; + return false; + } + return true; +} + +SerializationUID JSDeserializer::ReadType() +{ + SerializationUID uid; + if (position_ >= end_) { + return SerializationUID::UNKNOWN; + } + uid = static_cast(*position_); + if (uid < SerializationUID::JS_NULL || uid > SerializationUID::NATIVE_FUNCTION_POINTER) { + return SerializationUID::UNKNOWN; + } + position_++; // NOLINT(cppcoreguidelines-pro-bounds-pointer-arithmetic) + return uid; +} + +bool JSDeserializer::ReadInt(int32_t *value) +{ + size_t len = sizeof(int32_t); + if (len > static_cast(end_ - position_)) { + return false; + } + if (memcpy_s(value, len, position_, len) != EOK) { + UNREACHABLE(); + } + // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-pointer-arithmetic) + position_ += len; + return true; +} + +bool JSDeserializer::ReadInt(uint32_t *value) +{ + size_t len = sizeof(uint32_t); + if (len > static_cast(end_ - position_)) { + return false; + } + if (memcpy_s(value, len, position_, len) != EOK) { + UNREACHABLE(); + } + // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-pointer-arithmetic) + position_ += len; + return true; +} + +bool JSDeserializer::ReadObjectId(uint64_t *objectId) +{ + size_t len = sizeof(uint64_t); + if (len > static_cast(end_ - position_)) { + return false; + } + if (memcpy_s(objectId, len, position_, len) != EOK) { + UNREACHABLE(); + } + // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-pointer-arithmetic) + position_ += len; + return true; +} + +bool JSDeserializer::ReadDouble(double *value) +{ + size_t len = sizeof(double); + if (len > static_cast(end_ - position_)) { + return false; + } + if (memcpy_s(value, len, position_, len) != EOK) { + UNREACHABLE(); + } + // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-pointer-arithmetic) + position_ += len; + return true; +} + +JSDeserializer::~JSDeserializer() +{ + // NOLINTNEXTLINE(cppcoreguidelines-no-malloc) + free(begin_); + begin_ = nullptr; +} + +JSHandle JSDeserializer::DeserializeJSTaggedValue() +{ + SerializationUID uid = ReadType(); + if (uid == SerializationUID::UNKNOWN) { + return JSHandle(); + } + switch (uid) { + case SerializationUID::JS_NULL: + return JSHandle(thread_, JSTaggedValue::Null()); + case SerializationUID::JS_UNDEFINED: + return JSHandle(thread_, JSTaggedValue::Undefined()); + case SerializationUID::JS_TRUE: + return JSHandle(thread_, JSTaggedValue::True()); + case SerializationUID::JS_FALSE: + return JSHandle(thread_, JSTaggedValue::False()); + case SerializationUID::HOLE: + return JSHandle(thread_, JSTaggedValue::Hole()); + case SerializationUID::INT32: { + int32_t value; + if (!ReadInt(&value)) { + return JSHandle(); + } + return JSHandle(thread_, JSTaggedValue(value)); + } + case SerializationUID::UINT32: { + uint32_t value; + if (!ReadInt(&value)) { + return JSHandle(); + } + return JSHandle(thread_, JSTaggedValue(value)); + } + case SerializationUID::DOUBLE: { + double value; + if (!ReadDouble(&value)) { + return JSHandle(); + } + return JSHandle(thread_, JSTaggedValue(value)); + } + case SerializationUID::JS_ERROR: + case SerializationUID::EVAL_ERROR: + case SerializationUID::RANGE_ERROR: + case SerializationUID::REFERENCE_ERROR: + case SerializationUID::TYPE_ERROR: + case SerializationUID::URI_ERROR: + case SerializationUID::SYNTAX_ERROR: + return ReadJSError(uid); + case SerializationUID::JS_DATE: + return ReadJSDate(); + case SerializationUID::JS_PLAIN_OBJECT: + return ReadPlainObject(); + case SerializationUID::JS_ARRAY: + return ReadJSArray(); + case SerializationUID::ECMASTRING: + return ReadEcmaString(); + case SerializationUID::JS_MAP: + return ReadJSMap(); + case SerializationUID::JS_SET: + return ReadJSSet(); + case SerializationUID::JS_REG_EXP: + return ReadJSRegExp(); + case SerializationUID::JS_INT8_ARRAY: + return ReadJSTypedArray(SerializationUID::JS_INT8_ARRAY); + case SerializationUID::JS_UINT8_ARRAY: + return ReadJSTypedArray(SerializationUID::JS_UINT8_ARRAY); + case SerializationUID::JS_UINT8_CLAMPED_ARRAY: + return ReadJSTypedArray(SerializationUID::JS_UINT8_CLAMPED_ARRAY); + case SerializationUID::JS_INT16_ARRAY: + return ReadJSTypedArray(SerializationUID::JS_INT16_ARRAY); + case SerializationUID::JS_UINT16_ARRAY: + return ReadJSTypedArray(SerializationUID::JS_UINT16_ARRAY); + case SerializationUID::JS_INT32_ARRAY: + return ReadJSTypedArray(SerializationUID::JS_INT32_ARRAY); + case SerializationUID::JS_UINT32_ARRAY: + return ReadJSTypedArray(SerializationUID::JS_UINT32_ARRAY); + case SerializationUID::JS_FLOAT32_ARRAY: + return ReadJSTypedArray(SerializationUID::JS_FLOAT32_ARRAY); + case SerializationUID::JS_FLOAT64_ARRAY: + return ReadJSTypedArray(SerializationUID::JS_FLOAT64_ARRAY); + case SerializationUID::NATIVE_FUNCTION_POINTER: + return ReadNativeFunctionPointer(); + case SerializationUID::JS_ARRAY_BUFFER: + return ReadJSArrayBuffer(); + case SerializationUID::TAGGED_OBJECT_REFERNCE: + return ReadReference(); + default: + return JSHandle(); + } +} + +JSHandle JSDeserializer::ReadJSError(SerializationUID uid) +{ + ObjectFactory *factory = thread_->GetEcmaVM()->GetFactory(); + base::ErrorType errorType; + switch (uid) { + case SerializationUID::JS_ERROR: + errorType = base::ErrorType::ERROR; + break; + case SerializationUID::EVAL_ERROR: + errorType = base::ErrorType::EVAL_ERROR; + break; + case SerializationUID::RANGE_ERROR: + errorType = base::ErrorType::RANGE_ERROR; + break; + case SerializationUID::REFERENCE_ERROR: + errorType = base::ErrorType::REFERENCE_ERROR; + break; + case SerializationUID::TYPE_ERROR: + errorType = base::ErrorType::TYPE_ERROR; + break; + case SerializationUID::URI_ERROR: + errorType = base::ErrorType::URI_ERROR; + break; + case SerializationUID::SYNTAX_ERROR: + errorType = base::ErrorType::URI_ERROR; + break; + default: + UNREACHABLE(); + } + JSHandle msg = DeserializeJSTaggedValue(); + JSHandle handleMsg(msg); + JSHandle errorTag = JSHandle::Cast(factory->NewJSError(errorType, handleMsg)); + referenceMap_.insert(std::pair(objectId_++, errorTag)); + return errorTag; +} + +JSHandle JSDeserializer::ReadJSDate() +{ + ObjectFactory *factory = thread_->GetEcmaVM()->GetFactory(); + JSHandle env = thread_->GetEcmaVM()->GetGlobalEnv(); + JSHandle dateFunction = env->GetDateFunction(); + JSHandle date = + JSHandle::Cast(factory->NewJSObjectByConstructor(JSHandle(dateFunction), dateFunction)); + JSHandle dateTag = JSHandle::Cast(date); + referenceMap_.insert(std::pair(objectId_++, dateTag)); + if (!JudgeType(SerializationUID::JS_PLAIN_OBJECT) || !DefinePropertiesAndElements(dateTag)) { + return JSHandle(); + } + double timeValue; + if (!JudgeType(SerializationUID::DOUBLE) || !ReadDouble(&timeValue)) { + return JSHandle(); + } + date->SetTimeValue(thread_, JSTaggedValue(timeValue)); + double localOffset; + if (!JudgeType(SerializationUID::DOUBLE) || !ReadDouble(&localOffset)) { + return JSHandle(); + } + date->SetLocalOffset(thread_, JSTaggedValue(localOffset)); + return dateTag; +} + +JSHandle JSDeserializer::ReadJSArray() +{ + JSHandle jsArray = thread_->GetEcmaVM()->GetFactory()->NewJSArray(); + JSHandle arrayTag = JSHandle::Cast(jsArray); + referenceMap_.insert(std::pair(objectId_++, arrayTag)); + if (!JudgeType(SerializationUID::JS_PLAIN_OBJECT) || !DefinePropertiesAndElements(arrayTag)) { + return JSHandle(); + } + uint32_t arrLength; + if (!JudgeType(SerializationUID::UINT32) || !ReadInt(&arrLength)) { + return JSHandle(); + } + jsArray->SetLength(thread_, JSTaggedValue(arrLength)); + return arrayTag; +} + +JSHandle JSDeserializer::ReadEcmaString() +{ + uint32_t stringLength; + if (!ReadInt(&stringLength)) { + return JSHandle(); + } + ObjectFactory *factory = thread_->GetEcmaVM()->GetFactory(); + auto *string = reinterpret_cast(GetBuffer(stringLength + 1)); + if (string == nullptr) { + return JSHandle(); + } + + JSHandle ecmaString = factory->NewFromUtf8(string, stringLength); + auto stringTag = JSHandle(ecmaString); + referenceMap_.insert(std::pair(objectId_++, stringTag)); + return stringTag; +} + +JSHandle JSDeserializer::ReadPlainObject() +{ + JSHandle env = thread_->GetEcmaVM()->GetGlobalEnv(); + JSHandle objFunc(thread_, env->GetObjectFunction().GetObject()); + JSHandle jsObject = + thread_->GetEcmaVM()->GetFactory()->NewJSObjectByConstructor(JSHandle(objFunc), objFunc); + JSHandle objTag = JSHandle::Cast(jsObject); + referenceMap_.insert(std::pair(objectId_++, objTag)); + if (!DefinePropertiesAndElements(objTag)) { + return JSHandle(); + } + return objTag; +} + +JSHandle JSDeserializer::ReadJSMap() +{ + ObjectFactory *factory = thread_->GetEcmaVM()->GetFactory(); + JSHandle env = thread_->GetEcmaVM()->GetGlobalEnv(); + JSHandle mapFunction = env->GetBuiltinsMapFunction(); + JSHandle jsMap = + JSHandle::Cast(factory->NewJSObjectByConstructor(JSHandle(mapFunction), mapFunction)); + JSHandle mapTag = JSHandle::Cast(jsMap); + referenceMap_.insert(std::pair(objectId_++, mapTag)); + if (!JudgeType(SerializationUID::JS_PLAIN_OBJECT) || !DefinePropertiesAndElements(mapTag)) { + return JSHandle(); + } + uint32_t size; + if (!ReadInt(&size)) { + return JSHandle(); + } + JSHandle linkedMap = LinkedHashMap::Create(thread_); + jsMap->SetLinkedMap(thread_, linkedMap); + for (uint32_t i = 0; i < size; i++) { + JSHandle key = DeserializeJSTaggedValue(); + if (key.IsEmpty()) { + return JSHandle(); + } + JSHandle value = DeserializeJSTaggedValue(); + if (value.IsEmpty()) { + return JSHandle(); + } + JSMap::Set(thread_, jsMap, key, value); + } + return mapTag; +} + +JSHandle JSDeserializer::ReadJSSet() +{ + ObjectFactory *factory = thread_->GetEcmaVM()->GetFactory(); + JSHandle env = thread_->GetEcmaVM()->GetGlobalEnv(); + JSHandle setFunction = env->GetBuiltinsSetFunction(); + JSHandle jsSet = + JSHandle::Cast(factory->NewJSObjectByConstructor(JSHandle(setFunction), setFunction)); + JSHandle setTag = JSHandle::Cast(jsSet); + referenceMap_.insert(std::pair(objectId_++, setTag)); + if (!JudgeType(SerializationUID::JS_PLAIN_OBJECT) || !DefinePropertiesAndElements(setTag)) { + return JSHandle(); + } + uint32_t size; + if (!ReadInt(&size)) { + return JSHandle(); + } + JSHandle linkedSet = LinkedHashSet::Create(thread_); + jsSet->SetLinkedSet(thread_, linkedSet); + for (uint32_t i = 0; i < size; i++) { + JSHandle key = DeserializeJSTaggedValue(); + if (key.IsEmpty()) { + return JSHandle(); + } + JSSet::Add(thread_, jsSet, key); + } + return setTag; +} + +JSHandle JSDeserializer::ReadJSRegExp() +{ + ObjectFactory *factory = thread_->GetEcmaVM()->GetFactory(); + JSHandle env = thread_->GetEcmaVM()->GetGlobalEnv(); + JSHandle regexpFunction = env->GetRegExpFunction(); + JSHandle obj = factory->NewJSObjectByConstructor(JSHandle(regexpFunction), regexpFunction); + JSHandle regExp = JSHandle::Cast(obj); + JSHandle regexpTag = JSHandle::Cast(regExp); + referenceMap_.insert(std::pair(objectId_++, regexpTag)); + if (!JudgeType(SerializationUID::JS_PLAIN_OBJECT) || !DefinePropertiesAndElements(regexpTag)) { + return JSHandle(); + } + uint32_t bufferSize; + if (!ReadInt(&bufferSize)) { + return JSHandle(); + } + void *buffer = GetBuffer(bufferSize); + if (buffer == nullptr) { + return JSHandle(); + } + factory->NewJSRegExpByteCodeData(regExp, buffer, bufferSize); + JSHandle originalSource = DeserializeJSTaggedValue(); + regExp->SetOriginalSource(thread_, originalSource); + JSHandle originalFlags = DeserializeJSTaggedValue(); + regExp->SetOriginalFlags(thread_, originalFlags); + return regexpTag; +} + +JSHandle JSDeserializer::ReadJSTypedArray(SerializationUID uid) +{ + ObjectFactory *factory = thread_->GetEcmaVM()->GetFactory(); + JSHandle env = thread_->GetEcmaVM()->GetGlobalEnv(); + JSHandle target; + JSHandle obj; + JSHandle objTag; + switch (uid) { + case SerializationUID::JS_INT8_ARRAY: { + target = env->GetInt8ArrayFunction(); + break; + } + case SerializationUID::JS_UINT8_ARRAY: { + target = env->GetUint8ArrayFunction(); + break; + } + case SerializationUID::JS_UINT8_CLAMPED_ARRAY: { + target = env->GetUint8ClampedArrayFunction(); + break; + } + case SerializationUID::JS_INT16_ARRAY: { + target = env->GetInt16ArrayFunction(); + break; + } + case SerializationUID::JS_UINT16_ARRAY: { + target = env->GetUint16ArrayFunction(); + break; + } + case SerializationUID::JS_INT32_ARRAY: { + target = env->GetInt32ArrayFunction(); + break; + } + case SerializationUID::JS_UINT32_ARRAY: { + target = env->GetUint32ArrayFunction(); + break; + } + case SerializationUID::JS_FLOAT32_ARRAY: { + target = env->GetFloat32ArrayFunction(); + break; + } + case SerializationUID::JS_FLOAT64_ARRAY: { + target = env->GetFloat64ArrayFunction(); + break; + } + default: + UNREACHABLE(); + } + JSHandle typedArray = + JSHandle::Cast(factory->NewJSObjectByConstructor(JSHandle(target), target)); + obj = JSHandle::Cast(typedArray); + objTag = JSHandle::Cast(obj); + referenceMap_.insert(std::pair(objectId_++, objTag)); + if (!JudgeType(SerializationUID::JS_PLAIN_OBJECT) || !DefinePropertiesAndElements(objTag)) { + return JSHandle(); + } + + JSHandle viewedArrayBuffer = DeserializeJSTaggedValue(); + if (viewedArrayBuffer.IsEmpty()) { + return JSHandle(); + } + typedArray->SetViewedArrayBuffer(thread_, viewedArrayBuffer); + + JSHandle typedArrayName = DeserializeJSTaggedValue(); + if (typedArrayName.IsEmpty()) { + return JSHandle(); + } + typedArray->SetTypedArrayName(thread_, typedArrayName); + + JSTaggedValue byteLength; + if (!ReadJSTaggedValue(&byteLength) || !byteLength.IsNumber()) { + return JSHandle(); + } + typedArray->SetByteLength(thread_, byteLength); + + JSTaggedValue byteOffset; + if (!ReadJSTaggedValue(&byteOffset) || !byteOffset.IsNumber()) { + return JSHandle(); + } + typedArray->SetByteOffset(thread_, byteOffset); + + JSTaggedValue arrayLength; + if (!ReadJSTaggedValue(&arrayLength) || !byteOffset.IsNumber()) { + return JSHandle(); + } + typedArray->SetArrayLength(thread_, arrayLength); + return objTag; +} + +JSHandle JSDeserializer::ReadNativeFunctionPointer() +{ + JSTaggedValue pointer; + if (!ReadJSTaggedValue(&pointer)) { + return JSHandle(); + } + return JSHandle(thread_, pointer); +} + +JSHandle JSDeserializer::ReadJSArrayBuffer() +{ + ObjectFactory *factory = thread_->GetEcmaVM()->GetFactory(); + // read access length + JSTaggedValue taggedLength; + if (!ReadJSTaggedValue(&taggedLength)) { + return JSHandle(); + } + // read access shared + bool shared = false; + if (!ReadBoolean(&shared)) { + return JSHandle(); + } + // create jsarraybuffer + JSHandle arrayBufferTag; + uint32_t byteLength = JSTaggedNumber(taggedLength).ToUint32(); + if (shared) { + auto *bufferAddr = static_cast(GetBuffer(sizeof(uint64_t))); + void *bufferData = ToVoidPtr(*bufferAddr); + JSHandle arrayBuffer = factory->NewJSArrayBuffer(bufferData, byteLength, nullptr, nullptr); + arrayBufferTag = JSHandle::Cast(arrayBuffer); + referenceMap_.insert(std::pair(objectId_++, arrayBufferTag)); + } else { + void *fromBuffer = GetBuffer(byteLength); + if (fromBuffer == nullptr) { + return arrayBufferTag; + } + JSHandle arrayBuffer = factory->NewJSArrayBuffer(byteLength); + arrayBufferTag = JSHandle::Cast(arrayBuffer); + referenceMap_.insert(std::pair(objectId_++, arrayBufferTag)); + JSHandle np(thread_, arrayBuffer->GetArrayBufferData()); + void *toBuffer = np->GetExternalPointer(); + if (memcpy_s(toBuffer, byteLength, fromBuffer, byteLength) != EOK) { + UNREACHABLE(); + } + } + // read jsarraybuffer properties + if (!JudgeType(SerializationUID::JS_PLAIN_OBJECT) || !DefinePropertiesAndElements(arrayBufferTag)) { + return JSHandle(); + } + + return arrayBufferTag; +} + +bool JSDeserializer::ReadJSTaggedValue(JSTaggedValue *value) +{ + size_t len = sizeof(JSTaggedValue); + if (len > static_cast(end_ - position_)) { + return false; + } + if (memcpy_s(value, len, position_, len) != EOK) { + UNREACHABLE(); + } + // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-pointer-arithmetic) + position_ += len; + return true; +} + +void *JSDeserializer::GetBuffer(uint32_t bufferSize) +{ + const uint8_t *buffer = nullptr; + if (bufferSize > static_cast(end_ - position_)) { + return nullptr; + } + buffer = position_; + // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-pointer-arithmetic) + position_ += bufferSize; + auto *retBuffer = const_cast(buffer); + return static_cast(retBuffer); +} + +JSHandle JSDeserializer::ReadReference() +{ + uint64_t objId; + if (!ReadObjectId(&objId)) { + return JSHandle(); + } + auto objIter = referenceMap_.find(objId); + if (objIter == referenceMap_.end()) { + return JSHandle(); + } + return objIter->second; +} + +bool JSDeserializer::JudgeType(SerializationUID targetUid) +{ + return ReadType() != targetUid; +} + +bool JSDeserializer::DefinePropertiesAndElements(const JSHandle &obj) +{ + uint32_t elementLength; + if (!ReadInt(&elementLength)) { + return false; + } + for (uint32_t i = 0; i < elementLength; i++) { + JSHandle key = DeserializeJSTaggedValue(); + if (key.IsEmpty()) { + return false; + } + PropertyDescriptor desc(thread_); + if (!ReadDesc(&desc)) { + return false; + } + JSHandle value = DeserializeJSTaggedValue(); + if (value.IsEmpty()) { + return false; + } + desc.SetValue(value); + if (!JSTaggedValue::DefineOwnProperty(thread_, obj, key, desc)) { + return false; + } + } + + uint32_t propertyLength; + if (!ReadInt(&propertyLength)) { + return false; + } + for (uint32_t i = 0; i < propertyLength; i++) { + JSHandle key = DeserializeJSTaggedValue(); + if (key.IsEmpty()) { + return false; + } + PropertyDescriptor desc(thread_); + if (!ReadDesc(&desc)) { + return false; + } + JSHandle value = DeserializeJSTaggedValue(); + if (value.IsEmpty()) { + return false; + } + desc.SetValue(value); + if (!JSTaggedValue::DefineOwnProperty(thread_, obj, key, desc)) { + return false; + } + } + return true; +} + +bool JSDeserializer::ReadDesc(PropertyDescriptor *desc) +{ + bool isWritable = false; + if (!ReadBoolean(&isWritable)) { + return false; + } + bool isEnumerable = false; + if (!ReadBoolean(&isEnumerable)) { + return false; + } + bool isConfigurable = false; + if (!ReadBoolean(&isConfigurable)) { + return false; + } + bool hasWritable = false; + if (!ReadBoolean(&hasWritable)) { + return false; + } + bool hasEnumerable = false; + if (!ReadBoolean(&hasEnumerable)) { + return false; + } + bool hasConfigurable = false; + if (!ReadBoolean(&hasConfigurable)) { + return false; + } + if (hasWritable) { + desc->SetWritable(isWritable); + } + if (hasEnumerable) { + desc->SetEnumerable(isEnumerable); + } + if (hasConfigurable) { + desc->SetConfigurable(isConfigurable); + } + return true; +} + +bool JSDeserializer::ReadBoolean(bool *value) +{ + SerializationUID uid = ReadType(); + if (uid == SerializationUID::C_TRUE) { + *value = true; + return true; + } + if (uid == SerializationUID::C_FALSE) { + *value = false; + return true; + } + return false; +} + +bool Serializer::WriteValue(JSThread *thread, const JSHandle &value, + const JSHandle &transfer) +{ + if (data_ != nullptr) { + return false; + } + // NOLINTNEXTLINE(modernize-make-unique) + data_.reset(new SerializationData); + if (!PrepareTransfer(thread, transfer)) { + return false; + } + if (!valueSerializer_.SerializeJSTaggedValue(value)) { + return false; + } + if (!FinalizeTransfer(thread, transfer)) { + return false; + } + std::pair pair = valueSerializer_.ReleaseBuffer(); + data_->value_.reset(pair.first); + data_->dataSize_ = pair.second; + return true; +} + +std::unique_ptr Serializer::Release() +{ + return std::move(data_); +} + +bool Serializer::PrepareTransfer(JSThread *thread, const JSHandle &transfer) +{ + if (transfer->IsUndefined()) { + return true; + } + if (!transfer->IsJSArray()) { + return false; + } + auto len = static_cast(base::ArrayHelper::GetArrayLength(thread, transfer)); + int32_t k = 0; + while (k < len) { + bool exists = JSTaggedValue::HasProperty(thread, transfer, k); + if (exists) { + JSHandle element = JSArray::FastGetPropertyByValue(thread, transfer, k); + if (!element->IsArrayBuffer()) { + return false; + } + arrayBufferIdxs_.emplace_back(k); + } + k++; + } + return true; +} + +bool Serializer::FinalizeTransfer(JSThread *thread, const JSHandle &transfer) +{ + for (int idx : arrayBufferIdxs_) { + JSHandle element = JSArray::FastGetPropertyByValue(thread, transfer, idx); + JSArrayBuffer::Cast(element->GetHeapObject())->Detach(thread); + } + return true; +} + +JSHandle Deserializer::ReadValue() +{ + return valueDeserializer_.DeserializeJSTaggedValue(); +} +} // namespace panda::ecmascript diff --git a/runtime/js_serializer.h b/runtime/js_serializer.h new file mode 100644 index 000000000..b499dc590 --- /dev/null +++ b/runtime/js_serializer.h @@ -0,0 +1,255 @@ +/* + * Copyright (c) 2021-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_JS_SERIALIZER_H +#define ECMASCRIPT_JS_SERIALIZER_H + +#include + +#include "plugins/ecmascript/runtime/ecma_vm.h" +#include "plugins/ecmascript/runtime/js_date.h" +#include "plugins/ecmascript/runtime/js_map.h" +#include "plugins/ecmascript/runtime/js_native_pointer.h" +#include "plugins/ecmascript/runtime/js_object.h" +#include "plugins/ecmascript/runtime/js_thread.h" +#include "plugins/ecmascript/runtime/js_typed_array.h" +#include "plugins/ecmascript/runtime/regexp/dyn_chunk.h" + +namespace panda::ecmascript { +enum class SerializationUID : uint8_t { + // JS special values + JS_NULL = 0x01, + JS_UNDEFINED, + JS_TRUE, + JS_FALSE, + HOLE, + // Number types + INT32, + UINT32, + DOUBLE, + // Not support yet, BigInt type has not been implemented in ark engine + BIGINT, + ECMASTRING, + // Boolean types + C_TRUE, + C_FALSE, + // Tagged object reference mark + TAGGED_OBJECT_REFERNCE, + // Support tagged objct id reference begin + JS_DATE, + JS_REG_EXP, + JS_PLAIN_OBJECT, + JS_SET, + JS_MAP, + JS_ARRAY, + JS_ARRAY_BUFFER, + // TypedArray begin + JS_UINT8_ARRAY, + JS_UINT8_CLAMPED_ARRAY, + JS_UINT16_ARRAY, + JS_UINT32_ARRAY, + JS_INT8_ARRAY, + JS_INT16_ARRAY, + JS_INT32_ARRAY, + JS_FLOAT32_ARRAY, + JS_FLOAT64_ARRAY, + // TypedArray end + // Support tagged objct id reference end + // Error UIDs + JS_ERROR, + EVAL_ERROR, + RANGE_ERROR, + REFERENCE_ERROR, + TYPE_ERROR, + URI_ERROR, + SYNTAX_ERROR, + ERROR_MESSAGE_BEGIN, + ERROR_MESSAGE_END, + // NativeFunctionPointer + NATIVE_FUNCTION_POINTER, + UNKNOWN +}; + +class JSSerializer { +public: + explicit JSSerializer(JSThread *thread) : thread_(thread) {} + ~JSSerializer() = default; + bool SerializeJSTaggedValue(const JSHandle &value); + + // Return pointer to the buffer and its length, should not use this Serializer anymore after Release + std::pair ReleaseBuffer(); + +private: + bool WriteTaggedObject(const JSHandle &value); + bool WritePrimitiveValue(const JSHandle &value); + bool WriteInt(int32_t value); + bool WriteInt(uint32_t value); + bool WriteDouble(double value); + bool WriteRawData(const void *data, size_t length); + bool WriteType(SerializationUID uId); + bool AllocateBuffer(size_t bytes); + bool ExpandBuffer(size_t requestedSize); + bool WriteBoolean(bool value); + bool WriteJSError(const JSHandle &value); + bool WriteJSErrorHeader(JSType type); + bool WriteJSDate(const JSHandle &value); + bool WriteJSArray(const JSHandle &value); + bool WriteJSMap(const JSHandle &value); + bool WriteJSSet(const JSHandle &value); + bool WriteJSRegExp(const JSHandle &value); + bool WriteEcmaString(const JSHandle &value); + bool WriteJSTypedArray(const JSHandle &value, SerializationUID uId); + bool WritePlainObject(const JSHandle &value); + bool WriteLength(uint32_t length); + bool WriteNativeFunctionPointer(const JSHandle &value); + bool WriteJSArrayBuffer(const JSHandle &value); + bool WriteDesc(const PropertyDescriptor &desc); + bool IsSerialized(uintptr_t addr) const; + bool WriteIfSerialized(uintptr_t addr); + + NO_MOVE_SEMANTIC(JSSerializer); + NO_COPY_SEMANTIC(JSSerializer); + + JSThread *thread_; + uint8_t *buffer_ = nullptr; + uint64_t sizeLimit_ = 0; + size_t bufferSize_ = 0; + size_t bufferCapacity_ = 0; + // The Reference map is used for check whether a tagged object has been serialized + // Reference map works only if no gc happens during serialization + std::map referenceMap_; + uint64_t objectId_ = 0; +}; + +class JSDeserializer { +public: + JSDeserializer(JSThread *thread, uint8_t *data, size_t size) + : thread_(thread), + begin_(data), + position_(data), + end_(data + size) // NOLINT(cppcoreguidelines-pro-bounds-pointer-arithmetic) + { + } + ~JSDeserializer(); + JSHandle DeserializeJSTaggedValue(); + +private: + bool ReadInt(int32_t *value); + bool ReadInt(uint32_t *value); + bool ReadObjectId(uint64_t *objectId); + bool ReadDouble(double *value); + SerializationUID ReadType(); + JSHandle ReadJSError(SerializationUID uid); + JSHandle ReadJSDate(); + JSHandle ReadJSArray(); + JSHandle ReadPlainObject(); + JSHandle ReadEcmaString(); + JSHandle ReadJSMap(); + JSHandle ReadJSSet(); + JSHandle ReadJSRegExp(); + JSHandle ReadJSTypedArray(SerializationUID uid); + JSHandle ReadNativeFunctionPointer(); + JSHandle ReadJSArrayBuffer(); + JSHandle ReadReference(); + bool JudgeType(SerializationUID targetUid); + void *GetBuffer(uint32_t bufferSize); + bool ReadJSTaggedValue(JSTaggedValue *value); + bool DefinePropertiesAndElements(const JSHandle &obj); + bool ReadDesc(PropertyDescriptor *desc); + bool ReadBoolean(bool *value); + + NO_MOVE_SEMANTIC(JSDeserializer); + NO_COPY_SEMANTIC(JSDeserializer); + + JSThread *thread_ = nullptr; + uint8_t *begin_ = nullptr; + const uint8_t *position_ = nullptr; + const uint8_t *const end_ = nullptr; + uint64_t objectId_ = 0; + std::map> referenceMap_; +}; + +class SerializationData { +public: + SerializationData() : dataSize_(0), value_(nullptr) {} + ~SerializationData() = default; + + uint8_t *GetData() const + { + return value_.get(); + } + size_t GetSize() const + { + return dataSize_; + } + + NO_MOVE_SEMANTIC(SerializationData); + NO_COPY_SEMANTIC(SerializationData); + +private: + struct Deleter { + void operator()(uint8_t *ptr) const + { + // NOLINTNEXTLINE(cppcoreguidelines-no-malloc) + free(ptr); + } + }; + // NOLINTNEXTLINE(modernize-use-default-member-init) + size_t dataSize_; + std::unique_ptr value_; + +private: + friend class Serializer; +}; + +class Serializer { +public: + explicit Serializer(JSThread *thread) : valueSerializer_(thread) {} + ~Serializer() = default; + + bool WriteValue(JSThread *thread, const JSHandle &value, const JSHandle &transfer); + std::unique_ptr Release(); + + NO_MOVE_SEMANTIC(Serializer); + NO_COPY_SEMANTIC(Serializer); + +private: + bool PrepareTransfer(JSThread *thread, const JSHandle &transfer); + bool FinalizeTransfer(JSThread *thread, const JSHandle &transfer); + + ecmascript::JSSerializer valueSerializer_; + std::unique_ptr data_; + CVector arrayBufferIdxs_; +}; + +class Deserializer { +public: + explicit Deserializer(JSThread *thread, SerializationData *data) + : valueDeserializer_(thread, data->GetData(), data->GetSize()) + { + } + ~Deserializer() = default; + + JSHandle ReadValue(); + + NO_MOVE_SEMANTIC(Deserializer); + NO_COPY_SEMANTIC(Deserializer); + +private: + ecmascript::JSDeserializer valueDeserializer_; +}; +} // namespace panda::ecmascript + +#endif // ECMASCRIPT_JS_SERIALIZER_H diff --git a/runtime/js_set.cpp b/runtime/js_set.cpp new file mode 100644 index 000000000..2b4be7ce6 --- /dev/null +++ b/runtime/js_set.cpp @@ -0,0 +1,69 @@ +/* + * Copyright (c) 2021-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 "js_set.h" +#include "plugins/ecmascript/runtime/js_tagged_value.h" +#include "plugins/ecmascript/runtime/object_factory.h" +#include "linked_hash_table-inl.h" +#include "utils/bit_utils.h" + +namespace panda::ecmascript { +void JSSet::Add(JSThread *thread, const JSHandle &set, const JSHandle &value) +{ + if (!LinkedHashSet::IsKey(value.GetTaggedValue())) { + // throw error + THROW_TYPE_ERROR(thread, "the value must be Key of JSSet"); + } + JSHandle setHandle(thread, LinkedHashSet::Cast(set->GetLinkedSet().GetTaggedObject())); + + JSHandle newSet = LinkedHashSet::Add(thread, setHandle, value); + set->SetLinkedSet(thread, newSet); +} + +bool JSSet::Delete(const JSThread *thread, const JSHandle &set, const JSHandle &value) +{ + JSHandle setHandle(thread, LinkedHashSet::Cast(set->GetLinkedSet().GetTaggedObject())); + int entry = setHandle->FindElement(value.GetTaggedValue()); + if (entry == -1) { + return false; + } + setHandle->RemoveEntry(thread, entry); + JSHandle newSet = LinkedHashSet::Shrink(thread, setHandle); + set->SetLinkedSet(thread, newSet); + return true; +} + +void JSSet::Clear(const JSThread *thread, const JSHandle &set) +{ + LinkedHashSet *linkedSet = LinkedHashSet::Cast(set->GetLinkedSet().GetTaggedObject()); + linkedSet->Clear(thread); +} + +bool JSSet::Has(JSTaggedValue value) const +{ + return LinkedHashSet::Cast(GetLinkedSet().GetTaggedObject())->Has(value); +} + +int JSSet::GetSize() const +{ + return LinkedHashSet::Cast(GetLinkedSet().GetTaggedObject())->NumberOfElements(); +} + +JSTaggedValue JSSet::GetValue(int entry) const +{ + ASSERT_PRINT(entry >= 0 && entry < GetSize(), "entry must be non-negative integer less than capacity"); + return LinkedHashSet::Cast(GetLinkedSet().GetTaggedObject())->GetValue(entry); +} +} // namespace panda::ecmascript diff --git a/runtime/js_set.h b/runtime/js_set.h new file mode 100644 index 000000000..9db9af344 --- /dev/null +++ b/runtime/js_set.h @@ -0,0 +1,48 @@ +/* + * Copyright (c) 2021-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_JSSET_H +#define ECMASCRIPT_JSSET_H + +#include +#include "js_object.h" +#include "js_tagged_value-inl.h" + +namespace panda::ecmascript { +class JSSet : public JSObject { +public: + CAST_CHECK(JSSet, IsJSSet); + + static bool Delete(const JSThread *thread, const JSHandle &set, const JSHandle &value); + + static void Add(JSThread *thread, const JSHandle &set, const JSHandle &value); + + static void Clear(const JSThread *thread, const JSHandle &set); + + bool Has(JSTaggedValue value) const; + + int GetSize() const; + + JSTaggedValue GetValue(int entry) const; + + static constexpr size_t LINKED_SET_OFFSET = JSObject::SIZE; + ACCESSORS(LinkedSet, LINKED_SET_OFFSET, SIZE) + + DECL_VISIT_OBJECT_FOR_JS_OBJECT(JSObject, LINKED_SET_OFFSET, SIZE) + DECL_DUMP() +}; +} // namespace panda::ecmascript + +#endif // ECMASCRIPT_JSSET_H diff --git a/runtime/js_set_iterator.cpp b/runtime/js_set_iterator.cpp new file mode 100644 index 000000000..9cb6d1a97 --- /dev/null +++ b/runtime/js_set_iterator.cpp @@ -0,0 +1,111 @@ +/* + * Copyright (c) 2021-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 "plugins/ecmascript/runtime/js_set_iterator.h" +#include "plugins/ecmascript/runtime/builtins/builtins_errors.h" +#include "plugins/ecmascript/runtime/ecma_macros.h" +#include "plugins/ecmascript/runtime/js_array.h" +#include "plugins/ecmascript/runtime/js_set.h" +#include "plugins/ecmascript/runtime/linked_hash_table-inl.h" +#include "plugins/ecmascript/runtime/object_factory.h" + +namespace panda::ecmascript { +using BuiltinsBase = base::BuiltinsBase; +JSTaggedValue JSSetIterator::Next(EcmaRuntimeCallInfo *argv) +{ + ASSERT(argv); + JSThread *thread = argv->GetThread(); + [[maybe_unused]] EcmaHandleScope handleScope(thread); + // 1.If Type(O) is not Object, throw a TypeError exception. + JSHandle input(BuiltinsBase::GetThis(argv)); + + // 3.If O does not have all of the internal slots of a Set Iterator Instance (23.2.5.3), throw a TypeError + // exception. + if (!input->IsJSSetIterator()) { + THROW_TYPE_ERROR_AND_RETURN(thread, "this value is not a set iterator", JSTaggedValue::Exception()); + } + JSHandle iter(input); + iter->Update(thread); + JSHandle undefinedHandle(thread, JSTaggedValue::Undefined()); + // 4.Let s be O.[[IteratedSet]]. + JSHandle iteratedSet(thread, iter->GetIteratedSet()); + + // 5.Let index be O.[[SetNextIndex]]. + int index = iter->GetNextIndex().GetInt(); + IterationKind itemKind = IterationKind(iter->GetIterationKind().GetInt()); + // 7.If s is undefined, return CreateIterResultObject(undefined, true). + if (iteratedSet->IsUndefined()) { + return JSIterator::CreateIterResultObject(thread, undefinedHandle, true).GetTaggedValue(); + } + JSHandle set(iteratedSet); + int totalElements = set->NumberOfElements() + set->NumberOfDeletedElements(); + + while (index < totalElements) { + if (!set->GetKey(index).IsHole()) { + iter->SetNextIndex(thread, JSTaggedValue(index + 1)); + JSHandle key(thread, set->GetKey(index)); + // If itemKind is value + if (itemKind == IterationKind::VALUE || itemKind == IterationKind::KEY) { + return JSIterator::CreateIterResultObject(thread, key, false).GetTaggedValue(); + } + // If itemKind is key+value, then + ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); + JSHandle array(factory->NewTaggedArray(2)); // 2: key and value pair + array->Set(thread, 0, key); + array->Set(thread, 1, key); + JSHandle keyAndValue(JSArray::CreateArrayFromList(thread, array)); + return JSIterator::CreateIterResultObject(thread, keyAndValue, false).GetTaggedValue(); + } + index++; + } + // 13.Set O.[[IteratedSet]] to undefined. + iter->SetIteratedSet(thread, JSTaggedValue::Undefined()); + return JSIterator::CreateIterResultObject(thread, undefinedHandle, true).GetTaggedValue(); +} + +void JSSetIterator::Update(const JSThread *thread) +{ + [[maybe_unused]] DisallowGarbageCollection noGc; + JSTaggedValue iteratedSet = GetIteratedSet(); + if (iteratedSet.IsUndefined()) { + return; + } + LinkedHashSet *set = LinkedHashSet::Cast(iteratedSet.GetTaggedObject()); + if (set->GetNextTable().IsHole()) { + return; + } + int index = GetNextIndex().GetInt(); + JSTaggedValue nextTable = set->GetNextTable(); + while (!nextTable.IsHole()) { + index -= set->GetDeletedElementsAt(index); + set = LinkedHashSet::Cast(nextTable.GetTaggedObject()); + nextTable = set->GetNextTable(); + } + SetIteratedSet(thread, JSTaggedValue(set)); + SetNextIndex(thread, JSTaggedValue(index)); +} + +JSHandle JSSetIterator::CreateSetIterator(JSThread *thread, const JSHandle &obj, + IterationKind kind) +{ + ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); + if (!obj->IsJSSet()) { + JSHandle undefinedHandle(thread, JSTaggedValue::Undefined()); + THROW_TYPE_ERROR_AND_RETURN(thread, "obj is not JSSet", undefinedHandle); + } + JSHandle iter(factory->NewJSSetIterator(JSHandle(obj), kind)); + return iter; +} +} // namespace panda::ecmascript diff --git a/runtime/js_set_iterator.h b/runtime/js_set_iterator.h new file mode 100644 index 000000000..2c12080dc --- /dev/null +++ b/runtime/js_set_iterator.h @@ -0,0 +1,45 @@ +/* + * Copyright (c) 2021-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_JS_SET_ITERATOR_H +#define ECMASCRIPT_JS_SET_ITERATOR_H + +#include "js_object.h" +#include "js_iterator.h" + +namespace panda::ecmascript { +class JSSetIterator : public JSObject { +public: + CAST_CHECK(JSSetIterator, IsJSSetIterator); + + static JSHandle CreateSetIterator(JSThread *thread, const JSHandle &obj, + IterationKind kind); + + static JSTaggedValue Next(EcmaRuntimeCallInfo *argv); + + void Update(const JSThread *thread); + + static constexpr size_t ITERATED_SET_OFFSET = JSObject::SIZE; + ACCESSORS(IteratedSet, ITERATED_SET_OFFSET, NEXT_INDEX_OFFSET); + ACCESSORS(NextIndex, NEXT_INDEX_OFFSET, ITERATION_KIND_OFFSET); + ACCESSORS(IterationKind, ITERATION_KIND_OFFSET, SIZE); + + DECL_VISIT_OBJECT_FOR_JS_OBJECT(JSObject, ITERATED_SET_OFFSET, SIZE) + + DECL_DUMP() +}; +} // namespace panda::ecmascript + +#endif // ECMASCRIPT_JS_SET_ITERATOR_H diff --git a/runtime/js_stable_array.cpp b/runtime/js_stable_array.cpp new file mode 100644 index 000000000..fecd2990f --- /dev/null +++ b/runtime/js_stable_array.cpp @@ -0,0 +1,267 @@ +/* + * Copyright (c) 2021-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 "js_stable_array.h" +#include "plugins/ecmascript/runtime/base/builtins_base.h" +#include "plugins/ecmascript/runtime/ecma_vm.h" +#include "plugins/ecmascript/runtime/global_env.h" +#include "plugins/ecmascript/runtime/js_array.h" +#include "plugins/ecmascript/runtime/js_invoker.h" +#include "plugins/ecmascript/runtime/js_tagged_value-inl.h" +#include "plugins/ecmascript/runtime/object_factory.h" +#include "plugins/ecmascript/runtime/tagged_array.h" +#include "interpreter/fast_runtime_stub-inl.h" + +namespace panda::ecmascript { +JSTaggedValue JSStableArray::Push(JSHandle receiver, EcmaRuntimeCallInfo *argv) +{ + JSThread *thread = argv->GetThread(); + uint32_t argc = argv->GetArgsNumber(); + uint32_t oldLength = receiver->GetArrayLength(); + uint32_t newLength = argc + oldLength; + + TaggedArray *elements = TaggedArray::Cast(receiver->GetElements().GetTaggedObject()); + if (newLength > elements->GetLength()) { + elements = *JSObject::GrowElementsCapacity(thread, JSHandle::Cast(receiver), newLength); + } + + for (uint32_t k = 0; k < argc; k++) { + JSHandle value = argv->GetCallArg(k); + elements->Set(thread, oldLength + k, value.GetTaggedValue()); + } + receiver->SetArrayLength(thread, newLength); + + return JSTaggedValue(newLength); +} + +JSTaggedValue JSStableArray::Pop(JSHandle receiver, EcmaRuntimeCallInfo *argv) +{ + DISALLOW_GARBAGE_COLLECTION; + JSThread *thread = argv->GetThread(); + uint32_t length = receiver->GetArrayLength(); + if (length == 0) { + return JSTaggedValue::Undefined(); + } + + TaggedArray *elements = TaggedArray::Cast(receiver->GetElements().GetTaggedObject()); + uint32_t capacity = elements->GetLength(); + uint32_t index = length - 1; + auto result = elements->Get(index); + if (TaggedArray::ShouldTrim(thread, capacity, index)) { + elements->Trim(thread, index); + } else { + elements->Set(thread, index, JSTaggedValue::Hole()); + } + receiver->SetArrayLength(thread, index); + return result; +} + +JSTaggedValue JSStableArray::Splice(JSHandle receiver, EcmaRuntimeCallInfo *argv, + double start, double insertCount, double actualDeleteCount) +{ + JSThread *thread = argv->GetThread(); + uint32_t len = receiver->GetArrayLength(); + uint32_t argc = argv->GetArgsNumber(); + + JSHandle thisObjHandle(receiver); + JSTaggedValue newArray = JSArray::ArraySpeciesCreate(thread, thisObjHandle, JSTaggedNumber(actualDeleteCount)); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + JSHandle newArrayHandle(thread, newArray); + + JSHandle thisObjVal(thisObjHandle); + JSHandle lengthKey = thread->GlobalConstants()->GetHandledLengthString(); + TaggedArray *srcElements = TaggedArray::Cast(thisObjHandle->GetElements().GetTaggedObject()); + JSHandle srcElementsHandle(thread, srcElements); + if (newArray.IsStableJSArray(thread)) { + TaggedArray *destElements = TaggedArray::Cast(newArrayHandle->GetElements().GetTaggedObject()); + if (actualDeleteCount > destElements->GetLength()) { + destElements = *JSObject::GrowElementsCapacity(thread, newArrayHandle, actualDeleteCount); + } + + for (uint32_t idx = 0; idx < actualDeleteCount; idx++) { + destElements->Set(thread, idx, srcElementsHandle->Get(start + idx)); + } + JSHandle::Cast(newArrayHandle)->SetArrayLength(thread, actualDeleteCount); + } else { + JSMutableHandle fromKey(thread, JSTaggedValue::Undefined()); + JSMutableHandle toKey(thread, JSTaggedValue::Undefined()); + double k = 0; + while (k < actualDeleteCount) { + double from = start + k; + fromKey.Update(JSTaggedValue(from)); + bool exists = JSTaggedValue::HasProperty(thread, thisObjVal, fromKey); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + if (exists) { + JSHandle fromValue = JSArray::FastGetPropertyByValue(thread, thisObjVal, fromKey); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + toKey.Update(JSTaggedValue(k)); + if (newArrayHandle->IsJSProxy()) { + toKey.Update(JSTaggedValue::ToString(thread, toKey).GetTaggedValue()); + } + JSObject::CreateDataPropertyOrThrow(thread, newArrayHandle, toKey, fromValue); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + } + k++; + } + + JSHandle deleteCount(thread, JSTaggedValue(actualDeleteCount)); + JSTaggedValue::SetProperty(thread, JSHandle::Cast(newArrayHandle), lengthKey, deleteCount, + true); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + } + + uint32_t oldCapacity = srcElementsHandle->GetLength(); + uint32_t newCapacity = len - actualDeleteCount + insertCount; + if (insertCount < actualDeleteCount) { + for (uint32_t idx = start; idx < len - actualDeleteCount; idx++) { + auto element = srcElementsHandle->Get(idx + actualDeleteCount); + element = element.IsHole() ? JSTaggedValue::Undefined() : element; + srcElementsHandle->Set(thread, idx + insertCount, element); + } + + if (TaggedArray::ShouldTrim(thread, oldCapacity, newCapacity)) { + srcElementsHandle->Trim(thread, newCapacity); + } else { + for (uint32_t idx = newCapacity; idx < len; idx++) { + srcElementsHandle->Set(thread, idx, JSTaggedValue::Hole()); + } + } + } else { + if (newCapacity > oldCapacity) { + srcElementsHandle = JSObject::GrowElementsCapacity(thread, thisObjHandle, newCapacity); + } + for (uint32_t idx = len - actualDeleteCount; idx > start; idx--) { + auto element = srcElementsHandle->Get(idx + actualDeleteCount - 1); + element = element.IsHole() ? JSTaggedValue::Undefined() : element; + srcElementsHandle->Set(thread, idx + insertCount - 1, element); + } + } + + for (uint32_t i = 2, idx = start; i < argc; i++, idx++) { + srcElementsHandle->Set(thread, idx, argv->GetCallArg(i)); + } + + JSHandle newLenHandle(thread, JSTaggedValue(newCapacity)); + JSTaggedValue::SetProperty(thread, thisObjVal, lengthKey, newLenHandle, true); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + return newArrayHandle.GetTaggedValue(); +} + +JSTaggedValue JSStableArray::Shift(JSHandle receiver, EcmaRuntimeCallInfo *argv) +{ + DISALLOW_GARBAGE_COLLECTION; + JSThread *thread = argv->GetThread(); + uint32_t length = receiver->GetArrayLength(); + if (length == 0) { + return JSTaggedValue::Undefined(); + } + + TaggedArray *elements = TaggedArray::Cast(receiver->GetElements().GetTaggedObject()); + auto result = elements->Get(0); + for (uint32_t k = 1; k < length; k++) { + auto kValue = elements->Get(k); + if (kValue.IsHole()) { + elements->Set(thread, k - 1, JSTaggedValue::Undefined()); + } else { + elements->Set(thread, k - 1, kValue); + } + } + uint32_t capacity = elements->GetLength(); + uint32_t index = length - 1; + if (TaggedArray::ShouldTrim(thread, capacity, index)) { + elements->Trim(thread, index); + } else { + elements->Set(thread, index, JSTaggedValue::Hole()); + } + receiver->SetArrayLength(thread, index); + return result; +} + +JSTaggedValue JSStableArray::Join(JSHandle receiver, EcmaRuntimeCallInfo *argv) +{ + JSThread *thread = argv->GetThread(); + uint32_t length = receiver->GetArrayLength(); + JSHandle sepHandle = base::BuiltinsBase::GetCallArg(argv, 0); + int sep = ','; + uint32_t sepLength = 1; + JSHandle sepStringHandle; + if (!sepHandle->IsUndefined()) { + if (sepHandle->IsString()) { + sepStringHandle = JSHandle::Cast(sepHandle); + } else { + sepStringHandle = JSTaggedValue::ToString(thread, sepHandle); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + } + if (sepStringHandle->IsUtf8() && sepStringHandle->GetLength() == 1) { + // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-pointer-arithmetic) + sep = sepStringHandle->GetDataUtf8()[0]; + } else if (sepStringHandle->GetLength() == 0) { + sep = JSStableArray::SeparatorFlag::MINUS_TWO; + sepLength = 0; + } else { + sep = JSStableArray::SeparatorFlag::MINUS_ONE; + sepLength = sepStringHandle->GetLength(); + } + } + if (length == 0) { + const GlobalEnvConstants *globalConst = thread->GlobalConstants(); + return globalConst->GetEmptyString(); + } + TaggedArray *elements = TaggedArray::Cast(receiver->GetElements().GetTaggedObject()); + size_t allocateLength = 0; + bool isOneByte = (sep != JSStableArray::SeparatorFlag::MINUS_ONE) || sepStringHandle->IsUtf8(); + CVector> vec; + JSMutableHandle elementHandle(thread, JSTaggedValue::Undefined()); + const GlobalEnvConstants *globalConst = thread->GlobalConstants(); + for (uint32_t k = 0; k < length; k++) { + JSTaggedValue element = elements->Get(k); + if (!element.IsUndefinedOrNull() && !element.IsHole()) { + if (!element.IsString()) { + elementHandle.Update(element); + JSHandle strElement = JSTaggedValue::ToString(thread, elementHandle); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + element = strElement.GetTaggedValue(); + elements = TaggedArray::Cast(receiver->GetElements().GetTaggedObject()); + } + auto nextStr = EcmaString::Cast(element.GetTaggedObject()); + JSHandle nextStrHandle(thread, nextStr); + vec.push_back(nextStrHandle); + isOneByte = nextStr->IsUtf8() ? isOneByte : false; + allocateLength += nextStr->GetLength(); + } else { + vec.push_back(JSHandle(globalConst->GetHandledEmptyString())); + } + } + allocateLength += sepLength * (length - 1); + auto newString = EcmaString::AllocStringObject(allocateLength, isOneByte, thread->GetEcmaVM()); + int current = 0; + DISALLOW_GARBAGE_COLLECTION; + for (uint32_t k = 0; k < length; k++) { + if (k > 0) { + if (sep >= 0) { + newString->WriteData(static_cast(sep), current); + } else if (sep != JSStableArray::SeparatorFlag::MINUS_TWO) { + newString->WriteData(*sepStringHandle, current, allocateLength - current, sepLength); + } + current += sepLength; + } + JSHandle nextStr = vec[k]; + int nextLength = nextStr->GetLength(); + newString->WriteData(*nextStr, current, allocateLength - current, nextLength); + current += nextLength; + } + return JSTaggedValue(newString); +} +} // namespace panda::ecmascript diff --git a/runtime/js_stable_array.h b/runtime/js_stable_array.h new file mode 100644 index 000000000..66e634538 --- /dev/null +++ b/runtime/js_stable_array.h @@ -0,0 +1,35 @@ +/* + * Copyright (c) 2021-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_JS_STABLE_ARRAY_H +#define ECMASCRIPT_JS_STABLE_ARRAY_H + +#include +#include "plugins/ecmascript/runtime/js_array.h" +#include "plugins/ecmascript/runtime/js_tagged_value.h" + +namespace panda::ecmascript { +class JSStableArray { +public: + enum SeparatorFlag : int { MINUS_ONE = -1, MINUS_TWO = -2 }; + static JSTaggedValue Push(JSHandle receiver, EcmaRuntimeCallInfo *argv); + static JSTaggedValue Pop(JSHandle receiver, EcmaRuntimeCallInfo *argv); + static JSTaggedValue Splice(JSHandle receiver, EcmaRuntimeCallInfo *argv, + double start, double insertCount, double actualDeleteCount); + static JSTaggedValue Shift(JSHandle receiver, EcmaRuntimeCallInfo *argv); + static JSTaggedValue Join(JSHandle receiver, EcmaRuntimeCallInfo *argv); +}; +} // namespace panda::ecmascript +#endif // ECMASCRIPT_JS_STABLE_ARRAY_H diff --git a/runtime/js_string_iterator.cpp b/runtime/js_string_iterator.cpp new file mode 100644 index 000000000..b7b4a8b2a --- /dev/null +++ b/runtime/js_string_iterator.cpp @@ -0,0 +1,41 @@ +/* + * Copyright (c) 2021-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 "plugins/ecmascript/runtime/js_string_iterator.h" + +#include "plugins/ecmascript/runtime/ecma_vm.h" +#include "plugins/ecmascript/runtime/global_env.h" +#include "plugins/ecmascript/runtime/js_tagged_value.h" +#include "plugins/ecmascript/runtime/object_factory.h" +#include "libpandabase/macros.h" + +namespace panda::ecmascript { +JSHandle JSStringIterator::CreateStringIterator(const JSThread *thread, + const JSHandle &string) +{ + // 1. Assert: Type(string) is String. + // 2. Let iterator be ObjectCreate(%StringIteratorPrototype%, [[IteratedString]], [[StringIteratorNextIndex]] ?.) + JSHandle env = thread->GetEcmaVM()->GetGlobalEnv(); + JSHandle strIterCtor = env->GetStringIterator(); + ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); + JSHandle iterator = JSHandle::Cast( + factory->NewJSObjectByConstructor(JSHandle(strIterCtor), strIterCtor)); + // 3. Set iterator’s [[IteratedString]] internal slot to string. + // 4. Set iterator’s [[StringIteratorNextIndex]] internal slot to 0. + iterator->SetIteratedString(thread, string); + iterator->SetStringIteratorNextIndex(thread, JSTaggedValue(0)); + return iterator; +} +} // namespace panda::ecmascript diff --git a/runtime/js_string_iterator.h b/runtime/js_string_iterator.h new file mode 100644 index 000000000..12a7fa6d3 --- /dev/null +++ b/runtime/js_string_iterator.h @@ -0,0 +1,41 @@ +/* + * Copyright (c) 2021-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_JS_STRING_ITERATOR_H +#define ECMASCRIPT_JS_STRING_ITERATOR_H + +#include "plugins/ecmascript/runtime/ecma_string.h" +#include "plugins/ecmascript/runtime/js_handle.h" +#include "plugins/ecmascript/runtime/js_object.h" +#include "plugins/ecmascript/runtime/js_tagged_value-inl.h" + +namespace panda { +namespace ecmascript { +class JSStringIterator : public JSObject { +public: + CAST_CHECK(JSStringIterator, IsStringIterator); + + static JSHandle CreateStringIterator(const JSThread *thread, const JSHandle &string); + + static constexpr size_t ITERATED_STRING_OFFSET = JSObject::SIZE; + ACCESSORS(IteratedString, ITERATED_STRING_OFFSET, STRING_ITERATOR_NEXT_INDEX_OFFSET) + ACCESSORS(StringIteratorNextIndex, STRING_ITERATOR_NEXT_INDEX_OFFSET, SIZE) + + DECL_VISIT_OBJECT_FOR_JS_OBJECT(JSObject, ITERATED_STRING_OFFSET, SIZE) + DECL_DUMP() +}; +} // namespace ecmascript +} // namespace panda +#endif // ECMASCRIPT_JS_STRING_ITERATOR_H diff --git a/runtime/js_symbol.h b/runtime/js_symbol.h new file mode 100644 index 000000000..50abe274b --- /dev/null +++ b/runtime/js_symbol.h @@ -0,0 +1,162 @@ +/* + * Copyright (c) 2021-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_JSSYMBOL_H +#define ECMASCRIPT_JSSYMBOL_H + +#include "plugins/ecmascript/runtime/ecma_string.h" +#include "plugins/ecmascript/runtime/js_object.h" + +namespace panda { +namespace ecmascript { +class JSSymbol : public TaggedObject { +public: + static constexpr uint64_t IS_PRIVATE = 1U << 0U; + static constexpr uint64_t IS_WELL_KNOWN_SYMBOL = 1U << 1U; + static constexpr uint64_t IS_IN_PUBLIC_SYMBOL_TABLE = 1U << 2U; + static constexpr uint64_t IS_INTERESTING_SYMBOL = 1U << 3U; + static constexpr uint64_t IS_PRIVATE_NAME = 1U << 4U; + static constexpr uint64_t IS_PRIVATE_BRAND = 1U << 5U; + + static constexpr int SYMBOL_HAS_INSTANCE_TYPE = 0; + static constexpr int SYMBOL_TO_PRIMITIVE_TYPE = 1; + static constexpr int SYMBOL_DEFAULT_TYPE = 2; + + static constexpr const uint32_t LINEAR_X = 1103515245U; + static constexpr const uint32_t LINEAR_Y = 12345U; + static constexpr const uint32_t LINEAR_SEED = 987654321U; + +public: + CAST_CHECK(JSSymbol, IsSymbol); + + static inline uint32_t ComputeHash() + { + uint32_t hashSeed = LINEAR_SEED + std::time(nullptr); + uint32_t hash = hashSeed * LINEAR_X + LINEAR_Y; + return hash; + } + + bool IsPrivate() const + { + JSTaggedValue flags = this->GetFlags(); + ASSERT(flags.IsInteger()); + return (flags.GetRawData() & IS_PRIVATE) != 0U; + } + + void SetPrivate(const JSThread *thread) + { + JSTaggedValue flags = this->GetFlags(); + if (flags.IsUndefined()) { + SetFlags(thread, JSTaggedValue(IS_PRIVATE)); + return; + } + SetFlags(thread, JSTaggedValue(flags.GetRawData() | IS_PRIVATE)); + } + + bool IsWellKnownSymbol() const + { + JSTaggedValue flags = this->GetFlags(); + ASSERT(flags.IsInteger()); + return (flags.GetRawData() & IS_WELL_KNOWN_SYMBOL) != 0U; + } + + void SetWellKnownSymbol(const JSThread *thread) + { + JSTaggedValue flags = this->GetFlags(); + if (flags.IsUndefined()) { + SetFlags(thread, JSTaggedValue(static_cast(IS_WELL_KNOWN_SYMBOL))); + return; + } + flags = JSTaggedValue(static_cast(flags.GetRawData() | IS_WELL_KNOWN_SYMBOL)); + SetFlags(thread, flags); + } + + bool IsInPublicSymbolTable() const + { + JSTaggedValue flags = this->GetFlags(); + ASSERT(flags.IsInteger()); + return (flags.GetRawData() & IS_IN_PUBLIC_SYMBOL_TABLE) != 0U; + } + + void SetInPublicSymbolTable(const JSThread *thread) + { + JSTaggedValue flags = this->GetFlags(); + if (flags.IsUndefined()) { + SetFlags(thread, JSTaggedValue(static_cast(IS_IN_PUBLIC_SYMBOL_TABLE))); + return; + } + flags = JSTaggedValue(static_cast(flags.GetRawData() | IS_IN_PUBLIC_SYMBOL_TABLE)); + SetFlags(thread, flags); + } + + bool IsInterestingSymbol() const + { + JSTaggedValue flags = this->GetFlags(); + ASSERT(flags.IsInteger()); + return (flags.GetRawData() & IS_INTERESTING_SYMBOL) != 0U; + } + + void SetInterestingSymbol(const JSThread *thread) + { + JSTaggedValue flags = this->GetFlags(); + if (flags.IsUndefined()) { + SetFlags(thread, JSTaggedValue(static_cast(IS_INTERESTING_SYMBOL))); + return; + } + flags = JSTaggedValue(static_cast(flags.GetRawData() | IS_INTERESTING_SYMBOL)); + SetFlags(thread, flags); + } + + bool IsPrivateNameSymbol() const + { + JSTaggedValue flags = this->GetFlags(); + ASSERT(flags.IsInteger()); + return (flags.GetRawData() & IS_PRIVATE_NAME) != 0U; + } + + void SetPrivateNameSymbol(const JSThread *thread) + { + JSTaggedValue flags = this->GetFlags(); + if (flags.IsUndefined()) { + SetFlags(thread, JSTaggedValue(static_cast(IS_PRIVATE_NAME))); + return; + } + flags = JSTaggedValue(static_cast(flags.GetRawData() | IS_PRIVATE_NAME)); + SetFlags(thread, flags); + } + + static bool Equal(const JSSymbol &src, const JSSymbol &dst) + { + if (src.GetFlags() != dst.GetFlags()) { + return false; + } + EcmaString *srcString = EcmaString::Cast(src.GetDescription().GetTaggedObject()); + EcmaString *dstString = EcmaString::Cast(dst.GetDescription().GetTaggedObject()); + return EcmaString::StringsAreEqual(srcString, dstString); + } + +public: + static constexpr size_t HASHFIELD_OFFSET = TaggedObjectSize(); + ACCESSORS(HashField, HASHFIELD_OFFSET, FLAGS_OFFSET) + ACCESSORS(Flags, FLAGS_OFFSET, DESCRIPTION_OFFSET) + ACCESSORS(Description, DESCRIPTION_OFFSET, SIZE) + + DECL_DUMP() + + DECL_VISIT_OBJECT(HASHFIELD_OFFSET, SIZE) +}; +} // namespace ecmascript +} // namespace panda +#endif // ECMASCRIPT_NAME_H diff --git a/runtime/js_tagged_number.h b/runtime/js_tagged_number.h new file mode 100644 index 000000000..79d87e073 --- /dev/null +++ b/runtime/js_tagged_number.h @@ -0,0 +1,174 @@ +/* + * Copyright (c) 2021-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_JS_NUMBER_H +#define ECMASCRIPT_JS_NUMBER_H + +#include "plugins/ecmascript/runtime/base/number_helper.h" + +#include "plugins/ecmascript/runtime/ecma_macros.h" +#include "plugins/ecmascript/runtime/js_hclass.h" +#include "plugins/ecmascript/runtime/js_tagged_value.h" +#include "plugins/ecmascript/runtime/object_factory.h" + +namespace panda { +namespace ecmascript { +class JSTaggedNumber final : public JSTaggedValue { +public: + constexpr JSTaggedNumber() = default; + explicit JSTaggedNumber(double v) : JSTaggedValue(v) {} + constexpr explicit JSTaggedNumber(int v) : JSTaggedValue(v) {} + explicit JSTaggedNumber(unsigned int v) : JSTaggedValue(v) {} + explicit JSTaggedNumber(JSTaggedValue v) : JSTaggedValue(v.GetRawData()) + { + ASSERT_PRINT(v.IsNumber(), "can not convert non Number JSTaggedValue to JSTaggedNumber"); + } + + ~JSTaggedNumber() = default; + DEFAULT_COPY_SEMANTIC(JSTaggedNumber); + DEFAULT_MOVE_SEMANTIC(JSTaggedNumber); + + static inline constexpr JSTaggedNumber Exception() + { + return JSTaggedNumber(VALUE_EXCEPTION); + } + + inline bool IsException() const + { + return JSTaggedValue::IsException(); + } + + inline int32_t ToInt32() const + { + if (IsInt()) { + return GetInt(); + } + return base::NumberHelper::DoubleToInt(GetDouble(), base::INT32_BITS); + } + + inline uint32_t ToUint32() const + { + return ToInt32(); + } + + inline int16_t ToInt16() const + { + return base::NumberHelper::DoubleToInt(GetNumber(), base::INT16_BITS); + } + + inline uint16_t ToUint16() const + { + return ToInt16(); + } + + inline int8_t ToInt8() const + { + return base::NumberHelper::DoubleToInt(GetNumber(), base::INT8_BITS); + } + + inline uint8_t ToUint8() const + { + return ToInt8(); + } + + inline JSHandle ToString(const JSThread *thread) const + { + return base::NumberHelper::NumberToString(thread, *this); + } + + JSTaggedNumber operator-(JSTaggedNumber number) const + { + if (IsInt() && number.IsInt()) { + int64_t a0 = GetInt(); + int64_t a1 = number.GetInt(); + int64_t res = a0 - a1; + if (res > INT32_MAX || res < INT32_MIN) { + return JSTaggedNumber(static_cast(res)); + } + return JSTaggedNumber(static_cast(res)); + } + return JSTaggedNumber(GetNumber() - number.GetNumber()); + } + + JSTaggedNumber operator*(JSTaggedNumber number) const + { + if (IsInt() && number.IsInt()) { + int64_t intA = GetInt(); + int64_t intB = number.GetInt(); + int64_t res = intA * intB; + if (res > INT32_MAX || res < INT32_MIN) { + return JSTaggedNumber(static_cast(res)); + } + return JSTaggedNumber(static_cast(res)); + } + return JSTaggedNumber(GetNumber() * number.GetNumber()); + } + + JSTaggedNumber operator++() const + { + if (IsInt()) { + int32_t value = GetInt(); + if (value == INT32_MAX) { + return JSTaggedNumber(static_cast(value) + 1.0); + } + return JSTaggedNumber(value + 1); + } + return JSTaggedNumber(GetDouble() + 1.0); + } + + JSTaggedNumber operator--() const + { + if (IsInt()) { + int32_t value = GetInt(); + if (value == INT32_MIN) { + return JSTaggedNumber(static_cast(value) - 1.0); + } + return JSTaggedNumber(value - 1); + } + return JSTaggedNumber(GetDouble() - 1.0); + } + + inline bool operator!=(const JSTaggedNumber &number) const + { + return GetNumber() != number.GetNumber(); + } + + /* static */ + inline static bool SameValue(JSTaggedNumber x, JSTaggedNumber y) + { + double xValue = x.GetNumber(); + double yValue = y.GetNumber(); + // SameNumberValue(NaN, NaN) is true. + if (xValue != yValue) { + return std::isnan(xValue) && std::isnan(yValue); + } + // SameNumberValue(0.0, -0.0) is false. + return (std::signbit(xValue) == std::signbit(yValue)); + } + + inline static JSTaggedNumber FromIntOrDouble(JSThread *thread, JSTaggedValue tagged) + { + if (tagged.IsInt() || tagged.IsDouble()) { + return JSTaggedNumber(tagged); + } + THROW_TYPE_ERROR_AND_RETURN(thread, "Cannot convert value to a number", JSTaggedNumber::Exception()); + } + +private: + constexpr explicit JSTaggedNumber(JSTaggedType v) : JSTaggedValue(v) {} +}; +} // namespace ecmascript +} // namespace panda +#endif // ECMASCRIPT_JS_NUMBER_H diff --git a/runtime/js_tagged_value-inl.h b/runtime/js_tagged_value-inl.h new file mode 100644 index 000000000..f7eb1ae7c --- /dev/null +++ b/runtime/js_tagged_value-inl.h @@ -0,0 +1,1030 @@ +/* + * Copyright (c) 2021-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_TAGGED_VALUE_INL_H +#define ECMASCRIPT_TAGGED_VALUE_INL_H + +#include "plugins/ecmascript/runtime/js_tagged_value.h" + +#include "plugins/ecmascript/runtime/accessor_data.h" +#include "plugins/ecmascript/runtime/base/error_helper.h" +#include "plugins/ecmascript/runtime/base/number_helper.h" +#include "plugins/ecmascript/runtime/base/string_helper.h" +#include "plugins/ecmascript/runtime/ecma_macros.h" +#include "plugins/ecmascript/runtime/ecma_runtime_call_info.h" +#include "plugins/ecmascript/runtime/ecma_string-inl.h" +#include "plugins/ecmascript/runtime/js_hclass-inl.h" +#include "plugins/ecmascript/runtime/js_object.h" +#include "plugins/ecmascript/runtime/js_proxy.h" +#include "plugins/ecmascript/runtime/js_symbol.h" +#include "plugins/ecmascript/runtime/js_tagged_number.h" +#include "plugins/ecmascript/runtime/js_thread.h" +#include "plugins/ecmascript/runtime/mem/c_containers.h" +#include "plugins/ecmascript/runtime/mem/tagged_object-inl.h" +#include "plugins/ecmascript/runtime/object_factory.h" + +namespace panda::ecmascript { +// ecma6 7.1 Type Conversion +static constexpr double SAFE_NUMBER = 9007199254740991LL; +static constexpr uint32_t MAX_INDEX_LEN = 10; + +inline bool JSTaggedValue::ToBoolean() const +{ + if (IsInt()) { + return GetInt() != 0; + } + if (IsDouble()) { + double d = GetDouble(); + return !std::isnan(d) && d != 0; + } + + switch (GetRawData()) { + case JSTaggedValue::VALUE_UNDEFINED: + [[fallthrough]]; + case JSTaggedValue::VALUE_HOLE: + [[fallthrough]]; + case JSTaggedValue::VALUE_NULL: + [[fallthrough]]; + case JSTaggedValue::VALUE_FALSE: { + return false; + } + case JSTaggedValue::VALUE_TRUE: { + return true; + } + default: { + break; + } + } + + if (IsHeapObject()) { + TaggedObject *obj = GetTaggedObject(); + if (IsString()) { + auto str = static_cast(obj); + return str->GetLength() != 0; + } + + return true; + } + + UNREACHABLE(); +} + +inline JSTaggedNumber JSTaggedValue::ToNumber(JSThread *thread, const JSHandle &tagged) +{ + if (tagged->IsInt() || tagged->IsDouble()) { + return JSTaggedNumber(tagged.GetTaggedValue()); + } + + switch (tagged->GetRawData()) { + case JSTaggedValue::VALUE_UNDEFINED: + case JSTaggedValue::VALUE_HOLE: { + return JSTaggedNumber(base::NAN_VALUE); + } + case JSTaggedValue::VALUE_TRUE: { + return JSTaggedNumber(1); + } + case JSTaggedValue::VALUE_FALSE: + case JSTaggedValue::VALUE_NULL: { + return JSTaggedNumber(0); + } + default: { + break; + } + } + + if (tagged->IsString()) { + Span str; + auto strObj = static_cast(tagged->GetTaggedObject()); + size_t strLen = strObj->GetLength(); + if (strLen == 0) { + return JSTaggedNumber(0); + } + [[maybe_unused]] CVector buf; // Span will use buf.data(), shouldn't define inside 'if' + if (UNLIKELY(strObj->IsUtf16())) { + size_t len = base::utf_helper::Utf16ToUtf8Size(strObj->GetDataUtf16(), strLen) - 1; + buf.reserve(len); + len = base::utf_helper::ConvertRegionUtf16ToUtf8(strObj->GetDataUtf16(), buf.data(), strLen, len, 0); + str = Span(buf.data(), len); + } else { + str = Span(strObj->GetDataUtf8(), strLen); + } + double d = base::NumberHelper::StringToDouble(str.begin(), str.end(), 0, + base::ALLOW_BINARY + base::ALLOW_OCTAL + base::ALLOW_HEX); + return JSTaggedNumber(d); + } + if (tagged->IsECMAObject()) { + JSHandle primValue(thread, ToPrimitive(thread, tagged, PREFER_NUMBER)); + RETURN_VALUE_IF_ABRUPT_COMPLETION(thread, JSTaggedNumber::Exception()); + return ToNumber(thread, primValue); + } + if (tagged->IsSymbol()) { + THROW_TYPE_ERROR_AND_RETURN(thread, "Cannot convert a Symbol value to a number", JSTaggedNumber::Exception()); + } + + THROW_TYPE_ERROR_AND_RETURN(thread, "Cannot convert a Unknown value to a number", JSTaggedNumber::Exception()); +} + +inline JSTaggedNumber JSTaggedValue::ToInteger(JSThread *thread, const JSHandle &tagged) +{ + JSTaggedNumber number = ToNumber(thread, tagged); + RETURN_VALUE_IF_ABRUPT_COMPLETION(thread, JSTaggedNumber::Exception()); + + return JSTaggedNumber(base::NumberHelper::TruncateDouble(number.GetNumber())); +} + +inline int32_t JSTaggedValue::ToInt32(JSThread *thread, const JSHandle &tagged) +{ + JSTaggedNumber number = ToNumber(thread, tagged); + RETURN_VALUE_IF_ABRUPT_COMPLETION(thread, 0); + return base::NumberHelper::DoubleToInt(number.GetNumber(), base::INT32_BITS); +} + +inline uint32_t JSTaggedValue::ToUint32(JSThread *thread, const JSHandle &tagged) +{ + return ToInt32(thread, tagged); +} + +inline int16_t JSTaggedValue::ToInt16(JSThread *thread, const JSHandle &tagged) +{ + JSTaggedNumber number = ToNumber(thread, tagged); + RETURN_VALUE_IF_ABRUPT_COMPLETION(thread, 0); + + return base::NumberHelper::DoubleToInt(number.GetNumber(), base::INT16_BITS); +} + +inline uint16_t JSTaggedValue::ToUint16(JSThread *thread, const JSHandle &tagged) +{ + return ToInt16(thread, tagged); +} + +inline int8_t JSTaggedValue::ToInt8(JSThread *thread, const JSHandle &tagged) +{ + JSTaggedNumber number = ToNumber(thread, tagged); + RETURN_VALUE_IF_ABRUPT_COMPLETION(thread, 0); + + return base::NumberHelper::DoubleToInt(number.GetNumber(), base::INT8_BITS); +} + +inline uint8_t JSTaggedValue::ToUint8(JSThread *thread, const JSHandle &tagged) +{ + return ToInt8(thread, tagged); +} + +inline uint8_t JSTaggedValue::ToUint8Clamp(JSThread *thread, const JSHandle &tagged) +{ + JSTaggedNumber number = ToNumber(thread, tagged); + RETURN_VALUE_IF_ABRUPT_COMPLETION(thread, 0); + + double d = number.GetNumber(); + if (std::isnan(d) || d <= 0) { + return 0; + } + if (d >= UINT8_MAX) { + return UINT8_MAX; + } + + return lrint(d); +} + +inline JSTaggedNumber JSTaggedValue::ToLength(JSThread *thread, const JSHandle &tagged) +{ + JSTaggedNumber len = ToInteger(thread, tagged); + RETURN_VALUE_IF_ABRUPT_COMPLETION(thread, JSTaggedNumber::Exception()); + if (len.GetNumber() < 0.0) { + return JSTaggedNumber(static_cast(0)); + } + if (len.GetNumber() > SAFE_NUMBER) { + return JSTaggedNumber(static_cast(SAFE_NUMBER)); + } + return len; +} + +// ecma6 7.2 Testing and Comparison Operations +inline JSHandle JSTaggedValue::RequireObjectCoercible(JSThread *thread, + const JSHandle &tagged) +{ + if (tagged->IsUndefined() || tagged->IsNull()) { + THROW_TYPE_ERROR_AND_RETURN(thread, "RequireObjectCoercible", + JSHandle(thread, JSTaggedValue::Exception())); + } + return tagged; +} + +inline bool JSTaggedValue::IsCallable() const +{ + return IsHeapObject() && GetTaggedObject()->GetClass()->IsCallable(); +} + +inline bool JSTaggedValue::IsConstructor() const +{ + return IsHeapObject() && GetTaggedObject()->GetClass()->IsConstructor(); +} + +inline bool JSTaggedValue::IsExtensible(JSThread *thread) const +{ + if (UNLIKELY(IsJSProxy())) { + return JSProxy::IsExtensible(thread, JSHandle(thread, *this)); + } + + return IsHeapObject() && GetTaggedObject()->GetClass()->IsExtensible(); +} + +inline bool JSTaggedValue::IsClassConstructor() const +{ + return IsHeapObject() && GetTaggedObject()->GetClass()->IsClassConstructor(); +} + +inline bool JSTaggedValue::IsClassPrototype() const +{ + return IsHeapObject() && GetTaggedObject()->GetClass()->IsClassPrototype(); +} + +inline bool JSTaggedValue::IsPropertyKey(const JSHandle &key) +{ + return key->IsStringOrSymbol() || key->IsNumber(); +} + +inline bool JSTaggedValue::SameValue(const JSTaggedValue &x, const JSTaggedValue &y) +{ + // same object or special type must be same value + if (x == y) { + return true; + } + + if (x.IsNumber() && y.IsNumber()) { + return SameValueNumberic(x, y); + } + + if (x.IsString() && y.IsString()) { + return EcmaString::StringsAreEqual(static_cast(x.GetTaggedObject()), + static_cast(y.GetTaggedObject())); + } + return false; +} + +inline bool JSTaggedValue::SameValue(const JSHandle &xHandle, const JSHandle &yHandle) +{ + return SameValue(xHandle.GetTaggedValue(), yHandle.GetTaggedValue()); +} + +inline bool JSTaggedValue::SameValueZero(const JSTaggedValue &x, const JSTaggedValue &y) +{ + if (x == y) { + return true; + } + + if (x.IsNumber() && y.IsNumber()) { + double xValue = x.ExtractNumber(); + double yValue = y.ExtractNumber(); + // Compare xValue with yValue to deal with -0.0 + return (xValue == yValue) || (std::isnan(xValue) && std::isnan(yValue)); + } + + if (x.IsString() && y.IsString()) { + return EcmaString::StringsAreEqual(static_cast(x.GetTaggedObject()), + static_cast(y.GetTaggedObject())); + } + return false; +} + +inline bool JSTaggedValue::SameValueNumberic(const JSTaggedValue &x, const JSTaggedValue &y) +{ + double xValue = x.ExtractNumber(); + double yValue = y.ExtractNumber(); + // SameNumberValue(NaN, NaN) is true. + if (xValue != yValue) { + return std::isnan(xValue) && std::isnan(yValue); + } + // SameNumberValue(0.0, -0.0) is false. + return (std::signbit(xValue) == std::signbit(yValue)); +} + +inline bool JSTaggedValue::Less(JSThread *thread, const JSHandle &x, const JSHandle &y) +{ + ComparisonResult result = Compare(thread, x, y); + return result == ComparisonResult::LESS; +} + +inline bool JSTaggedValue::StrictNumberEquals(double x, double y) +{ + // Must check explicitly for NaN's on Windows, but -0 works fine. + if (std::isnan(x) || std::isnan(y)) { + return false; + } + return x == y; +} + +inline bool JSTaggedValue::StrictEqual([[maybe_unused]] const JSThread *thread, const JSHandle &x, + const JSHandle &y) +{ + if (x->IsNumber() && y->IsNumber()) { + return StrictNumberEquals(x->ExtractNumber(), y->ExtractNumber()); + } + + if (x.GetTaggedValue() == y.GetTaggedValue()) { + return true; + } + + if (x->IsString() && y->IsString()) { + return EcmaString::StringsAreEqual(x.GetObject(), y.GetObject()); + } + return false; +} + +inline ComparisonResult JSTaggedValue::StrictNumberCompare(double x, double y) +{ + if (std::isnan(x) || std::isnan(y)) { + return ComparisonResult::UNDEFINED; + } + if (x < y) { + return ComparisonResult::LESS; + } + if (x > y) { + return ComparisonResult::GREAT; + } + return ComparisonResult::EQUAL; +} + +inline bool JSTaggedValue::IsNumber() const +{ + return IsInt() || IsDouble(); +} + +inline bool JSTaggedValue::IsString() const +{ + return IsHeapObject() && GetTaggedObject()->GetClass()->IsString(); +} + +inline bool JSTaggedValue::IsStringOrSymbol() const +{ + return IsHeapObject() && GetTaggedObject()->GetClass()->IsStringOrSymbol(); +} + +inline bool JSTaggedValue::IsTaggedArray() const +{ + return IsHeapObject() && GetTaggedObject()->GetClass()->IsTaggedArray(); +} + +inline bool JSTaggedValue::IsNativePointer() const +{ + return IsJSNativePointer(); +} + +inline bool JSTaggedValue::IsJSNativePointer() const +{ + return IsHeapObject() && GetTaggedObject()->GetClass()->IsJSNativePointer(); +} + +inline bool JSTaggedValue::IsSymbol() const +{ + return IsHeapObject() && GetTaggedObject()->GetClass()->IsJSSymbol(); +} + +inline bool JSTaggedValue::IsJSProxy() const +{ + return IsHeapObject() && GetTaggedObject()->GetClass()->IsJSProxy(); +} + +inline bool JSTaggedValue::IsBoolean() const +{ + return IsTrue() || IsFalse(); +} + +inline bool JSTaggedValue::IsJSObject() const +{ + return IsHeapObject() && GetTaggedObject()->GetClass()->IsJSObject(); +} + +inline bool JSTaggedValue::IsECMAObject() const +{ + return IsHeapObject() && GetTaggedObject()->GetClass()->IsECMAObject(); +} + +inline bool JSTaggedValue::IsJSPromise() const +{ + return IsHeapObject() && GetTaggedObject()->GetClass()->IsJSPromise(); +} + +inline bool JSTaggedValue::IsRecord() const +{ + return IsHeapObject() && GetTaggedObject()->GetClass()->IsRecord(); +} + +inline bool JSTaggedValue::IsPromiseReaction() const +{ + return IsHeapObject() && GetTaggedObject()->GetClass()->IsPromiseReaction(); +} + +inline bool JSTaggedValue::IsJSPromiseReactionFunction() const +{ + return IsHeapObject() && GetTaggedObject()->GetClass()->IsJSPromiseReactionFunction(); +} + +inline bool JSTaggedValue::IsProgram() const +{ + return IsHeapObject() && GetTaggedObject()->GetClass()->IsProgram(); +} + +inline bool JSTaggedValue::IsLexicalFunction() const +{ + return IsHeapObject() && GetTaggedObject()->GetClass()->IsLexicalFunction(); +} + +inline bool JSTaggedValue::IsJSPromiseExecutorFunction() const +{ + return IsHeapObject() && GetTaggedObject()->GetClass()->IsJSPromiseExecutorFunction(); +} + +inline bool JSTaggedValue::IsJSPromiseAllResolveElementFunction() const +{ + return IsHeapObject() && GetTaggedObject()->GetClass()->IsJSPromiseAllResolveElementFunction(); +} + +inline bool JSTaggedValue::IsCompletionRecord() const +{ + return IsHeapObject() && GetTaggedObject()->GetClass()->IsCompletionRecord(); +} + +inline bool JSTaggedValue::IsResolvingFunctionsRecord() const +{ + return IsHeapObject() && GetTaggedObject()->GetClass()->IsResolvingFunctionsRecord(); +} + +inline bool JSTaggedValue::IsPromiseRecord() const +{ + return IsHeapObject() && GetTaggedObject()->GetClass()->IsPromiseRecord(); +} + +inline bool JSTaggedValue::IsJSLocale() const +{ + return IsHeapObject() && GetTaggedObject()->GetClass()->IsJSLocale(); +} + +inline bool JSTaggedValue::IsJSIntl() const +{ + return IsHeapObject() && GetTaggedObject()->GetClass()->IsJSIntl(); +} + +inline bool JSTaggedValue::IsJSDateTimeFormat() const +{ + return IsHeapObject() && GetTaggedObject()->GetClass()->IsJSDateTimeFormat(); +} + +inline bool JSTaggedValue::IsJSRelativeTimeFormat() const +{ + return IsHeapObject() && GetTaggedObject()->GetClass()->IsJSRelativeTimeFormat(); +} + +inline bool JSTaggedValue::IsJSNumberFormat() const +{ + return IsHeapObject() && GetTaggedObject()->GetClass()->IsJSNumberFormat(); +} + +inline bool JSTaggedValue::IsJSCollator() const +{ + return IsHeapObject() && GetTaggedObject()->GetClass()->IsJSCollator(); +} + +inline bool JSTaggedValue::IsJSPluralRules() const +{ + return IsHeapObject() && GetTaggedObject()->GetClass()->IsJSPluralRules(); +} + +inline bool JSTaggedValue::IsJSArrayList() const +{ + return IsHeapObject() && GetTaggedObject()->GetClass()->IsJSArrayList(); +} + +inline bool JSTaggedValue::IsSpecialContainer() const +{ + return IsHeapObject() && GetTaggedObject()->GetClass()->IsSpecialContainer(); +} + +inline bool JSTaggedValue::IsPromiseIteratorRecord() const +{ + return IsHeapObject() && GetTaggedObject()->GetClass()->IsPromiseIteratorRecord(); +} + +inline bool JSTaggedValue::IsPromiseCapability() const +{ + return IsHeapObject() && GetTaggedObject()->GetClass()->IsPromiseCapability(); +} + +inline bool JSTaggedValue::IsJSError() const +{ + return IsHeapObject() && GetTaggedObject()->GetClass()->IsJSError(); +} + +inline bool JSTaggedValue::IsJSFunctionExtraInfo() const +{ + return IsHeapObject() && GetTaggedObject()->GetClass()->IsJSFunctionExtraInfo(); +} + +inline bool JSTaggedValue::IsMicroJobQueue() const +{ + return IsHeapObject() && GetTaggedObject()->GetClass()->IsMicroJobQueue(); +} + +inline bool JSTaggedValue::IsPendingJob() const +{ + return IsHeapObject() && GetTaggedObject()->GetClass()->IsPendingJob(); +} + +inline bool JSTaggedValue::IsArguments() const +{ + return IsHeapObject() && GetTaggedObject()->GetClass()->IsArguments(); +} + +inline bool JSTaggedValue::IsDate() const +{ + return IsHeapObject() && GetTaggedObject()->GetClass()->IsDate(); +} + +inline bool JSTaggedValue::IsArray(JSThread *thread) const +{ + if (!IsHeapObject()) { + return false; + } + JSHClass *jsHclass = GetTaggedObject()->GetClass(); + if (jsHclass->IsJSArray()) { + return true; + } + + if (jsHclass->IsJSProxy()) { + return JSProxy::Cast(GetTaggedObject())->IsArray(thread); + } + return false; +} + +inline bool JSTaggedValue::IsJSArray() const +{ + return IsHeapObject() && GetTaggedObject()->GetClass()->IsJSArray(); +} + +inline bool JSTaggedValue::IsStableJSArray(JSThread *thread) const +{ + return IsHeapObject() && GetTaggedObject()->GetClass()->IsStableJSArray() && + !thread->IsStableArrayElementsGuardiansInvalid(); +} + +inline bool JSTaggedValue::IsStableJSArguments(JSThread *thread) const +{ + return IsHeapObject() && GetTaggedObject()->GetClass()->IsStableJSArguments() && + !thread->IsStableArrayElementsGuardiansInvalid(); +} + +inline bool JSTaggedValue::HasStableElements(JSThread *thread) const +{ + return IsHeapObject() && GetTaggedObject()->GetClass()->IsStableElements() && + !thread->IsStableArrayElementsGuardiansInvalid(); +} + +inline bool JSTaggedValue::IsTypedArray() const +{ + return IsHeapObject() && GetTaggedObject()->GetClass()->IsTypedArray(); +} + +inline bool JSTaggedValue::IsJSTypedArray() const +{ + return IsHeapObject() && GetTaggedObject()->GetClass()->IsJSTypedArray(); +} + +inline bool JSTaggedValue::IsJSInt8Array() const +{ + return IsHeapObject() && GetTaggedObject()->GetClass()->IsJSInt8Array(); +} + +inline bool JSTaggedValue::IsJSUint8Array() const +{ + return IsHeapObject() && GetTaggedObject()->GetClass()->IsJSUint8Array(); +} + +inline bool JSTaggedValue::IsJSUint8ClampedArray() const +{ + return IsHeapObject() && GetTaggedObject()->GetClass()->IsJSUint8ClampedArray(); +} + +inline bool JSTaggedValue::IsJSInt16Array() const +{ + return IsHeapObject() && GetTaggedObject()->GetClass()->IsJSInt16Array(); +} + +inline bool JSTaggedValue::IsJSUint16Array() const +{ + return IsHeapObject() && GetTaggedObject()->GetClass()->IsJSUint16Array(); +} + +inline bool JSTaggedValue::IsJSInt32Array() const +{ + return IsHeapObject() && GetTaggedObject()->GetClass()->IsJSInt32Array(); +} + +inline bool JSTaggedValue::IsJSUint32Array() const +{ + return IsHeapObject() && GetTaggedObject()->GetClass()->IsJSUint32Array(); +} + +inline bool JSTaggedValue::IsJSFloat32Array() const +{ + return IsHeapObject() && GetTaggedObject()->GetClass()->IsJSFloat32Array(); +} + +inline bool JSTaggedValue::IsJSFloat64Array() const +{ + return IsHeapObject() && GetTaggedObject()->GetClass()->IsJSFloat64Array(); +} + +inline bool JSTaggedValue::IsJSMap() const +{ + return IsHeapObject() && GetTaggedObject()->GetClass()->IsJSMap(); +} + +inline bool JSTaggedValue::IsJSWeakMap() const +{ + return IsHeapObject() && GetTaggedObject()->GetClass()->IsJSWeakMap(); +} + +inline bool JSTaggedValue::IsJSWeakSet() const +{ + return IsHeapObject() && GetTaggedObject()->GetClass()->IsJSWeakSet(); +} + +inline bool JSTaggedValue::IsJSSet() const +{ + return IsHeapObject() && GetTaggedObject()->GetClass()->IsJSSet(); +} + +inline bool JSTaggedValue::IsJSRegExp() const +{ + return IsHeapObject() && GetTaggedObject()->GetClass()->IsJSRegExp(); +} + +inline bool JSTaggedValue::IsJSFunction() const +{ + return IsHeapObject() && GetTaggedObject()->GetClass()->IsJSFunction(); +} + +inline bool JSTaggedValue::IsJSFunctionBase() const +{ + return IsHeapObject() && GetTaggedObject()->GetClass()->IsJSFunctionBase(); +} + +inline bool JSTaggedValue::IsBoundFunction() const +{ + return IsHeapObject() && GetTaggedObject()->GetClass()->IsJsBoundFunction(); +} + +inline bool JSTaggedValue::IsJSIntlBoundFunction() const +{ + return IsHeapObject() && GetTaggedObject()->GetClass()->IsJSIntlBoundFunction(); +} + +inline bool JSTaggedValue::IsProxyRevocFunction() const +{ + return IsHeapObject() && GetTaggedObject()->GetClass()->IsJSProxyRevocFunction(); +} + +inline bool JSTaggedValue::IsJSAsyncFunction() const +{ + return IsHeapObject() && GetTaggedObject()->GetClass()->IsJSAsyncFunction(); +} + +inline bool JSTaggedValue::IsJSAsyncGeneratorFunction() const +{ + return IsHeapObject() && GetTaggedObject()->GetClass()->IsJSAsyncGeneratorFunction(); +} + +inline bool JSTaggedValue::IsJSAsyncAwaitStatusFunction() const +{ + return IsHeapObject() && GetTaggedObject()->GetClass()->IsJSAsyncAwaitStatusFunction(); +} + +inline bool JSTaggedValue::IsJSAsyncGeneratorResolveNextFunction() const +{ + return IsHeapObject() && GetTaggedObject()->GetClass()->IsJSAsyncGeneratorResolveNextFunction(); +} + +inline bool JSTaggedValue::IsJSAsyncFromSyncIteratorValueUnwrapFunction() const +{ + return IsHeapObject() && GetTaggedObject()->GetClass()->IsJSAsyncFromSyncIteratorValueUnwrapFunction(); +} + +inline bool JSTaggedValue::IsJSPrimitiveRef() const +{ + return IsHeapObject() && GetTaggedObject()->GetClass()->IsJsPrimitiveRef(); +} + +inline bool JSTaggedValue::IsJSPrimitive() const +{ + return IsNumber() || IsStringOrSymbol() || IsBoolean(); +} + +inline bool JSTaggedValue::IsAccessorData() const +{ + return IsHeapObject() && GetTaggedObject()->GetClass()->IsAccessorData(); +} + +inline bool JSTaggedValue::IsInternalAccessor() const +{ + return IsHeapObject() && GetTaggedObject()->GetClass()->IsInternalAccessor(); +} + +inline bool JSTaggedValue::IsAccessor() const +{ + if (IsHeapObject()) { + auto *jshcalss = GetTaggedObject()->GetClass(); + return jshcalss->IsAccessorData() || jshcalss->IsInternalAccessor(); + } + + return false; +} + +inline bool JSTaggedValue::IsPrototypeHandler() const +{ + return IsHeapObject() && GetTaggedObject()->GetClass()->IsPrototypeHandler(); +} + +inline bool JSTaggedValue::IsTransitionHandler() const +{ + return IsHeapObject() && GetTaggedObject()->GetClass()->IsTransitionHandler(); +} + +inline bool JSTaggedValue::IsPropertyBox() const +{ + return IsHeapObject() && GetTaggedObject()->GetClass()->IsPropertyBox(); +} + +inline bool JSTaggedValue::IsProtoChangeDetails() const +{ + return IsHeapObject() && GetTaggedObject()->GetClass()->IsProtoChangeDetails(); +} +inline bool JSTaggedValue::IsProtoChangeMarker() const +{ + return IsHeapObject() && GetTaggedObject()->GetClass()->IsProtoChangeMarker(); +} + +inline bool JSTaggedValue::IsJSGlobalEnv() const +{ + return IsHeapObject() && GetTaggedObject()->GetClass()->IsJsGlobalEnv(); +} + +inline bool JSTaggedValue::IsForinIterator() const +{ + return IsHeapObject() && GetTaggedObject()->GetClass()->IsForinIterator(); +} + +inline bool JSTaggedValue::IsJSSetIterator() const +{ + return IsHeapObject() && GetTaggedObject()->GetClass()->IsJSSetIterator(); +} + +inline bool JSTaggedValue::IsJSMapIterator() const +{ + return IsHeapObject() && GetTaggedObject()->GetClass()->IsJSMapIterator(); +} + +inline bool JSTaggedValue::IsJSArrayIterator() const +{ + return IsHeapObject() && GetTaggedObject()->GetClass()->IsJSArrayIterator(); +} + +inline bool JSTaggedValue::IsIterator() const +{ + return IsHeapObject() && GetTaggedObject()->GetClass()->IsIterator(); +} + +inline bool JSTaggedValue::IsGeneratorFunction() const +{ + return IsHeapObject() && GetTaggedObject()->GetClass()->IsGeneratorFunction(); +} + +inline bool JSTaggedValue::IsGeneratorObject() const +{ + return IsHeapObject() && GetTaggedObject()->GetClass()->IsGeneratorObject(); +} + +inline bool JSTaggedValue::IsGeneratorContext() const +{ + return IsHeapObject() && GetTaggedObject()->GetClass()->IsGeneratorContext(); +} + +inline bool JSTaggedValue::IsAsyncFuncObject() const +{ + return IsHeapObject() && GetTaggedObject()->GetClass()->IsAsyncFuncObject(); +} + +inline bool JSTaggedValue::IsAsyncGeneratorFunction() const +{ + return IsHeapObject() && GetTaggedObject()->GetClass()->IsAsyncGeneratorFunction(); +} + +inline bool JSTaggedValue::IsJSAsyncFromSyncIteratorObject() const +{ + return IsHeapObject() && GetTaggedObject()->GetClass()->IsJSAsyncFromSyncIteratorObject(); +} + +inline bool JSTaggedValue::IsAsyncGeneratorObject() const +{ + return IsHeapObject() && GetTaggedObject()->GetClass()->IsAsyncGeneratorObject(); +} + +inline bool JSTaggedValue::IsDynClass() const +{ + return IsJSHClass(); +} + +inline bool JSTaggedValue::IsJSHClass() const +{ + return IsHeapObject() && GetTaggedObject()->GetClass()->IsHClass(); +} + +inline bool JSTaggedValue::IsObjectWrapper() const +{ + return IsHeapObject() && GetTaggedObject()->GetClass()->IsObjectWrapper(); +} + +inline bool JSTaggedValue::IsStringIterator() const +{ + return IsHeapObject() && GetTaggedObject()->GetClass()->IsStringIterator(); +} + +inline bool JSTaggedValue::IsArrayBuffer() const +{ + return IsHeapObject() && GetTaggedObject()->GetClass()->IsArrayBuffer(); +} + +inline bool JSTaggedValue::IsDataView() const +{ + return IsHeapObject() && GetTaggedObject()->GetClass()->IsDataView(); +} + +inline bool JSTaggedValue::IsTemplateMap() const +{ + return IsHeapObject() && GetTaggedObject()->GetClass()->IsTemplateMap(); +} + +inline bool JSTaggedValue::IsJSGlobalObject() const +{ + return IsHeapObject() && GetTaggedObject()->GetClass()->IsJSGlobalObject(); +} + +inline bool JSTaggedValue::IsMachineCodeObject() const +{ + return IsHeapObject() && GetTaggedObject()->GetClass()->IsMachineCodeObject(); +} + +inline bool JSTaggedValue::IsClassInfoExtractor() const +{ + return IsHeapObject() && GetTaggedObject()->GetClass()->IsClassInfoExtractor(); +} + +inline double JSTaggedValue::ExtractNumber() const +{ + ASSERT(IsNumber()); + return GetNumber(); +} + +// 9.4.2.4 ArraySetLength 3 to 7 +inline bool JSTaggedValue::ToArrayLength(JSThread *thread, const JSHandle &tagged, uint32_t *output) +{ + // 3. Let newLen be ToUint32(Desc.[[Value]]). + uint32_t newLen = ToUint32(thread, tagged); + // 4. ReturnIfAbrupt(newLen). + RETURN_VALUE_IF_ABRUPT_COMPLETION(thread, false); + + // 5. Let numberLen be ToNumber(Desc.[[Value]]). + JSTaggedNumber numberLen = ToNumber(thread, tagged); + // 6. ReturnIfAbrupt(newLen). + RETURN_VALUE_IF_ABRUPT_COMPLETION(thread, false); + + // 7. If newLen != numberLen, throw a RangeError exception. + if (JSTaggedNumber(newLen) != numberLen) { + THROW_RANGE_ERROR_AND_RETURN(thread, "Not a valid array length", false); + } + + *output = newLen; + return true; +} + +inline uint32_t JSTaggedValue::GetArrayLength() const +{ + ASSERT(IsNumber()); + if (IsInt()) { + return static_cast(GetInt()); + } + if (IsDouble()) { + ASSERT(GetDouble() <= TaggedArray::MAX_ARRAY_INDEX); + return static_cast(GetDouble()); + } + UNREACHABLE(); +} + +inline bool JSTaggedValue::ToElementIndex(JSTaggedValue key, uint32_t *output) +{ + if (key.IsInt()) { + int index = key.GetInt(); + if (index >= 0) { + *output = index; + return true; + } + } else if (key.IsDouble()) { + double d = key.GetDouble(); + uint32_t index = base::NumberHelper::DoubleToInt(d, base::INT32_BITS); + if (d - static_cast(index) == 0.0) { + *output = index; + return true; + } + } else if (key.IsString()) { + return StringToElementIndex(key, output); + } + return false; +} + +inline bool JSTaggedValue::StringToElementIndex(JSTaggedValue key, uint32_t *output) +{ + ASSERT(key.IsString()); + + auto strObj = static_cast(key.GetTaggedObject()); + uint32_t len = strObj->GetLength(); + if (len == 0 || len > MAX_INDEX_LEN) { // NOLINTNEXTLINEreadability-magic-numbers) + return false; + } + uint32_t c; + if (strObj->IsUtf16()) { + c = strObj->GetDataUtf16()[0]; // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-pointer-arithmetic) + } else { + c = strObj->GetDataUtf8()[0]; // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-pointer-arithmetic) + } + uint64_t n = 0; + if (c >= '0' && c <= '9') { + if (c == '0') { + if (len != 1) { + return false; + } + *output = 0; + return true; + } + + n = c - '0'; + for (uint32_t i = 1; i < len; i++) { + if (strObj->IsUtf16()) { + c = strObj->GetDataUtf16()[i]; // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-pointer-arithmetic) + } else { + c = strObj->GetDataUtf8()[i]; // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-pointer-arithmetic) + } + if (c < '0' || c > '9') { + return false; + } + // NOLINTNEXTLINE(readability-magic-numbers) + n = n * 10 + (c - '0'); // 10: decimal factor + } + if (n < JSObject::MAX_ELEMENT_INDEX) { + *output = n; + return true; + } + } + return false; +} + +inline uint32_t JSTaggedValue::GetKeyHashCode() const +{ + ASSERT(IsStringOrSymbol()); + if (IsString()) { + return EcmaString::Cast(GetTaggedObject())->GetHashcode(); + } + + return static_cast(JSSymbol::Cast(GetTaggedObject())->GetHashField().GetInt()); +} + +uint32_t JSTaggedValue::GetHashCode() +{ + if (IsStringOrSymbol()) { + return GetKeyHashCode(); + } + if (IsHeapObject()) { + return GetHeapObject()->GetHashCode(); + } + + // Int, Double, Special + uint64_t value = GetRawData(); + if (IsInt()) { + value = GetInt(); + } else if (IsDouble() && GetDouble() == 0.0) { + value = 0; + } + return GetHash32(reinterpret_cast(&value), sizeof(value) / sizeof(uint8_t)); +} +} // namespace panda::ecmascript +#endif // ECMASCRIPT_TAGGED_VALUE__INL_H diff --git a/runtime/js_tagged_value.cpp b/runtime/js_tagged_value.cpp new file mode 100644 index 000000000..879dfedd3 --- /dev/null +++ b/runtime/js_tagged_value.cpp @@ -0,0 +1,828 @@ +/* + * Copyright (c) 2021-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 "plugins/ecmascript/runtime/js_tagged_value.h" +#include "plugins/ecmascript/runtime/ecma_macros.h" +#include "plugins/ecmascript/runtime/ecma_vm.h" +#include "plugins/ecmascript/runtime/global_env.h" +#include "plugins/ecmascript/runtime/internal_call_params.h" +#include "plugins/ecmascript/runtime/js_array.h" +#include "plugins/ecmascript/runtime/js_arraylist.h" +#include "plugins/ecmascript/runtime/js_handle.h" +#include "plugins/ecmascript/runtime/js_primitive_ref.h" +#include "plugins/ecmascript/runtime/js_proxy.h" +#include "plugins/ecmascript/runtime/js_tagged_value-inl.h" +#include "plugins/ecmascript/runtime/js_thread.h" +#include "plugins/ecmascript/runtime/js_typed_array.h" +#include "plugins/ecmascript/runtime/tagged_array.h" +#include "js_object-inl.h" +#include "object_factory.h" + +namespace panda::ecmascript { +JSHandle GetTypeString(JSThread *thread, PreferredPrimitiveType type) +{ + ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); + if (type == NO_PREFERENCE) { + return factory->NewFromCanBeCompressString("default"); + } + if (type == PREFER_NUMBER) { + return factory->NewFromCanBeCompressString("number"); + } + return factory->NewFromCanBeCompressString("string"); +} + +JSHandle JSTaggedValue::ToPropertyKey(JSThread *thread, const JSHandle &tagged) +{ + if (tagged->IsStringOrSymbol() || tagged->IsNumber()) { + return tagged; + } + JSHandle key(thread, ToPrimitive(thread, tagged, PREFER_STRING)); + RETURN_HANDLE_IF_ABRUPT_COMPLETION(JSTaggedValue, thread); + if (key->IsSymbol()) { + return key; + } + JSHandle string = ToString(thread, key); + RETURN_HANDLE_IF_ABRUPT_COMPLETION(JSTaggedValue, thread); + return JSHandle::Cast(string); +} + +bool JSTaggedValue::IsInteger() const +{ + if (!IsNumber()) { + return false; + } + + if (IsInt()) { + return true; + } + + double thisValue = GetDouble(); + // If argument is NaN, +∞, or -∞, return false. + if (!std::isfinite(thisValue)) { + return false; + } + + // If floor(abs(argument)) ≠ abs(argument), return false. + if (std::floor(std::abs(thisValue)) != std::abs(thisValue)) { + return false; + } + + return true; +} + +bool JSTaggedValue::WithinInt32() const +{ + if (!IsNumber()) { + return false; + } + + double doubleValue = GetNumber(); + if (bit_cast(doubleValue) == bit_cast(-0.0)) { + return false; + } + + int32_t intvalue = base::NumberHelper::DoubleToInt(doubleValue, base::INT32_BITS); + return doubleValue == static_cast(intvalue); +} + +bool JSTaggedValue::IsZero() const +{ + if (GetRawData() == VALUE_ZERO) { + return true; + } + if (IsDouble()) { + const double limit = 1e-8; + return (std::abs(GetDouble() - 0.0) <= limit); + } + return false; +} + +bool JSTaggedValue::Equal(JSThread *thread, const JSHandle &x, const JSHandle &y) +{ + if (x->IsNumber()) { + if (y->IsNumber()) { + return StrictNumberEquals(x->ExtractNumber(), y->ExtractNumber()); + } + if (y->IsString()) { + JSTaggedNumber yNumber = ToNumber(thread, y); + RETURN_VALUE_IF_ABRUPT_COMPLETION(thread, false); + return StrictNumberEquals(x->ExtractNumber(), yNumber.GetNumber()); + } + if (y->IsBoolean()) { + JSTaggedNumber yNumber = ToNumber(thread, y); + RETURN_VALUE_IF_ABRUPT_COMPLETION(thread, false); + return StrictNumberEquals(x->ExtractNumber(), yNumber.GetNumber()); + } + if (y->IsHeapObject() && !y->IsSymbol()) { + JSHandle yPrimitive(thread, ToPrimitive(thread, y)); + RETURN_VALUE_IF_ABRUPT_COMPLETION(thread, false); + return Equal(thread, x, yPrimitive); + } + return false; + } + + if (x->IsString()) { + if (y->IsString()) { + return EcmaString::StringsAreEqual(static_cast(x->GetTaggedObject()), + static_cast(y->GetTaggedObject())); + } + if (y->IsNumber()) { + JSTaggedNumber xNumber = ToNumber(thread, x); + RETURN_VALUE_IF_ABRUPT_COMPLETION(thread, false); + return StrictNumberEquals(xNumber.GetNumber(), y->ExtractNumber()); + } + if (y->IsBoolean()) { + JSTaggedNumber xNumber = ToNumber(thread, x); + RETURN_VALUE_IF_ABRUPT_COMPLETION(thread, false); + JSTaggedNumber yNumber = ToNumber(thread, y); + RETURN_VALUE_IF_ABRUPT_COMPLETION(thread, false); + return StrictNumberEquals(xNumber.GetNumber(), yNumber.GetNumber()); + } + if (y->IsHeapObject() && !y->IsSymbol()) { + JSHandle yPrimitive(thread, ToPrimitive(thread, y)); + RETURN_VALUE_IF_ABRUPT_COMPLETION(thread, false); + return Equal(thread, x, yPrimitive); + } + return false; + } + + if (x->IsBoolean()) { + JSTaggedNumber xNumber = ToNumber(thread, x); + RETURN_VALUE_IF_ABRUPT_COMPLETION(thread, false); + return Equal(thread, JSHandle(thread, xNumber), y); + } + + if (x->IsSymbol()) { + if (y->IsSymbol()) { + return x.GetTaggedValue() == y.GetTaggedValue(); + } + if (y->IsHeapObject() && y->IsECMAObject()) { + JSHandle yPrimitive(thread, ToPrimitive(thread, y)); + RETURN_VALUE_IF_ABRUPT_COMPLETION(thread, false); + return Equal(thread, x, yPrimitive); + } + return false; + } + + if (x->IsHeapObject()) { + if (y->IsHeapObject()) { + // if same type, must call Type::StrictEqual() + JSType xType = x.GetTaggedValue().GetTaggedObject()->GetClass()->GetObjectType(); + JSType yType = y.GetTaggedValue().GetTaggedObject()->GetClass()->GetObjectType(); + if (xType == yType) { + return StrictEqual(thread, x, y); + } + } + if (y->IsNumber() || y->IsStringOrSymbol() || y->IsBoolean()) { + JSHandle x_primitive(thread, ToPrimitive(thread, x)); + RETURN_VALUE_IF_ABRUPT_COMPLETION(thread, false); + return Equal(thread, x_primitive, y); + } + return false; + } + + if (x->IsNull() && y->IsNull()) { + return true; + } + + if (x->IsUndefined() && y->IsUndefined()) { + return true; + } + + if (x->IsNull() && y->IsUndefined()) { + return true; + } + + if (x->IsUndefined() && y->IsNull()) { + return true; + } + + return false; +} + +ComparisonResult JSTaggedValue::Compare(JSThread *thread, const JSHandle &x, + const JSHandle &y) +{ + JSHandle primX(thread, ToPrimitive(thread, x)); + RETURN_VALUE_IF_ABRUPT_COMPLETION(thread, ComparisonResult::UNDEFINED); + JSHandle primY(thread, ToPrimitive(thread, y)); + RETURN_VALUE_IF_ABRUPT_COMPLETION(thread, ComparisonResult::UNDEFINED); + + if (primX->IsString() && primY->IsString()) { + auto xString = static_cast(primX->GetTaggedObject()); + auto yString = static_cast(primY->GetTaggedObject()); + int result = xString->Compare(yString); + if (result < 0) { + return ComparisonResult::LESS; + } + if (result == 0) { + return ComparisonResult::EQUAL; + } + return ComparisonResult::GREAT; + } + + JSTaggedNumber xNumber = ToNumber(thread, x); + RETURN_VALUE_IF_ABRUPT_COMPLETION(thread, ComparisonResult::UNDEFINED); + JSTaggedNumber yNumber = ToNumber(thread, y); + RETURN_VALUE_IF_ABRUPT_COMPLETION(thread, ComparisonResult::UNDEFINED); + return StrictNumberCompare(xNumber.GetNumber(), yNumber.GetNumber()); +} + +bool JSTaggedValue::IsSameTypeOrHClass(JSTaggedValue x, JSTaggedValue y) +{ + if (x.IsNumber() && y.IsNumber()) { + return true; + } + if (x.IsBoolean() && y.IsBoolean()) { + return true; + } + if (x.IsString() && y.IsString()) { + return true; + } + if (x.IsHeapObject() && y.IsHeapObject()) { + return x.GetTaggedObject()->GetClass() == y.GetTaggedObject()->GetClass(); + } + + return false; +} + +JSTaggedValue JSTaggedValue::ToPrimitive(JSThread *thread, const JSHandle &tagged, + PreferredPrimitiveType type) +{ + if (tagged->IsECMAObject()) { + EcmaVM *vm = thread->GetEcmaVM(); + JSHandle keyString = vm->GetGlobalEnv()->GetToPrimitiveSymbol(); + + JSHandle exoticToprim = GetProperty(thread, tagged, keyString).GetValue(); + RETURN_VALUE_IF_ABRUPT_COMPLETION(thread, JSTaggedValue::Exception()); + if (!exoticToprim->IsUndefined()) { + JSTaggedValue value = GetTypeString(thread, type).GetTaggedValue(); + InternalCallParams *arguments = thread->GetInternalCallParams(); + arguments->MakeArgv(value); + JSTaggedValue valueResult = JSFunction::Call(thread, exoticToprim, tagged, 1, arguments->GetArgv()); + RETURN_VALUE_IF_ABRUPT_COMPLETION(thread, JSTaggedValue::Exception()); + if (!valueResult.IsECMAObject()) { + return valueResult; + } + THROW_TYPE_ERROR_AND_RETURN(thread, "", JSTaggedValue::Exception()); + } else { + type = (type == NO_PREFERENCE) ? PREFER_NUMBER : type; + return OrdinaryToPrimitive(thread, tagged, type); + } + } + return tagged.GetTaggedValue(); +} + +JSTaggedValue JSTaggedValue::OrdinaryToPrimitive(JSThread *thread, const JSHandle &tagged, + PreferredPrimitiveType type) +{ + static_assert(PREFER_NUMBER == 0 && PREFER_STRING == 1); + ASSERT(tagged->IsECMAObject()); + auto globalConst = thread->GlobalConstants(); + for (uint8_t i = 0; i < 2; i++) { // 2: 2 means value has 2 target types, string or value. + JSHandle keyString; + if ((type ^ i) != 0) { + keyString = globalConst->GetHandledToStringString(); + } else { + keyString = globalConst->GetHandledValueOfString(); + } + JSHandle entryfunc = GetProperty(thread, tagged, keyString).GetValue(); + if (entryfunc->IsCallable()) { + JSTaggedValue valueResult = JSFunction::Call(thread, entryfunc, tagged, 0, nullptr); + RETURN_VALUE_IF_ABRUPT_COMPLETION(thread, JSTaggedValue::Exception()); + if (!valueResult.IsECMAObject()) { + return valueResult; + } + } + } + THROW_TYPE_ERROR_AND_RETURN(thread, "Cannot convert a illegal value to a Primitive", JSTaggedValue::Undefined()); +} + +JSHandle JSTaggedValue::ToString(JSThread *thread, const JSHandle &tagged) +{ + if (tagged->IsString()) { + return JSHandle(tagged); + } + auto globalConst = thread->GlobalConstants(); + if (tagged->IsSpecial()) { + switch (tagged->GetRawData()) { + case VALUE_UNDEFINED: { + return JSHandle(globalConst->GetHandledUndefinedString()); + } + case VALUE_NULL: { + return JSHandle(globalConst->GetHandledNullString()); + } + case VALUE_TRUE: { + return JSHandle(globalConst->GetHandledTrueString()); + } + case VALUE_FALSE: { + return JSHandle(globalConst->GetHandledFalseString()); + } + case VALUE_HOLE: { + return JSHandle(globalConst->GetHandledEmptyString()); + } + default: + break; + } + } + + if (tagged->IsNumber()) { + return base::NumberHelper::NumberToString(thread, tagged.GetTaggedValue()); + } + + auto emptyStr = globalConst->GetHandledEmptyString(); + if (tagged->IsECMAObject()) { + JSHandle primValue(thread, ToPrimitive(thread, tagged, PREFER_STRING)); + RETURN_VALUE_IF_ABRUPT_COMPLETION(thread, JSHandle(emptyStr)); + return ToString(thread, primValue); + } + // Already Include Symbol + THROW_TYPE_ERROR_AND_RETURN(thread, "Cannot convert a illegal value to a String", JSHandle(emptyStr)); +} + +JSTaggedValue JSTaggedValue::CanonicalNumericIndexString(JSThread *thread, const JSHandle &tagged) +{ + JSHandle str = thread->GetEcmaVM()->GetFactory()->NewFromCanBeCompressString("-0"); + if (tagged->IsString()) { + if (EcmaString::StringsAreEqual(static_cast(tagged->GetTaggedObject()), *str)) { + return JSTaggedValue(-0.0); + } + JSHandle tmp(thread, ToNumber(thread, tagged)); + if (SameValue(ToString(thread, tmp).GetTaggedValue(), tagged.GetTaggedValue())) { + return tmp.GetTaggedValue(); + } + } + return JSTaggedValue::Undefined(); +} + +JSHandle JSTaggedValue::ToObject(JSThread *thread, const JSHandle &tagged) +{ + ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); + if (tagged->IsInt() || tagged->IsDouble()) { + return JSHandle::Cast(factory->NewJSPrimitiveRef(PrimitiveType::PRIMITIVE_NUMBER, tagged)); + } + + switch (tagged->GetRawData()) { + case JSTaggedValue::VALUE_UNDEFINED: { + THROW_TYPE_ERROR_AND_RETURN(thread, "Cannot convert a UNDEFINED value to a JSObject", + JSHandle(thread, JSTaggedValue::Exception())); + } + case JSTaggedValue::VALUE_HOLE: { + THROW_TYPE_ERROR_AND_RETURN(thread, "Cannot convert a HOLE value to a JSObject", + JSHandle(thread, JSTaggedValue::Exception())); + } + case JSTaggedValue::VALUE_NULL: { + THROW_TYPE_ERROR_AND_RETURN(thread, "Cannot convert a NULL value to a JSObject", + JSHandle(thread, JSTaggedValue::Exception())); + } + case JSTaggedValue::VALUE_TRUE: + case JSTaggedValue::VALUE_FALSE: { + return JSHandle::Cast(factory->NewJSPrimitiveRef(PrimitiveType::PRIMITIVE_BOOLEAN, tagged)); + } + default: { + break; + } + } + + if (tagged->IsECMAObject()) { + return JSHandle::Cast(tagged); + } + if (tagged->IsSymbol()) { + return JSHandle::Cast(factory->NewJSPrimitiveRef(PrimitiveType::PRIMITIVE_SYMBOL, tagged)); + } + if (tagged->IsString()) { + return JSHandle::Cast(factory->NewJSPrimitiveRef(PrimitiveType::PRIMITIVE_STRING, tagged)); + } + THROW_TYPE_ERROR_AND_RETURN(thread, "Cannot convert a Unknown object value to a JSObject", + JSHandle(thread, JSTaggedValue::Exception())); +} + +// 7.3.1 Get ( O, P ) +OperationResult JSTaggedValue::GetProperty(JSThread *thread, const JSHandle &obj, + const JSHandle &key) +{ + if (obj->IsUndefined() || obj->IsNull() || obj->IsHole()) { + THROW_TYPE_ERROR_AND_RETURN(thread, "Obj is not a valid object", + OperationResult(thread, JSTaggedValue::Exception(), PropertyMetaData(false))); + } + ASSERT_PRINT(JSTaggedValue::IsPropertyKey(key), "Key is not a property key"); + + if (obj->IsJSProxy()) { + return JSProxy::GetProperty(thread, JSHandle(obj), key); + } + if (obj->IsTypedArray()) { + return JSTypedArray::GetProperty(thread, obj, JSTypedArray::ToPropKey(thread, key)); + } + + return JSObject::GetProperty(thread, obj, key); +} + +OperationResult JSTaggedValue::GetProperty(JSThread *thread, const JSHandle &obj, uint32_t key) +{ + if (obj->IsUndefined() || obj->IsNull() || obj->IsHole()) { + THROW_TYPE_ERROR_AND_RETURN(thread, "Obj is not a valid object", + OperationResult(thread, JSTaggedValue::Exception(), PropertyMetaData(false))); + } + + if (obj->IsJSProxy()) { + JSHandle keyHandle(thread, JSTaggedValue(key)); + return JSProxy::GetProperty(thread, JSHandle(obj), keyHandle); + } + + if (obj->IsTypedArray()) { + return JSTypedArray::GetProperty(thread, obj, key); + } + + return JSObject::GetProperty(thread, obj, key); +} + +OperationResult JSTaggedValue::GetProperty(JSThread *thread, const JSHandle &obj, + const JSHandle &key, const JSHandle &receiver) +{ + if (obj->IsUndefined() || obj->IsNull() || obj->IsHole()) { + THROW_TYPE_ERROR_AND_RETURN(thread, "Obj is not a valid object", + OperationResult(thread, JSTaggedValue::Exception(), PropertyMetaData(false))); + } + ASSERT_PRINT(JSTaggedValue::IsPropertyKey(key), "Key is not a property key"); + + if (obj->IsJSProxy()) { + return JSProxy::GetProperty(thread, JSHandle(obj), key, receiver); + } + if (obj->IsTypedArray()) { + return JSTypedArray::GetProperty(thread, obj, JSTypedArray::ToPropKey(thread, key), receiver); + } + + return JSObject::GetProperty(thread, obj, key, receiver); +} + +// 7.3.3 Set (O, P, V, Throw) +bool JSTaggedValue::SetProperty(JSThread *thread, const JSHandle &obj, + const JSHandle &key, const JSHandle &value, bool mayThrow) +{ + if (obj->IsUndefined() || obj->IsNull() || obj->IsHole()) { + THROW_TYPE_ERROR_AND_RETURN(thread, "Obj is not a Valid object", false); + } + + ASSERT_PRINT(JSTaggedValue::IsPropertyKey(key), "Key is not a property key"); + + // 4. Let success be O.[[Set]](P, V, O). + bool success = false; + if (obj->IsJSProxy()) { + success = JSProxy::SetProperty(thread, JSHandle(obj), key, value, mayThrow); + } else if (obj->IsTypedArray()) { + success = JSTypedArray::SetProperty(thread, obj, JSTypedArray::ToPropKey(thread, key), value, mayThrow); + } else { + success = JSObject::SetProperty(thread, obj, key, value, mayThrow); + } + // 5. ReturnIfAbrupt(success). + RETURN_VALUE_IF_ABRUPT_COMPLETION(thread, success); + // 6. If success is false and Throw is true, throw a TypeError exception. + // have done in JSObject::SetPropert. + return success; +} + +bool JSTaggedValue::SetProperty(JSThread *thread, const JSHandle &obj, uint32_t key, + const JSHandle &value, bool mayThrow) +{ + if (obj->IsUndefined() || obj->IsNull() || obj->IsHole()) { + THROW_TYPE_ERROR_AND_RETURN(thread, "Obj is not a Valid object", false); + } + + // 4. Let success be O.[[Set]](P, V, O). + bool success = false; + if (obj->IsJSProxy()) { + JSHandle keyHandle(thread, JSTaggedValue(key)); + success = JSProxy::SetProperty(thread, JSHandle(obj), keyHandle, value, mayThrow); + } else if (obj->IsTypedArray()) { + JSHandle keyHandle(thread, JSTaggedValue(key)); + success = JSTypedArray::SetProperty( + thread, obj, JSHandle(JSTaggedValue::ToString(thread, keyHandle)), value, mayThrow); + } else { + success = JSObject::SetProperty(thread, obj, key, value, mayThrow); + } + // 5. ReturnIfAbrupt(success). + RETURN_VALUE_IF_ABRUPT_COMPLETION(thread, success); + // 6. If success is false and Throw is true, throw a TypeError exception. + // have done in JSObject::SetPropert. + return success; +} + +bool JSTaggedValue::SetProperty(JSThread *thread, const JSHandle &obj, + const JSHandle &key, const JSHandle &value, + const JSHandle &receiver, bool mayThrow) +{ + if (obj->IsUndefined() || obj->IsNull() || obj->IsHole()) { + THROW_TYPE_ERROR_AND_RETURN(thread, "Obj is not a Valid object", false); + } + + ASSERT_PRINT(JSTaggedValue::IsPropertyKey(key), "Key is not a property key"); + + // 4. Let success be O.[[Set]](P, V, O). + bool success = false; + if (obj->IsJSProxy()) { + success = JSProxy::SetProperty(thread, JSHandle(obj), key, value, receiver, mayThrow); + } else if (obj->IsTypedArray()) { + success = + JSTypedArray::SetProperty(thread, obj, JSTypedArray::ToPropKey(thread, key), value, receiver, mayThrow); + } else { + success = JSObject::SetProperty(thread, obj, key, value, receiver, mayThrow); + } + // 5. ReturnIfAbrupt(success). + RETURN_VALUE_IF_ABRUPT_COMPLETION(thread, success); + // 6. If success is false and Throw is true, throw a TypeError exception. + // have done in JSObject::SetPropert. + return success; +} + +bool JSTaggedValue::DeleteProperty(JSThread *thread, const JSHandle &obj, + const JSHandle &key) +{ + if (obj->IsJSProxy()) { + return JSProxy::DeleteProperty(thread, JSHandle(obj), key); + } + + if (obj->IsSpecialContainer()) { + THROW_TYPE_ERROR_AND_RETURN(thread, "Can not delete property in Container Object", false); + } + + return JSObject::DeleteProperty(thread, JSHandle(obj), key); +} + +// 7.3.8 DeletePropertyOrThrow (O, P) +bool JSTaggedValue::DeletePropertyOrThrow(JSThread *thread, const JSHandle &obj, + const JSHandle &key) +{ + if (!obj->IsECMAObject()) { + THROW_TYPE_ERROR_AND_RETURN(thread, "Obj is not a valid object", false); + } + ASSERT_PRINT(JSTaggedValue::IsPropertyKey(key), "Key is not a property key"); + + // 3. Let success be O.[[Delete]](P). + bool success = DeleteProperty(thread, obj, key); + + // 4. ReturnIfAbrupt(success). + RETURN_VALUE_IF_ABRUPT_COMPLETION(thread, success); + // 5. If success is false, throw a TypeError exception + if (!success) { + THROW_TYPE_ERROR_AND_RETURN(thread, "Cannot delete property", false); + } + return success; +} + +// 7.3.7 DefinePropertyOrThrow (O, P, desc) +bool JSTaggedValue::DefinePropertyOrThrow(JSThread *thread, const JSHandle &obj, + const JSHandle &key, const PropertyDescriptor &desc) +{ + // 1. Assert: Type(O) is Object. + // 2. Assert: IsPropertyKey(P) is true. + ASSERT_PRINT(obj->IsECMAObject(), "Obj is not a valid object"); + ASSERT_PRINT(JSTaggedValue::IsPropertyKey(key), "Key is not a property key"); + // 3. Let success be ? O.[[DefineOwnProperty]](P, desc). + bool success = DefineOwnProperty(thread, obj, key, desc); + RETURN_VALUE_IF_ABRUPT_COMPLETION(thread, false); + // 4. If success is false, throw a TypeError exception. + if (!success) { + THROW_TYPE_ERROR_AND_RETURN(thread, "", false); + } + return success; +} + +bool JSTaggedValue::DefineOwnProperty(JSThread *thread, const JSHandle &obj, + const JSHandle &key, const PropertyDescriptor &desc) +{ + if (obj->IsJSArray()) { + return JSArray::DefineOwnProperty(thread, JSHandle(obj), key, desc); + } + + if (obj->IsJSProxy()) { + return JSProxy::DefineOwnProperty(thread, JSHandle(obj), key, desc); + } + + if (obj->IsTypedArray()) { + return JSTypedArray::DefineOwnProperty(thread, obj, JSTypedArray::ToPropKey(thread, key), desc); + } + + if (obj->IsSpecialContainer()) { + THROW_TYPE_ERROR_AND_RETURN(thread, "Can not defineProperty on Container Object", false); + } + + return JSObject::DefineOwnProperty(thread, JSHandle(obj), key, desc); +} + +bool JSTaggedValue::GetOwnProperty(JSThread *thread, const JSHandle &obj, + const JSHandle &key, PropertyDescriptor &desc) +{ + if (obj->IsJSProxy()) { + return JSProxy::GetOwnProperty(thread, JSHandle(obj), key, desc); + } + if (obj->IsTypedArray()) { + return JSTypedArray::GetOwnProperty(thread, obj, JSTypedArray::ToPropKey(thread, key), desc); + } + if (obj->IsSpecialContainer()) { + return GetContainerProperty(thread, obj, key, desc); + } + return JSObject::GetOwnProperty(thread, JSHandle(obj), key, desc); +} + +bool JSTaggedValue::SetPrototype(JSThread *thread, const JSHandle &obj, + const JSHandle &proto) +{ + if (obj->IsJSProxy()) { + return JSProxy::SetPrototype(thread, JSHandle(obj), proto); + } + if (obj->IsSpecialContainer()) { + THROW_TYPE_ERROR_AND_RETURN(thread, "Can not set Prototype on Container Object", false); + } + + return JSObject::SetPrototype(thread, JSHandle(obj), proto); +} + +bool JSTaggedValue::PreventExtensions(JSThread *thread, const JSHandle &obj) +{ + if (obj->IsJSProxy()) { + return JSProxy::PreventExtensions(thread, JSHandle(obj)); + } + return JSObject::PreventExtensions(thread, JSHandle(obj)); +} + +JSHandle JSTaggedValue::GetOwnPropertyKeys(JSThread *thread, const JSHandle &obj) +{ + if (obj->IsJSProxy()) { + return JSProxy::OwnPropertyKeys(thread, JSHandle(obj)); + } + if (obj->IsTypedArray()) { + return JSTypedArray::OwnPropertyKeys(thread, obj); + } + if (obj->IsSpecialContainer()) { + return GetOwnContainerPropertyKeys(thread, obj); + } + return JSObject::GetOwnPropertyKeys(thread, JSHandle(obj)); +} + +// 7.3.10 HasProperty (O, P) +bool JSTaggedValue::HasProperty(JSThread *thread, const JSHandle &obj, + const JSHandle &key) +{ + if (obj->IsJSProxy()) { + return JSProxy::HasProperty(thread, JSHandle(obj), key); + } + if (obj->IsTypedArray()) { + return JSTypedArray::HasProperty(thread, obj, JSTypedArray::ToPropKey(thread, key)); + } + if (obj->IsSpecialContainer()) { + return HasContainerProperty(thread, obj, key); + } + return JSObject::HasProperty(thread, JSHandle(obj), key); +} + +bool JSTaggedValue::HasProperty(JSThread *thread, const JSHandle &obj, uint32_t key) +{ + if (obj->IsJSProxy()) { + JSHandle keyHandle(thread, JSTaggedValue(key)); + return JSProxy::HasProperty(thread, JSHandle(obj), keyHandle); + } + if (obj->IsTypedArray()) { + JSHandle key_handle(thread, JSTaggedValue(key)); + return JSTypedArray::HasProperty(thread, obj, JSHandle(ToString(thread, key_handle))); + } + if (obj->IsSpecialContainer()) { + return HasContainerProperty(thread, obj, JSHandle(thread, JSTaggedValue(key))); + } + return JSObject::HasProperty(thread, JSHandle(obj), key); +} + +// 7.3.11 HasOwnProperty (O, P) +bool JSTaggedValue::HasOwnProperty(JSThread *thread, const JSHandle &obj, + const JSHandle &key) +{ + ASSERT_PRINT(JSTaggedValue::IsPropertyKey(key), "Key is not a property key"); + + PropertyDescriptor desc(thread); + return JSTaggedValue::GetOwnProperty(thread, obj, key, desc); +} + +bool JSTaggedValue::GlobalHasOwnProperty(JSThread *thread, const JSHandle &key) +{ + ASSERT_PRINT(JSTaggedValue::IsPropertyKey(key), "Key is not a property key"); + + PropertyDescriptor desc(thread); + return JSObject::GlobalGetOwnProperty(thread, key, desc); +} + +JSTaggedNumber JSTaggedValue::ToIndex(JSThread *thread, const JSHandle &tagged) +{ + if (tagged->IsUndefined()) { + return JSTaggedNumber(0); + } + JSTaggedNumber integerIndex = ToInteger(thread, tagged); + RETURN_VALUE_IF_ABRUPT_COMPLETION(thread, JSTaggedNumber::Exception()); + if (integerIndex.GetNumber() < 0) { + THROW_RANGE_ERROR_AND_RETURN(thread, "integerIndex < 0", JSTaggedNumber::Exception()); + } + JSTaggedNumber index = ToLength(thread, tagged); + RETURN_VALUE_IF_ABRUPT_COMPLETION(thread, JSTaggedNumber::Exception()); + if (!SameValue(integerIndex, index)) { + THROW_RANGE_ERROR_AND_RETURN(thread, "integerIndex != index", JSTaggedNumber::Exception()); + } + return index; +} + +JSHandle JSTaggedValue::ToPrototypeOrObj(JSThread *thread, const JSHandle &obj) +{ + JSHandle env = thread->GetEcmaVM()->GetGlobalEnv(); + + if (obj->IsNumber()) { + return JSHandle(thread, + env->GetNumberFunction().GetObject()->GetFunctionPrototype()); + } + if (obj->IsBoolean()) { + return JSHandle(thread, + env->GetBooleanFunction().GetObject()->GetFunctionPrototype()); + } + if (obj->IsString()) { + return JSHandle(thread, + env->GetStringFunction().GetObject()->GetFunctionPrototype()); + } + if (obj->IsSymbol()) { + return JSHandle(thread, + env->GetSymbolFunction().GetObject()->GetFunctionPrototype()); + } + + return obj; +} + +JSTaggedValue JSTaggedValue::GetSuperBase(JSThread *thread, const JSHandle &obj) +{ + if (obj->IsUndefined()) { + return JSTaggedValue::Undefined(); + } + + ASSERT(obj->IsECMAObject()); + return JSObject::Cast(obj.GetTaggedValue())->GetPrototype(thread); +} + +bool JSTaggedValue::HasContainerProperty([[maybe_unused]] JSThread *thread, const JSHandle &obj, + const JSHandle &key) +{ + auto *hclass = obj->GetTaggedObject()->GetClass(); + JSType jsType = hclass->GetObjectType(); + switch (jsType) { + case JSType::JS_ARRAY_LIST: { + return JSHandle::Cast(obj)->Has(key.GetTaggedValue()); + } + case JSType::JS_QUEUE: + break; + default: { + UNREACHABLE(); + } + } + return false; +} + +JSHandle JSTaggedValue::GetOwnContainerPropertyKeys(JSThread *thread, const JSHandle &obj) +{ + auto *hclass = obj->GetTaggedObject()->GetClass(); + JSType jsType = hclass->GetObjectType(); + switch (jsType) { + case JSType::JS_ARRAY_LIST: { + return JSArrayList::OwnKeys(thread, JSHandle::Cast(obj)); + } + case JSType::JS_QUEUE: + break; + default: { + UNREACHABLE(); + } + } + return thread->GetEcmaVM()->GetFactory()->EmptyArray(); +} + +bool JSTaggedValue::GetContainerProperty(JSThread *thread, const JSHandle &obj, + const JSHandle &key, PropertyDescriptor &desc) +{ + auto *hclass = obj->GetTaggedObject()->GetClass(); + JSType jsType = hclass->GetObjectType(); + switch (jsType) { + case JSType::JS_ARRAY_LIST: { + return JSArrayList::GetOwnProperty(thread, JSHandle::Cast(obj), key, desc); + } + case JSType::JS_QUEUE: + break; + default: { + UNREACHABLE(); + } + } + return false; +} +} // namespace panda::ecmascript diff --git a/runtime/js_tagged_value.h b/runtime/js_tagged_value.h new file mode 100644 index 000000000..23c2f88c8 --- /dev/null +++ b/runtime/js_tagged_value.h @@ -0,0 +1,354 @@ +/* + * Copyright (c) 2021-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_JS_TAGGED_VALUE_H +#define ECMASCRIPT_JS_TAGGED_VALUE_H + +#include "plugins/ecmascript/runtime/mem/c_string.h" +#include "include/coretypes/tagged_value.h" + +namespace panda::ecmascript { +class JSObject; +class JSTaggedNumber; +template +class JSHandle; +class TaggedArray; +class LinkedHashMap; +class LinkedHashSet; +class PropertyDescriptor; +class OperationResult; +class EcmaString; +class JSThread; + +// Don't switch the order! +enum PreferredPrimitiveType : uint8_t { PREFER_NUMBER = 0, PREFER_STRING, NO_PREFERENCE }; + +// Result of an abstract relational comparison of x and y, implemented according +// to ES6 section 7.2.11 Abstract Relational Comparison. +enum class ComparisonResult { + LESS, // x < y + EQUAL, // x = y + GREAT, // x > y + UNDEFINED // at least one of x or y was undefined or NaN +}; + +// NOLINTNEXTLINE(cppcoreguidelines-macro-usage) +#define RETURN_TAGGED_VALUE_IF_ABRUPT(thread) RETURN_VALUE_IF_ABRUPT_COMPLETION(thread, JSTaggedValue::Undefined()); + +using JSTaggedType = coretypes::TaggedType; + +class JSTaggedValue : public coretypes::TaggedValue { +public: + static JSTaggedValue Cast(ObjectHeader *object) + { + return JSTaggedValue(object); + } + + JSTaggedValue(void *) = delete; + + constexpr JSTaggedValue() = default; + constexpr explicit JSTaggedValue(coretypes::TaggedType v) : coretypes::TaggedValue(v) {} + constexpr explicit JSTaggedValue(int v) : coretypes::TaggedValue(v) {} + explicit JSTaggedValue(unsigned int v) : coretypes::TaggedValue(v) {} + constexpr explicit JSTaggedValue(bool v) : coretypes::TaggedValue(v) {} + explicit JSTaggedValue(double v) : coretypes::TaggedValue(v) {} + explicit JSTaggedValue(const ObjectHeader *v) : coretypes::TaggedValue(v) {} + explicit JSTaggedValue(const TaggedObject *v) : coretypes::TaggedValue(v) {} + explicit JSTaggedValue(const coretypes::TaggedValue &other) : coretypes::TaggedValue(other.GetRawData()) {} + explicit JSTaggedValue(int64_t v) : coretypes::TaggedValue(v) {}; + + ~JSTaggedValue() = default; + DEFAULT_COPY_SEMANTIC(JSTaggedValue); + DEFAULT_MOVE_SEMANTIC(JSTaggedValue); + + inline TaggedObject *GetWeakReferentUnChecked() const + { + return reinterpret_cast(GetRawData() & (~TAG_WEAK_MASK)); + } + + static inline constexpr JSTaggedValue False() + { + return JSTaggedValue(VALUE_FALSE); + } + + static inline constexpr JSTaggedValue True() + { + return JSTaggedValue(VALUE_TRUE); + } + + static inline constexpr JSTaggedValue Undefined() + { + return JSTaggedValue(VALUE_UNDEFINED); + } + + static inline constexpr JSTaggedValue Null() + { + return JSTaggedValue(VALUE_NULL); + } + + static inline constexpr JSTaggedValue Hole() + { + return JSTaggedValue(VALUE_HOLE); + } + + static inline constexpr JSTaggedValue Exception() + { + return JSTaggedValue(VALUE_EXCEPTION); + } + + inline double GetNumber() const + { + return IsInt() ? GetInt() : GetDouble(); + } + + inline TaggedObject *GetTaggedObject() const + { + return reinterpret_cast(GetHeapObject()); + } + + inline TaggedObject *GetRawTaggedObject() const + { + return reinterpret_cast(GetRawHeapObject()); + } + + inline TaggedObject *GetTaggedWeakRef() const + { + return reinterpret_cast(GetWeakReferent()); + } + + static JSTaggedValue OrdinaryToPrimitive(JSThread *thread, const JSHandle &tagged, + PreferredPrimitiveType type = PREFER_NUMBER); + + // ecma6 7.1 Type Conversion + static JSTaggedValue ToPrimitive(JSThread *thread, const JSHandle &tagged, + PreferredPrimitiveType type = NO_PREFERENCE); + bool ToBoolean() const; + static JSTaggedNumber ToNumber(JSThread *thread, const JSHandle &tagged); + static JSTaggedNumber ToInteger(JSThread *thread, const JSHandle &tagged); + static int32_t ToInt32(JSThread *thread, const JSHandle &tagged); + static uint32_t ToUint32(JSThread *thread, const JSHandle &tagged); + static int16_t ToInt16(JSThread *thread, const JSHandle &tagged); + static uint16_t ToUint16(JSThread *thread, const JSHandle &tagged); + static int8_t ToInt8(JSThread *thread, const JSHandle &tagged); + static uint8_t ToUint8(JSThread *thread, const JSHandle &tagged); + static uint8_t ToUint8Clamp(JSThread *thread, const JSHandle &tagged); + static JSHandle ToString(JSThread *thread, const JSHandle &tagged); + static JSHandle ToObject(JSThread *thread, const JSHandle &tagged); + static JSHandle ToPropertyKey(JSThread *thread, const JSHandle &tagged); + static JSTaggedNumber ToLength(JSThread *thread, const JSHandle &tagged); + static JSTaggedValue CanonicalNumericIndexString(JSThread *thread, const JSHandle &tagged); + static JSTaggedNumber ToIndex(JSThread *thread, const JSHandle &tagged); + + static bool ToArrayLength(JSThread *thread, const JSHandle &tagged, uint32_t *output); + static bool ToElementIndex(JSTaggedValue key, uint32_t *output); + static bool StringToElementIndex(JSTaggedValue key, uint32_t *output); + uint32_t GetArrayLength() const; + + // ecma6 7.2 Testing and Comparison Operations + bool IsCallable() const; + bool IsConstructor() const; + bool IsExtensible(JSThread *thread) const; + bool IsInteger() const; + bool WithinInt32() const; + bool IsZero() const; + static bool IsPropertyKey(const JSHandle &key); + static JSHandle RequireObjectCoercible(JSThread *thread, const JSHandle &tagged); + static bool SameValue(const JSTaggedValue &x, const JSTaggedValue &y); + static bool SameValue(const JSHandle &xHandle, const JSHandle &yHandle); + static bool SameValueZero(const JSTaggedValue &x, const JSTaggedValue &y); + static bool Less(JSThread *thread, const JSHandle &x, const JSHandle &y); + static bool Equal(JSThread *thread, const JSHandle &x, const JSHandle &y); + static bool StrictEqual(const JSThread *thread, const JSHandle &x, const JSHandle &y); + static bool SameValueNumberic(const JSTaggedValue &x, const JSTaggedValue &y); + + // ES6 7.4 Operations on Iterator Objects + static JSObject *CreateIterResultObject(JSThread *thread, const JSHandle &value, bool done); + + // ecma6 7.3 + static OperationResult GetProperty(JSThread *thread, const JSHandle &obj, + const JSHandle &key); + + static OperationResult GetProperty(JSThread *thread, const JSHandle &obj, uint32_t key); + static OperationResult GetProperty(JSThread *thread, const JSHandle &obj, + const JSHandle &key, const JSHandle &receiver); + static bool SetProperty(JSThread *thread, const JSHandle &obj, uint32_t key, + const JSHandle &value, bool mayThrow = false); + + static bool SetProperty(JSThread *thread, const JSHandle &obj, const JSHandle &key, + const JSHandle &value, bool mayThrow = false); + + static bool SetProperty(JSThread *thread, const JSHandle &obj, const JSHandle &key, + const JSHandle &value, const JSHandle &receiver, + bool mayThrow = false); + static bool DeleteProperty(JSThread *thread, const JSHandle &obj, + const JSHandle &key); + static bool DeletePropertyOrThrow(JSThread *thread, const JSHandle &obj, + const JSHandle &key); + static bool DefinePropertyOrThrow(JSThread *thread, const JSHandle &obj, + const JSHandle &key, const PropertyDescriptor &desc); + static bool DefineOwnProperty(JSThread *thread, const JSHandle &obj, + const JSHandle &key, const PropertyDescriptor &desc); + static bool GetOwnProperty(JSThread *thread, const JSHandle &obj, const JSHandle &key, + PropertyDescriptor &desc); + static bool SetPrototype(JSThread *thread, const JSHandle &obj, + const JSHandle &proto); + static bool PreventExtensions(JSThread *thread, const JSHandle &obj); + static JSHandle GetOwnPropertyKeys(JSThread *thread, const JSHandle &obj); + static bool HasProperty(JSThread *thread, const JSHandle &obj, const JSHandle &key); + static bool HasProperty(JSThread *thread, const JSHandle &obj, uint32_t key); + static bool HasOwnProperty(JSThread *thread, const JSHandle &obj, + const JSHandle &key); + static bool GlobalHasOwnProperty(JSThread *thread, const JSHandle &key); + + // Type + bool IsJSMap() const; + bool IsJSSet() const; + bool IsJSWeakMap() const; + bool IsJSWeakSet() const; + bool IsJSRegExp() const; + bool IsNumber() const; + bool IsString() const; + bool IsStringOrSymbol() const; + bool IsTaggedArray() const; + bool IsNativePointer() const; + bool IsJSNativePointer() const; + bool IsBoolean() const; + bool IsSymbol() const; + bool IsJSObject() const; + bool IsJSGlobalObject() const; + bool IsJSError() const; + bool IsArray(JSThread *thread) const; + bool IsJSArray() const; + bool IsStableJSArray(JSThread *thread) const; + bool IsStableJSArguments(JSThread *thread) const; + bool HasStableElements(JSThread *thread) const; + bool IsTypedArray() const; + bool IsJSTypedArray() const; + bool IsJSInt8Array() const; + bool IsJSUint8Array() const; + bool IsJSUint8ClampedArray() const; + bool IsJSInt16Array() const; + bool IsJSUint16Array() const; + bool IsJSInt32Array() const; + bool IsJSUint32Array() const; + bool IsJSFloat32Array() const; + bool IsJSFloat64Array() const; + bool IsArguments() const; + bool IsDate() const; + bool IsBoundFunction() const; + bool IsJSIntlBoundFunction() const; + bool IsProxyRevocFunction() const; + bool IsJSAsyncFunction() const; + bool IsJSAsyncGeneratorFunction() const; + bool IsJSAsyncAwaitStatusFunction() const; + bool IsJSAsyncGeneratorResolveNextFunction() const; + bool IsJSAsyncFromSyncIteratorValueUnwrapFunction() const; + bool IsClassConstructor() const; + bool IsClassPrototype() const; + bool IsJSFunction() const; + bool IsJSFunctionBase() const; + bool IsECMAObject() const; + bool IsJSPrimitiveRef() const; + bool IsJSPrimitive() const; + bool IsAccessorData() const; + bool IsInternalAccessor() const; + bool IsAccessor() const; + bool IsJSGlobalEnv() const; + bool IsJSProxy() const; + bool IsDynClass() const; + bool IsJSHClass() const; + bool IsObjectWrapper() const; + bool IsForinIterator() const; + bool IsStringIterator() const; + bool IsArrayBuffer() const; + + bool IsJSSetIterator() const; + bool IsJSMapIterator() const; + bool IsJSArrayIterator() const; + bool IsIterator() const; + bool IsGeneratorFunction() const; + bool IsGeneratorObject() const; + bool IsGeneratorContext() const; + bool IsAsyncFuncObject() const; + bool IsAsyncGeneratorFunction() const; + bool IsJSAsyncFromSyncIteratorObject() const; + bool IsAsyncGeneratorObject() const; + bool IsJSPromise() const; + bool IsRecord() const; + bool IsPromiseReaction() const; + bool IsProgram() const; + bool IsLexicalFunction() const; + bool IsJSPromiseReactionFunction() const; + bool IsJSPromiseExecutorFunction() const; + bool IsJSPromiseAllResolveElementFunction() const; + bool IsPromiseCapability() const; + bool IsPromiseIteratorRecord() const; + bool IsPromiseRecord() const; + bool IsResolvingFunctionsRecord() const; + bool IsCompletionRecord() const; + bool IsDataView() const; + bool IsTemplateMap() const; + bool IsJSFunctionExtraInfo() const; + bool IsMicroJobQueue() const; + bool IsPendingJob() const; + bool IsJSLocale() const; + bool IsJSDateTimeFormat() const; + bool IsJSRelativeTimeFormat() const; + bool IsJSIntl() const; + bool IsJSNumberFormat() const; + bool IsJSCollator() const; + bool IsJSPluralRules() const; + bool IsJSArrayList() const; + bool IsSpecialContainer() const; + + bool IsPrototypeHandler() const; + bool IsTransitionHandler() const; + bool IsPropertyBox() const; + bool IsProtoChangeMarker() const; + bool IsProtoChangeDetails() const; + bool IsMachineCodeObject() const; + bool IsClassInfoExtractor() const; + static bool IsSameTypeOrHClass(JSTaggedValue x, JSTaggedValue y); + + static ComparisonResult Compare(JSThread *thread, const JSHandle &x, + const JSHandle &y); + static ComparisonResult StrictNumberCompare(double x, double y); + static bool StrictNumberEquals(double x, double y); + + static JSHandle ToPrototypeOrObj(JSThread *thread, const JSHandle &obj); + inline uint32_t GetKeyHashCode() const; + inline uint32_t GetHashCode(); + static JSTaggedValue GetSuperBase(JSThread *thread, const JSHandle &obj); + + void DumpTaggedValue(JSThread *thread, std::ostream &os) const DUMP_API_ATTR; + void Dump(JSThread *thread, std::ostream &os) const DUMP_API_ATTR; + void D() const DUMP_API_ATTR; + void DumpForSnapshot(JSThread *thread, std::vector> &vec, + bool isVmMode = true) const; + static void DV(JSTaggedType val) DUMP_API_ATTR; + +private: + inline double ExtractNumber() const; + + void DumpSpecialValue([[maybe_unused]] JSThread *thread, std::ostream &os) const; + void DumpHeapObjectType([[maybe_unused]] JSThread *thread, std::ostream &os) const; + static bool HasContainerProperty(JSThread *thread, const JSHandle &obj, + const JSHandle &key); + static JSHandle GetOwnContainerPropertyKeys(JSThread *thread, const JSHandle &obj); + static bool GetContainerProperty(JSThread *thread, const JSHandle &obj, + const JSHandle &key, PropertyDescriptor &desc); +}; +} // namespace panda::ecmascript +#endif // ECMASCRIPT_JS_TAGGED_VALUE_H diff --git a/runtime/js_thread.cpp b/runtime/js_thread.cpp new file mode 100644 index 000000000..e7c5e6abe --- /dev/null +++ b/runtime/js_thread.cpp @@ -0,0 +1,244 @@ +/* + * Copyright (c) 2021-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 "plugins/ecmascript/runtime/js_thread.h" +#include "plugins/ecmascript/compiler/ecmascript_extensions/thread_environment_api.h" +#include "plugins/ecmascript/runtime/global_env_constants-inl.h" +#include "plugins/ecmascript/runtime/ic/properties_cache-inl.h" +#include "plugins/ecmascript/runtime/internal_call_params.h" +#include "plugins/ecmascript/runtime/interpreter/interpreter-inl.h" +#include "plugins/ecmascript/runtime/mem/machine_code.h" +#include "runtime/include/panda_vm.h" +#include "runtime/include/stack_walker-inl.h" + +namespace panda::ecmascript { +// static +JSThread *JSThread::Create(Runtime *runtime, PandaVM *vm) +{ + auto jsThread = new JSThread(runtime, vm); + jsThread->regionFactory_ = EcmaVM::Cast(vm)->GetRegionFactory(); + jsThread->InitBuffers(); + JSThread::SetCurrent(jsThread); + jsThread->NativeCodeBegin(); + return jsThread; +} + +JSThread::JSThread(Runtime *runtime, PandaVM *vm) + : ManagedThread(GetCurrentThreadId(), runtime->GetInternalAllocator(), vm, Thread::ThreadType::THREAD_TYPE_MANAGED, + panda_file::SourceLang::ECMASCRIPT) +{ + SetLanguageContext(runtime->GetLanguageContext(panda_file::SourceLang::ECMASCRIPT)); + auto chunk = EcmaVM::Cast(vm)->GetChunk(); + globalStorage_ = chunk->New(chunk); + internalCallParams_ = new InternalCallParams(); + propertiesCache_ = new PropertiesCache(); +} + +JSThread::~JSThread() +{ + for (auto n : handleStorageNodes_) { + delete n; + } + handleStorageNodes_.clear(); + currentHandleStorageIndex_ = -1; + handleScopeCount_ = 0; + handleScopeStorageNext_ = handleScopeStorageEnd_ = nullptr; + EcmaVM::Cast(GetVM())->GetChunk()->Delete(globalStorage_); + + regionFactory_ = nullptr; + if (internalCallParams_ != nullptr) { + delete internalCallParams_; + internalCallParams_ = nullptr; + } + if (propertiesCache_ != nullptr) { + delete propertiesCache_; + propertiesCache_ = nullptr; + } +} + +EcmaVM *JSThread::GetEcmaVM() const +{ + return EcmaVM::Cast(GetVM()); +} + +void JSThread::SetException(JSTaggedValue exception) +{ + ManagedThread::SetException(exception.GetHeapObject()); +} + +void JSThread::ClearException() +{ + ManagedThread::ClearException(); +} + +JSTaggedValue JSThread::GetCurrentLexenv() const +{ + auto *env = EcmascriptEnvironment::Cast(GetLanguageExtensionsData()); + return JSTaggedValue(env->GetLexicalEnv()); +} + +void JSThread::Iterate(const RootVisitor &v0, const RootRangeVisitor &v1) +{ + auto *exc = ManagedThread::GetException(); + if (!JSTaggedValue(exc).IsHole()) { + v0(Root::ROOT_VM, ObjectSlot(ToUintPtr(exc))); + v0(Root::ROOT_VM, + ObjectSlot(ToUintPtr(static_cast(this)) + ManagedThread::GetExceptionOffset())); + } + + for (auto stack = StackWalker::Create(this); stack.HasFrame(); stack.NextFrame()) { + stack.IterateObjects([&v0](auto &vreg) { + v0(Root::ROOT_FRAME, ObjectSlot(ToUintPtr(vreg.GetReference()))); + return true; + }); + } + IterateEcmascriptEnvironment(v0, v1); + + // visit internal call params; + internalCallParams_->Iterate(v1); + // visit tagged handle storage roots + if (currentHandleStorageIndex_ != -1) { + int32_t nid = currentHandleStorageIndex_; + for (int32_t i = 0; i <= nid; ++i) { + auto node = handleStorageNodes_.at(i); + auto start = node->data(); + auto end = (i != nid) ? &(node->data()[NODE_BLOCK_SIZE]) : handleScopeStorageNext_; + v1(ecmascript::Root::ROOT_HANDLE, ObjectSlot(ToUintPtr(start)), ObjectSlot(ToUintPtr(end))); + } + } + globalStorage_->IterateUsageGlobal([v0](EcmaGlobalStorage::Node *node) { + JSTaggedValue value(node->GetObject()); + if (value.IsHeapObject()) { + v0(ecmascript::Root::ROOT_HANDLE, ecmascript::ObjectSlot(node->GetObjectAddress())); + } + }); +} + +void JSThread::IterateWeakEcmaGlobalStorage(const WeakRootVisitor &visitor) +{ + globalStorage_->IterateWeakUsageGlobal([visitor](EcmaGlobalStorage::Node *node) { + JSTaggedValue value(node->GetObject()); + if (value.IsHeapObject()) { + auto object = value.GetTaggedObject(); + auto fwd = visitor(object); + if (fwd == nullptr) { + // undefind + node->SetObject(JSTaggedValue::Undefined().GetRawData()); + } else if (fwd != object) { + // update + node->SetObject(JSTaggedValue(fwd).GetRawData()); + } + } + }); +} + +void JSThread::IterateEcmascriptEnvironment(const RootVisitor &v0, const RootRangeVisitor &v1) +{ + auto *env = EcmascriptEnvironment::Cast(GetLanguageExtensionsData()); + auto adapter = [&v1]([[maybe_unused]] ObjectHeader *obj, ObjectSlot start, ObjectSlot end) { + v1(Root::ROOT_VM, start, end); + }; + while (env != nullptr) { + v0(Root::ROOT_VM, ObjectSlot(ToUintPtr(env) + EcmascriptEnvironment::GetConstantPoolOffset())); + v0(Root::ROOT_VM, ObjectSlot(ToUintPtr(env) + EcmascriptEnvironment::GetLexicalEnvOffset())); + v0(Root::ROOT_VM, ObjectSlot(ToUintPtr(env) + EcmascriptEnvironment::GetThisFuncOffset())); + + JSTaggedValue lex_env(env->GetLexicalEnv()); + while (!lex_env.IsJSGlobalEnv()) { + LexicalEnv::Cast(lex_env.GetHeapObject())->VisitRangeSlot(adapter); + lex_env = LexicalEnv::Cast(lex_env.GetHeapObject())->GetParentEnv(); + } + env = env->GetPrevEnvironment(); + } + + if (propertiesCache_ != nullptr) { + propertiesCache_->Clear(); + } + + if (!exception_.IsHole()) { + v0(Root::ROOT_VM, ObjectSlot(ToUintPtr(&exception_))); + } + + // visit global Constant + globalConst_.VisitRangeSlot(v1); +} + +uintptr_t *JSThread::ExpandHandleStorage() +{ + uintptr_t *result = nullptr; + int32_t lastIndex = handleStorageNodes_.size() - 1; + if (currentHandleStorageIndex_ == lastIndex) { + auto n = new std::array(); + handleStorageNodes_.push_back(n); + currentHandleStorageIndex_++; + result = reinterpret_cast(&n->data()[0]); + handleScopeStorageEnd_ = &n->data()[NODE_BLOCK_SIZE]; + } else { + currentHandleStorageIndex_++; + auto lastNode = handleStorageNodes_[currentHandleStorageIndex_]; + result = reinterpret_cast(&lastNode->data()[0]); + handleScopeStorageEnd_ = &lastNode->data()[NODE_BLOCK_SIZE]; + } + + return result; +} + +void JSThread::ShrinkHandleStorage(int prevIndex) +{ + currentHandleStorageIndex_ = prevIndex; + int32_t lastIndex = handleStorageNodes_.size() - 1; +#if ECMASCRIPT_ENABLE_ZAP_MEM + uintptr_t size = ToUintPtr(handleScopeStorageEnd_) - ToUintPtr(handleScopeStorageNext_); + memset_s(handleScopeStorageNext_, size, 0, size); + for (int32_t i = currentHandleStorageIndex_ + 1; i < lastIndex; i++) { + memset_s(handleStorageNodes_[i], NODE_BLOCK_SIZE * sizeof(JSTaggedType), 0, + NODE_BLOCK_SIZE * sizeof(JSTaggedType)); + } +#endif + + if (lastIndex > MIN_HANDLE_STORAGE_SIZE && currentHandleStorageIndex_ < MIN_HANDLE_STORAGE_SIZE) { + for (int i = MIN_HANDLE_STORAGE_SIZE; i < lastIndex; i++) { + auto node = handleStorageNodes_.back(); + delete node; + handleStorageNodes_.pop_back(); + } + } +} + +void JSThread::NotifyStableArrayElementsGuardians(JSHandle receiver) +{ + if (!receiver->GetJSHClass()->IsPrototype()) { + return; + } + if (!stableArrayElementsGuardians_) { + return; + } + auto env = GetEcmaVM()->GetGlobalEnv(); + if (receiver.GetTaggedValue() == env->GetObjectFunctionPrototype().GetTaggedValue() || + receiver.GetTaggedValue() == env->GetArrayPrototype().GetTaggedValue()) { + stableArrayElementsGuardians_ = false; + } +} + +void JSThread::ResetGuardians() +{ + stableArrayElementsGuardians_ = true; +} + +void JSThread::LoadFastStubModule([[maybe_unused]] const char *moduleFile) +{ + UNREACHABLE(); +} +} // namespace panda::ecmascript diff --git a/runtime/js_thread.h b/runtime/js_thread.h new file mode 100644 index 000000000..a366598d2 --- /dev/null +++ b/runtime/js_thread.h @@ -0,0 +1,382 @@ +/* + * Copyright (c) 2021-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_JS_THREAD_H +#define ECMASCRIPT_JS_THREAD_H + +#include "mem/c_string.h" +#include "runtime/include/thread.h" + +#include "include/managed_thread.h" +#include "plugins/ecmascript/runtime/ecma_global_storage.h" +#include "plugins/ecmascript/runtime/frames.h" +#include "plugins/ecmascript/runtime/global_env_constants.h" +#include "plugins/ecmascript/runtime/mem/object_xray.h" + +namespace panda::ecmascript { +class EcmaVM; +class RegionFactory; +class EcmascriptEnvironment; +class InternalCallParams; +class PropertiesCache; + +enum class MarkStatus : uint8_t { READY_TO_MARK, MARKING, MARK_FINISHED }; + +class JSThread : public ManagedThread { +public: + static constexpr int CONCURRENT_MARKING_BITFIELD_NUM = 2; + using MarkStatusBits = BitField; + using Address = uintptr_t; + static JSThread *Cast(ManagedThread *thread) + { + ASSERT(thread != nullptr); + return reinterpret_cast(thread); + } + + /** + * @brief GetCurrentRaw Unsafe method to get current JSThread. + * It can be used in hotspots to get the best performance. + * We can only use this method in places where the JSThread exists. + * @return pointer to JSThread + */ + static JSThread *GetCurrentRaw() + { + ManagedThread *managed_thread = ManagedThread::GetCurrent(); + ASSERT(managed_thread != nullptr); + ASSERT(managed_thread->GetThreadLang() == panda::panda_file::SourceLang::ECMASCRIPT); + return JSThread::Cast(managed_thread); + } + + /** + * @brief GetCurrent Safe method to gets current JSThread. + * @return pointer to JSThread or nullptr (if current thread is not a js thread) + */ + static JSThread *GetCurrent() + { + ManagedThread *managed_thread = ManagedThread::GetCurrent(); + if (managed_thread != nullptr && managed_thread->GetThreadLang() == panda::panda_file::SourceLang::ECMASCRIPT) { + return JSThread::Cast(managed_thread); + } + return nullptr; + } + + JSThread(Runtime *runtime, PandaVM *vm); + + ~JSThread() override; + + EcmaVM *GetEcmaVM() const; + + static JSThread *Create(Runtime *runtime, PandaVM *vm); + + int GetNestedLevel() const + { + return nestedLevel_; + } + + void SetNestedLevel(int level) + { + nestedLevel_ = level; + } + + const JSTaggedType *GetCurrentSPFrame() const + { + return currentFrame_; + } + + void SetCurrentSPFrame(JSTaggedType *sp) + { + currentFrame_ = sp; + } + + bool DoStackOverflowCheck(const JSTaggedType *sp); + + RegionFactory *GetRegionFactory() const + { + return regionFactory_; + } + + void Iterate(const RootVisitor &v0, const RootRangeVisitor &v1); + + uintptr_t *ExpandHandleStorage(); + void ShrinkHandleStorage(int prevIndex); + + JSTaggedType *GetHandleScopeStorageNext() const + { + return handleScopeStorageNext_; + } + + void SetHandleScopeStorageNext(JSTaggedType *value) + { + handleScopeStorageNext_ = value; + } + + JSTaggedType *GetHandleScopeStorageEnd() const + { + return handleScopeStorageEnd_; + } + + void SetHandleScopeStorageEnd(JSTaggedType *value) + { + handleScopeStorageEnd_ = value; + } + + int GetCurrentHandleStorageIndex() + { + return currentHandleStorageIndex_; + } + + void HandleScopeCountAdd() + { + handleScopeCount_++; + } + + void HandleScopeCountDec() + { + handleScopeCount_--; + } + + void SetException(JSTaggedValue exception); + + JSTaggedValue GetException() const + { + return JSTaggedValue(ManagedThread::GetException()); + } + + bool HasPendingException() const + { + return !JSTaggedValue(ManagedThread::GetException()).IsHole(); + } + + void ClearException(); + + EcmaGlobalStorage *GetEcmaGlobalStorage() const + { + return globalStorage_; + } + + const GlobalEnvConstants *GlobalConstants() const + { + return &globalConst_; + } + + EcmascriptEnvironment *GetEcmascriptEnv() const + { + return reinterpret_cast(GetLanguageExtensionsData()); + } + + void SetEcmascriptEnv(EcmascriptEnvironment *env) + { + SetLanguageExtensionsData(env); + } + + JSTaggedValue GetFunctionalObject() const + { + return functionalObject_; + } + + void SetFunctionalObject(JSTaggedValue functionalObject) + { + functionalObject_ = functionalObject; + } + + JSTaggedValue GetInvocationLexicalEnv() const + { + return invocationLexicalEnv_; + } + + void SetInvocationLexicalEnv(JSTaggedValue invocationLexicalEnv) + { + invocationLexicalEnv_ = invocationLexicalEnv; + } + + void NotifyStableArrayElementsGuardians(JSHandle receiver); + + bool IsStableArrayElementsGuardiansInvalid() const + { + return !stableArrayElementsGuardians_; + } + + void ResetGuardians(); + + JSTaggedValue GetCurrentLexenv() const; + + void InitializeFastRuntimeStubs(); + + void LoadFastStubModule(const char *moduleFile); + + InternalCallParams *GetInternalCallParams() const + { + return internalCallParams_; + } + + ThreadId GetThreadId() const + { + return GetId(); + } + + static ThreadId GetCurrentThreadId() + { + return os::thread::GetCurrentThreadId(); + } + + void IterateWeakEcmaGlobalStorage(const WeakRootVisitor &visitor); + + PropertiesCache *GetPropertiesCache() const + { + return propertiesCache_; + } + + static constexpr uint32_t GetPropertiesCacheOffset() + { + return MEMBER_OFFSET(JSThread, propertiesCache_); + } + + static constexpr uint32_t GetGlobalConstantsOffset() + { + return MEMBER_OFFSET(JSThread, globalConst_); + } + + static constexpr uint32_t GetGlobalStorageOffset() + { + return MEMBER_OFFSET(JSThread, globalStorage_); + } + + static constexpr uint32_t GetCurrentFrameOffset() + { + return MEMBER_OFFSET(JSThread, currentFrame_); + } + + void SetMarkStatus(MarkStatus status) + { + uint64_t newVal = MarkStatusBits::Update(threadStateBitField_, status); + threadStateBitField_ = newVal; + } + + bool IsReadyToMark() const + { + auto status = MarkStatusBits::Decode(threadStateBitField_); + return status == MarkStatus::READY_TO_MARK; + } + + bool IsMarking() const + { + auto status = MarkStatusBits::Decode(threadStateBitField_); + return status == MarkStatus::MARKING; + } + + bool IsMarkFinished() const + { + auto status = MarkStatusBits::Decode(threadStateBitField_); + return status == MarkStatus::MARK_FINISHED; + } + + bool CheckSafepoint() const; + + void SetGetStackSignal(bool isParseStack) + { + getStackSignal_ = isParseStack; + } + + bool GetStackSignal() const + { + return getStackSignal_; + } + + void SetGcState(bool gcState) + { + gcState_ = gcState; + } + + bool GetGcState() const + { + return gcState_; + } + static constexpr uint32_t GetExceptionOffset() + { + return MEMBER_OFFSET(JSThread, exception_); + } + + uintptr_t GetGlueAddr() const + { + return reinterpret_cast(this) + GetExceptionOffset(); + } + + static JSThread *GlueToJSThread(uintptr_t glue) + { + // very careful to modify here + return reinterpret_cast(glue - GetExceptionOffset()); + } + + void SetGlobalObject(JSTaggedValue globalObj) + { + globalObj_ = globalObj; + } + + JSTaggedValue GetGlobalObject() const + { + return globalObj_; + } + +private: + void IterateEcmascriptEnvironment(const RootVisitor &v0, const RootRangeVisitor &v1); + + NO_COPY_SEMANTIC(JSThread); + NO_MOVE_SEMANTIC(JSThread); + + void DumpStack() DUMP_API_ATTR; + + static constexpr uint32_t MAX_STACK_SIZE = 128 * 1024; + static constexpr uint32_t RESERVE_STACK_SIZE = 128; + static const uint32_t NODE_BLOCK_SIZE_LOG2 = 10; + static const uint32_t NODE_BLOCK_SIZE = 1U << NODE_BLOCK_SIZE_LOG2; + static constexpr int32_t MIN_HANDLE_STORAGE_SIZE = 2; + + // MM: handles, global-handles, and aot-stubs. + int nestedLevel_ = 0; + RegionFactory *regionFactory_ {nullptr}; + JSTaggedType *handleScopeStorageNext_ {nullptr}; + JSTaggedType *handleScopeStorageEnd_ {nullptr}; + std::vector *> handleStorageNodes_ {}; + int32_t currentHandleStorageIndex_ {-1}; + int32_t handleScopeCount_ {0}; + JSTaggedValue stubCode_ {JSTaggedValue::Hole()}; + + // Run-time state + bool getStackSignal_ {false}; + bool gcState_ {false}; + volatile uint64_t threadStateBitField_ {0ULL}; + JSTaggedType *frameBase_ {nullptr}; + bool stableArrayElementsGuardians_ {true}; + InternalCallParams *internalCallParams_ {nullptr}; + + // GLUE members start, very careful to modify here + JSTaggedValue exception_ {JSTaggedValue::Hole()}; + GlobalEnvConstants globalConst_; // Place-Holder + PropertiesCache *propertiesCache_ {nullptr}; + EcmaGlobalStorage *globalStorage_ {nullptr}; + JSTaggedType *currentFrame_ {nullptr}; + + JSTaggedValue globalObj_ {JSTaggedValue::Hole()}; + + // Uses to forward a0 argument (functional object) to InvokeHelper + JSTaggedValue functionalObject_; + JSTaggedValue invocationLexicalEnv_; + + friend class EcmaHandleScope; + friend class GlobalHandleCollection; +}; +} // namespace panda::ecmascript + +#endif // ECMASCRIPT_JS_THREAD_H diff --git a/runtime/js_typed_array.cpp b/runtime/js_typed_array.cpp new file mode 100644 index 000000000..e3a965bc3 --- /dev/null +++ b/runtime/js_typed_array.cpp @@ -0,0 +1,488 @@ +/* + * Copyright (c) 2021-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 "plugins/ecmascript/runtime/js_typed_array.h" +#include "plugins/ecmascript/runtime/accessor_data.h" +#include "plugins/ecmascript/runtime/base/typed_array_helper-inl.h" +#include "plugins/ecmascript/runtime/builtins/builtins_arraybuffer.h" + +namespace panda::ecmascript { +using TypedArrayHelper = base::TypedArrayHelper; +using BuiltinsArrayBuffer = builtins::BuiltinsArrayBuffer; + +JSHandle JSTypedArray::ToPropKey(JSThread *thread, const JSHandle &key) +{ + if (key->IsSymbol()) { + return key; + } + return JSHandle(JSTaggedValue::ToString(thread, key)); +} +// 9.4.5.1 [[GetOwnProperty]] ( P ) +bool JSTypedArray::GetOwnProperty(JSThread *thread, const JSHandle &typedarray, + const JSHandle &key, PropertyDescriptor &desc) +{ + // 1. Assert : IsPropertyKey(P) is true. + ASSERT(JSTaggedValue::IsPropertyKey(key)); + // 2. Assert: O is an Object that has a [[ViewedArrayBuffer]] internal slot. + // 3. If Type(P) is String, then + // a. Let numericIndex be CanonicalNumericIndexString(P). + // b. Assert: numericIndex is not an abrupt completion. + // c. If numericIndex is not undefined, then + // i. Let value be IntegerIndexedElementGet (O, numericIndex). + // ii. ReturnIfAbrupt(value). + // iii. If value is undefined, return undefined. + // iv. Return a PropertyDescriptor{ [[Value]]: value, [[Enumerable]]: true, [[Writable]]: true, + // [[Configurable]]: false }. + if (key->IsString()) { + JSTaggedValue numericIndex = JSTaggedValue::CanonicalNumericIndexString(thread, key); + RETURN_VALUE_IF_ABRUPT_COMPLETION(thread, false); + if (!numericIndex.IsUndefined()) { + JSHandle value = + JSTypedArray::IntegerIndexedElementGet(thread, typedarray, numericIndex).GetValue(); + RETURN_VALUE_IF_ABRUPT_COMPLETION(thread, false); + if (value->IsUndefined()) { + return false; + } + desc.SetValue(value); + desc.SetEnumerable(true); + desc.SetWritable(true); + desc.SetConfigurable(true); + return true; + } + } + // 4. Return OrdinaryGetOwnProperty(O, P). + return JSObject::OrdinaryGetOwnProperty(thread, JSHandle(typedarray), key, desc); +} + +// 9.4.5.2 [[HasProperty]] ( P ) +bool JSTypedArray::HasProperty(JSThread *thread, const JSHandle &typedarray, + const JSHandle &key) +{ + // 1. Assert: IsPropertyKey(P) is true. + ASSERT(JSTaggedValue::IsPropertyKey(key)); + // 2. Assert: O is an Object that has a [[ViewedArrayBuffer]] internal slot. + // 3. If Type(P) is String, then + // a. Let numericIndex be CanonicalNumericIndexString(P). + // b. Assert: numericIndex is not an abrupt completion. + // c. If numericIndex is not undefined, then + // i. Let buffer be the value of O’s [[ViewedArrayBuffer]] internal slot. + // ii. If IsDetachedBuffer(buffer) is true, throw a TypeError exception. + // iii. If IsInteger(numericIndex) is false, return false + // iv. If numericIndex = −0, return false. + // v. If numericIndex < 0, return false. + // vi. If numericIndex ≥ the value of O’s [[ArrayLength]] internal slot, return false. + // vii. Return true. + JSHandle typedarrayObj(typedarray); + if (key->IsString()) { + JSTaggedValue numericIndex = JSTaggedValue::CanonicalNumericIndexString(thread, key); + RETURN_VALUE_IF_ABRUPT_COMPLETION(thread, false); + if (!numericIndex.IsUndefined()) { + JSTaggedValue buffer = JSTypedArray::Cast(*typedarrayObj)->GetViewedArrayBuffer(); + if (BuiltinsArrayBuffer::IsDetachedBuffer(buffer)) { + THROW_TYPE_ERROR_AND_RETURN(thread, "Is Detached Buffer", false); + } + if (!numericIndex.IsInteger()) { + return false; + } + JSHandle numericIndexHandle(thread, numericIndex); + JSTaggedNumber numericIndexNumber = JSTaggedValue::ToNumber(thread, numericIndexHandle); + double tNegZero = -0.0; + auto eZero = JSTaggedNumber(tNegZero); + JSHandle zero(thread, JSTaggedValue(0)); + if (JSTaggedNumber::SameValue(numericIndexNumber, eZero)) { + return false; + } + + if (JSTaggedValue::Less(thread, numericIndexHandle, zero)) { + return false; + } + int32_t arrLen = TypedArrayHelper::GetArrayLength(thread, typedarrayObj); + JSHandle arrLenHandle(thread, JSTaggedValue(arrLen)); + return JSTaggedValue::Less(thread, numericIndexHandle, arrLenHandle); + } + } + // 4. Return OrdinaryHasProperty(O, P). + PropertyDescriptor desc(thread); + if (JSObject::OrdinaryGetOwnProperty(thread, typedarrayObj, key, desc)) { + return true; + } + JSTaggedValue parent = typedarrayObj->GetPrototype(thread); + if (!parent.IsNull()) { + return JSTaggedValue::HasProperty(thread, JSHandle(thread, parent), key); + } + return false; +} + +// 9.4.5.3 [[DefineOwnProperty]] ( P, Desc ) +bool JSTypedArray::DefineOwnProperty(JSThread *thread, const JSHandle &typedarray, + const JSHandle &key, const PropertyDescriptor &desc) +{ + // 1. Assert: IsPropertyKey(P) is true. + ASSERT(JSTaggedValue::IsPropertyKey(key)); + // 2. Assert: O is an Object that has a [[ViewedArrayBuffer]] internal slot. + // 3. If Type(P) is String, then + // a. Let numericIndex be CanonicalNumericIndexString (P). + // b. Assert: numericIndex is not an abrupt completion. + // c. If numericIndex is not undefined, then + JSHandle typedarrayObj(typedarray); + if (key->IsString()) { + JSTaggedValue numericIndex = JSTaggedValue::CanonicalNumericIndexString(thread, key); + RETURN_VALUE_IF_ABRUPT_COMPLETION(thread, false); + if (!numericIndex.IsUndefined()) { + // i. If IsInteger(numericIndex) is false, return false + // ii. Let intIndex be numericIndex. + // iii. If intIndex = −0, return false. + // iv. If intIndex < 0, return false. + // v. Let length be the value of O’s [[ArrayLength]] internal slot. + // vi. If intIndex ≥ length, return false. + // vii. If IsAccessorDescriptor(Desc) is true, return false. + // viii. If Desc has a [[Configurable]] field and if Desc.[[Configurable]] is true, return false. + // ix. If Desc has an [[Enumerable]] field and if Desc.[[Enumerable]] is false, return false. + // x. If Desc has a [[Writable]] field and if Desc.[[Writable]] is false, return false. + // xi. If Desc has a [[Value]] field, then + // 1. Let value be Desc.[[Value]]. + // 2. Return IntegerIndexedElementSet (O, intIndex, value). + // xii. Return true. + if (!numericIndex.IsInteger()) { + return false; + } + JSHandle numericIndexHandle(thread, numericIndex); + JSTaggedNumber numericIndexNumber = JSTaggedValue::ToNumber(thread, numericIndexHandle); + double tNegZero = -0.0; + auto eZero = JSTaggedNumber(tNegZero); + JSHandle zero(thread, JSTaggedValue(0)); + if (JSTaggedNumber::SameValue(numericIndexNumber, eZero)) { + return false; + } + if (JSTaggedValue::Less(thread, numericIndexHandle, zero)) { + return false; + } + int32_t arrLen = TypedArrayHelper::GetArrayLength(thread, typedarrayObj); + JSHandle arrLenHandle(thread, JSTaggedValue(arrLen)); + if (!JSTaggedValue::Less(thread, numericIndexHandle, arrLenHandle)) { + return false; + } + if (desc.IsAccessorDescriptor()) { + return false; + } + if (desc.HasConfigurable() && !desc.IsConfigurable()) { + return false; + } + if (desc.HasEnumerable() && !desc.IsEnumerable()) { + return false; + } + if (desc.HasWritable() && !desc.IsWritable()) { + return false; + } + if (desc.HasValue()) { + JSHandle value = desc.GetValue(); + return (JSTypedArray::IntegerIndexedElementSet(thread, typedarray, numericIndex, value)); + } + return true; + } + } + // 4. Return OrdinaryDefineOwnProperty(O, P, Desc). + return JSObject::OrdinaryDefineOwnProperty(thread, typedarrayObj, key, desc); +} + +// 9.4.5.4 [[Get]] ( P, Receiver ) +OperationResult JSTypedArray::GetProperty(JSThread *thread, const JSHandle &typedarray, + const JSHandle &key, const JSHandle &receiver) +{ + // 1. Assert : IsPropertyKey(P) is true. + ASSERT(JSTaggedValue::IsPropertyKey(key)); + // 2. If Type(P) is String and if SameValue(O, Receiver) is true, then + if (key->IsString() && JSTaggedValue::SameValue(typedarray, receiver)) { + // a. Let numericIndex be CanonicalNumericIndexString (P). + // b. Assert: numericIndex is not an abrupt completion. + // c. If numericIndex is not undefined, then + // i. Return IntegerIndexedElementGet (O, numericIndex). + JSTaggedValue numericIndex = JSTaggedValue::CanonicalNumericIndexString(thread, key); + RETURN_VALUE_IF_ABRUPT_COMPLETION( + thread, OperationResult(thread, JSTaggedValue::Exception(), PropertyMetaData(false))); + if (!numericIndex.IsUndefined()) { + return JSTypedArray::IntegerIndexedElementGet(thread, typedarray, numericIndex); + } + } + + // 3. Return the result of calling the default ordinary object [[Get]] internal method (9.1.8) on O + // passing P and Receiver as arguments. + return JSObject::GetProperty(thread, typedarray, key, receiver); +} + +// 9.4.5.5 [[Set]] ( P, V, Receiver ) +bool JSTypedArray::SetProperty(JSThread *thread, const JSHandle &typedarray, + const JSHandle &key, const JSHandle &value, + const JSHandle &receiver, bool mayThrow) +{ + // 1. Assert : IsPropertyKey(P) is true. + ASSERT(JSTaggedValue::IsPropertyKey(key)); + // 2. If Type(P) is String and if SameValue(O, Receiver) is true, then + if (key->IsString() && JSTaggedValue::SameValue(typedarray, receiver)) { + // a. Let numericIndex be CanonicalNumericIndexString (P). + // b. Assert: numericIndex is not an abrupt completion. + // c. If numericIndex is not undefined, then + // i. Return IntegerIndexedElementSet (O, numericIndex, V). + JSTaggedValue numericIndex = JSTaggedValue::CanonicalNumericIndexString(thread, key); + RETURN_VALUE_IF_ABRUPT_COMPLETION(thread, false); + if (!numericIndex.IsUndefined()) { + return JSTypedArray::IntegerIndexedElementSet(thread, typedarray, numericIndex, value); + } + } + // 3. Return the result of calling the default ordinary object [[Set]] internal method (9.1.8) on O passing + // P, V, and Receiver as arguments. + return JSObject::SetProperty(thread, typedarray, key, value, receiver, mayThrow); +} + +// 9.4.5.6 [[OwnPropertyKeys]] ( ) +JSHandle JSTypedArray::OwnPropertyKeys(JSThread *thread, const JSHandle &typedarray) +{ + ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); + // 1. Let keys be a new empty List. + // 2. Assert: O is an Object that has [[ViewedArrayBuffer]], [[ArrayLength]], [[ByteOffset]], and + // [[TypedArrayName]] internal slots. + // 3. Let len be the value of O’s [[ArrayLength]] internal slot. + JSHandle arrayObj(typedarray); + JSHandle objKeys = JSObject::GetOwnPropertyKeys(thread, arrayObj); + uint32_t objKeysLen = objKeys->GetLength(); + uint32_t bufferKeysLen = TypedArrayHelper::GetArrayLength(thread, arrayObj); + uint32_t length = objKeysLen + bufferKeysLen; + JSHandle nameList = factory->NewTaggedArray(length); + + // 4. For each integer i starting with 0 such that i < len, in ascending order, + // a. Add ToString(i) as the last element of keys. + uint32_t copyLength = 0; + JSMutableHandle tKey(thread, JSTaggedValue::Undefined()); + for (uint32_t k = 0; k < bufferKeysLen; k++) { + tKey.Update(JSTaggedValue(k)); + JSHandle sKey(JSTaggedValue::ToString(thread, tKey)); + nameList->Set(thread, copyLength, sKey.GetTaggedValue()); + copyLength++; + } + + // 5. For each own property key P of O such that Type(P) is String and P is not an integer index, in + // property creation order + // a. Add P as the last element of keys. + for (uint32_t i = 0; i < objKeysLen; i++) { + JSTaggedValue key = objKeys->Get(i); + if (JSTaggedValue(key).IsString()) { + nameList->Set(thread, copyLength, key); + copyLength++; + } + } + + // 6. For each own property key P of O such that Type(P) is Symbol, in property creation order + // a. Add P as the last element of keys. + for (uint32_t i = 0; i < objKeysLen; i++) { + JSTaggedValue key = objKeys->Get(i); + if (JSTaggedValue(key).IsSymbol()) { + nameList->Set(thread, copyLength, key); + copyLength++; + } + } + + // 7. Return keys. + return factory->CopyArray(nameList, length, copyLength); +} + +// 9.4.5.7 IntegerIndexedObjectCreate (prototype, internalSlotsList) + +// 9.4.5.8 IntegerIndexedElementGet ( O, index ) +OperationResult JSTypedArray::IntegerIndexedElementGet(JSThread *thread, const JSHandle &typedarray, + JSTaggedValue index) +{ + // 1. Assert: Type(index) is Number. + ASSERT(index.IsNumber()); + // 2. Assert: O is an Object that has [[ViewedArrayBuffer]], [[ArrayLength]], [[ByteOffset]], and + // [[TypedArrayName]] internal slots. + ASSERT(typedarray->IsTypedArray()); + // 3. Let buffer be the value of O’s [[ViewedArrayBuffer]] internal slot. + JSHandle typedarrayObj(typedarray); + JSTaggedValue buffer = JSTypedArray::Cast(*typedarrayObj)->GetViewedArrayBuffer(); + // 4. If IsDetachedBuffer(buffer) is true, throw a TypeError exception. + if (BuiltinsArrayBuffer::IsDetachedBuffer(buffer)) { + THROW_TYPE_ERROR_AND_RETURN(thread, "Is Detached Buffer", + OperationResult(thread, JSTaggedValue::Exception(), PropertyMetaData(false))); + } + // 5. If IsInteger(index) is false, return undefined + if (!index.IsInteger()) { + return OperationResult(thread, JSTaggedValue::Undefined(), PropertyMetaData(true)); + } + + // 6. If index = −0, return undefined. + // 7. Let length be the value of O’s [[ArrayLength]] internal slot. + // 8. If index < 0 or index ≥ length, return undefined. + JSHandle indexHandle(thread, index); + JSTaggedNumber indexNumber = JSTaggedValue::ToNumber(thread, indexHandle); + double tNegZero = -0.0; + auto eZero = JSTaggedNumber(tNegZero); + JSHandle zero(thread, JSTaggedValue(0)); + if (JSTaggedNumber::SameValue(indexNumber, eZero)) { + return OperationResult(thread, JSTaggedValue::Undefined(), PropertyMetaData(true)); + } + int32_t arrLen = TypedArrayHelper::GetArrayLength(thread, typedarrayObj); + JSHandle arrLenHandle(thread, JSTaggedValue(arrLen)); + if (JSTaggedValue::Less(thread, indexHandle, zero) || !JSTaggedValue::Less(thread, indexHandle, arrLenHandle)) { + return OperationResult(thread, JSTaggedValue::Undefined(), PropertyMetaData(true)); + } + // 9. Let offset be the value of O’s [[ByteOffset]] internal slot. + int32_t offset = TypedArrayHelper::GetByteOffset(thread, typedarrayObj); + // 10. Let arrayTypeName be the String value of O’s [[TypedArrayName]] internal slot. + // 11. Let elementSize be the Number value of the Element Size value specified in Table 49 for + // arrayTypeName. + int32_t elementSize = TypedArrayHelper::GetElementSize(typedarrayObj); + // 12. Let indexedPosition = (index × elementSize) + offset. + int32_t k = JSTaggedValue::ToInteger(thread, indexHandle).ToInt32(); + int32_t byteIndex = k * elementSize + offset; + // 13. Let elementType be the String value of the Element Type value in Table 49 for arrayTypeName. + DataViewType elementType = TypedArrayHelper::GetType(typedarrayObj); + // 14. Return GetValueFromBuffer(buffer, indexedPosition, elementType). + JSTaggedValue result = BuiltinsArrayBuffer::GetValueFromBuffer(buffer, byteIndex, elementType, true); + return OperationResult(thread, result, PropertyMetaData(true)); +} + +// static +bool JSTypedArray::FastCopyElementToArray(JSThread *thread, const JSHandle &typedArray, + JSHandle &array) +{ + // 2. Assert: O is an Object that has [[ViewedArrayBuffer]], [[ArrayLength]], [[ByteOffset]], and + // [[TypedArrayName]] internal slots. + ASSERT(typedArray->IsTypedArray()); + // 3. Let buffer be the value of O’s [[ViewedArrayBuffer]] internal slot. + JSHandle typedarrayObj(typedArray); + JSTaggedValue buffer = JSTypedArray::Cast(*typedarrayObj)->GetViewedArrayBuffer(); + // 4. If IsDetachedBuffer(buffer) is true, throw a TypeError exception. + if (BuiltinsArrayBuffer::IsDetachedBuffer(buffer)) { + THROW_TYPE_ERROR_AND_RETURN(thread, "Is Detached Buffer", false); + } + + // 7. Let length be the value of O’s [[ArrayLength]] internal slot. + // 8. If index < 0 or index ≥ length, return undefined. + int32_t arrLen = TypedArrayHelper::GetArrayLength(thread, typedarrayObj); + if (arrLen < 0) { + return false; + } + + // 9. Let offset be the value of O’s [[ByteOffset]] internal slot. + int32_t offset = TypedArrayHelper::GetByteOffset(thread, typedarrayObj); + // 11. Let elementSize be the Number value of the Element Size value specified in Table 49 for arrayTypeName. + int32_t elementSize = TypedArrayHelper::GetElementSize(typedarrayObj); + // 13. Let elementType be the String value of the Element Type value in Table 49 for arrayTypeName. + DataViewType elementType = TypedArrayHelper::GetType(typedarrayObj); + for (int index = 0; index < arrLen; index++) { + // 12. Let indexedPosition = (index × elementSize) + offset. + int32_t byteIndex = index * elementSize + offset; + // 14. Return GetValueFromBuffer(buffer, indexedPosition, elementType). + JSTaggedValue result = BuiltinsArrayBuffer::GetValueFromBuffer(buffer, byteIndex, elementType, true); + array->Set(thread, index, result); + } + return true; +} + +// static +OperationResult JSTypedArray::FastElementGet(JSThread *thread, const JSHandle &typedarray, + uint32_t index) +{ + // 2. Assert: O is an Object that has [[ViewedArrayBuffer]], [[ArrayLength]], [[ByteOffset]], and + // [[TypedArrayName]] internal slots. + ASSERT(typedarray->IsTypedArray()); + // 3. Let buffer be the value of O’s [[ViewedArrayBuffer]] internal slot. + JSHandle typedarrayObj(typedarray); + JSTaggedValue buffer = JSTypedArray::Cast(*typedarrayObj)->GetViewedArrayBuffer(); + // 4. If IsDetachedBuffer(buffer) is true, throw a TypeError exception. + if (BuiltinsArrayBuffer::IsDetachedBuffer(buffer)) { + THROW_TYPE_ERROR_AND_RETURN(thread, "Is Detached Buffer", + OperationResult(thread, JSTaggedValue::Exception(), PropertyMetaData(false))); + } + + // 7. Let length be the value of O’s [[ArrayLength]] internal slot. + // 8. If index < 0 or index ≥ length, return undefined. + int32_t arrLen = TypedArrayHelper::GetArrayLength(thread, typedarrayObj); + if (arrLen < 0 || index >= static_cast(arrLen)) { + return OperationResult(thread, JSTaggedValue::Undefined(), PropertyMetaData(true)); + } + // 9. Let offset be the value of O’s [[ByteOffset]] internal slot. + int32_t offset = TypedArrayHelper::GetByteOffset(thread, typedarrayObj); + // 11. Let elementSize be the Number value of the Element Size value specified in Table 49 for arrayTypeName. + int32_t elementSize = TypedArrayHelper::GetElementSize(typedarrayObj); + // 12. Let indexedPosition = (index × elementSize) + offset. + int32_t byteIndex = index * elementSize + offset; + // 13. Let elementType be the String value of the Element Type value in Table 49 for arrayTypeName. + DataViewType elementType = TypedArrayHelper::GetType(typedarrayObj); + // 14. Return GetValueFromBuffer(buffer, indexedPosition, elementType). + JSTaggedValue result = BuiltinsArrayBuffer::GetValueFromBuffer(buffer, byteIndex, elementType, true); + return OperationResult(thread, result, PropertyMetaData(true)); +} + +// 9.4.5.9 IntegerIndexedElementSet ( O, index, value ) +bool JSTypedArray::IntegerIndexedElementSet(JSThread *thread, const JSHandle &typedarray, + JSTaggedValue index, const JSHandle &value) +{ + // 1. Assert: Type(index) is Number. + ASSERT(index.IsNumber()); + // 2. Assert: O is an Object that has [[ViewedArrayBuffer]], [[ArrayLength]], [[ByteOffset]], and + // [[TypedArrayName]] internal slots. + ASSERT(typedarray->IsTypedArray()); + // 3. Let numValue be ToNumber(value). + JSTaggedNumber numVal = JSTaggedValue::ToNumber(thread, value); + // 4. ReturnIfAbrupt(numValue). + RETURN_VALUE_IF_ABRUPT_COMPLETION(thread, false); + + // 5. Let buffer be the value of O’s [[ViewedArrayBuffer]] internal slot. + JSHandle typedarrayObj(typedarray); + JSTaggedValue buffer = JSTypedArray::Cast(*typedarrayObj)->GetViewedArrayBuffer(); + // 6. If IsDetachedBuffer(buffer) is true, throw a TypeError exception. + if (BuiltinsArrayBuffer::IsDetachedBuffer(buffer)) { + THROW_TYPE_ERROR_AND_RETURN(thread, "Is Detached Buffer", false); + } + // 7. If IsInteger(index) is false, return false + if (!index.IsInteger()) { + return false; + } + + // 8. If index = −0, return false. + // 9. Let length be the value of O’s [[ArrayLength]] internal slot. + // 10. If index < 0 or index ≥ length, return false. + JSHandle indexHandle(thread, index); + JSTaggedNumber indexNumber = JSTaggedValue::ToNumber(thread, indexHandle); + double tNegZero = -0.0; + auto eZero = JSTaggedNumber(tNegZero); + JSHandle zero(thread, JSTaggedValue(0)); + if (JSTaggedNumber::SameValue(indexNumber, eZero)) { + return false; + } + int32_t arrLen = TypedArrayHelper::GetArrayLength(thread, typedarrayObj); + JSHandle arrLenHandle(thread, JSTaggedValue(arrLen)); + if (JSTaggedValue::Less(thread, indexHandle, zero) || !JSTaggedValue::Less(thread, indexHandle, arrLenHandle)) { + return false; + } + + // 11. Let offset be the value of O’s [[ByteOffset]] internal slot. + int32_t offset = TypedArrayHelper::GetByteOffset(thread, typedarrayObj); + // 12. Let arrayTypeName be the String value of O’s [[TypedArrayName]] internal slot. + // 13. Let elementSize be the Number value of the Element Size value specified in Table 49 for + // arrayTypeName. + int32_t elementSize = TypedArrayHelper::GetElementSize(typedarrayObj); + // 14. Let indexedPosition = (index × elementSize) + offset. + int32_t k = JSTaggedValue::ToInteger(thread, indexHandle).ToInt32(); + int32_t byteIndex = k * elementSize + offset; + // 15. Let elementType be the String value of the Element Type value in Table 49 for arrayTypeName. + DataViewType elementType = TypedArrayHelper::GetType(typedarrayObj); + // 16. Perform SetValueInBuffer(buffer, indexedPosition, elementType, numValue). + BuiltinsArrayBuffer::SetValueInBuffer(buffer, byteIndex, elementType, numVal, true); + // 17. Return true. + return true; +} +} // namespace panda::ecmascript diff --git a/runtime/js_typed_array.h b/runtime/js_typed_array.h new file mode 100644 index 000000000..cf548cb9c --- /dev/null +++ b/runtime/js_typed_array.h @@ -0,0 +1,98 @@ +/* + * Copyright (c) 2021-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_JS_TYPED_ARRAY_H +#define ECMASCRIPT_JS_TYPED_ARRAY_H + +#include "plugins/ecmascript/runtime/tagged_array.h" +#include "js_object.h" + +namespace panda::ecmascript { +class JSTypedArray : public JSObject { +public: + static JSTypedArray *Cast(ObjectHeader *object) + { + #if ECMASCRIPT_ENABLE_CAST_CHECK + if (!(JSTaggedValue(object).IsTypedArray() || JSTaggedValue(object).IsJSTypedArray())) { + std::abort(); + } + #else + ASSERT(JSTaggedValue(object).IsTypedArray() || JSTaggedValue(object).IsJSTypedArray()); + #endif + return static_cast(object); + } + + static JSHandle ToPropKey(JSThread *thread, const JSHandle &key); + + // 9.4.5 Integer-Indexed Exotic Objects + // 9.4.5.1 [[GetOwnProperty]] ( P ) + static bool GetOwnProperty(JSThread *thread, const JSHandle &typedarray, + const JSHandle &key, PropertyDescriptor &desc); + // 9.4.5.2 [[HasProperty]] ( P ) + static bool HasProperty(JSThread *thread, const JSHandle &typedarray, + const JSHandle &key); + // 9.4.5.3 [[DefineOwnProperty]] ( P, Desc ) + static bool DefineOwnProperty(JSThread *thread, const JSHandle &typedarray, + const JSHandle &key, const PropertyDescriptor &desc); + // 9.4.5.4 [[Get]] ( P, Receiver ) + static inline OperationResult GetProperty(JSThread *thread, const JSHandle &typedarray, + const JSHandle &key) + { + return GetProperty(thread, typedarray, key, typedarray); + } + static inline OperationResult GetProperty(JSThread *thread, const JSHandle &typedarray, + uint32_t index) + { + return FastElementGet(thread, typedarray, index); + } + static OperationResult GetProperty(JSThread *thread, const JSHandle &typedarray, + const JSHandle &key, const JSHandle &receiver); + // 9.4.5.5 [[Set]] ( P, V, Receiver ) + static inline bool SetProperty(JSThread *thread, const JSHandle &typedarray, + const JSHandle &key, const JSHandle &value, + bool mayThrow = false) + { + return SetProperty(thread, typedarray, key, value, typedarray, mayThrow); + } + static bool SetProperty(JSThread *thread, const JSHandle &typedarray, + const JSHandle &key, const JSHandle &value, + const JSHandle &receiver, bool mayThrow = false); + // 9.4.5.6 [[OwnPropertyKeys]] ( ) + static JSHandle OwnPropertyKeys(JSThread *thread, const JSHandle &typedarray); + // 9.4.5.7 IntegerIndexedObjectCreate (prototype, internalSlotsList) + // 9.4.5.8 IntegerIndexedElementGet ( O, index ) + static OperationResult IntegerIndexedElementGet(JSThread *thread, const JSHandle &typedarray, + JSTaggedValue index); + static OperationResult FastElementGet(JSThread *thread, const JSHandle &typedarray, uint32_t index); + static bool FastCopyElementToArray(JSThread *thread, const JSHandle &typedArray, + JSHandle &array); + // 9.4.5.9 IntegerIndexedElementSet ( O, index, value ) + static bool IntegerIndexedElementSet(JSThread *thread, const JSHandle &typedarray, + JSTaggedValue index, const JSHandle &value); + + static constexpr size_t VIEWED_ARRAY_BUFFER_OFFSET = JSObject::SIZE; + ACCESSORS(ViewedArrayBuffer, VIEWED_ARRAY_BUFFER_OFFSET, TYPED_ARRAY_NAME_OFFSET) + ACCESSORS(TypedArrayName, TYPED_ARRAY_NAME_OFFSET, BYTE_LENGTH_OFFSET) + ACCESSORS(ByteLength, BYTE_LENGTH_OFFSET, BYTE_OFFSET_OFFSET) + ACCESSORS(ByteOffset, BYTE_OFFSET_OFFSET, ARRAY_LENGTH_OFFSET) + ACCESSORS(ArrayLength, ARRAY_LENGTH_OFFSET, SIZE) + + static const uint32_t MAX_TYPED_ARRAY_INDEX = MAX_ELEMENT_INDEX; + DECL_DUMP() + + DECL_VISIT_OBJECT_FOR_JS_OBJECT(JSObject, VIEWED_ARRAY_BUFFER_OFFSET, SIZE) +}; +} // namespace panda::ecmascript +#endif // ECMASCRIPT_JS_TYPED_ARRAY_H diff --git a/runtime/js_weak_container.cpp b/runtime/js_weak_container.cpp new file mode 100644 index 000000000..5037e197e --- /dev/null +++ b/runtime/js_weak_container.cpp @@ -0,0 +1,99 @@ +/* + * Copyright (c) 2021-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 "plugins/ecmascript/runtime/js_weak_container.h" + +#include "plugins/ecmascript/runtime/js_tagged_value.h" +#include "plugins/ecmascript/runtime/linked_hash_table-inl.h" +#include "plugins/ecmascript/runtime/object_factory.h" +#include "libpandabase/utils/bit_utils.h" + +namespace panda::ecmascript { +void JSWeakMap::Set(JSThread *thread, const JSHandle &map, const JSHandle &key, + const JSHandle &value) +{ + [[maybe_unused]] EcmaHandleScope handleScope(thread); + if (!LinkedHashMap::IsKey(JSTaggedValue(key.GetTaggedValue()))) { + THROW_TYPE_ERROR(thread, "the value must be Key of JSMap"); + } + JSHandle mapHandle(thread, LinkedHashMap::Cast(map->GetLinkedMap().GetTaggedObject())); + + auto result = LinkedHashMap::Set(thread, mapHandle, key, value); + map->SetLinkedMap(thread, result); +} + +bool JSWeakMap::Delete(JSThread *thread, const JSHandle &map, const JSHandle &key) +{ + JSHandle mapHandle(thread, LinkedHashMap::Cast(map->GetLinkedMap().GetTaggedObject())); + int entry = mapHandle->FindElement(key.GetTaggedValue()); + if (entry == -1) { + return false; + } + mapHandle->RemoveEntry(thread, entry); + + JSHandle newMap = LinkedHashMap::Shrink(thread, mapHandle); + map->SetLinkedMap(thread, newMap); + return true; +} + +bool JSWeakMap::Has(JSTaggedValue key) const +{ + return LinkedHashMap::Cast(GetLinkedMap().GetTaggedObject())->Has(key); +} + +JSTaggedValue JSWeakMap::Get(JSTaggedValue key) const +{ + return LinkedHashMap::Cast(GetLinkedMap().GetTaggedObject())->Get(key); +} + +int JSWeakMap::GetSize() const +{ + return LinkedHashMap::Cast(GetLinkedMap().GetTaggedObject())->NumberOfElements(); +} + +void JSWeakSet::Add(JSThread *thread, const JSHandle &weakSet, const JSHandle &value) +{ + if (!LinkedHashSet::IsKey(value.GetTaggedValue())) { + THROW_TYPE_ERROR(thread, "the value must be Key of JSWeakSet"); + } + JSHandle weakSetHandle(thread, LinkedHashSet::Cast(weakSet->GetLinkedSet().GetTaggedObject())); + + auto result = LinkedHashSet::Add(thread, weakSetHandle, value); + weakSet->SetLinkedSet(thread, result); +} + +bool JSWeakSet::Delete(JSThread *thread, const JSHandle &weakSet, const JSHandle &value) +{ + JSHandle weakSetHandle(thread, LinkedHashSet::Cast(weakSet->GetLinkedSet().GetTaggedObject())); + int entry = weakSetHandle->FindElement(value.GetTaggedValue()); + if (entry == -1) { + return false; + } + weakSetHandle->RemoveEntry(thread, entry); + JSHandle newSet = LinkedHashSet::Shrink(thread, weakSetHandle); + weakSet->SetLinkedSet(thread, newSet); + return true; +} + +bool JSWeakSet::Has(JSTaggedValue value) const +{ + return LinkedHashSet::Cast(GetLinkedSet().GetTaggedObject())->Has(value); +} + +int JSWeakSet::GetSize() const +{ + return LinkedHashSet::Cast(GetLinkedSet().GetTaggedObject())->NumberOfElements(); +} +} // namespace panda::ecmascript diff --git a/runtime/js_weak_container.h b/runtime/js_weak_container.h new file mode 100644 index 000000000..d8aac384c --- /dev/null +++ b/runtime/js_weak_container.h @@ -0,0 +1,73 @@ +/* + * Copyright (c) 2021-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_JS_WEAK_CONTAINER_H +#define ECMASCRIPT_JS_WEAK_CONTAINER_H + +#include +#include "plugins/ecmascript/runtime/js_object.h" +#include "plugins/ecmascript/runtime/js_tagged_value-inl.h" + +namespace panda::ecmascript { +class JSWeakMap : public JSObject { +public: + static JSWeakMap *Cast(ObjectHeader *object) + { + ASSERT(JSTaggedValue(object).IsJSWeakMap()); + return static_cast(object); + } + + static bool Delete(JSThread *thread, const JSHandle &map, const JSHandle &key); + + static void Set(JSThread *thread, const JSHandle &map, const JSHandle &key, + const JSHandle &value); + + bool Has(JSTaggedValue key) const; + + JSTaggedValue Get(JSTaggedValue key) const; + + int GetSize() const; + + static constexpr size_t LINKED_MAP_OFFSET = JSObject::SIZE; + ACCESSORS(LinkedMap, LINKED_MAP_OFFSET, SIZE) + + DECL_VISIT_OBJECT_FOR_JS_OBJECT(JSObject, LINKED_MAP_OFFSET, SIZE) + DECL_DUMP() +}; + +class JSWeakSet : public JSObject { +public: + static JSWeakSet *Cast(ObjectHeader *object) + { + ASSERT(JSTaggedValue(object).IsJSWeakSet()); + return static_cast(object); + } + static bool Delete(JSThread *thread, const JSHandle &set, const JSHandle &value); + + static void Add(JSThread *thread, const JSHandle &set, const JSHandle &value); + + bool Has(JSTaggedValue value) const; + + int GetSize() const; + + static constexpr size_t LINKED_SET_OFFSET = JSObject::SIZE; + ACCESSORS(LinkedSet, LINKED_SET_OFFSET, SIZE) + + DECL_VISIT_OBJECT_FOR_JS_OBJECT(JSObject, LINKED_SET_OFFSET, SIZE) + + DECL_DUMP() +}; +} // namespace panda::ecmascript +#endif // ECMASCRIPT_JS_WEAK_CONTAINER_H diff --git a/runtime/layout_info-inl.h b/runtime/layout_info-inl.h new file mode 100644 index 000000000..d37210754 --- /dev/null +++ b/runtime/layout_info-inl.h @@ -0,0 +1,174 @@ +/* + * Copyright (c) 2021-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_LAYOUT_INFO_INL_H +#define ECMASCRIPT_LAYOUT_INFO_INL_H + +#include "plugins/ecmascript/runtime/layout_info.h" +#include "plugins/ecmascript/runtime/tagged_array-inl.h" +#include "plugins/ecmascript/runtime/ic/properties_cache-inl.h" + +namespace panda::ecmascript { +inline int32_t LayoutInfo::GetPropertiesCapacity() const +{ + return static_cast((GetLength() - ELEMENTS_START_INDEX) >> 1U); +} + +inline int32_t LayoutInfo::NumberOfElements() const +{ + return TaggedArray::Get(NUMBER_OF_PROPERTIES_INDEX).GetInt(); +} + +inline void LayoutInfo::SetNumberOfElements(const JSThread *thread, int32_t properties) +{ + return TaggedArray::Set(thread, NUMBER_OF_PROPERTIES_INDEX, JSTaggedValue(properties)); +} + +inline uint32_t LayoutInfo::GetKeyIndex(int32_t index) const +{ + return ELEMENTS_START_INDEX + (static_cast(index) << 1U); +} + +inline uint32_t LayoutInfo::GetAttrIndex(int32_t index) const +{ + return ELEMENTS_START_INDEX + (static_cast(index) << 1U) + 1; +} + +inline void LayoutInfo::SetPropertyInit(const JSThread *thread, int32_t index, const JSTaggedValue &key, + const PropertyAttributes &attr) +{ + uint32_t fixed_idx = GetKeyIndex(index); + TaggedArray::Set(thread, fixed_idx, key); + TaggedArray::Set(thread, fixed_idx + 1, attr.GetNormalTagged()); +} + +inline void LayoutInfo::SetNormalAttr(const JSThread *thread, int32_t index, const PropertyAttributes &attr) +{ + uint32_t fixed_idx = GetAttrIndex(index); + PropertyAttributes oldAttr(TaggedArray::Get(fixed_idx)); + oldAttr.SetNormalAttr(attr.GetNormalAttr()); + TaggedArray::Set(thread, fixed_idx, oldAttr.GetTaggedValue()); +} + +inline JSTaggedValue LayoutInfo::GetKey(int32_t index) const +{ + uint32_t fixed_idx = GetKeyIndex(index); + return TaggedArray::Get(fixed_idx); +} + +inline PropertyAttributes LayoutInfo::GetAttr(int32_t index) const +{ + uint32_t fixed_idx = GetAttrIndex(index); + return PropertyAttributes(TaggedArray::Get(fixed_idx)); +} + +inline JSTaggedValue LayoutInfo::GetSortedKey(int32_t index) const +{ + uint32_t fixed_idx = GetSortedIndex(index); + return GetKey(fixed_idx); +} + +inline uint32_t LayoutInfo::GetSortedIndex(int32_t index) const +{ + return GetAttr(index).GetSortedIndex(); +} + +inline void LayoutInfo::SetSortedIndex(const JSThread *thread, int32_t index, int32_t sortedIndex) +{ + uint32_t fixed_idx = GetAttrIndex(index); + PropertyAttributes attr(TaggedArray::Get(fixed_idx)); + attr.SetSortedIndex(sortedIndex); + TaggedArray::Set(thread, fixed_idx, attr.GetTaggedValue()); +} + +inline int32_t LayoutInfo::FindElementWithCache(JSThread *thread, JSHClass *cls, JSTaggedValue key, + int32_t propertiesNumber) +{ + ASSERT(NumberOfElements() >= propertiesNumber); + const int32_t MAX_ELEMENTS_LINER_SEARCH = 9; // 9: Builtins Object properties number is nine; + if (propertiesNumber <= MAX_ELEMENTS_LINER_SEARCH) { + Span sp(GetProperties(), propertiesNumber); + for (int32_t i = 0; i < propertiesNumber; i++) { + if (sp[i].key_ == key) { + return i; + } + } + return -1; + } + + PropertiesCache *cache = thread->GetPropertiesCache(); + int32_t index = cache->Get(cls, key); + if (index == PropertiesCache::NOT_FOUND) { + index = BinarySearch(key, propertiesNumber); + cache->Set(cls, key, index); + } + return index; +} + +inline int32_t LayoutInfo::BinarySearch(JSTaggedValue key, int32_t propertiesNumber) +{ + ASSERT(NumberOfElements() >= propertiesNumber); + int32_t low = 0; + int32_t elements = NumberOfElements(); + int32_t high = elements - 1; + uint32_t keyHash = key.GetKeyHashCode(); + + ASSERT(low <= high); + + while (low <= high) { + int32_t mid = low + (high - low) / 2; // 2: half + JSTaggedValue midKey = GetSortedKey(mid); + uint32_t midHash = midKey.GetKeyHashCode(); + if (midHash > keyHash) { + high = mid - 1; + } else if (midHash < keyHash) { + low = mid + 1; + } else { + int32_t sortIndex = GetSortedIndex(mid); + JSTaggedValue currentKey = GetKey(sortIndex); + if (currentKey == key) { + return sortIndex < propertiesNumber ? sortIndex : -1; + } + int32_t midLeft = mid; + int32_t midRight = mid; + while (midLeft - 1 >= 0) { + sortIndex = GetSortedIndex(--midLeft); + currentKey = GetKey(sortIndex); + if (currentKey.GetKeyHashCode() == keyHash) { + if (currentKey == key) { + return sortIndex < propertiesNumber ? sortIndex : -1; + } + } else { + break; + } + } + while (midRight + 1 < elements) { + sortIndex = GetSortedIndex(++midRight); + currentKey = GetKey(sortIndex); + if (currentKey.GetKeyHashCode() == keyHash) { + if (currentKey == key) { + return sortIndex < propertiesNumber ? sortIndex : -1; + } + } else { + break; + } + } + return -1; + } + } + return -1; +} +} // namespace panda::ecmascript +#endif // ECMASCRIPT_LAYOUT_INFO_INL_H diff --git a/runtime/layout_info.cpp b/runtime/layout_info.cpp new file mode 100644 index 000000000..fb1753a36 --- /dev/null +++ b/runtime/layout_info.cpp @@ -0,0 +1,120 @@ +/* + * Copyright (c) 2021-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 "plugins/ecmascript/runtime/layout_info-inl.h" +#include "plugins/ecmascript/runtime/ecma_string.h" +#include "plugins/ecmascript/runtime/js_symbol.h" +#include "plugins/ecmascript/runtime/mem/assert_scope-inl.h" + +namespace panda::ecmascript { +void LayoutInfo::AddKey(const JSThread *thread, [[maybe_unused]] int index, const JSTaggedValue &key, + const PropertyAttributes &attr) +{ + DISALLOW_GARBAGE_COLLECTION; + int number = NumberOfElements(); + ASSERT(attr.GetOffset() == static_cast(number)); + ASSERT(number + 1 <= GetPropertiesCapacity()); + ASSERT(number == index); + SetNumberOfElements(thread, number + 1); + SetPropertyInit(thread, number, key, attr); + + uint32_t keyHash = key.GetKeyHashCode(); + int insertIndex = number; + for (; insertIndex > 0; --insertIndex) { + JSTaggedValue prevKey = GetSortedKey(insertIndex - 1); + if (prevKey.GetKeyHashCode() <= keyHash) { + break; + } + SetSortedIndex(thread, insertIndex, GetSortedIndex(insertIndex - 1)); + } + SetSortedIndex(thread, insertIndex, number); +} + +void LayoutInfo::GetAllKeys(const JSThread *thread, int end, int offset, TaggedArray *keyArray) +{ + ASSERT(end <= NumberOfElements()); + ASSERT_PRINT(offset + end <= static_cast(keyArray->GetLength()), + "keyArray capacity is not enough for dictionary"); + + DISALLOW_GARBAGE_COLLECTION; + int enumKeys = 0; + for (int i = 0; i < end; i++) { + JSTaggedValue key = GetKey(i); + if (key.IsString()) { + keyArray->Set(thread, enumKeys + offset, key); + enumKeys++; + } + } + + if (enumKeys < end) { + for (int i = 0; i < end; i++) { + JSTaggedValue key = GetKey(i); + if (key.IsSymbol()) { + keyArray->Set(thread, enumKeys + offset, key); + enumKeys++; + } + } + } +} + +void LayoutInfo::GetAllKeys([[maybe_unused]] const JSThread *thread, int end, std::vector &keyVector) +{ + ASSERT(end <= NumberOfElements()); + int enumKeys = 0; + for (int i = 0; i < end; i++) { + JSTaggedValue key = GetKey(i); + if (key.IsString()) { + keyVector.emplace_back(key); + enumKeys++; + } + } +} + +void LayoutInfo::GetAllEnumKeys(const JSThread *thread, int end, int offset, TaggedArray *keyArray, + uint32_t *keys) +{ + ASSERT(end <= NumberOfElements()); + ASSERT_PRINT(offset + end <= static_cast(keyArray->GetLength()), + "keyArray capacity is not enough for dictionary"); + + DISALLOW_GARBAGE_COLLECTION; + int enumKeys = 0; + for (int i = 0; i < end; i++) { + JSTaggedValue key = GetKey(i); + if (key.IsString() && GetAttr(i).IsEnumerable()) { + keyArray->Set(thread, enumKeys + offset, key); + enumKeys++; + } + } + *keys += enumKeys; +} + +void LayoutInfo::GetAllNames(const JSThread *thread, int end, const JSHandle &keyArray, + uint32_t *length) +{ + DISALLOW_GARBAGE_COLLECTION; + int arrayIndex = 0; + for (int i = 0; i < end; i++) { + JSTaggedValue key = GetKey(i); + if (key.IsString()) { + PropertyAttributes attr = GetAttr(i); + if (attr.IsEnumerable()) { + keyArray->Set(thread, arrayIndex++, key); + } + } + } + *length += arrayIndex; +} +} // namespace panda::ecmascript diff --git a/runtime/layout_info.h b/runtime/layout_info.h new file mode 100644 index 000000000..847701739 --- /dev/null +++ b/runtime/layout_info.h @@ -0,0 +1,92 @@ +/* + * Copyright (c) 2021-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_LAYOUT_INFO_H +#define ECMASCRIPT_LAYOUT_INFO_H + +#include "plugins/ecmascript/runtime/tagged_array.h" +#include "plugins/ecmascript/runtime/property_attributes.h" +#include "plugins/ecmascript/runtime/js_object.h" + +namespace panda::ecmascript { +struct Properties { + JSTaggedValue key_; + JSTaggedValue attr_; +}; + +class LayoutInfo : private TaggedArray { +public: + static constexpr int32_t MIN_PROPERTIES_LENGTH = JSObject::MIN_PROPERTIES_LENGTH; + static constexpr int32_t MAX_PROPERTIES_LENGTH = PropertyAttributes::MAX_CAPACITY_OF_PROPERTIES; + static constexpr int32_t NUMBER_OF_PROPERTIES_INDEX = 0; + static constexpr int32_t ELEMENTS_START_INDEX = 1; + + inline static LayoutInfo *Cast(ObjectHeader *obj) + { + ASSERT(JSTaggedValue(obj).IsTaggedArray()); + return reinterpret_cast(obj); + } + + int32_t GetPropertiesCapacity() const; + int32_t NumberOfElements() const; + void SetNumberOfElements(const JSThread *thread, int32_t properties); + uint32_t GetKeyIndex(int index) const; + uint32_t GetAttrIndex(int index) const; + void SetPropertyInit(const JSThread *thread, int32_t index, const JSTaggedValue &key, + const PropertyAttributes &attr); + void SetKey(const JSThread *thread, int32_t index, const JSTaggedValue &key); + void SetNormalAttr(const JSThread *thread, int32_t index, const PropertyAttributes &attr); + JSTaggedValue GetKey(int32_t index) const; + PropertyAttributes GetAttr(int32_t index) const; + JSTaggedValue GetSortedKey(int32_t index) const; + uint32_t GetSortedIndex(int32_t index) const; + void SetSortedIndex(const JSThread *thread, int32_t index, int32_t sortedIndex); + void AddKey(const JSThread *thread, int32_t index, const JSTaggedValue &key, const PropertyAttributes &attr); + + inline uint32_t GetLength() const + { + return TaggedArray::GetLength(); + } + + inline Properties *GetProperties() const + { + return reinterpret_cast(reinterpret_cast(this) + TaggedArray::DATA_OFFSET + + ELEMENTS_START_INDEX * JSTaggedValue::TaggedTypeSize()); + } + + static inline uint32_t ComputeArrayLength(uint32_t properties_number) + { + return (properties_number << 1U) + ELEMENTS_START_INDEX; + } + + static inline uint32_t ComputeGrowCapacity(uint32_t old_capacity) + { + uint32_t new_capacity = old_capacity + MIN_PROPERTIES_LENGTH; + return new_capacity > MAX_PROPERTIES_LENGTH ? MAX_PROPERTIES_LENGTH : new_capacity; + } + + int32_t FindElementWithCache(JSThread *thread, JSHClass *cls, JSTaggedValue key, int32_t propertiesNumber); + int32_t FindElement(JSTaggedValue key, int32_t propertiesNumber); + int32_t BinarySearch(JSTaggedValue key, int32_t propertiesNumber); + void GetAllKeys(const JSThread *thread, int32_t end, int32_t offset, TaggedArray *keyArray); + void GetAllKeys(const JSThread *thread, int32_t end, std::vector &keyVector); + void GetAllEnumKeys(const JSThread *thread, int32_t end, int32_t offset, TaggedArray *keyArray, uint32_t *keys); + void GetAllNames(const JSThread *thread, int32_t end, const JSHandle &keyArray, uint32_t *length); + + DECL_DUMP() +}; +} // namespace panda::ecmascript + +#endif // ECMASCRIPT_LAYOUT_INFO_H diff --git a/runtime/lexical_env.h b/runtime/lexical_env.h new file mode 100644 index 000000000..dfb7cab12 --- /dev/null +++ b/runtime/lexical_env.h @@ -0,0 +1,61 @@ +/* + * Copyright (c) 2021-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_LEXICALENV_H +#define ECMASCRIPT_LEXICALENV_H + +#include "plugins/ecmascript/runtime/js_object.h" + +namespace panda::ecmascript { +class LexicalEnv : public TaggedArray { +public: + static constexpr uint32_t PARENT_ENV_INDEX = 0; + static constexpr uint32_t RESERVED_ENV_LENGTH = 1; + + static LexicalEnv *Cast(ObjectHeader *object) + { + ASSERT(JSTaggedValue(object).IsTaggedArray()); + return static_cast(object); + } + + static size_t ComputeSize(uint32_t numSlots) + { + return TaggedArray::ComputeSize(JSTaggedValue::TaggedTypeSize(), numSlots + RESERVED_ENV_LENGTH); + } + + void SetParentEnv(JSThread *thread, JSTaggedValue value) + { + Set(thread, PARENT_ENV_INDEX, value); + } + + JSTaggedValue GetParentEnv() const + { + return Get(PARENT_ENV_INDEX); + } + + JSTaggedValue GetProperties(uint32_t index) const + { + return Get(index + RESERVED_ENV_LENGTH); + } + + void SetProperties(JSThread *thread, uint32_t index, JSTaggedValue value) + { + Set(thread, index + RESERVED_ENV_LENGTH, value); + } + + DECL_DUMP() +}; +} // namespace panda::ecmascript +#endif // ECMASCRIPT_LEXICALENV_H diff --git a/runtime/linked_hash_table-inl.h b/runtime/linked_hash_table-inl.h new file mode 100644 index 000000000..0d8f7a917 --- /dev/null +++ b/runtime/linked_hash_table-inl.h @@ -0,0 +1,271 @@ +/* + * Copyright (c) 2021-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_LINKED_HASH_TABLE_INL_H +#define ECMASCRIPT_LINKED_HASH_TABLE_INL_H + +#include "linked_hash_table.h" +#include "tagged_array-inl.h" +#include "utils/bit_utils.h" + +namespace panda::ecmascript { +template +JSTaggedValue LinkedHashTable::GetElement(int index) const +{ + ASSERT(index >= 0 && index < static_cast(GetLength())); + return Get(index); +} + +template +void LinkedHashTable::SetElement(const JSThread *thread, int index, JSTaggedValue element) +{ + ASSERT(index >= 0 && index < static_cast(GetLength())); + Set(thread, index, element); +} + +template +int LinkedHashTable::NumberOfElements() const +{ + return Get(NUMBER_OF_ELEMENTS_INDEX).GetInt(); +} + +template +int LinkedHashTable::NumberOfDeletedElements() const +{ + return Get(NUMBER_OF_DELETED_ELEMENTS_INDEX).GetInt(); +} + +template +int LinkedHashTable::Capacity() const +{ + return JSTaggedValue(Get(CAPACITY_INDEX)).GetInt(); +} + +template +void LinkedHashTable::SetNumberOfElements(const JSThread *thread, int nof) +{ + Set(thread, NUMBER_OF_ELEMENTS_INDEX, JSTaggedValue(nof)); +} + +template +void LinkedHashTable::SetNumberOfDeletedElements(const JSThread *thread, int nod) +{ + Set(thread, NUMBER_OF_DELETED_ELEMENTS_INDEX, JSTaggedValue(nod)); +} + +template +void LinkedHashTable::SetCapacity(const JSThread *thread, int capacity) +{ + Set(thread, CAPACITY_INDEX, JSTaggedValue(capacity)); +} + +template +void LinkedHashTable::SetNextTable(const JSThread *thread, JSTaggedValue nextTable) +{ + Set(thread, NEXT_TABLE_INDEX, nextTable); +} + +template +JSTaggedValue LinkedHashTable::GetNextTable() const +{ + return JSTaggedValue(Get(NEXT_TABLE_INDEX)); +} + +template +int LinkedHashTable::GetDeletedNum(int entry) const +{ + ASSERT_PRINT(!GetNextTable().IsUndefined(), "function only execute after rehash"); + return GetNextEntry(entry).GetInt(); +} + +template +void LinkedHashTable::SetDeletedNum(const JSThread *thread, int entry, JSTaggedValue num) +{ + ASSERT_PRINT(!GetNextTable().IsUndefined(), "function only execute after rehash"); + SetNextEntry(thread, entry, num); +} + +template +int LinkedHashTable::GetDeletedElementsAt(int entry) const +{ + ASSERT_PRINT(!GetNextTable().IsUndefined(), "function only execute after rehash"); + int currentEntry = entry - 1; + while (currentEntry >= 0) { + if (GetKey(currentEntry).IsHole()) { + return GetDeletedNum(currentEntry); + } + currentEntry--; + } + return 0; +} + +template +uint32_t LinkedHashTable::HashToBucket(uint32_t hash) const +{ + return hash & static_cast(Capacity() - 1); +} + +template +uint32_t LinkedHashTable::BucketToIndex(uint32_t bucket) +{ + return bucket + ELEMENTS_START_INDEX; +} + +template +uint32_t LinkedHashTable::EntryToIndex(uint32_t entry) const +{ + return ELEMENTS_START_INDEX + Capacity() + entry * (HashObject::ENTRY_SIZE + 1); +} + +template +void LinkedHashTable::SetKey(const JSThread *thread, int entry, JSTaggedValue key) +{ + int index = EntryToIndex(entry); + SetElement(thread, index, key); +} + +template +JSTaggedValue LinkedHashTable::GetKey(int entry) const +{ + int index = EntryToIndex(entry); + return GetElement(index); +} + +template +JSTaggedValue LinkedHashTable::GetValue(int entry) const +{ + int index = EntryToIndex(entry) + HashObject::ENTRY_VALUE_INDEX; + return GetElement(index); +} + +template +void LinkedHashTable::SetValue(const JSThread *thread, int entry, JSTaggedValue value) +{ + int index = EntryToIndex(entry) + HashObject::ENTRY_VALUE_INDEX; + SetElement(thread, index, value); +} + +template +JSTaggedValue LinkedHashTable::GetNextEntry(int entry) const +{ + int index = EntryToIndex(entry) + HashObject::ENTRY_SIZE; + return GetElement(index); +} + +template +void LinkedHashTable::SetNextEntry(const JSThread *thread, int entry, JSTaggedValue nextEntry) +{ + int index = EntryToIndex(entry) + HashObject::ENTRY_SIZE; + SetElement(thread, index, nextEntry); +} + +template +void LinkedHashTable::InsertNewEntry(const JSThread *thread, int bucket, int entry) +{ + int bucketIndex = BucketToIndex(bucket); + JSTaggedValue previousEntry = GetElement(bucketIndex); + SetNextEntry(thread, entry, previousEntry); + SetElement(thread, bucketIndex, JSTaggedValue(entry)); +} + +template +int LinkedHashTable::FindElement(JSTaggedValue key) const +{ + if (!IsKey(key)) { + return -1; + } + int hash = LinkedHash::Hash(key); + int bucket = HashToBucket(hash); + for (JSTaggedValue entry = GetElement(BucketToIndex(bucket)); !entry.IsHole(); + entry = GetNextEntry(entry.GetInt())) { + JSTaggedValue element = GetKey(entry.GetInt()); + if (element.IsHole()) { + continue; + } + if (element.IsWeak()) { + element.RemoveWeakTag(); + } + if (HashObject::IsMatch(key, element)) { + return entry.GetInt(); + } + } + return -1; +} // namespace panda::ecmascript + +template +bool LinkedHashTable::HasSufficientCapacity(int numOfAddElements) const +{ + int numberOfElements = NumberOfElements(); + int numOfDelElements = NumberOfDeletedElements(); + int capacity = Capacity(); + int nof = numberOfElements + numOfAddElements; + // Return true if: + // 50% is still free after adding numOfAddElements elements and + // at most 50% of the free elements are deleted elements. + if ((nof < capacity) && ((numOfDelElements <= (capacity - nof) / 2))) { // 2: half + int neededFree = nof / 2; // 2: half + if (nof + neededFree <= capacity) { + return true; + } + } + return false; +} + +template +int LinkedHashTable::ComputeCapacity(uint32_t atLeastSpaceFor) +{ + // Add 50% slack to make slot collisions sufficiently unlikely. + // See matching computation in HashTable::HasSufficientCapacity(). + uint32_t rawCap = atLeastSpaceFor + (atLeastSpaceFor >> 1UL); + int capacity = static_cast(helpers::math::GetPowerOfTwoValue32(rawCap)); + return (capacity > MIN_CAPACITY) ? capacity : MIN_CAPACITY; +} + +template +void LinkedHashTable::RemoveEntry(const JSThread *thread, int entry) +{ + ASSERT_PRINT(entry >= 0 && entry < Capacity(), "entry must be a non-negative integer less than capacity"); + int index = EntryToIndex(entry); + for (int i = 0; i < HashObject::ENTRY_SIZE; i++) { + SetElement(thread, index + i, JSTaggedValue::Hole()); + } + SetNumberOfElements(thread, NumberOfElements() - 1); + SetNumberOfDeletedElements(thread, NumberOfDeletedElements() + 1); +} + +template +int LinkedHashTable::ComputeCapacityWithShrink(int currentCapacity, int atLeastSpaceFor) +{ + // Shrink to fit the number of elements if only a quarter of the + // capacity is filled with elements. + if (atLeastSpaceFor > (currentCapacity / 4)) { // 4: quarter + return currentCapacity; + } + // Recalculate the smaller capacity actually needed. + int newCapacity = ComputeCapacity(atLeastSpaceFor); + ASSERT_PRINT(newCapacity > atLeastSpaceFor, "new capacity must greater than atLeastSpaceFor"); + // Don't go lower than room for MIN_SHRINK_CAPACITY elements. + if (newCapacity < Derived::MIN_SHRINK_CAPACITY) { + return currentCapacity; + } + return newCapacity; +} + +bool LinkedHashMapObject::IsMatch(JSTaggedValue key, JSTaggedValue other) +{ + return JSTaggedValue::SameValueZero(key, other); +} +} // namespace panda::ecmascript +#endif // ECMASCRIPT_LINKED_HASH_TABLE_INL_H diff --git a/runtime/linked_hash_table.cpp b/runtime/linked_hash_table.cpp new file mode 100644 index 000000000..b38d7ec1a --- /dev/null +++ b/runtime/linked_hash_table.cpp @@ -0,0 +1,273 @@ +/* + * Copyright (c) 2021-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 "plugins/ecmascript/runtime/js_object-inl.h" + +#include "libpandabase/utils/bit_utils.h" +#include "linked_hash_table-inl.h" +#include "object_factory.h" + +namespace panda::ecmascript { +template +JSHandle LinkedHashTable::Create(const JSThread *thread, JSType tableType, bool isWeak, + int numberOfElements) +{ + ASSERT_PRINT(numberOfElements > 0, "size must be a non-negative integer"); + ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); + auto capacity = static_cast(numberOfElements); + ASSERT_PRINT(helpers::math::IsPowerOfTwo(capacity), "capacity must be pow of '2'"); + int length = ELEMENTS_START_INDEX + numberOfElements + numberOfElements * (HashObject::ENTRY_SIZE + 1); + + auto table = JSHandle(factory->NewLinkedHashTable(length, tableType, isWeak)); + table->SetNumberOfElements(thread, 0); + table->SetNumberOfDeletedElements(thread, 0); + table->SetCapacity(thread, capacity); + return table; +} + +template +JSHandle LinkedHashTable::Insert(const JSThread *thread, const JSHandle &table, + const JSHandle &key, + const JSHandle &value) +{ + ASSERT(IsKey(key.GetTaggedValue())); + int hash = LinkedHash::Hash(key.GetTaggedValue()); + int entry = table->FindElement(key.GetTaggedValue()); + if (entry != -1) { + table->SetValue(thread, entry, value.GetTaggedValue()); + return table; + } + + JSHandle newTable = GrowCapacity(thread, table); + + int bucket = newTable->HashToBucket(hash); + entry = newTable->NumberOfElements() + newTable->NumberOfDeletedElements(); + newTable->InsertNewEntry(thread, bucket, entry); + newTable->SetKey(thread, entry, key.GetTaggedValue()); + // The ENTRY_VALUE_INDEX of LinkedHashSet is 0. SetValue will cause the overwitten key. + if (std::is_same_v) { + newTable->SetValue(thread, entry, value.GetTaggedValue()); + } + newTable->SetNumberOfElements(thread, newTable->NumberOfElements() + 1); + + return newTable; +} + +template +void LinkedHashTable::Rehash(const JSThread *thread, const JSHandle &newTable) +{ + ASSERT_PRINT(*newTable != nullptr && newTable->Capacity() > NumberOfElements(), "can not rehash to new table"); + // Rehash elements to new table + int numberOfAllElements = NumberOfElements() + NumberOfDeletedElements(); + int desEntry = 0; + int currentDeletedElements = 0; + SetNextTable(thread, newTable.GetTaggedValue()); + for (int i = 0; i < numberOfAllElements; i++) { + int fromIndex = EntryToIndex(i); + JSTaggedValue key = GetElement(fromIndex); + if (key.IsHole()) { + // store num_of_deleted_element before entry i; it will be used when iterator update. + currentDeletedElements++; + SetDeletedNum(thread, i, JSTaggedValue(currentDeletedElements)); + continue; + } + + if (key.IsWeak()) { + // If the key is a weak reference, we use the weak referent to calculate the new index in the new table. + key.RemoveWeakTag(); + } + + int bucket = newTable->HashToBucket(LinkedHash::Hash(key)); + newTable->InsertNewEntry(thread, bucket, desEntry); + int desIndex = newTable->EntryToIndex(desEntry); + for (int j = 0; j < HashObject::ENTRY_SIZE; j++) { + newTable->SetElement(thread, desIndex + j, GetElement(fromIndex + j)); + } + desEntry++; + } + newTable->SetNumberOfElements(thread, NumberOfElements()); + newTable->SetNumberOfDeletedElements(thread, 0); +} + +template +JSHandle LinkedHashTable::GrowCapacity(const JSThread *thread, + const JSHandle &table, + int numberOfAddedElements) +{ + if (table->HasSufficientCapacity(numberOfAddedElements)) { + return table; + } + int newCapacity = ComputeCapacity(table->NumberOfElements() + numberOfAddedElements); + auto tableClass = table->GetClass(); + JSHandle newTable = + Create(thread, tableClass->GetObjectType(), tableClass->IsWeakContainer(), newCapacity); + table->Rehash(thread, newTable); + return newTable; +} + +template +JSHandle LinkedHashTable::Remove(const JSThread *thread, const JSHandle &table, + const JSHandle &key) +{ + int entry = table->FindElement(key.GetTaggedValue()); + if (entry == -1) { + return table; + } + + table->RemoveEntry(thread, entry); + return Shrink(thread, table); +} + +template +JSHandle LinkedHashTable::Shrink(const JSThread *thread, const JSHandle &table, + int additionalCapacity) +{ + int newCapacity = ComputeCapacityWithShrink(table->Capacity(), table->NumberOfElements() + additionalCapacity); + if (newCapacity == table->Capacity()) { + return table; + } + + auto tableClass = table->GetClass(); + JSHandle newTable = + Create(thread, tableClass->GetObjectType(), tableClass->IsWeakContainer(), newCapacity); + if (newTable.IsEmpty()) { + // No enough memory. Use the origin table. + return table; + } + + table->Rehash(thread, newTable); + return newTable; +} + +// LinkedHashMap +JSHandle LinkedHashMap::Create(const JSThread *thread, bool isWeak, int numberOfElements) +{ + return LinkedHashTable::Create(thread, JSType::LINKED_HASH_MAP, isWeak, + numberOfElements); +} + +JSHandle LinkedHashMap::Delete(const JSThread *thread, const JSHandle &obj, + const JSHandle &key) +{ + return LinkedHashTable::Remove(thread, obj, key); +} + +JSHandle LinkedHashMap::Set(const JSThread *thread, const JSHandle &obj, + const JSHandle &key, const JSHandle &value) +{ + return LinkedHashTable::Insert(thread, obj, key, value); +} + +JSTaggedValue LinkedHashMap::Get(JSTaggedValue key) const +{ + int entry = FindElement(key); + if (entry == -1) { + return JSTaggedValue::Undefined(); + } + return GetValue(entry); +} + +bool LinkedHashMap::Has(JSTaggedValue key) const +{ + int entry = FindElement(key); + return entry != -1; +} + +void LinkedHashMap::Clear(const JSThread *thread) +{ + int numberOfElements = NumberOfElements() + NumberOfDeletedElements(); + for (int entry = 0; entry < numberOfElements; entry++) { + SetKey(thread, entry, JSTaggedValue::Hole()); + SetValue(thread, entry, JSTaggedValue::Hole()); + } + SetNumberOfElements(thread, 0); + SetNumberOfDeletedElements(thread, numberOfElements); +} + +JSHandle LinkedHashMap::Shrink(const JSThread *thread, const JSHandle &table, + int additionalCapacity) +{ + return LinkedHashTable::Shrink(thread, table, additionalCapacity); +} + +// LinkedHashSet +JSHandle LinkedHashSet::Create(const JSThread *thread, bool isWeak, int numberOfElements) +{ + return LinkedHashTable::Create(thread, JSType::LINKED_HASH_SET, isWeak, + numberOfElements); +} + +JSHandle LinkedHashSet::Delete(const JSThread *thread, const JSHandle &obj, + const JSHandle &key) +{ + return LinkedHashTable::Remove(thread, obj, key); +} + +JSHandle LinkedHashSet::Add(const JSThread *thread, const JSHandle &obj, + const JSHandle &key) +{ + return LinkedHashTable::Insert(thread, obj, key, key); +} + +bool LinkedHashSet::Has(JSTaggedValue key) const +{ + int entry = FindElement(key); + return entry != -1; +} + +void LinkedHashSet::Clear(const JSThread *thread) +{ + int numberOfElements = NumberOfElements() + NumberOfDeletedElements(); + for (int entry = 0; entry < numberOfElements; entry++) { + SetKey(thread, entry, JSTaggedValue::Hole()); + } + SetNumberOfElements(thread, 0); + SetNumberOfDeletedElements(thread, numberOfElements); +} + +JSHandle LinkedHashSet::Shrink(const JSThread *thread, const JSHandle &table, + int additionalCapacity) +{ + return LinkedHashTable::Shrink(thread, table, additionalCapacity); +} + +int LinkedHash::Hash(JSTaggedValue key) +{ + if (key.IsDouble() && key.GetDouble() == 0.0) { + key = JSTaggedValue(0); + } + if (key.IsSymbol()) { + auto symbolString = JSSymbol::Cast(key.GetHeapObject()); + return static_cast(symbolString->GetHashField()).GetInt(); + } + if (key.IsString()) { + auto keyString = reinterpret_cast(key.GetHeapObject()); + return keyString->GetHashcode(); + } + if (key.IsECMAObject()) { + int32_t hash = ECMAObject::Cast(key.GetHeapObject())->GetHash(); + if (hash == 0) { + uint64_t keyValue = key.GetRawData(); + hash = GetHash32(reinterpret_cast(&keyValue), sizeof(keyValue) / sizeof(uint8_t)); + ECMAObject::Cast(key.GetHeapObject())->SetHash(hash); + } + return hash; + } + + // Int, Double, Special and HeapObject(except symbol and string) + uint64_t keyValue = key.GetRawData(); + return GetHash32(reinterpret_cast(&keyValue), sizeof(keyValue) / sizeof(uint8_t)); +} +} // namespace panda::ecmascript diff --git a/runtime/linked_hash_table.h b/runtime/linked_hash_table.h new file mode 100644 index 000000000..180ff050d --- /dev/null +++ b/runtime/linked_hash_table.h @@ -0,0 +1,210 @@ +/* + * Copyright (c) 2021-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_LINKED_HASH_TABLE_H +#define ECMASCRIPT_LINKED_HASH_TABLE_H + +#include "plugins/ecmascript/runtime/js_tagged_value.h" +#include "js_handle.h" +#include "js_symbol.h" +#include "js_tagged_number.h" +#include "tagged_array.h" + +namespace panda::ecmascript { +/** + * memory in LinkedHashTable is divided into 3 parts + * 1.array[0-2] is used to store common information of hashtale such as numberOfElements and capacity + * 2.array[3,3+capacity] is buckets which store the position of entry + * 3.array[3+capacity+1,3+capacity + capacity*(entry_size+1)] is the entry stored in order, the last element of an entry + * is a number which point to next entry. + * */ +template +class LinkedHashTable : public TaggedArray { +public: + static const int MIN_CAPACITY = 4; + static const int NUMBER_OF_ELEMENTS_INDEX = 0; + static const int NUMBER_OF_DELETED_ELEMENTS_INDEX = 1; + static const int CAPACITY_INDEX = 2; + static const int NEXT_TABLE_INDEX = 3; + static const int ELEMENTS_START_INDEX = 4; + // Don't shrink a HashTable below this capacity. + static const int MIN_SHRINK_CAPACITY = 16; + + static JSHandle Create(const JSThread *thread, JSType tableType, bool isWeak, int numberOfElements); + + static JSHandle Insert(const JSThread *thread, const JSHandle &table, + const JSHandle &key, const JSHandle &value); + + static JSHandle GrowCapacity(const JSThread *thread, const JSHandle &table, + int numberOfAddedElements = 1); + + static JSHandle Remove(const JSThread *thread, const JSHandle &table, + const JSHandle &key); + + static JSHandle Shrink(const JSThread *thread, const JSHandle &table, int additionalCapacity = 0); + + void Rehash(const JSThread *thread, const JSHandle &newTable); + + inline bool HasSufficientCapacity(int numOfAddElements) const; + + inline int FindElement(JSTaggedValue key) const; + + inline void RemoveEntry(const JSThread *thread, int entry); + + inline static int ComputeCapacity(uint32_t atLeastSpaceFor); + + inline static int ComputeCapacityWithShrink(int currentCapacity, int atLeastSpaceFor); + + inline int NumberOfElements() const; + + inline int NumberOfDeletedElements() const; + + inline int Capacity() const; + + inline JSTaggedValue GetKey(int entry) const; + + inline JSTaggedValue GetValue(int entry) const; + + inline static bool IsKey(JSTaggedValue key) + { + return !key.IsHole(); + } + + inline void SetNumberOfElements(const JSThread *thread, int nof); + + inline void SetNumberOfDeletedElements(const JSThread *thread, int nod); + + inline void SetCapacity(const JSThread *thread, int capacity); + + inline JSTaggedValue GetNextTable() const; + + inline void SetNextTable(const JSThread *thread, JSTaggedValue nextTable); + + inline int GetDeletedElementsAt(int entry) const; + +protected: + inline JSTaggedValue GetElement(int index) const; + + inline void SetElement(const JSThread *thread, int index, JSTaggedValue element); + + inline void SetKey(const JSThread *thread, int entry, JSTaggedValue key); + + inline void SetValue(const JSThread *thread, int entry, JSTaggedValue value); + + inline JSTaggedValue GetNextEntry(int entry) const; + + inline void SetNextEntry(const JSThread *thread, int entry, JSTaggedValue nextEntry); + + inline uint32_t HashToBucket(uint32_t hash) const; + + inline static uint32_t BucketToIndex(uint32_t bucket); + + // min entry = 0 + inline uint32_t EntryToIndex(uint32_t entry) const; + + inline void InsertNewEntry(const JSThread *thread, int bucket, int entry); + + inline int GetDeletedNum(int entry) const; + + inline void SetDeletedNum(const JSThread *thread, int entry, JSTaggedValue num); +}; + +class LinkedHash { +public: + static int Hash(JSTaggedValue key); +}; + +class LinkedHashMapObject { +public: + // key must be string now for other object has no 'equals' method + static inline bool IsMatch(JSTaggedValue key, JSTaggedValue other); + + static inline int Hash(JSTaggedValue key) + { + return key.GetHashCode(); + } + + static const int ENTRY_SIZE = 2; + static const int ENTRY_VALUE_INDEX = 1; +}; + +class LinkedHashMap : public LinkedHashTable { +public: + static LinkedHashMap *Cast(ObjectHeader *obj) + { + return static_cast(obj); + } + static JSHandle Create(const JSThread *thread, bool isWeak = false, + int numberOfElements = MIN_CAPACITY); + + static JSHandle Delete(const JSThread *thread, const JSHandle &obj, + const JSHandle &key); + + static JSHandle Set(const JSThread *thread, const JSHandle &obj, + const JSHandle &key, const JSHandle &value); + + JSTaggedValue Get(JSTaggedValue key) const; + + static JSHandle Shrink(const JSThread *thread, const JSHandle &table, + int additionalCapacity = 0); + + bool Has(JSTaggedValue key) const; + + void Clear(const JSThread *thread); + DECL_DUMP() +}; + +class LinkedHashSetObject { +public: + // key must be string now for other object has no 'equals' method + static inline bool IsMatch(JSTaggedValue key, JSTaggedValue other) + { + return JSTaggedValue::SameValueZero(key, other); + } + + static inline int Hash(JSTaggedValue key) + { + return key.GetHashCode(); + } + + static const int ENTRY_SIZE = 1; + static const int ENTRY_VALUE_INDEX = 0; +}; + +class LinkedHashSet : public LinkedHashTable { +public: + static LinkedHashSet *Cast(ObjectHeader *obj) + { + return static_cast(obj); + } + static JSHandle Create(const JSThread *thread, bool isWeak = false, + int numberOfElements = MIN_CAPACITY); + + static JSHandle Delete(const JSThread *thread, const JSHandle &obj, + const JSHandle &key); + + static JSHandle Add(const JSThread *thread, const JSHandle &obj, + const JSHandle &key); + + static JSHandle Shrink(const JSThread *thread, const JSHandle &table, + int additionalCapacity = 0); + + bool Has(JSTaggedValue key) const; + + void Clear(const JSThread *thread); + DECL_DUMP() +}; +} // namespace panda::ecmascript +#endif // PANDA_RUNTIME_ECMASCRIPT_LINKED_HASH_TABLE_H diff --git a/runtime/literal_data_extractor.cpp b/runtime/literal_data_extractor.cpp new file mode 100644 index 000000000..9314e7166 --- /dev/null +++ b/runtime/literal_data_extractor.cpp @@ -0,0 +1,190 @@ +/* + * Copyright (c) 2021-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 "literal_data_extractor.h" +#include "js_function_kind.h" +#include "plugins/ecmascript/runtime/base/string_helper.h" +#include "plugins/ecmascript/runtime/ecma_string.h" + +#include "plugins/ecmascript/runtime/js_thread.h" +#include "plugins/ecmascript/runtime/tagged_array-inl.h" +#include "libpandafile/literal_data_accessor-inl.h" + +namespace panda::ecmascript { +using LiteralTag = panda_file::LiteralTag; +using StringData = panda_file::StringData; +using LiteralValue = panda_file::LiteralDataAccessor::LiteralValue; + +void LiteralDataExtractor::ExtractObjectDatas(JSThread *thread, const panda_file::File *pf, size_t index, + JSMutableHandle elements, + JSMutableHandle properties, PandaFileTranslator *pft) +{ + ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); + + LOG_ECMA(DEBUG) << "Panda File" << pf->GetFilename(); + panda_file::File::EntityId literalArraysId = pf->GetLiteralArraysId(); + panda_file::LiteralDataAccessor lda(*pf, literalArraysId); + + uint32_t num = lda.GetLiteralValsNum(index) / 2; // 2: half + elements.Update(factory->NewTaggedArray(num).GetTaggedValue()); + properties.Update(factory->NewTaggedArray(num).GetTaggedValue()); + uint32_t epos = 0; + uint32_t ppos = 0; + const uint8_t pairSize = 2; + lda.EnumerateLiteralVals(index, [elements, properties, &epos, &ppos, factory, thread, pft, + pf](const LiteralValue &value, const LiteralTag &tag) { + JSTaggedValue jt = JSTaggedValue::Null(); + bool flag = false; + switch (tag) { + case LiteralTag::INTEGER: { + jt = JSTaggedValue(std::get(value)); + break; + } + case LiteralTag::DOUBLE: { + jt = JSTaggedValue(std::get(value)); + break; + } + case LiteralTag::BOOL: { + jt = JSTaggedValue(std::get(value)); + break; + } + case LiteralTag::STRING: { + StringData sd = pf->GetStringData(panda_file::File::EntityId(std::get(value))); + EcmaString *str = factory->GetRawStringFromStringTable(sd.data, sd.utf16_length, sd.is_ascii); + jt = JSTaggedValue(str); + uint32_t idx = 0; + if (JSTaggedValue::ToElementIndex(jt, &idx) && ppos % pairSize == 0) { + flag = true; + } + break; + } + case LiteralTag::METHOD: { + ASSERT(pft != nullptr); + uint32_t methodId = std::get(value); + JSHandle jsFunc = pft->DefineMethodInLiteral(methodId, FunctionKind::NORMAL_FUNCTION); + jt = jsFunc.GetTaggedValue(); + break; + } + case LiteralTag::GENERATORMETHOD: { + ASSERT(pft != nullptr); + uint32_t methodId = std::get(value); + JSHandle jsFunc = pft->DefineMethodInLiteral(methodId, FunctionKind::GENERATOR_FUNCTION); + jt = jsFunc.GetTaggedValue(); + break; + } + case LiteralTag::ASYNCGENERATORMETHOD: { + ASSERT(pft != nullptr); + uint32_t methodId = std::get(value); + JSHandle jsFunc = + pft->DefineMethodInLiteral(methodId, FunctionKind::ASYNC_GENERATOR_FUNCTION); + jt = jsFunc.GetTaggedValue(); + break; + } + case LiteralTag::ACCESSOR: { + JSHandle accessor = factory->NewAccessorData(); + jt = JSTaggedValue(accessor.GetTaggedValue()); + break; + } + case LiteralTag::NULLVALUE: { + break; + } + default: { + UNREACHABLE(); + break; + } + } + if (epos % pairSize == 0 && !flag) { + properties->Set(thread, ppos++, jt); + } else { + elements->Set(thread, epos++, jt); + } + }); +} + +JSHandle LiteralDataExtractor::GetDatasIgnoreType(JSThread *thread, const panda_file::File *pf, + size_t index, PandaFileTranslator *pft) +{ + ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); + + LOG_ECMA(DEBUG) << "Panda File" << pf->GetFilename(); + panda_file::File::EntityId literalArraysId = pf->GetLiteralArraysId(); + panda_file::LiteralDataAccessor lda(*pf, literalArraysId); + + uint32_t num = lda.GetLiteralValsNum(index) / 2; // 2: half + JSHandle literals = factory->NewTaggedArray(num); + uint32_t pos = 0; + lda.EnumerateLiteralVals(index, [literals, &pos, factory, thread, pft, + pf](const panda_file::LiteralDataAccessor::LiteralValue &value, + const LiteralTag &tag) { + JSTaggedValue jt = JSTaggedValue::Null(); + switch (tag) { + case LiteralTag::INTEGER: { + jt = JSTaggedValue(std::get(value)); + break; + } + case LiteralTag::DOUBLE: { + jt = JSTaggedValue(std::get(value)); + break; + } + case LiteralTag::BOOL: { + jt = JSTaggedValue(std::get(value)); + break; + } + case LiteralTag::STRING: { + StringData sd = pf->GetStringData(panda_file::File::EntityId(std::get(value))); + EcmaString *str = factory->GetRawStringFromStringTable(sd.data, sd.utf16_length, sd.is_ascii); + jt = JSTaggedValue(str); + break; + } + case LiteralTag::METHOD: { + ASSERT(pft != nullptr); + uint32_t methodId = std::get(value); + JSHandle jsFunc = pft->DefineMethodInLiteral(methodId, FunctionKind::NORMAL_FUNCTION); + jt = jsFunc.GetTaggedValue(); + break; + } + case LiteralTag::GENERATORMETHOD: { + ASSERT(pft != nullptr); + uint32_t methodId = std::get(value); + JSHandle jsFunc = pft->DefineMethodInLiteral(methodId, FunctionKind::GENERATOR_FUNCTION); + jt = jsFunc.GetTaggedValue(); + break; + } + case LiteralTag::ASYNCGENERATORMETHOD: { + ASSERT(pft != nullptr); + uint32_t methodId = std::get(value); + JSHandle jsFunc = + pft->DefineMethodInLiteral(methodId, FunctionKind::ASYNC_GENERATOR_FUNCTION); + jt = jsFunc.GetTaggedValue(); + break; + } + case LiteralTag::ACCESSOR: { + JSHandle accessor = factory->NewAccessorData(); + jt = accessor.GetTaggedValue(); + break; + } + case LiteralTag::NULLVALUE: { + break; + } + default: { + UNREACHABLE(); + break; + } + } + literals->Set(thread, pos++, jt); + }); + return literals; +} +} // namespace panda::ecmascript diff --git a/runtime/literal_data_extractor.h b/runtime/literal_data_extractor.h new file mode 100644 index 000000000..2c467e8f7 --- /dev/null +++ b/runtime/literal_data_extractor.h @@ -0,0 +1,42 @@ +/* + * Copyright (c) 2021-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_LITERAL_DATA_EXTRACTOR_H +#define ECMASCRIPT_LITERAL_DATA_EXTRACTOR_H + +#include "plugins/ecmascript/runtime/class_linker/panda_file_translator.h" +#include "plugins/ecmascript/runtime/js_tagged_value-inl.h" + +namespace panda::ecmascript { +using EntityId = panda_file::File::EntityId; + +enum class FieldTag : uint8_t { OBJECTLITERAL = 0, ARRAYLITERAL }; + +class LiteralDataExtractor { +public: + explicit LiteralDataExtractor() = default; + virtual ~LiteralDataExtractor() = default; + + DEFAULT_NOEXCEPT_MOVE_SEMANTIC(LiteralDataExtractor); + DEFAULT_COPY_SEMANTIC(LiteralDataExtractor); + + static void ExtractObjectDatas(JSThread *thread, const panda_file::File *pf, size_t index, + JSMutableHandle elements, JSMutableHandle properties, + PandaFileTranslator *pft); + static JSHandle GetDatasIgnoreType(JSThread *thread, const panda_file::File *pf, size_t index, + PandaFileTranslator *pft = nullptr); +}; +} // namespace panda::ecmascript +#endif // ECMASCRIPT_LITERAL_DATA_EXTRACTOR_H diff --git a/runtime/mem/allocator-inl.h b/runtime/mem/allocator-inl.h new file mode 100644 index 000000000..413cb1be3 --- /dev/null +++ b/runtime/mem/allocator-inl.h @@ -0,0 +1,199 @@ +/* + * Copyright (c) 2021-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_MEM_ALLOCATOR_INL_H +#define ECMASCRIPT_MEM_ALLOCATOR_INL_H + +#include + +#include "plugins/ecmascript/runtime/mem/allocator.h" +#include "plugins/ecmascript/runtime/mem/concurrent_sweeper.h" +#include "plugins/ecmascript/runtime/mem/heap.h" +#include "plugins/ecmascript/runtime/mem/space.h" +#include "plugins/ecmascript/runtime/free_object.h" + +namespace panda::ecmascript { +BumpPointerAllocator::BumpPointerAllocator(const Space *space) + : BumpPointerAllocator(space->GetAllocateAreaBegin(), space->GetAllocateAreaEnd()) +{ +} + +BumpPointerAllocator::BumpPointerAllocator(uintptr_t begin, uintptr_t end) : begin_(begin), top_(begin), end_(end) {} + +void BumpPointerAllocator::Reset() +{ + begin_ = 0; + top_ = 0; + end_ = 0; +} + +void BumpPointerAllocator::Reset(uintptr_t begin, uintptr_t end) +{ + begin_ = begin; + top_ = begin; + end_ = end; +} + +void BumpPointerAllocator::Reset(const Space *space) +{ + Reset(space->GetAllocateAreaBegin(), space->GetAllocateAreaEnd()); +} + +uintptr_t BumpPointerAllocator::Allocate(size_t size) +{ + if (UNLIKELY(size == 0)) { + LOG_ECMA_MEM(FATAL) << "size must have a size bigger than 0"; + UNREACHABLE(); + } + size = AlignUp(size, static_cast(MemAlignment::MEM_ALIGN_OBJECT)); + // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-pointer-arithmetic) + if (UNLIKELY(top_ + size > end_)) { + return 0; + } + uintptr_t result = top_; + // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-pointer-arithmetic) + top_ += size; + return result; +} + +FreeListAllocator::FreeListAllocator(const Space *space) : heap_(space->GetHeap()), type_(space->GetSpaceType()) +{ + freeList_ = std::make_unique(); + bpAllocator_.Reset(space); + FreeObject::Cast(bpAllocator_.GetTop())->SetAvailable(bpAllocator_.Available()); + FreeObject::Cast(bpAllocator_.GetTop())->SetNext(nullptr); +} + +void FreeListAllocator::Reset(const Space *space) +{ + heap_ = space->GetHeap(); + type_ = space->GetSpaceType(); + sweeping_ = false; + freeList_ = std::make_unique(); + bpAllocator_.Reset(space); + FreeObject::FillFreeObject(heap_->GetEcmaVM(), bpAllocator_.GetTop(), bpAllocator_.Available()); +} + +void FreeListAllocator::Reset(Heap *heap) +{ + heap_ = heap; + freeList_ = std::make_unique(); + sweeping_ = false; + bpAllocator_.Reset(); +} + +void FreeListAllocator::AddFree(Region *region) +{ + auto begin = region->GetBegin(); + auto end = region->GetEnd(); + Free(begin, end); +} + +uintptr_t FreeListAllocator::Allocate(size_t size) +{ + if (UNLIKELY(size < static_cast(TaggedObject::TaggedObjectSize()))) { + return 0; + } + auto ret = bpAllocator_.Allocate(size); + if (LIKELY(ret != 0)) { + FreeObject::FillFreeObject(heap_->GetEcmaVM(), bpAllocator_.GetTop(), bpAllocator_.Available()); + allocationSizeAccumulator_ += size; + return ret; + } + FreeObject *object = freeList_->Allocator(size); + if (LIKELY(object != nullptr && !object->IsEmpty())) { + return Allocate(object, size); + } + + if (sweeping_) { + // Concurrent sweep maybe sweep same region + heap_->GetSweeper()->FillSweptRegion(type_); + object = freeList_->Allocator(size); + if (LIKELY(object != nullptr && !object->IsEmpty())) { + return Allocate(object, size); + } + + // Parallel + heap_->GetSweeper()->WaitingTaskFinish(type_); + object = freeList_->Allocator(size); + if (LIKELY(object != nullptr && !object->IsEmpty())) { + return Allocate(object, size); + } + } + + return 0; +} + +uintptr_t FreeListAllocator::Allocate(FreeObject *object, size_t size) +{ + FreeBumpPoint(); + bpAllocator_.Reset(object->GetBegin(), object->GetEnd()); + auto ret = bpAllocator_.Allocate(size); + if (ret != 0 && bpAllocator_.Available() > 0) { + FreeObject::FillFreeObject(heap_->GetEcmaVM(), bpAllocator_.GetTop(), bpAllocator_.Available()); + allocationSizeAccumulator_ += size; + } + return ret; +} + +void FreeListAllocator::FreeBumpPoint() +{ + auto begin = bpAllocator_.GetTop(); + auto end = bpAllocator_.GetEnd(); + Free(begin, end); + bpAllocator_.Reset(); +} + +void FreeListAllocator::Free(uintptr_t begin, uintptr_t end, bool isAdd) +{ + ASSERT(heap_ != nullptr); + size_t size = end - begin; + if (size != 0) { + FreeObject::FillFreeObject(heap_->GetEcmaVM(), begin, size); + } + if (UNLIKELY(size < FreeObject::SIZE_OFFSET)) { + return; + } + + freeList_->Free(begin, size, isAdd); +} + +void FreeListAllocator::RebuildFreeList() +{ + bpAllocator_.Reset(); + freeList_->Rebuild(); +} + +void FreeListAllocator::Merge(FreeListAllocator *other) +{ + ASSERT(type_ == other->type_); + other->FreeBumpPoint(); + freeList_->Merge(other->freeList_.get()); +} + +void FreeListAllocator::FillFreeList(FreeObjectKind *kind) +{ + freeList_->AddKind(kind); +} + +size_t FreeListAllocator::GetAvailableSize() const +{ + if (sweeping_) { + heap_->GetSweeper()->WaitingTaskFinish(type_); + } + return freeList_->GetFreeObjectSize(); +} +} // namespace panda::ecmascript +#endif // ECMASCRIPT_MEM_ALLOCATOR_INL_H diff --git a/runtime/mem/allocator.h b/runtime/mem/allocator.h new file mode 100644 index 000000000..cc7e30b63 --- /dev/null +++ b/runtime/mem/allocator.h @@ -0,0 +1,140 @@ +/* + * Copyright (c) 2021-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_MEM_ALLOCATOR_H +#define ECMASCRIPT_MEM_ALLOCATOR_H + +#include + +#include "plugins/ecmascript/runtime/mem/free_object_list.h" +#include "plugins/ecmascript/runtime/mem/heap.h" +#include "plugins/ecmascript/runtime/mem/mem.h" + +namespace panda::ecmascript { +class Space; +class Region; +class Heap; + +class Allocator { +public: + Allocator() = default; + virtual ~Allocator() = default; + NO_COPY_SEMANTIC(Allocator); + NO_MOVE_SEMANTIC(Allocator); +}; + +class BumpPointerAllocator : public Allocator { +public: + BumpPointerAllocator() = default; + ~BumpPointerAllocator() override = default; + NO_COPY_SEMANTIC(BumpPointerAllocator); + NO_MOVE_SEMANTIC(BumpPointerAllocator); + + inline explicit BumpPointerAllocator(const Space *space); + inline BumpPointerAllocator(uintptr_t begin, uintptr_t end); + + inline void Reset(); + inline void Reset(const Space *space); + inline void Reset(uintptr_t begin, uintptr_t end); + inline uintptr_t Allocate(size_t size); + + uintptr_t GetTop() const + { + return top_; + } + + uintptr_t GetEnd() const + { + return end_; + } + + void Swap(const BumpPointerAllocator &other) + { + begin_ = other.begin_; + top_ = other.top_; + end_ = other.end_; + } + + size_t Available() const + { + return (end_ - top_); + } + +private: + uintptr_t begin_ {0}; + uintptr_t top_ {0}; + uintptr_t end_ {0}; +}; + +class FreeListAllocator : public Allocator { +public: + FreeListAllocator() = default; + ~FreeListAllocator() override = default; + + NO_COPY_SEMANTIC(FreeListAllocator); + NO_MOVE_SEMANTIC(FreeListAllocator); + + inline explicit FreeListAllocator(const Space *space); + + inline void Reset(const Space *space); + inline void Reset(Heap *heap); + + inline uintptr_t Allocate(size_t size); + inline void AddFree(Region *region); + + inline void RebuildFreeList(); + inline void FillFreeList(FreeObjectKind *kind); + + inline void Swap(FreeListAllocator &other) + { + heap_ = other.heap_; + bpAllocator_.Swap(other.bpAllocator_); + freeList_.swap(other.freeList_); + type_ = other.type_; + sweeping_ = other.sweeping_; + } + + inline void Merge(FreeListAllocator *other); + + inline void FreeBumpPoint(); + + inline void Free(uintptr_t begin, uintptr_t end, bool isAdd = true); + inline void SplitFreeObject(FreeObject *current, size_t allocateSize); + + inline size_t GetAvailableSize() const; + + void SetSweeping(bool sweeping) + { + sweeping_ = sweeping; + } + + size_t GetAllocatedSize() const + { + return allocationSizeAccumulator_; + } + +private: + inline uintptr_t Allocate(FreeObject *object, size_t size); + + BumpPointerAllocator bpAllocator_; + std::unique_ptr freeList_; + Heap *heap_{nullptr}; + MemSpaceType type_ = OLD_SPACE; + bool sweeping_ = false; + size_t allocationSizeAccumulator_ = 0; +}; +} // namespace panda::ecmascript + +#endif // ECMASCRIPT_MEM_ALLOCATOR_H diff --git a/runtime/mem/area.h b/runtime/mem/area.h new file mode 100644 index 000000000..23531d5b6 --- /dev/null +++ b/runtime/mem/area.h @@ -0,0 +1,76 @@ +/* + * Copyright (c) 2021-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_MEM_AREA_H +#define ECMASCRIPT_MEM_AREA_H + +namespace panda::ecmascript { +class Area { +public: + Area(uintptr_t begin, size_t capacity) + // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-pointer-arithmetic) + : begin_(begin), end_(begin + capacity), next_(nullptr), prev_(nullptr) + { + } + ~Area() = default; + NO_COPY_SEMANTIC(Area); + NO_MOVE_SEMANTIC(Area); + + uintptr_t GetBegin() const + { + return begin_; + } + + uintptr_t GetEnd() const + { + return end_; + } + + void LinkPrev(Area *prev) + { + prev_ = prev; + } + + void LinkNext(Area *next) + { + next_ = next; + } + + size_t GetSize() + { + return end_ - begin_; + } + +private: + template + friend class EcmaList; + friend class Worker; + Area *GetPrev() const + { + return prev_; + } + + Area *GetNext() const + { + return next_; + } + uintptr_t begin_; + uintptr_t end_; + Area *next_; + Area *prev_; +}; +} // namespace panda::ecmascript + +#endif // ECMASCRIPT_MEM_AREA_H diff --git a/runtime/mem/assert_scope-inl.h b/runtime/mem/assert_scope-inl.h new file mode 100644 index 000000000..d2388e0bd --- /dev/null +++ b/runtime/mem/assert_scope-inl.h @@ -0,0 +1,67 @@ +/* + * Copyright (c) 2021-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_MEM_ASSERT_SCOPE_INL_H +#define ECMASCRIPT_MEM_ASSERT_SCOPE_INL_H + +#include "assert_scope.h" + +namespace panda::ecmascript { +// Thread-local storage for assert data. Default all asserts to "allow". +// NOLINTNEXTLINE(hicpp-signed-bitwise) +static thread_local size_t currentAssertData(~0); + +template +AssertScopeT::AssertScopeT() : oldData_(currentAssertData) +{ + switch (type) { + case AssertType::GARBAGE_COLLECTION_ASSERT: + currentAssertData = AssertGarbageCollectBit::Update(oldData_.value(), isAllow); + break; + case AssertType::HEAP_ALLOC_ASSERT: + currentAssertData = AssertHeapAllocBit::Update(oldData_.value(), isAllow); + break; + default: + break; + } +} + +template +AssertScopeT::~AssertScopeT() +{ + if (!oldData_.has_value()) { + return; + } + + currentAssertData = oldData_.value(); + oldData_.reset(); +} + +// static +template +bool AssertScopeT::IsAllowed() +{ + switch (type) { + case AssertType::GARBAGE_COLLECTION_ASSERT: + return AssertGarbageCollectBit::Decode(currentAssertData); + case AssertType::HEAP_ALLOC_ASSERT: + return AssertHeapAllocBit::Decode(currentAssertData); + default: + return true; + } +} +} // namespace panda::ecmascript + +#endif // ECMASCRIPT_MEM_ASSERT_SCOPE_INL_H \ No newline at end of file diff --git a/runtime/mem/assert_scope.h b/runtime/mem/assert_scope.h new file mode 100644 index 000000000..1bb100685 --- /dev/null +++ b/runtime/mem/assert_scope.h @@ -0,0 +1,89 @@ +/* + * Copyright (c) 2021-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_MEM_ASSERT_SCOPE_H +#define ECMASCRIPT_MEM_ASSERT_SCOPE_H + +#include + +#include "libpandabase/macros.h" +#include "utils/bit_field.h" + +namespace panda::ecmascript { +using AssertGarbageCollectBit = panda::BitField; +using AssertHeapAllocBit = AssertGarbageCollectBit::NextFlag; + +#ifndef NDEBUG +constexpr bool IS_ALLOW_CHECK = true; +#else +constexpr bool IS_ALLOW_CHECK = false; +#endif + +enum class AssertType : uint8_t { GARBAGE_COLLECTION_ASSERT = 0, HEAP_ALLOC_ASSERT, LAST_ASSERT_TYPE }; + +template +class AssertScopeT { +public: + static bool IsAllowed() + { + return true; + } +}; + +template +class AssertScopeT { +public: + AssertScopeT(); + + ~AssertScopeT(); + + static bool IsAllowed(); + + NO_COPY_SEMANTIC(AssertScopeT); + DEFAULT_NOEXCEPT_MOVE_SEMANTIC(AssertScopeT); + +private: + std::optional oldData_; +}; + +using DisallowGarbageCollection = AssertScopeT; +using AllowGarbageCollection = AssertScopeT; +using DisAllowHeapAlloc = AssertScopeT; +using AllowHeapAlloc = AssertScopeT; + +#if (!defined NDEBUG) || (defined RUN_TEST) +// NOLINTNEXTLINE(cppcoreguidelines-macro-usage) +#define DISALLOW_GARBAGE_COLLECTION [[maybe_unused]] DisallowGarbageCollection no_gc +// NOLINTNEXTLINE(cppcoreguidelines-macro-usage) +#define ALLOW_GARBAGE_COLLECTION [[maybe_unused]] AllowGarbageCollection allow_gc +// NOLINTNEXTLINE(cppcoreguidelines-macro-usage) +#define DISALLOW_HEAP_ALLOC [[maybe_unused]] DisAllowHeapAlloc no_heap_alloc +// NOLINTNEXTLINE(cppcoreguidelines-macro-usage) +#define ALLOW_HEAP_ALLOC [[maybe_unused]] AllowHeapAlloc allow_heap_alloc +#else +#define DISALLOW_GARBAGE_COLLECTION +#define ALLOW_GARBAGE_COLLECTION +#define DISALLOW_HEAP_ALLOC +#define ALLOW_HEAP_ALLOC +#endif + +// NOLINTNEXTLINE(cppcoreguidelines-macro-usage) +#define CHECK_NO_GC ASSERT_PRINT(AllowGarbageCollection::IsAllowed(), "disallow execute garbage collection."); + +// NOLINTNEXTLINE(cppcoreguidelines-macro-usage) +#define CHECK_NO_HEAP_ALLOC (AllowHeapAlloc::IsAllowed(), "disallow execute heap alloc."); +} // namespace panda::ecmascript + +#endif // ECMASCRIPT_MEM_ASSERT_SCOPE_H diff --git a/runtime/mem/barriers-inl.h b/runtime/mem/barriers-inl.h new file mode 100644 index 000000000..b0fb0f510 --- /dev/null +++ b/runtime/mem/barriers-inl.h @@ -0,0 +1,74 @@ +/* + * Copyright (c) 2021-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_MEM_BARRIERS_INL_H +#define ECMASCRIPT_MEM_BARRIERS_INL_H + + +#include "plugins/ecmascript/runtime/js_thread.h" +#include "plugins/ecmascript/runtime/mem/barriers.h" +#include "plugins/ecmascript/runtime/mem/mem.h" +#include "plugins/ecmascript/runtime/mem/region-inl.h" +#include "plugins/ecmascript/runtime/runtime_api.h" + +#include "libpandabase/mem/gc_barrier.h" + +namespace panda::ecmascript { +static inline void MarkingBarrier(uintptr_t slotAddr, Region *objectRegion, TaggedObject *value, + Region *valueRegion) +{ + auto heap = valueRegion->GetHeap(); + bool isOnlySemi = heap->IsSemiMarkNeeded(); + if (!JSTaggedValue(value).IsWeak()) { + if (isOnlySemi && !valueRegion->InYoungGeneration()) { + return; + } + auto valueBitmap = valueRegion->GetOrCreateMarkBitmap(); + if (!RuntimeApi::AtomicTestAndSet(valueBitmap, value)) { + RuntimeApi::PushWorkList(heap->GetWorkList(), 0, value, valueRegion); + } + } + if (!isOnlySemi && !objectRegion->InYoungAndCSetGeneration() && valueRegion->InCollectSet()) { + auto set = objectRegion->GetOrCreateCrossRegionRememberedSet(); + RuntimeApi::AtomicInsertCrossRegionRememberedSet(set, slotAddr); + } +} + +static inline void WriteBarrier(void *obj, size_t offset, JSTaggedType value) +{ + ASSERT(value != JSTaggedValue::VALUE_UNDEFINED); + Region *objectRegion = Region::ObjectAddressToRange(static_cast(obj)); + Region *valueRegion = Region::ObjectAddressToRange(reinterpret_cast(value)); + uintptr_t slotAddr = ToUintPtr(obj) + offset; + if (!objectRegion->InYoungGeneration() && valueRegion->InYoungGeneration()) { + // Should align with '8' in 64 and 32 bit platform + ASSERT((slotAddr % static_cast(MemAlignment::MEM_ALIGN_OBJECT)) == 0); + objectRegion->InsertOldToNewRememberedSet(slotAddr); + } + + if (valueRegion->IsMarking()) { + MarkingBarrier(slotAddr, objectRegion, reinterpret_cast(value), valueRegion); + } +} + +/* static */ +template +inline T Barriers::GetDynValue(const void *obj, size_t offset) +{ + return ObjectAccessor::GetDynValue(obj, offset); +} +} // namespace panda::ecmascript + +#endif // ECMASCRIPT_MEM_BARRIERS_INL_H diff --git a/runtime/mem/barriers.h b/runtime/mem/barriers.h new file mode 100644 index 000000000..6c57e83a1 --- /dev/null +++ b/runtime/mem/barriers.h @@ -0,0 +1,37 @@ +/* + * Copyright (c) 2021-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_MEM_BARRIERS_H +#define ECMASCRIPT_MEM_BARRIERS_H + +#include "plugins/ecmascript/runtime/mem/mark_word.h" + +namespace panda::ecmascript { +class Barriers { +public: + template + static inline bool AtomicSetDynPrimitive(volatile void *obj, size_t offset, T oldValue, T value) + { + volatile auto atomicField = reinterpret_cast *>(ToUintPtr(obj) + offset); + return std::atomic_compare_exchange_strong_explicit(atomicField, &oldValue, value, std::memory_order_release, + std::memory_order_relaxed); + } + + template + static inline T GetDynValue(const void *obj, size_t offset); +}; +} // namespace panda::ecmascript + +#endif // ECMASCRIPT_MEM_BARRIERS_H diff --git a/runtime/mem/c_containers.h b/runtime/mem/c_containers.h new file mode 100644 index 000000000..29caa67c2 --- /dev/null +++ b/runtime/mem/c_containers.h @@ -0,0 +1,63 @@ +/* + * Copyright (c) 2021-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_MEM_C_CONTAINERS_H +#define ECMASCRIPT_MEM_C_CONTAINERS_H + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "plugins/ecmascript/runtime/mem/caddress_allocator.h" + +namespace panda::ecmascript { +template +using CVector = std::vector>; + +template +using CList = std::list>; + +template> +using CMap = std::map>>; + +template> +using CMultiMap = std::multimap>>; + +template, class KeyEqual = std::equal_to> +using CUnorderedMultiMap = + std::unordered_multimap>>; + +template +using CDeque = std::deque>; + +template> +using CQueue = std::queue; + +template, class KeyEqual = std::equal_to> +using CUnorderedMap = std::unordered_map>>; + +template, class KeyEqual = std::equal_to> +using CUnorderedSet = std::unordered_set>; +} // namespace panda::ecmascript + +#endif // ECMASCRIPT_MEM_C_CONTAINERS_H diff --git a/runtime/mem/c_string.cpp b/runtime/mem/c_string.cpp new file mode 100644 index 000000000..2d86c01ae --- /dev/null +++ b/runtime/mem/c_string.cpp @@ -0,0 +1,146 @@ +/* + * Copyright (c) 2021-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 "plugins/ecmascript/runtime/mem/c_string.h" + +#include +#include + +#include "plugins/ecmascript/runtime/ecma_string-inl.h" +#include "plugins/ecmascript/runtime/js_symbol.h" +#include "plugins/ecmascript/runtime/mem/c_containers.h" +#include "libpandabase/macros.h" + +namespace panda::ecmascript { +constexpr int BASE = 10; + +int64_t CStringToLL(const CString &str) +{ + [[maybe_unused]] char *endPtr = nullptr; + int64_t result = std::strtoll(str.c_str(), &endPtr, BASE); + ASSERT(!(result == 0 && str.c_str() == endPtr) && "CString argument is not long long int"); + return result; +} + +uint64_t CStringToULL(const CString &str) +{ + [[maybe_unused]] char *endPtr = nullptr; + uint64_t result = std::strtoull(str.c_str(), &endPtr, BASE); + ASSERT(!(result == 0 && str.c_str() == endPtr) && "CString argument is not unsigned long long int"); + return result; +} + +float CStringToF(const CString &str) +{ + [[maybe_unused]] char *endPtr = nullptr; + float result = std::strtof(str.c_str(), &endPtr); + ASSERT(result != HUGE_VALF && "CString argument is not float"); + ASSERT(!(result == 0 && str.c_str() == endPtr) && "CString argument is not float"); + return result; +} + +double CStringToD(const CString &str) +{ + [[maybe_unused]] char *endPtr = nullptr; + double result = std::strtod(str.c_str(), &endPtr); + ASSERT(result != HUGE_VALF && "CString argument is not double"); + ASSERT(!(result == 0 && str.c_str() == endPtr) && "CString argument is not double"); + return result; +} + +template +CString ConvertToString(T sp) +{ + CString res; + res.reserve(sp.size()); + + // Also support ascii that great than 127, so using unsigned char here + constexpr size_t maxChar = std::numeric_limits::max(); + + for (const auto &c : sp) { + if (c > maxChar) { + return ""; + } + res.push_back(c); + } + + return res; +} + +// NB! the following function need additional mem allocation, don't use when unnecessary! +CString ConvertToString(const std::string &str) +{ + CString res; + res.reserve(str.size()); + for (auto c : str) { + res.push_back(c); + } + return res; +} + +CString ConvertToString(const EcmaString *s, StringConvertedUsage usage) +{ + if (s == nullptr) { + return CString(""); + } + return ConvertToString(s, 0, s->GetLength(), usage); +} + +CString ConvertToString(const EcmaString *s, uint32_t start, uint32_t length, StringConvertedUsage usage) +{ + if (s == nullptr) { + return CString(""); + } + if (s->IsUtf16()) { + // Should convert utf-16 to utf-8, because uint16_t likely great than maxChar, will convert fail + bool modify = (usage != StringConvertedUsage::PRINT); + // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-pointer-arithmetic) + size_t len = base::utf_helper::Utf16ToUtf8Size(s->GetDataUtf16() + start, length, modify) - 1; + CVector buf(len); + // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-pointer-arithmetic) + len = base::utf_helper::ConvertRegionUtf16ToUtf8(s->GetDataUtf16() + start, buf.data(), length, len, 0, modify); + Span sp(buf.data(), len); + return ConvertToString(sp); + } + + Span sp(s->GetDataUtf8(), s->GetLength()); + return ConvertToString(sp.SubSpan(start, length)); +} + +CString ConvertToString(JSTaggedValue key) +{ + ASSERT(key.IsStringOrSymbol()); + if (key.IsString()) { + return ConvertToString(EcmaString::ConstCast(key.GetTaggedObject())); + } + + ecmascript::JSTaggedValue desc = JSSymbol::Cast(key.GetTaggedObject())->GetDescription(); + if (desc.IsUndefined()) { + return CString("Symbol()"); + } + + return ConvertToString(EcmaString::ConstCast(desc.GetTaggedObject())); +} + +std::string CstringConvertToString(const CString &str) +{ + std::string res; + res.reserve(str.size()); + for (auto c : str) { + res.push_back(c); + } + return res; +} +} // namespace panda::ecmascript diff --git a/runtime/mem/c_string.h b/runtime/mem/c_string.h new file mode 100644 index 000000000..acb3284c6 --- /dev/null +++ b/runtime/mem/c_string.h @@ -0,0 +1,87 @@ +/* + * Copyright (c) 2021-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_MEM_C_STRING_H +#define ECMASCRIPT_MEM_C_STRING_H + +#include +#include +#include + +#include "plugins/ecmascript/runtime/common.h" +#include "plugins/ecmascript/runtime/mem/caddress_allocator.h" + +namespace panda::ecmascript { +class EcmaString; +class JSTaggedValue; + +using CString = std::basic_string, CAddressAllocator>; +using CStringStream = std::basic_stringstream, CAddressAllocator>; + +// PRINT will skip '\0' in utf16 during conversion of utf8 +enum StringConvertedUsage { PRINT, LOGICOPERATION }; + +int64_t CStringToLL(const CString &str); +uint64_t CStringToULL(const CString &str); +float CStringToF(const CString &str); +double CStringToD(const CString &str); + +CString ConvertToString(const std::string &str); +std::string CstringConvertToString(const CString &str); + +// '\u0000' is skip according to holdZero +CString ConvertToString(const ecmascript::EcmaString *s, StringConvertedUsage usage = StringConvertedUsage::PRINT); +CString ConvertToString(const ecmascript::EcmaString *s, uint32_t start, uint32_t length, + StringConvertedUsage usage = StringConvertedUsage::PRINT); +CString ConvertToString(ecmascript::JSTaggedValue key); + +template +std::enable_if_t, CString> FloatToCString(T number) +{ + CStringStream strStream; + strStream << number; + return strStream.str(); +} + +template +std::enable_if_t, CString> ToCString(T number) +{ + if (number == 0) { + return CString("0"); + } + bool IsNeg = false; + if (number < 0) { + number = -number; + IsNeg = true; + } + + static constexpr uint32_t BUFF_SIZE = std::numeric_limits::digits10 + 3; // 3: Reserved for sign bit and '\0'. + std::array buf {}; + uint32_t position = BUFF_SIZE - 1; + buf[position] = '\0'; + while (number > 0) { + // NOLINTNEXTLINE(readability-magic-numbers) + buf[--position] = number % 10 + '0'; // 10 : decimal + // NOLINTNEXTLINE(readability-magic-numbers) + number /= 10; // 10 : decimal + } + if (IsNeg) { + buf[--position] = '-'; + } + return CString(&buf[position]); +} +} // namespace panda::ecmascript + +#endif // ECMASCRIPT_MEM_C_STRING_H diff --git a/runtime/mem/caddress_allocator.h b/runtime/mem/caddress_allocator.h new file mode 100644 index 000000000..1aee597d3 --- /dev/null +++ b/runtime/mem/caddress_allocator.h @@ -0,0 +1,163 @@ +/* + * Copyright (c) 2021-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 RUNTIME_ECMASCRIPT_C_ADDRESS_ALLOCATOR_H +#define RUNTIME_ECMASCRIPT_C_ADDRESS_ALLOCATOR_H + +#include "plugins/ecmascript/runtime/mem/chunk.h" + +namespace panda::ecmascript { +template +class CAddressAllocator { +public: + // using by std allocator + using value_type = T; + using pointer = T *; + using reference = T &; + using const_pointer = const T *; + using const_reference = const T &; + using size_type = size_t; + using difference_type = ptrdiff_t; + + template + struct Rebind { + using other = CAddressAllocator; + }; + + template + using rebind = Rebind; + + CAddressAllocator() = default; + + template + explicit CAddressAllocator(const CAddressAllocator &other [[maybe_unused]]) + { + } + + CAddressAllocator(const CAddressAllocator &) = default; + CAddressAllocator &operator=(const CAddressAllocator &) = default; + CAddressAllocator(CAddressAllocator &&other) noexcept = default; + CAddressAllocator &operator=(CAddressAllocator &&other) noexcept = default; + ~CAddressAllocator() = default; + + // NOLINTNEXTLINE(readability-identifier-naming) + size_type max_size() const + { + return static_cast(-1) / sizeof(T); + } + + bool operator==([[maybe_unused]] CAddressAllocator const &other) const + { + return false; + } + + bool operator!=([[maybe_unused]] CAddressAllocator const &other) const + { + return true; + } + + // NOLINTNEXTLINE(readability-identifier-naming) + pointer allocate(size_type n, [[maybe_unused]] const void *ptr = nullptr) + { + ASSERT(n <= max_size()); + return static_cast(Allocate(n * sizeof(T))); + } + + // NOLINTNEXTLINE(readability-identifier-naming) + void deallocate([[maybe_unused]] pointer p, [[maybe_unused]] size_type n) + { + Free(static_cast(p)); + } + + template + void construct(U *p, Args &&... args) // NOLINT(readability-identifier-naming) + { + if (p == nullptr) { + return; + } + ::new (static_cast(p)) U(std::forward(args)...); + } + template + void destroy(U *p) // NOLINT(readability-identifier-naming) + { + if (p == nullptr) { + return; + } + p->~U(); + } + + [[nodiscard]] void *Allocate(size_t size) + { + if (size == 0) { + LOG_ECMA_MEM(FATAL) << "size must have a size bigger than 0"; + UNREACHABLE(); + } + // NOLINTNEXTLINE(cppcoreguidelines-no-malloc) + void *ptr = malloc(size); + if (ptr == nullptr) { + LOG_ECMA_MEM(FATAL) << "malloc failed"; + UNREACHABLE(); + } + return ptr; + } + + template + [[nodiscard]] S *New(Args &&... args) + { + auto p = reinterpret_cast(Allocate(sizeof(S))); + new (p) S(std::forward(args)...); + return reinterpret_cast(p); + } + + template + void Finalize(S *ptr) + { + ASSERT(ptr != nullptr); + // NOLINTNEXTLINE(readability-braces-around-statements,bugprone-suspicious-semicolon) + if constexpr (std::is_class_v) { + ptr->~S(); + } + Free(ptr); + } + + [[nodiscard]] T *AllocArray(size_t size) + { + return static_cast(Allocate(size * sizeof(T))); + } + + void Delete(T *ptr) + { + if (ptr == nullptr) { + return; + } + // NOLINTNEXTLINE(readability-braces-around-statements,bugprone-suspicious-semicolon) + if constexpr (std::is_class_v) { + ptr->~T(); + } + Free(ptr); + } + + void Free(void *mem) + { + if (mem == nullptr) { + return; + } + // NOLINTNEXTLINE(cppcoreguidelines-no-malloc) + free(mem); + } +}; +} // namespace panda::ecmascript + +#endif // RUNTIME_ECMASCRIPT_C_ADDRESS_ALLOCATOR_H diff --git a/runtime/mem/chunk.cpp b/runtime/mem/chunk.cpp new file mode 100644 index 000000000..94553801e --- /dev/null +++ b/runtime/mem/chunk.cpp @@ -0,0 +1,80 @@ +/* + * Copyright (c) 2021-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 "plugins/ecmascript/runtime/mem/chunk.h" + +#include "plugins/ecmascript/runtime/mem/heap.h" + +namespace panda::ecmascript { +Chunk::Chunk(RegionFactory *factory) : factory_(factory) {} + +Area *Chunk::NewArea(size_t size) +{ + auto area = factory_->AllocateArea(size); + if (area == nullptr) { + LOG_ECMA_MEM(FATAL) << "OOM Chunk::NewArea area is nullptr"; + UNREACHABLE(); + } + areaList_.AddNode(area); + currentArea_ = area; + return area; +} + +uintptr_t Chunk::Expand(size_t size) +{ + ASSERT(end_ - ptr_ < size); + + Area *head = currentArea_; + size_t newSize; + if (head != nullptr) { + // NOLINTNEXTLINE(hicpp-signed-bitwise) + newSize = size + (head->GetSize() << 1); + } else { + newSize = sizeof(Area) + MEM_ALIGN + size; + } + + if (newSize < MIN_CHUNK_AREA_SIZE) { + newSize = MIN_CHUNK_AREA_SIZE; + } else if (newSize > MAX_CHUNK_AREA_SIZE) { + size_t minNewSize = sizeof(Area) + MEM_ALIGN + size; + newSize = std::max(minNewSize, MAX_CHUNK_AREA_SIZE); + } + + if (newSize > static_cast(std::numeric_limits::max())) { + LOG_ECMA_MEM(FATAL) << "OOM chunk"; + UNREACHABLE(); + } + + Area *area = NewArea(newSize); + if (area == nullptr) { + LOG_ECMA_MEM(FATAL) << "OOM chunk"; + UNREACHABLE(); + } + uintptr_t result = AlignUp(area->GetBegin(), MEM_ALIGN); + ptr_ = result + size; + end_ = area->GetEnd(); + return result; +} + +void Chunk::ReleaseMemory() +{ + while (!areaList_.IsEmpty()) { + Area *node = areaList_.PopBack(); + factory_->FreeArea(node); + } + ptr_ = 0; + end_ = 0; +} +} // namespace panda::ecmascript diff --git a/runtime/mem/chunk.h b/runtime/mem/chunk.h new file mode 100644 index 000000000..d831f6f8d --- /dev/null +++ b/runtime/mem/chunk.h @@ -0,0 +1,99 @@ +/* + * Copyright (c) 2021-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 RUNTIME_ECMASCRIPT_CHUNK_H +#define RUNTIME_ECMASCRIPT_CHUNK_H + +#include "plugins/ecmascript/runtime/mem/ecma_list.h" +#include "plugins/ecmascript/runtime/mem/area.h" + +namespace panda::ecmascript { +class RegionFactory; + +class Chunk { +public: + static constexpr size_t MEM_ALIGN = 8U; + + explicit Chunk(RegionFactory *factory); + ~Chunk() + { + ReleaseMemory(); + } + + NO_COPY_SEMANTIC(Chunk); + NO_MOVE_SEMANTIC(Chunk); + + [[nodiscard]] void *Allocate(size_t size) + { + if (size == 0) { + LOG_ECMA_MEM(FATAL) << "size must have a size bigger than 0"; + UNREACHABLE(); + } + uintptr_t result = ptr_; + size = AlignUp(size, MEM_ALIGN); + if (UNLIKELY(size > end_ - ptr_)) { + result = Expand(size); + } else { + ptr_ += size; + } + + return reinterpret_cast(result); + } + + template + [[nodiscard]] T *NewArray(size_t size) + { + return static_cast(Allocate(size * sizeof(T))); + } + + template + [[nodiscard]] T *New(Args &&... args) + { + auto p = reinterpret_cast(Allocate(sizeof(T))); + new (p) T(std::forward(args)...); + return reinterpret_cast(p); + } + + template + void Delete(T *ptr) + { + ASSERT(ptr != nullptr); + // NOLINTNEXTLINE(readability-braces-around-statements,bugprone-suspicious-semicolon) + if constexpr (std::is_class_v) { + ptr->~T(); + } + Free(ptr); + } + + void Free([[maybe_unused]] void *mem) + { + // do nothing + } + +private: + uintptr_t Expand(size_t size); + Area *NewArea(size_t size); + void ReleaseMemory(); + + uintptr_t ptr_{0}; + uintptr_t end_{0}; + + Area *currentArea_{nullptr}; + EcmaList areaList_{}; + RegionFactory *factory_{nullptr}; +}; +} // namespace panda::ecmascript + +#endif // RUNTIME_ECMASCRIPT_CHUNK_H diff --git a/runtime/mem/chunk_allocator.h b/runtime/mem/chunk_allocator.h new file mode 100644 index 000000000..f2f072155 --- /dev/null +++ b/runtime/mem/chunk_allocator.h @@ -0,0 +1,151 @@ +/* + * Copyright (c) 2021-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 RUNTIME_ECMASCRIPT_CHUNK_ALLOCATOR_H +#define RUNTIME_ECMASCRIPT_CHUNK_ALLOCATOR_H + +#include "plugins/ecmascript/runtime/mem/chunk.h" + +namespace panda::ecmascript { +template +class ChunkAllocator { +public: + // used for std allocator + using value_type = T; + using pointer = T *; + using reference = T &; + using const_pointer = const T *; + using const_reference = const T &; + using size_type = size_t; + using difference_type = ptrdiff_t; + + template + struct Rebind { + using other = ChunkAllocator; + }; + + template + using rebind = Rebind; + + explicit ChunkAllocator(Chunk *chunk) : chunk_(chunk) {} + + template + explicit ChunkAllocator(const ChunkAllocator &other) : chunk_(other.chunk_) + { + } + template + friend class ChunkAllocator; + + ChunkAllocator(const ChunkAllocator &) = default; + ChunkAllocator &operator=(const ChunkAllocator &) = default; + ChunkAllocator(ChunkAllocator &&other) noexcept + { + chunk_ = other.chunk_; + other.chunk_ = nullptr; + } + ChunkAllocator &operator=(ChunkAllocator &&other) noexcept + { + chunk_ = other.chunk_; + other.chunk_ = nullptr; + return *this; + } + ~ChunkAllocator() = default; + + // NOLINTNEXTLINE(readability-identifier-naming) + size_type max_size() const + { + return static_cast(-1) / sizeof(T); + } + + // NOLINTNEXTLINE(readability-identifier-naming) + pointer address(reference x) const + { + return &x; + } + // NOLINTNEXTLINE(readability-identifier-naming) + const_pointer address(const_reference x) const + { + return &x; + } + + // NOLINTNEXTLINE(readability-identifier-naming) + pointer allocate(size_type n, [[maybe_unused]] const void *ptr = nullptr) + { + ASSERT(n <= max_size()); + return chunk_->NewArray(n); + } + + // NOLINTNEXTLINE(readability-identifier-naming) + void deallocate([[maybe_unused]] pointer p, [[maybe_unused]] size_type n) {} + + template + void construct(U *p, Args &&... args) // NOLINT(readability-identifier-naming) + { + ::new (static_cast(p)) U(std::forward(args)...); + } + template + void destroy(U *p) // NOLINT(readability-identifier-naming) + { + if (p == nullptr) { + return; + } + p->~U(); + } + + bool operator==(ChunkAllocator const &other) const + { + return chunk_ == other.chunk_; + } + bool operator!=(ChunkAllocator const &other) const + { + return chunk_ != other.chunk_; + } + + [[nodiscard]] void *Alloc(size_t size) + { + return chunk_->NewArray(size); + } + + [[nodiscard]] T *AllocArray(size_t size) + { + return chunk_->NewArray(size); + } + + void Delete(T *ptr) + { + if (ptr == nullptr) { + LOG_ECMA_MEM(FATAL) << "free nullptr"; + UNREACHABLE(); + } + // NOLINTNEXTLINE(readability-braces-around-statements,bugprone-suspicious-semicolon) + if constexpr (std::is_class_v) { + ptr->~T(); + } + Free(ptr); + } + + void Free([[maybe_unused]] void *mem) {} + + Chunk *chunk() + { + return chunk_; + } + +private: + Chunk *chunk_; +}; +} // namespace panda::ecmascript + +#endif // RUNTIME_ECMASCRIPT_CHUNK_ALLOCATOR_H diff --git a/runtime/mem/chunk_containers.h b/runtime/mem/chunk_containers.h new file mode 100644 index 000000000..85bc5f271 --- /dev/null +++ b/runtime/mem/chunk_containers.h @@ -0,0 +1,93 @@ +/* + * Copyright (c) 2021-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 LIBPANDABASE_ECMASCRIPT_MEM_CONTAINERS_H +#define LIBPANDABASE_ECMASCRIPT_MEM_CONTAINERS_H + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "plugins/ecmascript/runtime/mem/chunk_allocator.h" + +namespace panda::ecmascript { +template +class ChunkVector : public std::vector> { +public: + explicit ChunkVector(Chunk *chunk) : std::vector>(ChunkAllocator(chunk)) {} + + ChunkVector(size_t size, Chunk *chunk) : std::vector>(size, T(), ChunkAllocator(chunk)) {} + + ChunkVector(size_t size, T def, Chunk *chunk) + : std::vector>(size, def, ChunkAllocator(chunk)) + { + } + ~ChunkVector() = default; + NO_COPY_SEMANTIC(ChunkVector); + NO_MOVE_SEMANTIC(ChunkVector); +}; + +template> +class ChunkMap : public std::map>> { +public: + // Constructs an empty map. + explicit ChunkMap(Chunk *chunk) + : std::map>>(Compare(), + ChunkAllocator>(chunk)) + { + } + ~ChunkMap() = default; + NO_COPY_SEMANTIC(ChunkMap); + NO_MOVE_SEMANTIC(ChunkMap); +}; + +template, typename KeyEqual = std::equal_to> +class ChunkUnorderedMap : public std::unordered_map>> { +public: + // NOLINTNEXTLINE(readability-magic-numbers) + explicit ChunkUnorderedMap(Chunk *chunk, size_t bucket_count = 100) + : std::unordered_map>>( + bucket_count, Hash(), KeyEqual(), ChunkAllocator>(chunk)) + { + } + ~ChunkUnorderedMap() = default; + NO_COPY_SEMANTIC(ChunkUnorderedMap); + NO_MOVE_SEMANTIC(ChunkUnorderedMap); +}; + +template> +class ChunkMultimap : public std::multimap>> { +public: + // Constructs an empty multimap. + explicit ChunkMultimap(Chunk *chunk) + : std::multimap>>( + Compare(), ChunkAllocator>(chunk)) + { + } + ~ChunkMultimap() = default; + NO_COPY_SEMANTIC(ChunkMultimap); + NO_MOVE_SEMANTIC(ChunkMultimap); +}; +} // namespace panda::ecmascript + +#endif // LIBPANDABASE_ECMASCRIPT_MEM_CONTAINERS_H diff --git a/runtime/mem/clock_scope.h b/runtime/mem/clock_scope.h new file mode 100644 index 000000000..d3baeacb9 --- /dev/null +++ b/runtime/mem/clock_scope.h @@ -0,0 +1,56 @@ +/* + * Copyright (c) 2021-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_MEM_CLOCK_SCOPE_H +#define ECMASCRIPT_MEM_CLOCK_SCOPE_H + +#include +#include "chrono" +#include "libpandabase/utils/logger.h" + +namespace panda::ecmascript { +class ClockScope { +using Clock = std::chrono::high_resolution_clock; +using Duration = std::chrono::duration; + +public: + explicit ClockScope() + { + start_ = Clock::now(); + } + + ~ClockScope() = default; + + Duration GetPauseTime() const + { + return Clock::now() - start_; + } + + float TotalSpentTime() const + { + auto duration = std::chrono::duration_cast(Clock::now() - start_); + return static_cast(duration.count()) / MILLION_TIME; + } + +private: + NO_COPY_SEMANTIC(ClockScope); + NO_MOVE_SEMANTIC(ClockScope); + + Clock::time_point start_; + static constexpr uint32_t MILLION_TIME = 1000; +}; +} // namespace panda::ecmascript + +#endif // ECMASCRIPT_MEM_CLOCK_SCOPE_H diff --git a/runtime/mem/compress_collector.cpp b/runtime/mem/compress_collector.cpp new file mode 100644 index 000000000..e1eff4d6e --- /dev/null +++ b/runtime/mem/compress_collector.cpp @@ -0,0 +1,160 @@ +/* + * Copyright (c) 2021-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 "plugins/ecmascript/runtime/mem/compress_collector.h" + +#include "plugins/ecmascript/runtime/ecma_vm.h" +#include "plugins/ecmascript/runtime/mem/clock_scope.h" +#include "plugins/ecmascript/runtime/mem/concurrent_marker.h" +#include "plugins/ecmascript/runtime/mem/mem_manager.h" +#include "plugins/ecmascript/runtime/mem/object_xray-inl.h" +#include "plugins/ecmascript/runtime/mem/mark_stack.h" +#include "plugins/ecmascript/runtime/mem/mem.h" +#include "plugins/ecmascript/runtime/mem/parallel_marker-inl.h" +#include "plugins/ecmascript/runtime/mem/space-inl.h" +#include "plugins/ecmascript/runtime/runtime_call_id.h" +#include "plugins/ecmascript/runtime/vmstat/runtime_stat.h" + +namespace panda::ecmascript { +CompressCollector::CompressCollector(Heap *heap) + : heap_(heap), workList_(heap->GetWorkList()) {} + +void CompressCollector::RunPhases() +{ + ecmascript::JSThread *thread = heap_->GetEcmaVM()->GetJSThread(); + INTERPRETER_TRACE(thread, CompressCollector_RunPhases); + ClockScope clockScope; + + ECMA_BYTRACE_NAME(BYTRACE_TAG_ARK, "CompressCollector::RunPhases"); + bool concurrentMark = heap_->CheckConcurrentMark(thread); + if (concurrentMark) { + ECMA_GC_LOG() << "CompressCollector after ConcurrentMarking"; + heap_->GetConcurrentMarker()->Reset(); // HPPGC use mark result to move TaggedObject. + } + InitializePhase(); + MarkingPhase(); + SweepPhases(); + FinishPhase(); + heap_->GetEcmaVM()->GetEcmaGCStats()->StatisticCompressCollector(clockScope.GetPauseTime(), youngAndOldAliveSize_, + youngSpaceCommitSize_, oldSpaceCommitSize_, + nonMoveSpaceFreeSize_, nonMoveSpaceCommitSize_); + ECMA_GC_LOG() << "CompressCollector::RunPhases " << clockScope.TotalSpentTime(); +} + +void CompressCollector::InitializePhase() +{ + ECMA_BYTRACE_NAME(BYTRACE_TAG_ARK, "CompressCollector::InitializePhase"); + heap_->Prepare(); + auto callback = [](Region *current) { + // ensure mark bitmap + auto bitmap = current->GetMarkBitmap(); + if (bitmap == nullptr) { + current->GetOrCreateMarkBitmap(); + } else { + bitmap->ClearAllBits(); + } + auto rememberset = current->GetOldToNewRememberedSet(); + if (rememberset != nullptr) { + rememberset->ClearAllBits(); + } + }; + heap_->EnumerateNonMovableRegions(callback); + workList_->Initialize(TriggerGCType::COMPRESS_FULL_GC, ParallelGCTaskPhase::COMPRESS_HANDLE_GLOBAL_POOL_TASK); + heap_->GetCompressGcMarker()->Initialized(); + heap_->GetEvacuationAllocator()->Initialize(TriggerGCType::COMPRESS_FULL_GC); + + youngAndOldAliveSize_ = 0; + nonMoveSpaceFreeSize_ = 0; + youngSpaceCommitSize_ = heap_->GetFromSpace()->GetCommittedSize(); + oldSpaceCommitSize_ = heap_->GetCompressSpace()->GetCommittedSize(); + nonMoveSpaceCommitSize_ = heap_->GetNonMovableSpace()->GetCommittedSize(); +} + +void CompressCollector::MarkingPhase() +{ + ECMA_BYTRACE_NAME(BYTRACE_TAG_ARK, "CompressCollector::MarkingPhase"); + heap_->GetCompressGcMarker()->MarkRoots(0); + heap_->GetCompressGcMarker()->ProcessMarkStack(0); + heap_->WaitRunningTaskFinished(); +} + +void CompressCollector::SweepPhases() +{ + ECMA_BYTRACE_NAME(BYTRACE_TAG_ARK, "CompressCollector::SweepPhases"); + // process weak reference + auto totalThreadCount = Platform::GetCurrentPlatform()->GetTotalThreadNum() + 1; // gc thread and main thread + for (uint32_t i = 0; i < totalThreadCount; i++) { + ProcessQueue *queue = workList_->GetWeakReferenceQueue(i); + + while (true) { + auto obj = queue->PopBack(); + if (UNLIKELY(obj == nullptr)) { + break; + } + ObjectSlot slot(ToUintPtr(obj)); + JSTaggedValue value(slot.GetTaggedType()); + auto header = value.GetTaggedWeakRef(); + + Region *objectRegion = Region::ObjectAddressToRange(header); + if (!objectRegion->InYoungAndOldGeneration()) { + auto markBitmap = objectRegion->GetMarkBitmap(); + if (!markBitmap->Test(header)) { + slot.Update(static_cast(JSTaggedValue::Undefined().GetRawData())); + } + } else { + MarkWord markWord(header); + if (markWord.IsForwardingAddress()) { + TaggedObject *dst = markWord.ToForwardingAddress(); + auto weakRef = JSTaggedValue(JSTaggedValue(dst).CreateAndGetWeakRef()).GetRawTaggedObject(); + slot.Update(weakRef); + } else { + slot.Update(static_cast(JSTaggedValue::Undefined().GetRawData())); + } + } + } + } + + auto stringTable = heap_->GetEcmaVM()->GetEcmaStringTable(); + WeakRootVisitor gcUpdateWeak = [](TaggedObject *header) { + Region *objectRegion = Region::ObjectAddressToRange(header); + if (!objectRegion->InYoungAndOldGeneration()) { + auto markBitmap = objectRegion->GetMarkBitmap(); + if (markBitmap->Test(header)) { + return header; + } + return reinterpret_cast(ToUintPtr(nullptr)); + } + + MarkWord markWord(header); + if (markWord.IsForwardingAddress()) { + return markWord.ToForwardingAddress(); + } + return reinterpret_cast(ToUintPtr(nullptr)); + }; + stringTable->SweepWeakReference(gcUpdateWeak); + heap_->GetEcmaVM()->GetJSThread()->IterateWeakEcmaGlobalStorage(gcUpdateWeak); + heap_->GetEcmaVM()->ProcessReferences(gcUpdateWeak); + + heap_->UpdateDerivedObjectInStack(); + heap_->GetSweeper()->SweepPhases(true); +} + +void CompressCollector::FinishPhase() +{ + ECMA_BYTRACE_NAME(BYTRACE_TAG_ARK, "CompressCollector::FinishPhase"); + workList_->Finish(youngAndOldAliveSize_); + heap_->GetEvacuationAllocator()->Finalize(TriggerGCType::COMPRESS_FULL_GC); +} +} // namespace panda::ecmascript diff --git a/runtime/mem/compress_collector.h b/runtime/mem/compress_collector.h new file mode 100644 index 000000000..1cd68e77b --- /dev/null +++ b/runtime/mem/compress_collector.h @@ -0,0 +1,59 @@ +/* + * Copyright (c) 2021-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_MEM_COMPRESS_COLLECTOR_H +#define ECMASCRIPT_MEM_COMPRESS_COLLECTOR_H + +#include "plugins/ecmascript/runtime/mem/parallel_work_helper.h" +#include "plugins/ecmascript/runtime/mem/semi_space_collector.h" + +namespace panda { +namespace ecmascript { +class Heap; +class JSHClass; + +class CompressCollector : public GarbageCollector { +public: + explicit CompressCollector(Heap *heap); + ~CompressCollector() override = default; + + NO_COPY_SEMANTIC(CompressCollector); + NO_MOVE_SEMANTIC(CompressCollector); + + void RunPhases(); + +private: + void InitializePhase(); + void MarkingPhase(); + void SweepPhases(); + void FinishPhase(); + + Heap *heap_; + size_t youngAndOldAliveSize_ = 0; + size_t nonMoveSpaceFreeSize_ = 0; + size_t youngSpaceCommitSize_ = 0; + size_t oldSpaceCommitSize_ = 0; + size_t nonMoveSpaceCommitSize_ = 0; + + // obtain from heap + WorkerHelper *workList_ {nullptr}; + + friend class WorkerHelper; + friend class Heap; +}; +} // namespace ecmascript +} // namespace panda + +#endif // ECMASCRIPT_MEM_COMPRESS_COLLECTOR_H diff --git a/runtime/mem/concurrent_marker.cpp b/runtime/mem/concurrent_marker.cpp new file mode 100644 index 000000000..bf62a2ba6 --- /dev/null +++ b/runtime/mem/concurrent_marker.cpp @@ -0,0 +1,163 @@ +/* + * Copyright (c) 2021-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 "plugins/ecmascript/runtime/mem/concurrent_marker.h" + +#include "plugins/ecmascript/runtime/mem/allocator-inl.h" +#include "plugins/ecmascript/runtime/mem/clock_scope.h" +#include "plugins/ecmascript/runtime/mem/heap-inl.h" +#include "plugins/ecmascript/runtime/mem/object_xray-inl.h" +#include "plugins/ecmascript/runtime/mem/mark_word.h" +#include "plugins/ecmascript/runtime/mem/parallel_marker-inl.h" +#include "plugins/ecmascript/runtime/mem/space-inl.h" +#include "plugins/ecmascript/runtime/mem/verification.h" +#include "plugins/ecmascript/runtime/platform/platform.h" +#include "os/mutex.h" + +namespace panda::ecmascript { +ConcurrentMarker::ConcurrentMarker(Heap *heap) + : heap_(heap), + vm_(heap->GetEcmaVM()), + thread_(vm_->GetJSThread()), + workList_(heap->GetWorkList()) +{ +} + +void ConcurrentMarker::ConcurrentMarking() +{ + ECMA_GC_LOG() << "ConcurrentMarker: Concurrent Mark Begin"; + + heap_->Prepare(); + ECMA_BYTRACE_NAME(BYTRACE_TAG_ARK, "ConcurrentMarker::ConcurrentMarking"); + thread_->SetMarkStatus(MarkStatus::MARKING); + if (!heap_->IsSemiMarkNeeded()) { + heapObjectSize_ = heap_->GetHeapObjectSize(); + if (heap_->GetSweeper()->CanSelectCset()) { + const_cast(heap_->GetOldSpace())->SelectCSet(); + } + } else { + heapObjectSize_ = heap_->GetNewSpace()->GetHeapObjectSize(); + } + + InitializeMarking(); + Platform::GetCurrentPlatform()->PostTask(std::make_unique(heap_)); + if (heap_->IsSemiMarkNeeded() && heap_->IsParallelGCEnabled()) { + heap_->PostParallelGCTask(ParallelGCTaskPhase::CONCURRENT_HANDLE_OLD_TO_NEW_TASK); + } +} + +void ConcurrentMarker::FinishPhase() +{ + size_t aliveSize = 0; + workList_->Finish(aliveSize); +} + +void ConcurrentMarker::ReMarking() +{ + ECMA_GC_LOG() << "ConcurrentMarker: Remarking Begin"; + Marker *nonMoveMarker = heap_->GetNonMovableMarker(); + nonMoveMarker->MarkRoots(0); + if (heap_->IsSemiMarkNeeded() && !heap_->IsParallelGCEnabled()) { + heap_->GetNonMovableMarker()->ProcessOldToNew(0); + } else { + nonMoveMarker->ProcessMarkStack(0); + } + heap_->WaitRunningTaskFinished(); +} + +void ConcurrentMarker::HandleMarkFinished() // js-thread wait for sweep +{ + os::memory::LockHolder lock(waitMarkingFinishedMutex_); + if (notifyMarkingFinished_) { + heap_->CollectGarbage(TriggerGCType::OLD_GC); + } +} + +void ConcurrentMarker::WaitConcurrentMarkingFinished() // call in EcmaVm thread, wait for mark finished +{ + os::memory::LockHolder lock(waitMarkingFinishedMutex_); + if (!notifyMarkingFinished_) { + vmThreadWaitMarkingFinished_ = true; + waitMarkingFinishedCV_.Wait(&waitMarkingFinishedMutex_); + } +} + +void ConcurrentMarker::Reset(bool isClearCSet) +{ + FinishPhase(); + thread_->SetMarkStatus(MarkStatus::READY_TO_MARK); + notifyMarkingFinished_ = false; + duration_ = 0.0; + if (isClearCSet) { + // Mix space gc clear cset when evacuation allocator finalize + const_cast(heap_->GetOldSpace())->ClearRegionFromCSet(); + } + heap_->EnumerateRegions([](Region *current) { + current->SetMarking(false); + current->ClearFlag(RegionFlags::IS_IN_PROMOTE_SET); + }); +} + +// -------------------- privete method ------------------------------------------ +void ConcurrentMarker::InitializeMarking() +{ + heap_->EnumerateRegions([](Region *region) { + // ensure mark bitmap + auto markBitmap = region->GetMarkBitmap(); + if (markBitmap == nullptr) { + region->GetOrCreateMarkBitmap(); + } else { + markBitmap->ClearAllBits(); + } + auto rememberset = region->GetCrossRegionRememberedSet(); + if (rememberset != nullptr) { + rememberset->ClearAllBits(); + } + region->SetMarking(true); + }); + if (heap_->IsSemiMarkNeeded()) { + heap_->EnumerateNewSpaceRegions([](Region *current) { + current->ResetAliveObject(); + }); + } else { + heap_->EnumerateRegions([](Region *current) { + current->ResetAliveObject(); + }); + } + workList_->Initialize(TriggerGCType::OLD_GC, ParallelGCTaskPhase::CONCURRENT_HANDLE_GLOBAL_POOL_TASK); + heap_->GetNonMovableMarker()->MarkRoots(0); +} + +bool ConcurrentMarker::MarkerTask::Run(uint32_t threadId) +{ + ClockScope clockScope; + heap_->GetNonMovableMarker()->ProcessMarkStack(threadId); + heap_->WaitRunningTaskFinished(); + heap_->GetConcurrentMarker()->MarkingFinished(); + heap_->GetConcurrentMarker()->SetDuration(clockScope.TotalSpentTime()); + return true; +} + +void ConcurrentMarker::MarkingFinished() +{ + os::memory::LockHolder lock(waitMarkingFinishedMutex_); + thread_->SetMarkStatus(MarkStatus::MARK_FINISHED); + if (vmThreadWaitMarkingFinished_) { + vmThreadWaitMarkingFinished_ = false; + waitMarkingFinishedCV_.Signal(); + } + notifyMarkingFinished_ = true; +} +} // namespace panda::ecmascript diff --git a/runtime/mem/concurrent_marker.h b/runtime/mem/concurrent_marker.h new file mode 100644 index 000000000..10216640a --- /dev/null +++ b/runtime/mem/concurrent_marker.h @@ -0,0 +1,95 @@ +/* + * Copyright (c) 2021-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_MEM_CONCURRENT_MARKER_H +#define ECMASCRIPT_MEM_CONCURRENT_MARKER_H + +#include +#include + +#include "plugins/ecmascript/runtime/mem/object_xray.h" +#include "plugins/ecmascript/runtime/mem/parallel_work_helper.h" +#include "plugins/ecmascript/runtime/mem/space.h" +#include "plugins/ecmascript/runtime/platform/task.h" + +#include "os/mutex.h" + +namespace panda::ecmascript { +class Heap; + +class ConcurrentMarker { +public: + explicit ConcurrentMarker(Heap *heap); + ~ConcurrentMarker() = default; + + void ConcurrentMarking(); + void FinishPhase(); + void ReMarking(); + + void HandleMarkFinished(); // call in vm thread. + void WaitConcurrentMarkingFinished(); // call in main thread + void Reset(bool isClearCSet = true); + + double GetDuration() const + { + return duration_; + } + + double GetHeapObjectSize() const + { + return heapObjectSize_; + } + +private: + NO_COPY_SEMANTIC(ConcurrentMarker); + NO_MOVE_SEMANTIC(ConcurrentMarker); + + class MarkerTask : public Task { + public: + explicit MarkerTask(Heap *heap) : heap_(heap) {} + ~MarkerTask() override = default; + bool Run(uint32_t threadId) override; + + private: + NO_COPY_SEMANTIC(MarkerTask); + NO_MOVE_SEMANTIC(MarkerTask); + + Heap *heap_ {nullptr}; + }; + + void SetDuration(double duration) + { + duration_ = duration; + } + + void InitializeMarking(); + void MarkingFinished(); + + Heap *heap_ {nullptr}; + EcmaVM *vm_ {nullptr}; + JSThread *thread_ {nullptr}; + + // obtain from heap + WorkerHelper *workList_ {nullptr}; + size_t heapObjectSize_ {0}; + double duration_ {0.0}; + + bool notifyMarkingFinished_ {false}; // notify js-thread that marking is finished and need sweep + bool vmThreadWaitMarkingFinished_ {false}; // jsMainThread waiting for concurrentGC FINISHED + os::memory::Mutex waitMarkingFinishedMutex_; + os::memory::ConditionVariable waitMarkingFinishedCV_; +}; +} // namespace panda::ecmascript +#endif // ECMASCRIPT_MEM_CONCURRENT_MARKER_H diff --git a/runtime/mem/concurrent_sweeper.cpp b/runtime/mem/concurrent_sweeper.cpp new file mode 100644 index 000000000..ebe2bbcf3 --- /dev/null +++ b/runtime/mem/concurrent_sweeper.cpp @@ -0,0 +1,258 @@ +/* + * Copyright (c) 2021-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 "plugins/ecmascript/runtime/mem/concurrent_sweeper.h" + +#include "plugins/ecmascript/runtime/js_hclass-inl.h" +#include "plugins/ecmascript/runtime/mem/allocator-inl.h" +#include "plugins/ecmascript/runtime/mem/free_object_list.h" +#include "plugins/ecmascript/runtime/mem/heap.h" +#include "plugins/ecmascript/runtime/mem/mem_manager.h" +#include "plugins/ecmascript/runtime/mem/space-inl.h" +#include "plugins/ecmascript/runtime/platform/platform.h" + +namespace panda::ecmascript { +ConcurrentSweeper::ConcurrentSweeper(Heap *heap, bool concurrentSweep) + : heap_(heap), concurrentSweep_(concurrentSweep) +{ +} + +void ConcurrentSweeper::SweepPhases(bool compressGC) +{ + if (concurrentSweep_) { + // Add all region to region list. Ensure all task finish + if (!compressGC) { + heap_->GetOldSpace()->EnumerateNonCollectRegionSet([this](Region *current) { + AddRegion(OLD_SPACE, current); + }); + } + heap_->GetNonMovableSpace()->EnumerateRegions([this](Region *current) { AddRegion(NON_MOVABLE, current); }); + heap_->GetMachineCodeSpace()->EnumerateRegions([this](Region *current) { + AddRegion(MACHINE_CODE_SPACE, current); + }); + + // Prepare + isSweeping_ = true; + startSpaceType_ = compressGC ? NON_MOVABLE : OLD_SPACE; + for (int type = startSpaceType_; type < FREE_LIST_NUM; type++) { + auto spaceType = static_cast(type); + FreeListAllocator &allocator = heap_->GetHeapManager()->GetFreeListAllocator(spaceType); + remainderTaskNum_[type] = FREE_LIST_NUM - startSpaceType_; + allocator.SetSweeping(true); + allocator.RebuildFreeList(); + } + + if (!compressGC) { + Platform::GetCurrentPlatform()->PostTask(std::make_unique(this, OLD_SPACE)); + canSelectCset_ = true; + } else { + canSelectCset_ = false; + } + Platform::GetCurrentPlatform()->PostTask(std::make_unique(this, NON_MOVABLE)); + Platform::GetCurrentPlatform()->PostTask(std::make_unique(this, MACHINE_CODE_SPACE)); + } else { + if (!compressGC) { + SweepSpace(OLD_SPACE, + const_cast(heap_->GetOldSpace()), heap_->GetHeapManager()->GetOldSpaceAllocator()); + canSelectCset_ = true; + } else { + canSelectCset_ = false; + } + SweepSpace(NON_MOVABLE, const_cast(heap_->GetNonMovableSpace()), + heap_->GetHeapManager()->GetNonMovableSpaceAllocator()); + SweepSpace(MACHINE_CODE_SPACE, const_cast(heap_->GetMachineCodeSpace()), + heap_->GetHeapManager()->GetMachineCodeSpaceAllocator()); + } + SweepHugeSpace(); +} + +void ConcurrentSweeper::SweepSpace(MemSpaceType type, bool isMain) +{ + FreeListAllocator &allocator = heap_->GetHeapManager()->GetFreeListAllocator(type); + Region *current = GetRegionSafe(type); + while (current != nullptr) { + FreeRegion(current, allocator, isMain); + // Main thread sweeping region is added; + if (!isMain) { + AddSweptRegionSafe(type, current); + } + current = GetRegionSafe(type); + } + + if (!isMain) { + os::memory::LockHolder holder(mutexs_[type]); + if (--remainderTaskNum_[type] == 0) { + cvs_[type].SignalAll(); + } + } +} + +void ConcurrentSweeper::SweepSpace(MemSpaceType type, Space *space, FreeListAllocator &allocator) +{ + allocator.RebuildFreeList(); + if (type == OLD_SPACE) { + auto *oldSpace = static_cast(space); + oldSpace->EnumerateNonCollectRegionSet([this, &allocator](Region *current) { + FreeRegion(current, allocator); + }); + } else { + space->EnumerateRegions([this, &allocator](Region *current) { + FreeRegion(current, allocator); + }); + } +} + +void ConcurrentSweeper::SweepHugeSpace() +{ + auto *space = const_cast(heap_->GetHugeObjectSpace()); + Region *currentRegion = space->GetRegionList().GetFirst(); + + while (currentRegion != nullptr) { + Region *next = currentRegion->GetNext(); + auto markBitmap = currentRegion->GetOrCreateMarkBitmap(); + bool isMarked = false; + markBitmap->IterateOverMarkedChunks([&isMarked]([[maybe_unused]] void *mem) { isMarked = true; }); + if (!isMarked) { + space->Free(currentRegion); + } + currentRegion = next; + } +} + +void ConcurrentSweeper::FreeRegion(Region *current, FreeListAllocator &allocator, bool isMain) +{ + auto markBitmap = current->GetOrCreateMarkBitmap(); + ASSERT(markBitmap != nullptr); + uintptr_t freeStart = current->GetBegin(); + markBitmap->IterateOverMarkedChunks([this, ¤t, &freeStart, &allocator, isMain](void *mem) { + ASSERT(current->InRange(ToUintPtr(mem))); + auto header = reinterpret_cast(mem); + auto klass = header->GetClass(); + auto size = klass->SizeFromJSHClass(header); + + uintptr_t freeEnd = ToUintPtr(mem); + if (freeStart != freeEnd) { + FreeLiveRange(allocator, current, freeStart, freeEnd, isMain); + } + freeStart = freeEnd + size; + }); + uintptr_t freeEnd = current->GetEnd(); + if (freeStart != freeEnd) { + FreeLiveRange(allocator, current, freeStart, freeEnd, isMain); + } +} + +void ConcurrentSweeper::FillSweptRegion(MemSpaceType type) +{ + if (sweptList_[type].empty()) { + return; + } + FreeListAllocator &allocator = heap_->GetHeapManager()->GetFreeListAllocator(type); + Region *region = nullptr; + while ((region = GetSweptRegionSafe(type)) != nullptr) { + region->EnumerateKinds([&allocator](FreeObjectKind *kind) { + if (kind == nullptr || kind->Empty()) { + return; + } + allocator.FillFreeList(kind); + }); + } +} + +void ConcurrentSweeper::FreeLiveRange(FreeListAllocator &allocator, Region *current, uintptr_t freeStart, + uintptr_t freeEnd, bool isMain) +{ + heap_->ClearSlotsRange(current, freeStart, freeEnd); + allocator.Free(freeStart, freeEnd, isMain); +} + +void ConcurrentSweeper::AddRegion(MemSpaceType type, Region *region) +{ + sweepingList_[type].emplace_back(region); +} + +Region *ConcurrentSweeper::GetRegionSafe(MemSpaceType type) +{ + os::memory::LockHolder holder(mutexs_[type]); + Region *region = nullptr; + if (!sweepingList_[type].empty()) { + region = sweepingList_[type].back(); + sweepingList_[type].pop_back(); + } + return region; +} + +void ConcurrentSweeper::AddSweptRegionSafe(MemSpaceType type, Region *region) +{ + os::memory::LockHolder holder(mutexs_[type]); + sweptList_[type].emplace_back(region); +} + +Region *ConcurrentSweeper::GetSweptRegionSafe(MemSpaceType type) +{ + os::memory::LockHolder holder(mutexs_[type]); + Region *region = nullptr; + if (!sweptList_[type].empty()) { + region = sweptList_[type].back(); + sweptList_[type].pop_back(); + } + return region; +} + +void ConcurrentSweeper::EnsureAllTaskFinished() +{ + if (!isSweeping_) { + return; + } + for (int i = startSpaceType_; i < FREE_LIST_NUM; i++) { + WaitingTaskFinish(static_cast(i)); + } + isSweeping_ = false; +} + +void ConcurrentSweeper::WaitingTaskFinish(MemSpaceType type) +{ + if (remainderTaskNum_[type] > 0) { + SweepSpace(type); + { + os::memory::LockHolder holder(mutexs_[type]); + while (remainderTaskNum_[type] > 0) { + cvs_[type].Wait(&mutexs_[type]); + } + } + } + FinishSweeping(type); +} + +void ConcurrentSweeper::FinishSweeping(MemSpaceType type) +{ + FillSweptRegion(type); + FreeListAllocator &allocator = heap_->GetHeapManager()->GetFreeListAllocator(type); + allocator.SetSweeping(false); + if (type == OLD_SPACE) { + heap_->RecomputeLimits(); + } +} + +bool ConcurrentSweeper::SweeperTask::Run([[maybe_unused]] uint32_t threadIndex) +{ + int sweepTypeNum = FREE_LIST_NUM - sweeper_->startSpaceType_; + for (size_t i = sweeper_->startSpaceType_; i < FREE_LIST_NUM; i++) { + auto type = static_cast(((i + type_) % sweepTypeNum) + sweeper_->startSpaceType_); + sweeper_->SweepSpace(type, false); + } + return true; +} +} // namespace panda::ecmascript diff --git a/runtime/mem/concurrent_sweeper.h b/runtime/mem/concurrent_sweeper.h new file mode 100644 index 000000000..014118a3c --- /dev/null +++ b/runtime/mem/concurrent_sweeper.h @@ -0,0 +1,99 @@ +/* + * Copyright (c) 2021-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_MEM_CONCURRENT_SWEEPER_H +#define ECMASCRIPT_MEM_CONCURRENT_SWEEPER_H + +#include +#include + +#include "plugins/ecmascript/runtime/mem/space.h" +#include "plugins/ecmascript/runtime/platform/task.h" +#include "os/mutex.h" + +namespace panda::ecmascript { +class FreeListAllocator; + +class ConcurrentSweeper { +public: + ConcurrentSweeper(Heap *heap, bool concurrentSweep); + ~ConcurrentSweeper() = default; + + NO_COPY_SEMANTIC(ConcurrentSweeper); + NO_MOVE_SEMANTIC(ConcurrentSweeper); + + void SweepPhases(bool compressGC = false); + + void EnsureAllTaskFinished(); + // Ensure task finish + void WaitingTaskFinish(MemSpaceType type); + + void FillSweptRegion(MemSpaceType type); + + bool IsConcurrentSweepEnabled() + { + return concurrentSweep_; + } + + bool CanSelectCset() const + { + return canSelectCset_; + } + +private: + class SweeperTask : public Task { + public: + SweeperTask(ConcurrentSweeper *sweeper, MemSpaceType type) : sweeper_(sweeper), type_(type) {}; + ~SweeperTask() override = default; + bool Run(uint32_t threadIndex) override; + + NO_COPY_SEMANTIC(SweeperTask); + NO_MOVE_SEMANTIC(SweeperTask); + + private: + ConcurrentSweeper *sweeper_; + MemSpaceType type_; + }; + + void SweepSpace(MemSpaceType type, bool isMain = true); + void SweepSpace(MemSpaceType type, Space *space, FreeListAllocator &allocator); + void SweepHugeSpace(); + void FinishSweeping(MemSpaceType type); + + void FreeRegion(Region *current, FreeListAllocator &allocator, bool isMain = true); + void FreeLiveRange(FreeListAllocator &allocator, Region *current, uintptr_t freeStart, uintptr_t freeEnd, + bool isMain); + + void AddRegion(MemSpaceType type, Region *region); + Region *GetRegionSafe(MemSpaceType type); + + void AddSweptRegionSafe(MemSpaceType type, Region *region); + Region *GetSweptRegionSafe(MemSpaceType type); + + std::array mutexs_; + std::array cvs_; + std::array remainderTaskNum_ = {0, 0, 0}; + + std::array, FREE_LIST_NUM> sweepingList_; + std::array, FREE_LIST_NUM> sweptList_; + + Heap *heap_; + bool concurrentSweep_ {false}; + bool isSweeping_ {false}; + bool canSelectCset_ {false}; + MemSpaceType startSpaceType_ = MemSpaceType::OLD_SPACE; +}; +} // namespace panda::ecmascript +#endif // ECMASCRIPT_MEM_CONCURRENT_SWEEPER_H diff --git a/runtime/mem/ecma_list.h b/runtime/mem/ecma_list.h new file mode 100644 index 000000000..481059071 --- /dev/null +++ b/runtime/mem/ecma_list.h @@ -0,0 +1,155 @@ +/* + * Copyright (c) 2021-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_MEM_ECMALIST_H +#define ECMASCRIPT_MEM_ECMALIST_H + +#include "plugins/ecmascript/runtime/mem/mem.h" + +namespace panda::ecmascript { +// Invoking std::list will cause cross invoking, which is time-consuming. +// Therefore, we implement ecma list inside the vm. + +template +class EcmaList { +public: + EcmaList() : first_(nullptr), last_(nullptr) {} + + explicit EcmaList(T *node) : first_(node), last_(node) + { + node->LinkPrev(nullptr); + node->LinkNext(nullptr); + } + ~EcmaList() = default; + NO_COPY_SEMANTIC(EcmaList); + NO_MOVE_SEMANTIC(EcmaList); + void AddNode(T *node) + { + ASSERT(node != nullptr); + if (LIKELY(first_ != nullptr)) { + T *lastNext = last_->GetNext(); + node->LinkNext(lastNext); + node->LinkPrev(last_); + last_->LinkNext(node); + if (lastNext) { + lastNext->LinkPrev(node); + } else { + last_ = node; + } + } else { + node->LinkPrev(nullptr); + node->LinkNext(nullptr); + first_ = last_ = node; + } + length_++; + } + + void AddNodeToFirst(T *node) + { + ASSERT(node != nullptr); + if (LIKELY(last_ != nullptr)) { + node->LinkNext(first_); + node->LinkPrev(first_->GetPrev()); + first_->LinkPrev(node); + first_ = node; + } else { + node->LinkPrev(nullptr); + node->LinkNext(nullptr); + first_ = last_ = node; + } + length_++; + } + + T *PopBack() + { + T *node = last_; + RemoveNode(last_); + return node; + } + + void RemoveNode(T *node) + { + ASSERT(HasNode(node)); + if (last_ == node) { + // NOLINTNEXTLINE(clang-analyzer-core.CallAndMessage) + last_ = node->GetPrev(); + } + if (first_ == node) { + // NOLINTNEXTLINE(clang-analyzer-core.CallAndMessage) + first_ = node->GetNext(); + } + // NOLINTNEXTLINE(clang-analyzer-core.CallAndMessage) + T *next = node->GetNext(); + // NOLINTNEXTLINE(clang-analyzer-core.CallAndMessage) + T *prev = node->GetPrev(); + if (next != nullptr) { + next->LinkPrev(prev); + } + if (prev != nullptr) { + prev->LinkNext(next); + } + // NOLINTNEXTLINE(clang-analyzer-core.CallAndMessage) + node->LinkPrev(nullptr); + // NOLINTNEXTLINE(clang-analyzer-core.CallAndMessage) + node->LinkNext(nullptr); + length_--; + } + + bool HasNode(T *node) + { + T *it = first_; + while (it != nullptr) { + if (it == node) { + return true; + } + it = it->GetNext(); + } + return false; + } + + T *GetFirst() const + { + return first_; + } + + T *GetLast() const + { + return last_; + } + + bool IsEmpty() const + { + return last_ == nullptr; + } + + void Clear() + { + first_ = last_ = nullptr; + length_ = 0; + } + + uint32_t GetLength() const + { + return length_; + } + +private: + T *first_; + T *last_; + uint32_t length_{0}; +}; +} // namespace panda::ecmascript + +#endif // ECMASCRIPT_MEM_ECMALIST_H diff --git a/runtime/mem/ecma_reference_processor.cpp b/runtime/mem/ecma_reference_processor.cpp new file mode 100644 index 000000000..df0a76bdf --- /dev/null +++ b/runtime/mem/ecma_reference_processor.cpp @@ -0,0 +1,137 @@ +/* + * Copyright (c) 2021-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 "ecma_reference_processor.h" +#include "mem/object_helpers.h" +#include "runtime/include/coretypes/class.h" +#include "runtime/mem/gc/gc.h" +#include "plugins/ecmascript/runtime/ecma_vm.h" +#include "plugins/ecmascript/runtime/js_hclass.h" +#include "plugins/ecmascript/runtime/linked_hash_table-inl.h" +#include "plugins/ecmascript/runtime/mem/tagged_object.h" + +namespace panda::mem::ecmascript { + +EcmaReferenceProcessor::EcmaReferenceProcessor(panda::ecmascript::EcmaVM *vm) : vm_(vm), gc_(vm->GetGC()) {} + +template +bool EnumerateKeys(Container *container, const Callback &cb) +{ + int total_elements = container->NumberOfElements() + container->NumberOfDeletedElements(); + for (int i = 0; i < total_elements; ++i) { + panda::ecmascript::JSTaggedValue key = container->GetKey(i); + if (key.IsHole()) { + continue; + } + ASSERT(key.IsHeapObject()); + if (cb(i, key.GetHeapObject())) { + return true; + } + } + return false; +} + +bool EcmaReferenceProcessor::IsReference([[maybe_unused]] const BaseClass *base_cls, const ObjectHeader *ref, + const ReferenceCheckPredicateT &ref_pred) const +{ + auto phase = gc_->GetGCPhase(); + ASSERT(IsMarking(phase)); + if (gc_->GetType() == GCType::G1_GC && (phase == GCPhase::GC_PHASE_MARK || phase == GCPhase::GC_PHASE_REMARK) && + !gc_->IsFullGC()) { + // dont process refs on conc-mark in G1, it can cause a high pause + LOG(DEBUG, REF_PROC) << "Skip reference: " << ref << " because it's G1 with phase: " << static_cast(phase); + return false; + } + ASSERT(ref->ClassAddr()->IsDynamicClass()); + + static_assert(panda::ecmascript::JSHClass::GetHClassOffset() == panda::coretypes::Class::GetRuntimeClassOffset()); + auto *as_hcls = reinterpret_cast(ref); + if ((as_hcls->GetHClass()->GetFlags() & panda::Class::DYNAMIC_CLASSROOT) != 0) { + return false; + } + + auto *hcls = panda::ecmascript::JSHClass::FromHClass(ref->ClassAddr()); + if (!hcls->IsWeakContainer()) { + return false; + } + auto is_reference_checker = [this, &ref_pred](int /* unused */, ObjectHeader *key) { + return ref_pred(key) && !gc_->IsMarked(key); + }; + auto object_type = hcls->GetObjectType(); + switch (object_type) { + case panda::ecmascript::JSType::LINKED_HASH_MAP: + return EnumerateKeys(static_cast(ref), is_reference_checker); + case panda::ecmascript::JSType::LINKED_HASH_SET: + return EnumerateKeys(static_cast(ref), is_reference_checker); + default: + LOG(FATAL, REF_PROC) << "Unknown weak container"; + } + return false; +} + +void EcmaReferenceProcessor::HandleReference([[maybe_unused]] GC *gc, [[maybe_unused]] GCMarkingStackType *objectsStack, + [[maybe_unused]] const BaseClass *base_class, const ObjectHeader *object, + [[maybe_unused]] const ReferenceProcessPredicateT &pred) +{ + dyn_weak_references_.insert(const_cast(object)); +} + +void EcmaReferenceProcessor::ProcessReferences([[maybe_unused]] bool concurrent, + [[maybe_unused]] bool clear_soft_references, + [[maybe_unused]] GCPhase gc_phase, + [[maybe_unused]] const mem::GC::ReferenceClearPredicateT &pred) +{ + panda::ecmascript::JSThread *thread = vm_->GetAssociatedJSThread(); + while (!dyn_weak_references_.empty()) { + ObjectHeader *reference = *dyn_weak_references_.begin(); + dyn_weak_references_.erase(*dyn_weak_references_.begin()); + auto *hcls = panda::ecmascript::JSHClass::FromHClass(reference->ClassAddr()); + ASSERT(hcls->IsWeakContainer()); + auto object_type = hcls->GetObjectType(); + if (object_type == panda::ecmascript::JSType::LINKED_HASH_MAP) { + auto *map = static_cast(reference); + auto map_handler = [this, map, thread](int index, ObjectHeader *key) { + if (!gc_->IsMarked(key)) { + map->RemoveEntry(thread, index); + } + return false; + }; + EnumerateKeys(map, map_handler); + } else if (object_type == panda::ecmascript::JSType::LINKED_HASH_SET) { + auto *set = static_cast(reference); + auto set_handler = [this, set, thread](int index, ObjectHeader *key) { + if (!gc_->IsMarked(key)) { + set->RemoveEntry(thread, index); + } + return false; + }; + EnumerateKeys(set, set_handler); + } else { + LOG(FATAL, REF_PROC) << "Unknown weak container"; + } + } + + panda::ecmascript::WeakRootVisitor gcUpdateWeak = + [this](panda::ecmascript::TaggedObject *header) -> panda::ecmascript::TaggedObject * { + if (gc_->InGCSweepRange(header) && !gc_->IsMarked(header)) { + return nullptr; + } + + return header; + }; + vm_->ProcessReferences(gcUpdateWeak); +} + +} // namespace panda::mem::ecmascript diff --git a/runtime/mem/ecma_reference_processor.h b/runtime/mem/ecma_reference_processor.h new file mode 100644 index 000000000..ccda21737 --- /dev/null +++ b/runtime/mem/ecma_reference_processor.h @@ -0,0 +1,79 @@ +/* + * Copyright (c) 2021-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 PLUGINS_ECMASCRIPT_RUNTIME_MEM_ECMA_REFERENCE_PROCESSOR_H +#define PLUGINS_ECMASCRIPT_RUNTIME_MEM_ECMA_REFERENCE_PROCESSOR_H + +#include "runtime/mem/gc/reference-processor/reference_processor.h" + +namespace panda { +class ObjectHeader; +class BaseClass; +namespace mem { +enum class GCPhase; +class Reference; +class GC; +} // namespace mem +namespace ecmascript { +class EcmaVM; +} // namespace ecmascript +} // namespace panda + +namespace panda::mem::ecmascript { + +/** + * Mechanism for processing JS weak references + */ +class EcmaReferenceProcessor : public panda::mem::ReferenceProcessor { +public: + explicit EcmaReferenceProcessor(panda::ecmascript::EcmaVM *vm); + + bool IsReference(const BaseClass *base_cls, const ObjectHeader *ref, + const ReferenceCheckPredicateT &ref_pred) const override; + + void HandleReference(GC *gc, GCMarkingStackType *objectsStack, const BaseClass *base_class, + const ObjectHeader *object, const ReferenceProcessPredicateT &pred) override; + + void ProcessReferences(bool concurrent, bool clear_soft_references, GCPhase gc_phase, + const mem::GC::ReferenceClearPredicateT &pred) override; + + panda::mem::Reference *CollectClearedReferences() override + { + return nullptr; + } + + void ScheduleForEnqueue([[maybe_unused]] Reference *cleared_references) override + { + UNREACHABLE(); + } + + void Enqueue([[maybe_unused]] panda::mem::Reference *cleared_references) override + { + UNREACHABLE(); + } + + size_t GetReferenceQueueSize() const override + { + return dyn_weak_references_.size(); + } + +private: + PandaUnorderedSet dyn_weak_references_; + panda::ecmascript::EcmaVM *vm_; + GC *gc_; +}; + +} // namespace panda::mem::ecmascript +#endif // PLUGINS_ECMASCRIPT_RUNTIME_MEM_ECMA_REFERENCE_PROCESSOR_H diff --git a/runtime/mem/evacuation_allocator-inl.h b/runtime/mem/evacuation_allocator-inl.h new file mode 100644 index 000000000..915a7ca4a --- /dev/null +++ b/runtime/mem/evacuation_allocator-inl.h @@ -0,0 +1,46 @@ +/* + * Copyright (c) 2021-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_MEM_EVACUATION_ALLOCATOR_INL_H +#define ECMASCRIPT_MEM_EVACUATION_ALLOCATOR_INL_H + +#include "plugins/ecmascript/runtime/mem/evacuation_allocator.h" + +#include "plugins/ecmascript/runtime/mem/heap-inl.h" + +namespace panda::ecmascript { +Region *EvacuationAllocator::ExpandOldSpace() +{ + os::memory::LockHolder lock(oldAllocatorLock_); + return heap_->ExpandCompressSpace(); +} + +void EvacuationAllocator::FreeSafe(uintptr_t begin, uintptr_t end) +{ + os::memory::LockHolder lock(oldAllocatorLock_); + oldSpaceAllocator_.Free(begin, end); +} + +void EvacuationAllocator::Free(uintptr_t begin, uintptr_t end, bool isAdd) +{ + oldSpaceAllocator_.Free(begin, end, isAdd); +} + +void EvacuationAllocator::FillFreeList(FreeObjectKind *kind) +{ + oldSpaceAllocator_.FillFreeList(kind); +} +} // namespace panda::ecmascript +#endif // ECMASCRIPT_MEM_EVACUATION_ALLOCATOR_INL_H diff --git a/runtime/mem/evacuation_allocator.cpp b/runtime/mem/evacuation_allocator.cpp new file mode 100644 index 000000000..5eaebb615 --- /dev/null +++ b/runtime/mem/evacuation_allocator.cpp @@ -0,0 +1,157 @@ +/* + * Copyright (c) 2021-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 "plugins/ecmascript/runtime/mem/evacuation_allocator-inl.h" + +#include "plugins/ecmascript/runtime/js_hclass-inl.h" +#include "plugins/ecmascript/runtime/mem/mem_manager.h" +#include "plugins/ecmascript/runtime/mem/free_object_kind.h" +#include "plugins/ecmascript/runtime/mem/mark_word.h" +#include "plugins/ecmascript/runtime/mem/space.h" +#include "plugins/ecmascript/runtime/platform/platform.h" + +namespace panda::ecmascript { +void EvacuationAllocator::Initialize(TriggerGCType type) +{ + // Reset new space allocator + auto heapManager = heap_->GetHeapManager(); + heap_->GetNewSpace()->GetCurrentRegion()->SetHighWaterMark(heapManager->GetNewSpaceAllocator().GetTop()); + heap_->InitializeFromSpace(); + auto fromSpace = heap_->GetFromSpace(); + newSpaceAllocator_.Reset(fromSpace); + heap_->FlipNewSpace(); + // Reset old space allocator + if (type == TriggerGCType::OLD_GC) { + oldSpaceAllocator_.Reset(heap_); + } else if (type == TriggerGCType::COMPRESS_FULL_GC) { + heap_->InitializeCompressSpace(); + auto compressSpace = const_cast(heap_->GetCompressSpace()); + auto currentRegion = compressSpace->GetCurrentRegion(); + currentRegion->SetAliveObject(currentRegion->GetSize()); + oldSpaceAllocator_.Reset(compressSpace); + } else { + oldSpaceAllocator_.Swap(heapManager->GetOldSpaceAllocator()); + } +} + +void EvacuationAllocator::Finalize(TriggerGCType type) +{ + // Swap + auto heapManager = heap_->GetHeapManager(); + if (type == TriggerGCType::OLD_GC) { + auto oldSpace = const_cast(heap_->GetOldSpace()); + oldSpace->RemoveCSetFromList(); + oldSpace->Merge(const_cast(heap_->GetCompressSpace())); + heapManager->GetOldSpaceAllocator().Merge(&oldSpaceAllocator_); + } else { + if (type == TriggerGCType::COMPRESS_FULL_GC) { + heap_->FlipCompressSpace(); + } + heapManager->GetOldSpaceAllocator().Swap(oldSpaceAllocator_); + } + heapManager->GetNewSpaceAllocator().Swap(newSpaceAllocator_); + + // Reclaim Region + if (heap_->IsParallelGCEnabled()) { + isFreeTaskFinish_ = false; + Platform::GetCurrentPlatform()->PostTask(std::make_unique(this, type)); + } else { + ReclaimRegions(type); + } + + heap_->SetNewSpaceAgeMark(newSpaceAllocator_.GetTop()); +} + +bool EvacuationAllocator::AddRegionToOld(Region *region) +{ + if (!region->InYoungGeneration()) { + os::memory::LockHolder lock(oldAllocatorLock_); + const_cast(heap_->GetOldSpace())->RemoveRegionFromCSetAndList(region); + return heap_->AddRegionToCompressSpace(region); + } + { + os::memory::LockHolder lock(youngAllocatorLock_); + const_cast(heap_->GetFromSpace())->RemoveRegion(region); + } + os::memory::LockHolder lock(oldAllocatorLock_); + return heap_->AddRegionToCompressSpace(region); +} + +bool EvacuationAllocator::AddRegionToYoung(Region *region) +{ + ASSERT(region->InYoungGeneration()); + os::memory::LockHolder lock(youngAllocatorLock_); + const_cast(heap_->GetFromSpace())->RemoveRegion(region); + return heap_->AddRegionToToSpace(region); +} + +uintptr_t EvacuationAllocator::AllocateOld(size_t size) +{ + os::memory::LockHolder lock(oldAllocatorLock_); + uintptr_t result = oldSpaceAllocator_.Allocate(size); + if (UNLIKELY(result == 0)) { + // Compress bugfix + if (!heap_->FillOldSpaceAndTryGC(&oldSpaceAllocator_, false)) { + return 0; + } + result = oldSpaceAllocator_.Allocate(size); + } + return result; +} + +uintptr_t EvacuationAllocator::AllocateYoung(size_t size) +{ + os::memory::LockHolder lock(youngAllocatorLock_); + uintptr_t result = newSpaceAllocator_.Allocate(size); + if (UNLIKELY(result == 0)) { + if (!heap_->FillNewSpaceAndTryGC(&newSpaceAllocator_, false)) { + return 0; + } + result = newSpaceAllocator_.Allocate(size); + } + return result; +} + +void EvacuationAllocator::ReclaimRegions(TriggerGCType gcType) +{ + if (gcType == TriggerGCType::COMPRESS_FULL_GC) { + const_cast(heap_->GetCompressSpace())->ReclaimRegions(); + } else if (gcType == TriggerGCType::OLD_GC) { + const_cast(heap_->GetOldSpace())->ReclaimRegionCSet(); + } + const_cast(heap_->GetFromSpace())->ReclaimRegions(); + if (!isFreeTaskFinish_) { + os::memory::LockHolder holder(mutex_); + isFreeTaskFinish_ = true; + condition_.SignalAll(); + } +} + +void EvacuationAllocator::WaitFreeTaskFinish() +{ + if (!isFreeTaskFinish_) { + os::memory::LockHolder holder(mutex_); + while (!isFreeTaskFinish_) { + condition_.Wait(&mutex_); + } + } +} + +bool EvacuationAllocator::AsyncFreeRegionTask::Run([[maybe_unused]] uint32_t threadIndex) +{ + allocator_->ReclaimRegions(gcType_); + return true; +} +} // namespace panda::ecmascript diff --git a/runtime/mem/evacuation_allocator.h b/runtime/mem/evacuation_allocator.h new file mode 100644 index 000000000..6b794f534 --- /dev/null +++ b/runtime/mem/evacuation_allocator.h @@ -0,0 +1,84 @@ +/* + * Copyright (c) 2021-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_MEM_EVACUATION_ALLOCATOR_H +#define ECMASCRIPT_MEM_EVACUATION_ALLOCATOR_H + +#include "plugins/ecmascript/runtime/mem/allocator.h" +#include "plugins/ecmascript/runtime/mem/free_object_list.h" +#include "plugins/ecmascript/runtime/mem/heap.h" +#include "plugins/ecmascript/runtime/mem/mem_controller.h" +#include "plugins/ecmascript/runtime/platform/task.h" +#include "os/mutex.h" + +namespace panda::ecmascript { +class Heap; +class EvacuationAllocator { +public: + explicit EvacuationAllocator(Heap *heap) : heap_(heap) {}; + ~EvacuationAllocator() = default; + NO_COPY_SEMANTIC(EvacuationAllocator); + NO_MOVE_SEMANTIC(EvacuationAllocator); + + void Initialize(TriggerGCType type); + void Finalize(TriggerGCType type); + + inline uintptr_t GetNewSpaceTop() + { + return newSpaceAllocator_.GetTop(); + } + + bool AddRegionToOld(Region *region); + bool AddRegionToYoung(Region *region); + + inline Region *ExpandOldSpace(); + uintptr_t AllocateOld(size_t size); + uintptr_t AllocateYoung(size_t size); + + inline void FreeSafe(uintptr_t begin, uintptr_t end); + inline void Free(uintptr_t begin, uintptr_t end, bool isAdd = true); + inline void FillFreeList(FreeObjectKind *kind); + + void ReclaimRegions(TriggerGCType type); + + void WaitFreeTaskFinish(); + +private: + class AsyncFreeRegionTask : public Task { + public: + AsyncFreeRegionTask(EvacuationAllocator *allocator, TriggerGCType type) + : allocator_(allocator), gcType_(type) {} + ~AsyncFreeRegionTask() override = default; + bool Run(uint32_t threadIndex) override; + + NO_COPY_SEMANTIC(AsyncFreeRegionTask); + NO_MOVE_SEMANTIC(AsyncFreeRegionTask); + private: + EvacuationAllocator *allocator_; + TriggerGCType gcType_; + }; + + Heap *heap_; + bool isFreeTaskFinish_ = true; + BumpPointerAllocator newSpaceAllocator_; + FreeListAllocator oldSpaceAllocator_; + os::memory::Mutex youngAllocatorLock_; + os::memory::Mutex oldAllocatorLock_; + // Async free region task + os::memory::Mutex mutex_; + os::memory::ConditionVariable condition_; +}; +} // namespace panda::ecmascript +#endif // ECMASCRIPT_MEM_EVACUATION_ALLOCATOR_H diff --git a/runtime/mem/free_object_kind.cpp b/runtime/mem/free_object_kind.cpp new file mode 100644 index 000000000..fee3869f2 --- /dev/null +++ b/runtime/mem/free_object_kind.cpp @@ -0,0 +1,71 @@ +/* + * Copyright (c) 2021-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 "plugins/ecmascript/runtime/mem/free_object_kind.h" + +#include "plugins/ecmascript/runtime/free_object.h" +#include "plugins/ecmascript/runtime/mem/free_object_list.h" + +namespace panda::ecmascript { +void FreeObjectKind::Free(uintptr_t begin, size_t size) +{ + auto freeObject = FreeObject::Cast(begin); + freeObject->SetNext(freeObject_); + freeObject_ = freeObject; + available_ += size; +} + +void FreeObjectKind::Rebuild() +{ + freeObject_ = nullptr; + available_ = 0; + isAdded_ = false; + next_ = nullptr; + prev_ = nullptr; +} + +FreeObject *FreeObjectKind::SearchSmallFreeObject(size_t size) +{ + FreeObject *curFreeObject = nullptr; + if (freeObject_ != nullptr && freeObject_->Available() >= size) { + curFreeObject = freeObject_; + freeObject_ = freeObject_->GetNext(); + curFreeObject->SetNext(nullptr); + available_ -= curFreeObject->Available(); + } + return curFreeObject; +} + +FreeObject *FreeObjectKind::SearchLargeFreeObject(size_t size) +{ + FreeObject *prevFreeObject = freeObject_; + FreeObject *curFreeObject = freeObject_; + while (curFreeObject != nullptr) { + if (curFreeObject->Available() >= size) { + if (curFreeObject == freeObject_) { + freeObject_ = curFreeObject->GetNext(); + } else { + prevFreeObject->SetNext(curFreeObject->GetNext()); + } + curFreeObject->SetNext(nullptr); + available_ -= curFreeObject->Available(); + return curFreeObject; + } + prevFreeObject = curFreeObject; + curFreeObject = curFreeObject->GetNext(); + } + return nullptr; +} +} // namespace panda::ecmascript diff --git a/runtime/mem/free_object_kind.h b/runtime/mem/free_object_kind.h new file mode 100644 index 000000000..046a16df1 --- /dev/null +++ b/runtime/mem/free_object_kind.h @@ -0,0 +1,69 @@ +/* + * Copyright (c) 2021-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_MEM_FREE_OBJECT_KIND_H +#define ECMASCRIPT_MEM_FREE_OBJECT_KIND_H + +#include + +#include "libpandabase/macros.h" + +namespace panda::ecmascript { +using KindType = int32_t; + +class FreeObject; + +class FreeObjectKind { +public: + explicit FreeObjectKind(KindType type) : kindType_(type) + { + Rebuild(); + } + ~FreeObjectKind() = default; + + inline bool Empty() const + { + return available_ == 0; + } + + inline size_t Available() const + { + return available_; + } + + void Free(uintptr_t begin, size_t size); + + void Rebuild(); + + FreeObject *SearchSmallFreeObject(size_t size); + FreeObject *SearchLargeFreeObject(size_t size); + + NO_COPY_SEMANTIC(FreeObjectKind); + NO_MOVE_SEMANTIC(FreeObjectKind); + + static constexpr KindType INVALID_KIND_TYPE = -1; + +private: + FreeObjectKind *next_ = nullptr; + FreeObjectKind *prev_ = nullptr; + KindType kindType_ = INVALID_KIND_TYPE; + size_t available_ = 0; + bool isAdded_ = false; + FreeObject *freeObject_ = nullptr; + + friend class FreeObjectList; +}; +} // namespace panda::ecmascript +#endif // ECMASCRIPT_MEM_FREE_OBJECT_LIST_H diff --git a/runtime/mem/free_object_list-inl.h b/runtime/mem/free_object_list-inl.h new file mode 100644 index 000000000..908c41fb9 --- /dev/null +++ b/runtime/mem/free_object_list-inl.h @@ -0,0 +1,57 @@ +/* + * Copyright (c) 2021-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_MEM_FREE_OBJECT_LIST_INL_H +#define ECMASCRIPT_MEM_FREE_OBJECT_LIST_INL_H + +#include "plugins/ecmascript/runtime/mem/free_object_list.h" + +#include + +namespace panda::ecmascript { +KindType FreeObjectList::SelectKindType(size_t size) const +{ + if (size < SMALL_KIND_MAX_SIZE) { + if (UNLIKELY(size < MIN_SIZE)) { + return FreeObjectKind::INVALID_KIND_TYPE; + } + return (size >> INTERVAL_OFFSET) - smallKindOffsetIndex; + } + if (size < LARGE_KIND_MAX_SIZE) { + return MAX_BIT_OF_SIZET - __builtin_clzl(size) + LOG2_OFFSET; + } + if (size >= HUGE_KIND_MAX_SIZE) { + return NUMBER_OF_LAST_HUGE; + } + + return NUMBER_OF_LAST_LARGE; +} + +void FreeObjectList::SetNoneEmptyBit(KindType type) +{ + noneEmptyKindBitMap_ |= 1ULL << static_cast(type); +} + +void FreeObjectList::ClearNoneEmptyBit(KindType type) +{ + noneEmptyKindBitMap_ &= ~(1ULL << static_cast(type)); +} + +inline size_t FreeObjectList::CalcNextNoneEmptyIndex(KindType start) +{ + return __builtin_ffsll(noneEmptyKindBitMap_ >> static_cast(start)) + start - 1; +} +} // namespace panda::ecmascript +#endif // ECMASCRIPT_MEM_FREE_OBJECT_LIST_INL_H diff --git a/runtime/mem/free_object_list.cpp b/runtime/mem/free_object_list.cpp new file mode 100644 index 000000000..bf0a1684e --- /dev/null +++ b/runtime/mem/free_object_list.cpp @@ -0,0 +1,230 @@ +/* + * Copyright (c) 2021-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 "plugins/ecmascript/runtime/mem/free_object_list.h" + +#include "plugins/ecmascript/runtime/free_object.h" +#include "plugins/ecmascript/runtime/mem/free_object_kind.h" +#include "plugins/ecmascript/runtime/mem/free_object_list-inl.h" +#include "plugins/ecmascript/runtime/mem/mem.h" + +namespace panda::ecmascript { +FreeObjectList::FreeObjectList() : kinds_(new FreeObjectKind *[NUMBER_OF_KINDS](), NUMBER_OF_KINDS), + lastKinds_(new FreeObjectKind *[NUMBER_OF_KINDS](), NUMBER_OF_KINDS) +{ + for (int i = 0; i < NUMBER_OF_KINDS; i++) { + kinds_[i] = nullptr; + lastKinds_[i] = nullptr; + } + noneEmptyKindBitMap_ = 0; +} + +FreeObjectList::~FreeObjectList() +{ + delete[] kinds_.data(); + delete[] lastKinds_.data(); + noneEmptyKindBitMap_ = 0; +} + +FreeObject *FreeObjectList::Allocator(size_t size) +{ + if (noneEmptyKindBitMap_ == 0) { + return nullptr; + } + size = AlignUp(size, static_cast(MemAlignment::MEM_ALIGN_OBJECT)); + // find from suitable + KindType type = SelectKindType(size); + if (type == FreeObjectKind::INVALID_KIND_TYPE) { + return nullptr; + } + + KindType lastType = type - 1; + for (type = CalcNextNoneEmptyIndex(type); type > lastType && type < NUMBER_OF_KINDS; + type = CalcNextNoneEmptyIndex(type + 1)) { + lastType = type; + FreeObjectKind *current = kinds_[type]; + while (current != nullptr) { + if (current->Available() < size) { + current = current->next_; + continue; + } + FreeObjectKind *next = nullptr; + FreeObject *object = nullptr; + if (type <= SMALL_KIND_MAX_INDEX) { + object = current->SearchSmallFreeObject(size); + } else { + next = current->next_; + object = current->SearchLargeFreeObject(size); + } + if (current->Empty()) { + RemoveKind(current); + } + if (object != nullptr) { + size_t objectSize = object->Available(); + available_ -= objectSize; + if (objectSize >= size) { + return object; + } + } + current = next; + } + } + return nullptr; +} + +void FreeObjectList::Free(uintptr_t start, size_t size, bool isAdd) +{ + if (start == 0 || size == 0) { + return; + } + size = AlignUp(size, static_cast(MemAlignment::MEM_ALIGN_OBJECT)); + KindType type = SelectKindType(size); + if (type == FreeObjectKind::INVALID_KIND_TYPE) { + return; + } + + Region *region = Region::ObjectAddressToRange(reinterpret_cast(start)); + auto kind = region->GetFreeObjectKind(type); + if (kind == nullptr) { + LOG_ECMA(FATAL) << "The kind of region is nullptr"; + return; + } + kind->Free(start, size); + + if (isAdd) { + if (kind->isAdded_) { + available_ += size; + } else { + AddKind(kind); + } + } +} + +void FreeObjectList::Rebuild() +{ + EnumerateKinds([](FreeObjectKind *kind) { kind->Rebuild(); }); + for (int i = 0; i < NUMBER_OF_KINDS; i++) { + kinds_[i] = nullptr; + lastKinds_[i] = nullptr; + } + available_ = 0; + noneEmptyKindBitMap_ = 0; +} + +size_t FreeObjectList::GetFreeObjectSize() const +{ + return available_; +} + +bool FreeObjectList::AddKind(FreeObjectKind *kind) +{ + if (kind == nullptr || kind->Empty() || kind->isAdded_) { + return false; + } + KindType type = kind->kindType_; + FreeObjectKind *top = kinds_[type]; + if (kind == top) { + return false; + } + if (top != nullptr) { + top->prev_ = kind; + } + kind->isAdded_ = true; + kind->next_ = top; + if (lastKinds_[type] == nullptr) { + lastKinds_[type] = kind; + } + kinds_[type] = kind; + SetNoneEmptyBit(type); + available_ += kind->Available(); + return true; +} + +void FreeObjectList::RemoveKind(FreeObjectKind *kind) +{ + if (kind == nullptr) { + return; + } + KindType type = kind->kindType_; + FreeObjectKind *top = kinds_[type]; + FreeObjectKind *end = lastKinds_[type]; + if (top == kind) { + kinds_[type] = top->next_; + } + if (end == kind) { + lastKinds_[type] = end->prev_; + } + if (kind->prev_ != nullptr) { + kind->prev_->next_ = kind->next_; + } + if (kind->next_ != nullptr) { + kind->next_->prev_ = kind->prev_; + } + if (kinds_[type] == nullptr) { + ClearNoneEmptyBit(type); + } + available_ -= kind->Available(); + kind->Rebuild(); +} + +void FreeObjectList::Merge(FreeObjectList *list) +{ + list->EnumerateTopAndLastKinds([this](FreeObjectKind *kind, FreeObjectKind *end) { + if (kind == nullptr || kind->Empty()) { + return; + } + KindType type = kind->kindType_; + FreeObjectKind *top = kinds_[type]; + if (top == nullptr) { + top = kind; + } else { + lastKinds_[type]->next_ = kind; + kind->prev_ = lastKinds_[type]; + } + lastKinds_[type] = end; + SetNoneEmptyBit(type); + }); + available_ += list->available_; + list->Rebuild(); +} + +template +void FreeObjectList::EnumerateKinds(const Callback &cb) const +{ + for (KindType i = 0; i < NUMBER_OF_KINDS; i++) { + EnumerateKinds(i, cb); + } +} + +template +void FreeObjectList::EnumerateKinds(KindType type, const Callback &cb) const +{ + FreeObjectKind *current = kinds_[type]; + while (current != nullptr) { + // maybe reset + FreeObjectKind *next = current->next_; + cb(current); + current = next; + } +} + +template +void FreeObjectList::EnumerateTopAndLastKinds(const Callback &cb) const +{ + for (KindType i = 0; i < NUMBER_OF_KINDS; i++) { + cb(kinds_[i], lastKinds_[i]); + } +} +} // namespace panda::ecmascript diff --git a/runtime/mem/free_object_list.h b/runtime/mem/free_object_list.h new file mode 100644 index 000000000..bccd18e0f --- /dev/null +++ b/runtime/mem/free_object_list.h @@ -0,0 +1,87 @@ +/* + * Copyright (c) 2021-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_MEM_FREE_OBJECT_LIST_H +#define ECMASCRIPT_MEM_FREE_OBJECT_LIST_H + +#include + +#include "plugins/ecmascript/runtime/mem/free_object_kind.h" +#include "utils/span.h" + +namespace panda::ecmascript { +class FreeObjectList { +public: + FreeObjectList(); + ~FreeObjectList(); + + FreeObject *Allocator(size_t size); + + void Free(uintptr_t start, size_t size, bool isAdd = true); + + void Rebuild(); + + bool AddKind(FreeObjectKind *kind); + + void RemoveKind(FreeObjectKind *kind); + + void Merge(FreeObjectList *list); + + template + void EnumerateKinds(const Callback &cb) const; + + template + void EnumerateKinds(KindType type, const Callback &cb) const; + + template + void EnumerateTopAndLastKinds(const Callback &cb) const; + + NO_COPY_SEMANTIC(FreeObjectList); + NO_MOVE_SEMANTIC(FreeObjectList); + + size_t GetFreeObjectSize() const; + + static int NumberOfKinds() + { + return NUMBER_OF_KINDS; + } + +private: + static constexpr int NUMBER_OF_KINDS = 39; + static constexpr size_t MIN_SIZE = 16; + static constexpr size_t SMALL_KIND_MAX_SIZE = 256; + static constexpr size_t LARGE_KIND_MAX_SIZE = 65536; + static constexpr size_t HUGE_KIND_MAX_SIZE = 255 * 1024; + static constexpr int SMALL_KIND_MAX_INDEX = 29; + static constexpr int NUMBER_OF_LAST_LARGE = NUMBER_OF_KINDS - 2; + static constexpr int NUMBER_OF_LAST_HUGE = NUMBER_OF_KINDS - 1; + static constexpr size_t INTERVAL_OFFSET = 3; + static constexpr size_t LOG2_OFFSET = 21; + static constexpr size_t MAX_BIT_OF_SIZET = sizeof(size_t) << INTERVAL_OFFSET; + const int smallKindOffsetIndex = 2; + + inline KindType SelectKindType(size_t size) const; + + inline void SetNoneEmptyBit(KindType type); + inline void ClearNoneEmptyBit(KindType type); + inline size_t CalcNextNoneEmptyIndex(KindType start); + + size_t available_ = 0; + uint64_t noneEmptyKindBitMap_; + Span kinds_ {}; + Span lastKinds_ {}; +}; +} // namespace panda::ecmascript +#endif // ECMASCRIPT_MEM_FREE_OBJECT_LIST_H diff --git a/runtime/mem/gc_stats.cpp b/runtime/mem/gc_stats.cpp new file mode 100644 index 000000000..57e78edfc --- /dev/null +++ b/runtime/mem/gc_stats.cpp @@ -0,0 +1,152 @@ +/* + * Copyright (c) 2021-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 "plugins/ecmascript/runtime/mem/gc_stats.h" + +#include "plugins/ecmascript/runtime/mem/heap.h" +#include "plugins/ecmascript/runtime/mem/mem.h" + +namespace panda::ecmascript { +void GCStats::PrintStatisticResult(bool isForce) +{ + LOG(ERROR, RUNTIME) << "GCStats statistic: "; + if ((isForce && semiGCCount_ != 0) || (!isForce && semiGCCount_ != lastSemiGCCount_)) { + lastSemiGCCount_ = semiGCCount_; + LOG(ERROR, RUNTIME) << " SemiCollector statistic: total semi gc count " << semiGCCount_; + LOG(ERROR, RUNTIME) << " MIN pause time: " << PrintTimeMilliseconds(semiGCMinPause_) << "ms" + << " MAX pause time: " << PrintTimeMilliseconds(semiGCMAXPause_) << "ms" + << " total pause time: " << PrintTimeMilliseconds(semiGCTotalPause_) << "ms" + << " average pause time: " << PrintTimeMilliseconds(semiGCTotalPause_ / semiGCCount_) + << "ms" + << " tatal alive size: " << sizeToMB(semiTotalAliveSize_) << "MB" + << " average alive size: " << sizeToMB(semiTotalAliveSize_ / semiGCCount_) << "MB" + << " tatal commit size: " << sizeToMB(semiTotalCommitSize_) << "MB" + << " average commit size: " << sizeToMB(semiTotalCommitSize_ / semiGCCount_) << "MB" + << " semi aliveRate: " << double(semiTotalAliveSize_) / semiTotalCommitSize_ + << " total promote size: " << sizeToMB(semiTotalPromoteSize_) << "MB" + << " average promote size: " << sizeToMB(semiTotalPromoteSize_ / semiGCCount_) << "MB"; + } + + if ((isForce && oldGCCount_ != 0) || (!isForce && lastOldGCCount_ != oldGCCount_)) { + lastOldGCCount_ = oldGCCount_; + LOG(ERROR, RUNTIME) << " MixCollector statistic: total old gc count " << oldGCCount_; + LOG(ERROR, RUNTIME) << " MIN pause time: " << PrintTimeMilliseconds(oldGCMinPause_) << "ms" + << " MAX pause time: " << PrintTimeMilliseconds(oldGCMAXPause_) << "ms" + << " total pause time: " << PrintTimeMilliseconds(oldGCTotalPause_) << "ms" + << " average pause time: " << PrintTimeMilliseconds(oldGCTotalPause_ / oldGCCount_) << "ms" + << " total free size: " << sizeToMB(oldTotalFreeSize_) << "MB" + << " average free size: " << sizeToMB(oldTotalFreeSize_ / oldGCCount_) << "MB" + << " old space total commit size: " << sizeToMB(oldSpaceTotalCommitSize_) << "MB" + << " old space average commit size: " << sizeToMB(oldSpaceTotalCommitSize_ / oldGCCount_) + << "MB" + << " non move space total commit size: " << sizeToMB(oldNonMoveTotalCommitSize_) << "MB" + << " non move space average commit size: " + << sizeToMB(oldNonMoveTotalCommitSize_ / oldGCCount_) << "MB" + << " old free rate: " + << static_cast(oldTotalFreeSize_) / + static_cast(oldSpaceTotalCommitSize_ + oldNonMoveTotalCommitSize_); + } + + if ((isForce && compressGCCount_ != 0) || (!isForce && compressGCCount_ != lastCompressGCCount_)) { + lastCompressGCCount_ = compressGCCount_; + LOG(ERROR, RUNTIME) << " compressCollector statistic: total compress gc count " << compressGCCount_; + LOG(ERROR, RUNTIME) << " MIN pause time: " << PrintTimeMilliseconds(compressGCMinPause_) << "ms" + << " MAX pause time: " << PrintTimeMilliseconds(compressGCMaxPause_) << "ms" + << " total pause time: " << PrintTimeMilliseconds(compressGCTotalPause_) << "ms" + << " average pause time: " + << PrintTimeMilliseconds(compressGCTotalPause_ / compressGCCount_) << "ms" + << " young and old total alive size: " << sizeToMB(compressYoungAndOldAliveSize_) << "MB" + << " young and old average alive size: " + << sizeToMB(compressYoungAndOldAliveSize_ / compressGCCount_) << "MB" + << " young total commit size: " << sizeToMB(compressYoungCommitSize_) << "MB" + << " old total commit size: " << sizeToMB(compressOldCommitSize_) << "MB" + << " young and old average commit size: " + << sizeToMB((compressYoungCommitSize_ + compressOldCommitSize_) / compressGCCount_) << "MB" + << " young and old free rate: " + << 1 - static_cast(compressYoungAndOldAliveSize_) / + static_cast(compressYoungCommitSize_ + compressOldCommitSize_) + << " non move total free size: " << sizeToMB(compressNonMoveTotalFreeSize_) << "MB" + << " non move total commit size: " << sizeToMB(compressNonMoveTotalCommitSize_) << "MB" + << " non move free rate: " + << static_cast(compressNonMoveTotalFreeSize_) / + static_cast(compressNonMoveTotalCommitSize_); + } + + if (isForce && heap_ != nullptr) { + auto *regionFactory = const_cast(heap_->GetRegionFactory()); + LOG(ERROR, RUNTIME) << "Memory statistic:"; + LOG(ERROR, RUNTIME) << " anno memory usage size:" << regionFactory->GetAnnoMemoryUsage() + << " anno memory max usage size:" << regionFactory->GetMaxAnnoMemoryUsage() + << " native memory usage size:" << regionFactory->GetNativeMemoryUsage() + << " native memory max usage size:" << regionFactory->GetMaxNativeMemoryUsage(); + } +} + +void GCStats::StatisticSemiCollector(Duration time, size_t aliveSize, size_t promoteSize, size_t commitSize) +{ + auto timeToMillion = TimeToMicroseconds(time); + if (semiGCCount_ == 0) { + semiGCMinPause_ = timeToMillion; + semiGCMAXPause_ = timeToMillion; + } else { + semiGCMinPause_ = std::min(semiGCMinPause_, timeToMillion); + semiGCMAXPause_ = std::max(semiGCMAXPause_, timeToMillion); + } + semiGCTotalPause_ += timeToMillion; + semiTotalAliveSize_ += aliveSize; + semiTotalCommitSize_ += commitSize; + semiTotalPromoteSize_ += promoteSize; + semiGCCount_++; +} + +void GCStats::StatisticOldCollector(Duration time, size_t freeSize, size_t oldSpaceCommitSize, + size_t nonMoveSpaceCommitSize) +{ + auto timeToMillion = TimeToMicroseconds(time); + if (oldGCCount_ == 0) { + oldGCMinPause_ = timeToMillion; + oldGCMAXPause_ = timeToMillion; + } else { + oldGCMinPause_ = std::min(oldGCMinPause_, timeToMillion); + oldGCMAXPause_ = std::max(oldGCMAXPause_, timeToMillion); + } + oldGCTotalPause_ += timeToMillion; + oldTotalFreeSize_ += freeSize; + oldSpaceTotalCommitSize_ += oldSpaceCommitSize; + oldNonMoveTotalCommitSize_ += nonMoveSpaceCommitSize; + oldGCCount_++; +} + +void GCStats::StatisticCompressCollector(Duration time, size_t youngAndOldAliveSize, size_t youngCommitSize, + size_t oldCommitSize, size_t nonMoveSpaceFreeSize, + size_t nonMoveSpaceCommitSize) +{ + auto timeToMillion = TimeToMicroseconds(time); + if (compressGCCount_ == 0) { + compressGCMinPause_ = timeToMillion; + compressGCMaxPause_ = timeToMillion; + } else { + compressGCMinPause_ = std::min(compressGCMinPause_, timeToMillion); + compressGCMaxPause_ = std::max(compressGCMaxPause_, timeToMillion); + } + compressGCTotalPause_ += timeToMillion; + compressYoungAndOldAliveSize_ += youngAndOldAliveSize; + compressYoungCommitSize_ += youngCommitSize; + compressOldCommitSize_ += oldCommitSize; + compressNonMoveTotalFreeSize_ += nonMoveSpaceFreeSize; + compressNonMoveTotalCommitSize_ += nonMoveSpaceCommitSize; + compressGCCount_++; +} +} // namespace panda::ecmascript diff --git a/runtime/mem/gc_stats.h b/runtime/mem/gc_stats.h new file mode 100644 index 000000000..800913099 --- /dev/null +++ b/runtime/mem/gc_stats.h @@ -0,0 +1,95 @@ +/* + * Copyright (c) 2021-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_MEM_GC_STATS_H +#define ECMASCRIPT_MEM_GC_STATS_H + +#include +#include "chrono" +#include "libpandabase/utils/logger.h" + +namespace panda::ecmascript { +class Heap; +class GCStats { + using Duration = std::chrono::duration; + +public: + explicit GCStats(const Heap *heap) : heap_(heap) {}; + ~GCStats() = default; + + void PrintStatisticResult(bool isForce = false); + + void StatisticSemiCollector(Duration time, size_t aliveSize, size_t promoteSize, size_t commitSize); + void StatisticOldCollector(Duration time, size_t freeSize, size_t oldSpaceCommitSize, + size_t nonMoveSpaceCommitSize); + void StatisticCompressCollector(Duration time, size_t youngAndOldAliveSize, size_t youngCommitSize, + size_t oldCommitSize, size_t nonMoveSpaceFreeSize, size_t nonMoveSpaceCommitSize); + +private: + size_t TimeToMicroseconds(Duration time) + { + return std::chrono::duration_cast(time).count(); + } + + float PrintTimeMilliseconds(uint64_t time) + { + return static_cast(time) / MILLION_TIME; + } + + float sizeToMB(size_t size) + { + return static_cast(size) / MB; + } + + size_t lastSemiGCCount_ = 0; + size_t semiGCCount_ = 0; + size_t semiGCMinPause_ = 0; + size_t semiGCMAXPause_ = 0; + size_t semiGCTotalPause_ = 0; + size_t semiTotalAliveSize_ = 0; + size_t semiTotalCommitSize_ = 0; + size_t semiTotalPromoteSize_ = 0; + + size_t lastOldGCCount_ = 0; + size_t oldGCCount_ = 0; + size_t oldGCMinPause_ = 0; + size_t oldGCMAXPause_ = 0; + size_t oldGCTotalPause_ = 0; + size_t oldTotalFreeSize_ = 0; + size_t oldSpaceTotalCommitSize_ = 0; + size_t oldNonMoveTotalCommitSize_ = 0; + + size_t lastCompressGCCount_ = 0; + size_t compressGCCount_ = 0; + size_t compressGCMinPause_ = 0; + size_t compressGCMaxPause_ = 0; + size_t compressGCTotalPause_ = 0; + size_t compressYoungAndOldAliveSize_ = 0; + size_t compressYoungCommitSize_ = 0; + size_t compressOldCommitSize_ = 0; + size_t compressNonMoveTotalFreeSize_ = 0; + size_t compressNonMoveTotalCommitSize_ = 0; + + const Heap *heap_; + + static constexpr uint32_t MILLION_TIME = 1000; + static constexpr uint32_t MB = 1 * 1024 * 1024; + + NO_COPY_SEMANTIC(GCStats); + NO_MOVE_SEMANTIC(GCStats); +}; +} // namespace panda::ecmascript + +#endif // ECMASCRIPT_MEM_GC_STATS_H diff --git a/runtime/mem/heap-inl.h b/runtime/mem/heap-inl.h new file mode 100644 index 000000000..d1430c141 --- /dev/null +++ b/runtime/mem/heap-inl.h @@ -0,0 +1,272 @@ +/* + * Copyright (c) 2021-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_MEM_HEAP_INL_H +#define ECMASCRIPT_MEM_HEAP_INL_H + +#include "plugins/ecmascript/runtime/mem/heap.h" + +#include "plugins/ecmascript/runtime/ecma_vm.h" +#include "plugins/ecmascript/runtime/mem/allocator-inl.h" +#include "plugins/ecmascript/runtime/mem/mem_controller.h" +#include "plugins/ecmascript/runtime/mem/space-inl.h" +#include "plugins/ecmascript/runtime/hprof/heap_tracker.h" +#include "plugins/ecmascript/runtime/mem/remembered_set.h" + +namespace panda::ecmascript { +template +void Heap::EnumerateOldSpaceRegions(const Callback &cb, Region *region) const +{ + oldSpace_->EnumerateRegions(cb, region); + nonMovableSpace_->EnumerateRegions(cb); + hugeObjectSpace_->EnumerateRegions(cb); + machineCodeSpace_->EnumerateRegions(cb); +} + +template +void Heap::EnumerateSnapShotSpaceRegions(const Callback &cb) const +{ + snapshotSpace_->EnumerateRegions(cb); +} + +template +void Heap::EnumerateNewSpaceRegions(const Callback &cb) const +{ + toSpace_->EnumerateRegions(cb); +} + +template +void Heap::EnumerateNonMovableRegions(const Callback &cb) const +{ + snapshotSpace_->EnumerateRegions(cb); + nonMovableSpace_->EnumerateRegions(cb); + hugeObjectSpace_->EnumerateRegions(cb); + machineCodeSpace_->EnumerateRegions(cb); +} + +template +void Heap::EnumerateRegions(const Callback &cb) const +{ + toSpace_->EnumerateRegions(cb); + oldSpace_->EnumerateRegions(cb); + snapshotSpace_->EnumerateRegions(cb); + nonMovableSpace_->EnumerateRegions(cb); + hugeObjectSpace_->EnumerateRegions(cb); + machineCodeSpace_->EnumerateRegions(cb); +} + +template +void Heap::IteratorOverObjects(const Callback &cb) const +{ + toSpace_->IterateOverObjects(cb); + oldSpace_->IterateOverObjects(cb); + nonMovableSpace_->IterateOverObjects(cb); + hugeObjectSpace_->IterateOverObjects(cb); +} + +bool Heap::FillNewSpaceAndTryGC(BumpPointerAllocator *spaceAllocator, bool allowGc) +{ + if (toSpace_->Expand(spaceAllocator->GetTop())) { + spaceAllocator->Reset(toSpace_); + TryTriggerConcurrentMarking(allowGc); + return true; + } + + if (toSpace_->GetCommittedSize() == SEMI_SPACE_SIZE_CAPACITY + && !GetEcmaVM()->GetAssociatedJSThread()->IsReadyToMark()) { + toSpace_->SetMaximumCapacity(std::min(SEMI_SPACE_SIZE_CAPACITY + SEMI_SPACE_OVERSHOOT_SIZE, + MAX_SEMI_SPACE_SIZE_STARTUP)); + if (toSpace_->Expand(spaceAllocator->GetTop())) { + spaceAllocator->Reset(toSpace_); + return true; + } + } + + if (allowGc) { + CollectGarbage(TriggerGCType::SEMI_GC); + return true; + } + return false; +} + +bool Heap::FillOldSpaceAndTryGC(FreeListAllocator *spaceAllocator, bool allowGc) +{ + if (oldSpace_->Expand()) { + if (!allowGc) { + auto currentRegion = GetCompressSpace()->GetCurrentRegion(); + currentRegion->SetAliveObject(currentRegion->GetSize()); + } + spaceAllocator->AddFree(oldSpace_->GetCurrentRegion()); + return true; + } + if (allowGc) { + CollectGarbage(TriggerGCType::OLD_GC); + return true; + } + return false; +} + +bool Heap::FillNonMovableSpaceAndTryGC(FreeListAllocator *spaceAllocator, bool allowGc) +{ + if (nonMovableSpace_->Expand()) { + spaceAllocator->AddFree(nonMovableSpace_->GetCurrentRegion()); + return true; + } + if (allowGc) { + CollectGarbage(TriggerGCType::NON_MOVE_GC); + return true; + } + return false; +} + +bool Heap::FillSnapShotSpace(BumpPointerAllocator *spaceAllocator) +{ + bool result = snapshotSpace_->Expand(spaceAllocator->GetTop()); + if (result) { + spaceAllocator->Reset(snapshotSpace_); + } + return result; +} + +bool Heap::FillMachineCodeSpaceAndTryGC(FreeListAllocator *spaceAllocator, bool allowGc) +{ + if (machineCodeSpace_->Expand()) { + spaceAllocator->AddFree(machineCodeSpace_->GetCurrentRegion()); + return true; + } + if (allowGc) { + CollectGarbage(TriggerGCType::MACHINE_CODE_GC); + return true; + } + return false; +} + +Region *Heap::ExpandCompressSpace() +{ + if (compressSpace_->Expand()) { + return compressSpace_->GetCurrentRegion(); + } + return nullptr; +} + +bool Heap::AddRegionToCompressSpace(Region *region) +{ + return compressSpace_->AddRegionToList(region); +} + +bool Heap::AddRegionToToSpace(Region *region) +{ + return toSpace_->AddRegionToList(region); +} + +void Heap::OnAllocateEvent(uintptr_t address) +{ + if (tracker_ != nullptr && address != 0) { + tracker_->AllocationEvent(address); + } +} + +void Heap::OnMoveEvent(uintptr_t address, uintptr_t forwardAddress) +{ + if (tracker_ != nullptr) { + tracker_->MoveEvent(address, forwardAddress); + } +} + +void Heap::SetNewSpaceAgeMark(uintptr_t mark) +{ + ASSERT(toSpace_ != nullptr); + toSpace_->SetAgeMark(mark); +} + +void Heap::SetNewSpaceMaximumCapacity(size_t maximumCapacity) +{ + ASSERT(toSpace_ != nullptr); + SetMaximumCapacity(toSpace_, maximumCapacity); +} + +void Heap::InitializeFromSpace() +{ + if (fromSpace_->GetCommittedSize() == 0) { + fromSpace_->Initialize(); + } +} + +void Heap::InitializeCompressSpace() +{ + if (compressSpace_->GetCommittedSize() == 0) { + compressSpace_->Initialize(); + } +} + +void Heap::SwapSpace() +{ + ASSERT(toSpace_ != nullptr); + ASSERT(fromSpace_ != nullptr); + toSpace_->Swap(fromSpace_); +} + +void Heap::ReclaimFromSpaceRegions() +{ + ASSERT(fromSpace_ != nullptr); + fromSpace_->ReclaimRegions(); +} + +void Heap::SetFromSpaceMaximumCapacity(size_t maximumCapacity) +{ + ASSERT(fromSpace_ != nullptr); + SetMaximumCapacity(fromSpace_, maximumCapacity); +} + +void Heap::ResetDelayGCMode() +{ + ASSERT(memController_ != nullptr); + memController_->ResetDelayGCMode(); +} + +void Heap::SetMaximumCapacity(SemiSpace *space, size_t maximumCapacity) +{ + space->SetMaximumCapacity(maximumCapacity); +} + +void Heap::ClearSlotsRange(Region *current, uintptr_t freeStart, uintptr_t freeEnd) +{ + auto set = current->GetOldToNewRememberedSet(); + if (set != nullptr) { + set->ClearRange(freeStart, freeEnd); + } + set = current->GetCrossRegionRememberedSet(); + if (set != nullptr) { + set->ClearRange(freeStart, freeEnd); + } +} + +size_t Heap::GetCommittedSize() const +{ + size_t result = toSpace_->GetCommittedSize() + oldSpace_->GetCommittedSize() + hugeObjectSpace_->GetCommittedSize() + + nonMovableSpace_->GetCommittedSize() + machineCodeSpace_->GetCommittedSize(); + return result; +} + +size_t Heap::GetHeapObjectSize() const +{ + size_t result = toSpace_->GetHeapObjectSize() + oldSpace_->GetHeapObjectSize() + + hugeObjectSpace_->GetHeapObjectSize() + nonMovableSpace_->GetHeapObjectSize() + + machineCodeSpace_->GetCommittedSize(); + return result; +} +} // namespace panda::ecmascript + +#endif // ECMASCRIPT_MEM_HEAP_INL_H diff --git a/runtime/mem/heap.cpp b/runtime/mem/heap.cpp new file mode 100644 index 000000000..8af357633 --- /dev/null +++ b/runtime/mem/heap.cpp @@ -0,0 +1,604 @@ +/* + * Copyright (c) 2021-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 "plugins/ecmascript/runtime/mem/heap-inl.h" + +#include + +#include "plugins/ecmascript/runtime/ecma_vm.h" +#include "plugins/ecmascript/runtime/mem/assert_scope-inl.h" +#include "plugins/ecmascript/runtime/mem/compress_collector.h" +#include "plugins/ecmascript/runtime/mem/concurrent_marker.h" +#include "plugins/ecmascript/runtime/mem/concurrent_sweeper.h" +#include "plugins/ecmascript/runtime/mem/mem_manager.h" +#include "plugins/ecmascript/runtime/mem/evacuation_allocator.h" +#include "plugins/ecmascript/runtime/mem/mark_stack.h" +#include "plugins/ecmascript/runtime/mem/mem_controller.h" +#include "plugins/ecmascript/runtime/mem/mix_space_collector.h" +#include "plugins/ecmascript/runtime/mem/parallel_evacuation.h" +#include "plugins/ecmascript/runtime/mem/parallel_marker-inl.h" +#include "plugins/ecmascript/runtime/mem/parallel_work_helper.h" +#include "plugins/ecmascript/runtime/mem/semi_space_collector.h" +#include "plugins/ecmascript/runtime/mem/verification.h" + +static constexpr int MAX_PARALLEL_THREAD_NUM = 3; + +namespace panda::ecmascript { +Heap::Heap(EcmaVM *ecmaVm) : ecmaVm_(ecmaVm), regionFactory_(ecmaVm->GetRegionFactory()) {} + +void Heap::Initialize() +{ + memController_ = CreateMemController(this, "no-gc-for-start-up"); + + if (memController_->IsDelayGCMode()) { + toSpace_ = new SemiSpace(this, DEFAULT_SEMI_SPACE_SIZE, MAX_SEMI_SPACE_SIZE_STARTUP); + fromSpace_ = new SemiSpace(this, DEFAULT_SEMI_SPACE_SIZE, MAX_SEMI_SPACE_SIZE_STARTUP); + } else { + toSpace_ = new SemiSpace(this); + fromSpace_ = new SemiSpace(this); + } + + toSpace_->Initialize(); + // not set up from space + oldSpace_ = new OldSpace(this); + compressSpace_ = new OldSpace(this); + oldSpace_->Initialize(); + nonMovableSpace_ = new NonMovableSpace(this); + nonMovableSpace_->Initialize(); + snapshotSpace_ = new SnapShotSpace(this); + machineCodeSpace_ = new MachineCodeSpace(this); + machineCodeSpace_->Initialize(); + hugeObjectSpace_ = new HugeObjectSpace(this); + paralledGc_ = ecmaVm_->GetJSOptions().IsEnableParallelGC(); + concurrentMarkingEnabled_ = ecmaVm_->GetJSOptions().IsEnableConcurrentMark(); +#if ECMASCRIPT_DISABLE_PARALLEL_GC + paralledGc_ = false; +#endif +#if defined(IS_STANDARD_SYSTEM) + paralledGc_ = false; + concurrentMarkingEnabled_ = false; +#endif + workList_ = new WorkerHelper(this, Platform::GetCurrentPlatform()->GetTotalThreadNum() + 1); + semiSpaceCollector_ = new SemiSpaceCollector(this, paralledGc_); + compressCollector_ = new CompressCollector(this); + + derivedPointers_ = new ChunkMap(ecmaVm_->GetChunk()); + mixSpaceCollector_ = new MixSpaceCollector(this); + sweeper_ = new ConcurrentSweeper(this, ecmaVm_->GetJSOptions().IsEnableConcurrentSweep()); + concurrentMarker_ = new ConcurrentMarker(this); + nonMovableMarker_ = new NonMovableMarker(this); + semiGcMarker_ = new SemiGcMarker(this); + compressGcMarker_ = new CompressGcMarker(this); + evacuationAllocator_ = new EvacuationAllocator(this); + evacuation_ = new ParallelEvacuation(this); +} + +void Heap::FlipNewSpace() +{ + SemiSpace *newSpace = fromSpace_; + fromSpace_ = toSpace_; + toSpace_ = newSpace; +} + +void Heap::FlipCompressSpace() +{ + OldSpace *oldSpace = compressSpace_; + compressSpace_ = oldSpace_; + oldSpace_ = oldSpace; +} + +void Heap::Destroy() +{ + Prepare(); + if (toSpace_ != nullptr) { + toSpace_->Destroy(); + delete toSpace_; + toSpace_ = nullptr; + } + if (fromSpace_ != nullptr) { + fromSpace_->Destroy(); + delete fromSpace_; + fromSpace_ = nullptr; + } + if (oldSpace_ != nullptr) { + oldSpace_->Destroy(); + delete oldSpace_; + oldSpace_ = nullptr; + } + if (compressSpace_ != nullptr) { + compressSpace_->Destroy(); + delete compressSpace_; + compressSpace_ = nullptr; + } + if (nonMovableSpace_ != nullptr) { + nonMovableSpace_->Destroy(); + delete nonMovableSpace_; + nonMovableSpace_ = nullptr; + } + if (snapshotSpace_ != nullptr) { + snapshotSpace_->Destroy(); + delete snapshotSpace_; + snapshotSpace_ = nullptr; + } + if (machineCodeSpace_ != nullptr) { + machineCodeSpace_->Destroy(); + delete machineCodeSpace_; + machineCodeSpace_ = nullptr; + } + if (hugeObjectSpace_ != nullptr) { + hugeObjectSpace_->Destroy(); + delete hugeObjectSpace_; + hugeObjectSpace_ = nullptr; + } + + delete workList_; + workList_ = nullptr; + delete semiSpaceCollector_; + semiSpaceCollector_ = nullptr; + delete mixSpaceCollector_; + mixSpaceCollector_ = nullptr; + delete compressCollector_; + compressCollector_ = nullptr; + regionFactory_ = nullptr; + delete memController_; + memController_ = nullptr; + delete sweeper_; + sweeper_ = nullptr; + delete derivedPointers_; + derivedPointers_ = nullptr; + delete concurrentMarker_; + concurrentMarker_ = nullptr; + delete nonMovableMarker_; + nonMovableMarker_ = nullptr; + delete semiGcMarker_; + semiGcMarker_ = nullptr; + delete compressGcMarker_; + compressGcMarker_ = nullptr; + delete evacuation_; + evacuation_ = nullptr; + delete evacuationAllocator_; + evacuationAllocator_ = nullptr; +} + +void Heap::Prepare() +{ + WaitRunningTaskFinished(); + sweeper_->EnsureAllTaskFinished(); + evacuationAllocator_->WaitFreeTaskFinish(); +} + +void Heap::CollectGarbage(TriggerGCType gcType) +{ + CHECK_NO_GC +#if ECMASCRIPT_ENABLE_HEAP_VERIFY + isVerifying_ = true; + // pre gc heap verify + sweeper_->EnsureAllTaskFinished(); + auto failCount = Verification(this).VerifyAll(); + if (failCount > 0) { + LOG(FATAL, GC) << "Before gc heap corrupted and " << failCount << " corruptions"; + } + isVerifying_ = false; + // verify need semiGC or fullGC. + if (gcType != TriggerGCType::SEMI_GC) { + gcType = TriggerGCType::COMPRESS_FULL_GC; + } +#endif + +#if ECMASCRIPT_SWITCH_GC_MODE_TO_COMPRESS_GC + gcType = TriggerGCType::COMPRESS_FULL_GC; +#endif + bool isDelayGCMode = memController_->IsDelayGCMode(); + if (isCompressGCRequested_ && GetEcmaVM()->GetAssociatedJSThread()->IsReadyToMark() && + gcType != TriggerGCType::COMPRESS_FULL_GC && !isDelayGCMode) { + gcType = TriggerGCType::COMPRESS_FULL_GC; + } + memController_->StartCalculationBeforeGC(); + + OPTIONAL_LOG(ecmaVm_, ERROR, ECMASCRIPT) << "Heap::CollectGarbage, gcType = " << gcType << " global CommittedSize" + << GetCommittedSize() << " global limit" << globalSpaceAllocLimit_; + switch (gcType) { + case TriggerGCType::SEMI_GC: + if (isDelayGCMode) { + SetFromSpaceMaximumCapacity(SEMI_SPACE_SIZE_CAPACITY); + SetNewSpaceMaximumCapacity(SEMI_SPACE_SIZE_CAPACITY); + compressCollector_->RunPhases(); + ResetDelayGCMode(); + } else { + bool isOldGCTriggered = false; + if (!concurrentMarkingEnabled_) { + isOldGCTriggered = + GetCommittedSize() > HALF_MAX_HEAP_SIZE ? CheckAndTriggerOldGC() : CheckAndTriggerCompressGC(); + } + if (!isOldGCTriggered) { + mixSpaceCollector_->RunPhases(); + } + } + break; + case TriggerGCType::OLD_GC: + mixSpaceCollector_->RunPhases(); + break; + case TriggerGCType::NON_MOVE_GC: + case TriggerGCType::HUGE_GC: + case TriggerGCType::MACHINE_CODE_GC: + mixSpaceCollector_->RunPhases(); + break; + case TriggerGCType::COMPRESS_FULL_GC: + compressCollector_->RunPhases(); + if (isCompressGCRequested_) { + isCompressGCRequested_ = false; + } + break; + default: + UNREACHABLE(); + break; + } + + if (gcType == TriggerGCType::COMPRESS_FULL_GC || + (gcType != TriggerGCType::SEMI_GC && !isOnlySemi_ && !sweeper_->IsConcurrentSweepEnabled())) { + // Only when the gc type is not semiGC and after the old space sweeping has been finished, + // the limits of old space and global space can be recomputed. + RecomputeLimits(); + } + memController_->StopCalculationAfterGC(gcType); + + if (toSpace_->GetMaximumCapacity() != SEMI_SPACE_SIZE_CAPACITY || + toSpace_->GetCommittedSize() > SEMI_SPACE_SIZE_CAPACITY * SEMI_SPACE_RETENTION_RATIO) { + auto capacity = AlignUp(static_cast(toSpace_->GetCommittedSize() / SEMI_SPACE_RETENTION_RATIO), + PANDA_POOL_ALIGNMENT_IN_BYTES); + capacity = std::max(capacity, SEMI_SPACE_SIZE_CAPACITY); + capacity = std::min(capacity, MAX_SEMI_SPACE_SIZE_STARTUP); + toSpace_->SetMaximumCapacity(capacity); + } + + OPTIONAL_LOG(ecmaVm_, ERROR, ECMASCRIPT) << " GC after: isOnlySemi_" << isOnlySemi_ << " global CommittedSize" + << GetCommittedSize() << " global limit" << globalSpaceAllocLimit_; + +#if ECMASCRIPT_ENABLE_GC_LOG + ecmaVm_->GetEcmaGCStats()->PrintStatisticResult(); +#endif + +#if ECMASCRIPT_ENABLE_HEAP_VERIFY + // post gc heap verify + isVerifying_ = true; + sweeper_->EnsureAllTaskFinished(); + failCount = Verification(this).VerifyAll(); + if (failCount > 0) { + LOG(FATAL, GC) << "After gc heap corrupted and " << failCount << " corruptions"; + } + isVerifying_ = false; +#endif +} + +void Heap::ThrowOutOfMemoryError(size_t size, const std::string &functionName) +{ + LOG_ECMA_MEM(FATAL) << "OOM when trying to allocate " << size << " bytes" + << " function name: " << functionName.c_str(); +} + +size_t Heap::VerifyHeapObjects() const +{ + size_t failCount = 0; + { + VerifyObjectVisitor verifier(this, &failCount); + toSpace_->IterateOverObjects(verifier); + } + + { + VerifyObjectVisitor verifier(this, &failCount); + oldSpace_->IterateOverObjects(verifier); + } + + { + VerifyObjectVisitor verifier(this, &failCount); + nonMovableSpace_->IterateOverObjects(verifier); + } + + { + VerifyObjectVisitor verifier(this, &failCount); + hugeObjectSpace_->IterateOverObjects(verifier); + } + return failCount; +} + +void Heap::RecomputeLimits() +{ + double gcSpeed = memController_->CalculateMarkCompactSpeedPerMS(); + double mutatorSpeed = memController_->GetCurrentOldSpaceAllocationThroughtputPerMS(); + size_t oldSpaceSize = oldSpace_->GetCommittedSize() + hugeObjectSpace_->GetCommittedSize(); + size_t newSpaceCapacity = toSpace_->GetCommittedSize(); + + double growingFactor = memController_->CalculateGrowingFactor(gcSpeed, mutatorSpeed); + auto newOldSpaceLimit = memController_->CalculateAllocLimit(oldSpaceSize, DEFAULT_OLD_SPACE_SIZE, + MAX_OLD_SPACE_SIZE, newSpaceCapacity, growingFactor); + auto newGlobalSpaceLimit = memController_->CalculateAllocLimit(GetCommittedSize(), DEFAULT_HEAP_SIZE, MAX_HEAP_SIZE, + newSpaceCapacity, growingFactor); + globalSpaceAllocLimit_ = newGlobalSpaceLimit; + oldSpaceAllocLimit_ = newOldSpaceLimit; +} + +bool Heap::CheckConcurrentMark(JSThread *thread) +{ + if (ConcurrentMarkingEnable() && !thread->IsReadyToMark()) { + if (thread->IsMarking()) { + [[maybe_unused]] ClockScope clockScope; + ECMA_BYTRACE_NAME(BYTRACE_TAG_ARK, "Heap::CheckConcurrentMark"); + WaitConcurrentMarkingFinished(); + ECMA_GC_LOG() << "wait concurrent marking finish pause time " << clockScope.TotalSpentTime(); + } + memController_->RecordAfterConcurrentMark(isOnlySemi_, concurrentMarker_); + return true; + } + return false; +} + +void Heap::TryTriggerConcurrentMarking(bool allowGc) +{ + // When the concurrent mark is enabled, concurrent mark can be tried to triggered. When the size of old space or + // global space reaches to the limits, isFullMarkNeeded will be true. If the predicted duration of the current full + // mark can allow the new space and old space to allocate to their limits, full mark will be triggered. In the same + // way, if the size of the new space reaches to the capacity, and the predicted duration of the current semi mark + // can exactly allow the new space to allocate to the capacity, semi mark can be triggered. But when it will spend + // a lot of time in full mark, the compress full GC will be requested after the spaces reach to limits. And If the + // global space is larger than the half max heap size, we will turn to use full mark and trigger mix GC. + if (!concurrentMarkingEnabled_ || !allowGc || !GetEcmaVM()->GetAssociatedJSThread()->IsReadyToMark() || + GetMemController()->IsDelayGCMode()) { + return; + } + bool isFullMarkNeeded = false; + double oldSpaceMarkDuration = 0; + double newSpaceMarkDuration = 0; + double newSpaceRemainSize = 0; + double newSpaceAllocToLimitDuration = 0; + double oldSpaceAllocToLimitDuration = 0; + double oldSpaceAllocSpeed = memController_->GetOldSpaceAllocationThroughtPerMS(); + double oldSpaceConcurrentMarkSpeed = memController_->GetFullSpaceConcurrentMarkSpeedPerMS(); + size_t oldSpaceCommittedSize = oldSpace_->GetCommittedSize() + hugeObjectSpace_->GetCommittedSize(); + size_t globalSpaceCommittedSize = GetCommittedSize(); + if (oldSpaceConcurrentMarkSpeed == 0 || oldSpaceAllocSpeed == 0) { + if (oldSpaceCommittedSize >= OLD_SPACE_LIMIT_BEGIN) { + isOnlySemi_ = false; + ECMA_GC_LOG() << "Trigger the first full mark"; + TriggerConcurrentMarking(); + } + } else { + if (oldSpaceCommittedSize >= oldSpaceAllocLimit_ || globalSpaceCommittedSize >= globalSpaceAllocLimit_) { + isFullMarkNeeded = true; + } + oldSpaceAllocToLimitDuration = + static_cast(oldSpaceAllocLimit_ - oldSpaceCommittedSize) / 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; + } + } + + double newSpaceAllocSpeed = memController_->GetNewSpaceAllocationThroughtPerMS(); + double newSpaceConcurrentMarkSpeed = memController_->GetNewSpaceConcurrentMarkSpeedPerMS(); + + if (newSpaceConcurrentMarkSpeed == 0 || newSpaceAllocSpeed == 0) { + if (toSpace_->GetCommittedSize() >= SEMI_SPACE_TRIGGER_CONCURRENT_MARK) { + isOnlySemi_ = true; + TriggerConcurrentMarking(); + ECMA_GC_LOG() << "Trigger the first semi mark"; + } + return; + } + newSpaceAllocToLimitDuration = + static_cast(toSpace_->GetMaximumCapacity() - toSpace_->GetCommittedSize()) / newSpaceAllocSpeed; + newSpaceMarkDuration = toSpace_->GetHeapObjectSize() / newSpaceConcurrentMarkSpeed; + // 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) { + isOnlySemi_ = false; + TriggerConcurrentMarking(); + ECMA_GC_LOG() << "Trigger full mark"; + return; + } + + if (oldSpaceCommittedSize >= oldSpaceAllocLimit_ || globalSpaceCommittedSize >= globalSpaceAllocLimit_) { + if (oldSpaceCommittedSize > HALF_MAX_HEAP_SIZE) { + isOnlySemi_ = false; + TriggerConcurrentMarking(); + ECMA_GC_LOG() << "HEAP is larger than half of the max size. Trigger full mark"; + } else { + isCompressGCRequested_ = true; + ECMA_GC_LOG() << "Request compress GC"; + } + } + return; + } + + if (newSpaceRemainSize < DEFAULT_REGION_SIZE) { + isOnlySemi_ = true; + TriggerConcurrentMarking(); + ECMA_GC_LOG() << "Trigger semi mark"; + } +} + +void Heap::TriggerConcurrentMarking() +{ + if (concurrentMarkingEnabled_ && !isCompressGCRequested_) { + concurrentMarker_->ConcurrentMarking(); + } +} + +void Heap::CheckNeedFullMark() +{ + if ((oldSpace_->GetCommittedSize() + hugeObjectSpace_->GetCommittedSize()) > oldSpaceAllocLimit_) { + SetOnlyMarkSemi(false); + } +} + +bool Heap::CheckAndTriggerOldGC() +{ + if ((oldSpace_->GetCommittedSize() + hugeObjectSpace_->GetCommittedSize()) <= oldSpaceAllocLimit_) { + return false; + } + CollectGarbage(TriggerGCType::OLD_GC); + return true; +} + +bool Heap::CheckAndTriggerCompressGC() +{ + if (GetCommittedSize() <= globalSpaceAllocLimit_) { + return false; + } + CollectGarbage(TriggerGCType::COMPRESS_FULL_GC); + return true; +} + +bool Heap::CheckAndTriggerNonMovableGC() +{ + if (nonMovableSpace_->GetCommittedSize() <= DEFAULT_NON_MOVABLE_SPACE_LIMIT) { + return false; + } + CollectGarbage(TriggerGCType::NON_MOVE_GC); + return true; +} + +bool Heap::CheckAndTriggerMachineCodeGC() +{ + if (machineCodeSpace_->GetCommittedSize() <= DEFAULT_MACHINE_CODE_SPACE_LIMIT) { + return false; + } + CollectGarbage(TriggerGCType::MACHINE_CODE_GC); + return true; +} + +void Heap::UpdateDerivedObjectInStack() +{ + if (derivedPointers_->empty()) { + return; + } + for (auto derived : *derivedPointers_) { + auto baseAddr = reinterpret_cast(derived.first.first); + JSTaggedValue base = *baseAddr; + if (base.IsHeapObject()) { + uintptr_t baseOldObject = derived.second; + auto *derivedAddr = reinterpret_cast(derived.first.second); +#ifndef NDEBUG + LOG_ECMA(DEBUG) << std::hex << "fix base before:" << baseAddr << " base old Value: " << baseOldObject + << " derived:" << derivedAddr << " old Value: " << *derivedAddr << std::endl; +#endif + // derived is always bigger than base + *derivedAddr = reinterpret_cast(base.GetHeapObject()) + (*derivedAddr - baseOldObject); +#ifndef NDEBUG + LOG_ECMA(DEBUG) << std::hex << "fix base after:" << baseAddr << " base New Value: " << base.GetHeapObject() + << " derived:" << derivedAddr << " New Value: " << *derivedAddr << std::endl; +#endif + } + } + derivedPointers_->clear(); +} + +void Heap::WaitRunningTaskFinished() +{ + os::memory::LockHolder holder(waitTaskFinishedMutex_); + while (runningTastCount_ > 0) { + waitTaskFinishedCV_.Wait(&waitTaskFinishedMutex_); + } +} + +void Heap::WaitConcurrentMarkingFinished() +{ + concurrentMarker_->WaitConcurrentMarkingFinished(); +} + +void Heap::SetConcurrentMarkingEnable(bool flag) +{ + concurrentMarkingEnabled_ = flag; +} + +bool Heap::ConcurrentMarkingEnable() const +{ + return concurrentMarkingEnabled_; +} + +void Heap::PostParallelGCTask(ParallelGCTaskPhase gcTask) +{ + IncreaseTaskCount(); + Platform::GetCurrentPlatform()->PostTask(std::make_unique(this, gcTask)); +} + +void Heap::IncreaseTaskCount() +{ + os::memory::LockHolder holder(waitTaskFinishedMutex_); + runningTastCount_++; +} + +bool Heap::CheckCanDistributeTask() +{ + os::memory::LockHolder holder(waitTaskFinishedMutex_); + return (runningTastCount_ < Platform::GetCurrentPlatform()->GetTotalThreadNum() - 1) && + (runningTastCount_ <= MAX_PARALLEL_THREAD_NUM); +} + +void Heap::ReduceTaskCount() +{ + os::memory::LockHolder holder(waitTaskFinishedMutex_); + runningTastCount_--; + if (runningTastCount_ == 0) { + waitTaskFinishedCV_.SignalAll(); + } +} + +bool Heap::ParallelGCTask::Run(uint32_t threadIndex) +{ + switch (taskPhase_) { + case ParallelGCTaskPhase::SEMI_HANDLE_THREAD_ROOTS_TASK: + heap_->GetSemiGcMarker()->MarkRoots(threadIndex); + heap_->GetSemiGcMarker()->ProcessMarkStack(threadIndex); + break; + case ParallelGCTaskPhase::SEMI_HANDLE_SNAPSHOT_TASK: + heap_->GetSemiGcMarker()->ProcessSnapshotRSet(threadIndex); + break; + case ParallelGCTaskPhase::SEMI_HANDLE_GLOBAL_POOL_TASK: + heap_->GetSemiGcMarker()->ProcessMarkStack(threadIndex); + break; + case ParallelGCTaskPhase::OLD_HANDLE_GLOBAL_POOL_TASK: + heap_->GetNonMovableMarker()->ProcessMarkStack(threadIndex); + break; + case ParallelGCTaskPhase::COMPRESS_HANDLE_GLOBAL_POOL_TASK: + heap_->GetCompressGcMarker()->ProcessMarkStack(threadIndex); + break; + case ParallelGCTaskPhase::CONCURRENT_HANDLE_GLOBAL_POOL_TASK: + heap_->GetNonMovableMarker()->ProcessMarkStack(threadIndex); + break; + case ParallelGCTaskPhase::CONCURRENT_HANDLE_OLD_TO_NEW_TASK: + heap_->GetNonMovableMarker()->ProcessOldToNew(threadIndex); + break; + default: + break; + } + heap_->ReduceTaskCount(); + return true; +} + +size_t Heap::GetArrayBufferSize() const +{ + size_t result = 0; + this->IteratorOverObjects([&result](TaggedObject *obj) { + JSHClass *jsClass = obj->GetClass(); + result += jsClass->IsArrayBuffer() ? jsClass->GetObjectSize() : 0; + }); + return result; +} +} // namespace panda::ecmascript diff --git a/runtime/mem/heap.h b/runtime/mem/heap.h new file mode 100644 index 000000000..2196e9ed0 --- /dev/null +++ b/runtime/mem/heap.h @@ -0,0 +1,426 @@ +/* + * Copyright (c) 2021-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_MEM_HEAP_H +#define ECMASCRIPT_MEM_HEAP_H + +#include "plugins/ecmascript/runtime/base/config.h" +#include "plugins/ecmascript/runtime/mem/chunk_containers.h" +#include "plugins/ecmascript/runtime/mem/mark_stack.h" +#include "plugins/ecmascript/runtime/mem/space.h" +#include "plugins/ecmascript/runtime/platform/platform.h" +#include "plugins/ecmascript/runtime/mem/parallel_work_helper.h" + +namespace panda::ecmascript { +class EcmaVM; +class MemManager; +class SemiSpaceCollector; +class MixSpaceCollector; +class CompressCollector; +class BumpPointerAllocator; +class FreeListAllocator; +class RegionFactory; +class HeapTracker; +class MemController; +class ConcurrentSweeper; +class ConcurrentMarker; +class Marker; +class ParallelEvacuation; +class EvacuationAllocator; +class WorkerHelper; + +using DerivedDataKey = std::pair; + +class Heap { +public: + explicit Heap(EcmaVM *ecmaVm); + ~Heap() = default; + NO_COPY_SEMANTIC(Heap); + NO_MOVE_SEMANTIC(Heap); + void Initialize(); + void Destroy(); + void Prepare(); + + const SemiSpace *GetNewSpace() const + { + return toSpace_; + } + + inline void SetNewSpaceAgeMark(uintptr_t mark); + + inline void SetNewSpaceMaximumCapacity(size_t maximumCapacity); + + const SemiSpace *GetFromSpace() const + { + return fromSpace_; + } + + const OldSpace *GetCompressSpace() const + { + return compressSpace_; + } + + inline void InitializeFromSpace(); + inline void InitializeCompressSpace(); + + inline void SwapSpace(); + + inline void ReclaimFromSpaceRegions(); + + inline void SetFromSpaceMaximumCapacity(size_t maximumCapacity); + + const OldSpace *GetOldSpace() const + { + return oldSpace_; + } + + const NonMovableSpace *GetNonMovableSpace() const + { + return nonMovableSpace_; + } + + const HugeObjectSpace *GetHugeObjectSpace() const + { + return hugeObjectSpace_; + } + + const MachineCodeSpace *GetMachineCodeSpace() const + { + return machineCodeSpace_; + } + + SemiSpaceCollector *GetSemiSpaceCollector() const + { + return semiSpaceCollector_; + } + + MixSpaceCollector *GetMixSpaceCollector() const + { + return mixSpaceCollector_; + } + + CompressCollector *GetCompressCollector() const + { + return compressCollector_; + } + + ConcurrentSweeper *GetSweeper() const + { + return sweeper_; + } + + ParallelEvacuation *GetEvacuation() const + { + return evacuation_; + } + + EvacuationAllocator *GetEvacuationAllocator() const + { + return evacuationAllocator_; + } + + ConcurrentMarker *GetConcurrentMarker() const + { + return concurrentMarker_; + } + + EcmaVM *GetEcmaVM() const + { + return ecmaVm_; + } + + WorkerHelper *GetWorkList() const + { + return workList_; + } + + void FlipNewSpace(); + + void FlipCompressSpace(); + + template + void EnumerateOldSpaceRegions(const Callback &cb, Region *region = nullptr) const; + + template + void EnumerateNewSpaceRegions(const Callback &cb) const; + + template + void EnumerateSnapShotSpaceRegions(const Callback &cb) const; + + template + void EnumerateNonMovableRegions(const Callback &cb) const; + + template + void EnumerateRegions(const Callback &cb) const; + + template + void IteratorOverObjects(const Callback &cb) const; + + void CollectGarbage(TriggerGCType gcType); + + inline bool FillNewSpaceAndTryGC(BumpPointerAllocator *spaceAllocator, bool allowGc = true); + inline bool FillOldSpaceAndTryGC(FreeListAllocator *spaceAllocator, bool allowGc = true); + inline bool FillNonMovableSpaceAndTryGC(FreeListAllocator *spaceAllocator, bool allowGc = true); + inline bool FillSnapShotSpace(BumpPointerAllocator *spaceAllocator); + inline bool FillMachineCodeSpaceAndTryGC(FreeListAllocator *spaceAllocator, bool allowGc = true); + inline Region *ExpandCompressSpace(); + inline bool AddRegionToCompressSpace(Region *region); + inline bool AddRegionToToSpace(Region *region); + + void ThrowOutOfMemoryError(size_t size, const std::string &functionName); + + void SetHeapManager(MemManager *heapManager) + { + heapManager_ = heapManager; + } + + MemManager *GetHeapManager() const + { + return heapManager_; + } + + void StartHeapTracking(HeapTracker *tracker) + { + tracker_ = tracker; + } + + void StopHeapTracking() + { + tracker_ = nullptr; + } + + inline void OnAllocateEvent(uintptr_t address); + inline void OnMoveEvent(uintptr_t address, uintptr_t forwardAddress); + + void TryTriggerConcurrentMarking(bool allowGc); + + void TriggerConcurrentMarking(); + + void CheckNeedFullMark(); + + bool CheckConcurrentMark(JSThread *thread); + + bool CheckAndTriggerOldGC(); + + bool CheckAndTriggerCompressGC(); + + bool CheckAndTriggerNonMovableGC(); + + bool CheckAndTriggerMachineCodeGC(); + + const RegionFactory *GetRegionFactory() const + { + return regionFactory_; + } + + SnapShotSpace *GetSnapShotSpace() const + { + return snapshotSpace_; + } + + bool IsLive(TaggedObject *object) const + { + if (!ContainObject(object)) { + return false; + } + + // semi space + if (toSpace_->IsLive(object)) { + return true; + } + // old space + if (oldSpace_->IsLive(object)) { + return true; + } + // non movable space + if (nonMovableSpace_->IsLive(object)) { + return true; + } + // huge object space + if (hugeObjectSpace_->IsLive(object)) { + return true; + } + return false; + } + + bool ContainObject(TaggedObject *object) const + { + // semi space + if (toSpace_->ContainObject(object)) { + return true; + } + // old space + if (oldSpace_->ContainObject(object)) { + return true; + } + // non movable space + if (nonMovableSpace_->ContainObject(object)) { + return true; + } + // huge object space + if (hugeObjectSpace_->ContainObject(object)) { + return true; + } + return false; + } + + void RecomputeLimits(); + + MemController *GetMemController() const + { + return memController_; + } + + size_t GetOldSpaceAllocLimit() + { + return oldSpaceAllocLimit_; + } + + void SetGlobalSpaceAllocLimit(size_t limit) + { + globalSpaceAllocLimit_ = limit; + } + + inline void ResetDelayGCMode(); + + size_t VerifyHeapObjects() const; + + inline void ClearSlotsRange(Region *current, uintptr_t freeStart, uintptr_t freeEnd); + + ChunkMap *GetDerivedPointers() const + { + return derivedPointers_; + } +#if ECMASCRIPT_ENABLE_HEAP_VERIFY + bool GetIsVerifying() const + { + return isVerifying_; + } +#endif + + void UpdateDerivedObjectInStack(); + static constexpr uint32_t STACK_MAP_DEFALUT_DERIVED_SIZE = 8U; + + void WaitRunningTaskFinished(); + + bool CheckCanDistributeTask(); + + void PostParallelGCTask(ParallelGCTaskPhase gcTask); + + bool IsParallelGCEnabled() const + { + return paralledGc_; + } + + void WaitConcurrentMarkingFinished(); + + void SetConcurrentMarkingEnable(bool flag); + + bool ConcurrentMarkingEnable() const; + + inline bool IsSemiMarkNeeded() const + { + return isOnlySemi_; + } + + void SetOnlyMarkSemi(bool onlySemi) + { + isOnlySemi_ = onlySemi; + } + + Marker *GetNonMovableMarker() const + { + return nonMovableMarker_; + } + + Marker *GetSemiGcMarker() const + { + return semiGcMarker_; + } + + Marker *GetCompressGcMarker() const + { + return compressGcMarker_; + } + + size_t GetArrayBufferSize() const; + + inline size_t GetCommittedSize() const; + + inline size_t GetHeapObjectSize() const; + +private: + void IncreaseTaskCount(); + + void ReduceTaskCount(); + + class ParallelGCTask : public Task { + public: + ParallelGCTask(Heap *heap, ParallelGCTaskPhase taskPhase) : heap_(heap), taskPhase_(taskPhase) {}; + ~ParallelGCTask() override = default; + bool Run(uint32_t threadIndex) override; + + NO_COPY_SEMANTIC(ParallelGCTask); + NO_MOVE_SEMANTIC(ParallelGCTask); + + private: + Heap *heap_ {nullptr}; + ParallelGCTaskPhase taskPhase_; + }; + + EcmaVM *ecmaVm_ {nullptr}; + SemiSpace *fromSpace_ {nullptr}; + SemiSpace *toSpace_ {nullptr}; + OldSpace *oldSpace_ {nullptr}; + OldSpace *compressSpace_ {nullptr}; + HugeObjectSpace *hugeObjectSpace_ {nullptr}; + SnapShotSpace *snapshotSpace_ {nullptr}; + NonMovableSpace *nonMovableSpace_ {nullptr}; + MachineCodeSpace *machineCodeSpace_ {nullptr}; + SemiSpaceCollector *semiSpaceCollector_ {nullptr}; + MixSpaceCollector *mixSpaceCollector_ {nullptr}; + CompressCollector *compressCollector_ {nullptr}; + ConcurrentSweeper *sweeper_ {nullptr}; + Marker *nonMovableMarker_ {nullptr}; + Marker *semiGcMarker_ {nullptr}; + Marker *compressGcMarker_ {nullptr}; + ParallelEvacuation *evacuation_ {nullptr}; + EvacuationAllocator *evacuationAllocator_ {nullptr}; + MemManager *heapManager_ {nullptr}; + RegionFactory *regionFactory_ {nullptr}; + HeapTracker *tracker_ {nullptr}; + MemController *memController_ {nullptr}; + size_t oldSpaceAllocLimit_ {OLD_SPACE_LIMIT_BEGIN}; + size_t globalSpaceAllocLimit_ {GLOBAL_SPACE_LIMIT_BEGIN}; + ChunkMap *derivedPointers_ {nullptr}; +#if ECMASCRIPT_ENABLE_HEAP_VERIFY + bool isVerifying_ {false}; +#endif + + ConcurrentMarker *concurrentMarker_ {nullptr}; + uint32_t runningTastCount_ {0}; + os::memory::Mutex waitTaskFinishedMutex_; + os::memory::ConditionVariable waitTaskFinishedCV_; + bool paralledGc_ {true}; + WorkerHelper *workList_ {nullptr}; + + bool concurrentMarkingEnabled_ {true}; + bool isOnlySemi_ {true}; + bool isCompressGCRequested_ {false}; + inline void SetMaximumCapacity(SemiSpace *space, size_t maximumCapacity); +}; +} // namespace panda::ecmascript + +#endif // ECMASCRIPT_MEM_HEAP_H diff --git a/runtime/mem/machine_code.h b/runtime/mem/machine_code.h new file mode 100644 index 000000000..8605f94c8 --- /dev/null +++ b/runtime/mem/machine_code.h @@ -0,0 +1,81 @@ +/* + * Copyright (c) 2021-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 PANDA_RUNTIME_ECMASCRIPT_MEM_MACHINE_CODE_H +#define PANDA_RUNTIME_ECMASCRIPT_MEM_MACHINE_CODE_H + +#include "plugins/ecmascript/runtime/ecma_macros.h" +#include "plugins/ecmascript/runtime/js_tagged_value.h" +#include "plugins/ecmascript/runtime/mem/tagged_object.h" + +namespace panda { +namespace ecmascript { +class MachineCode : public TaggedObject { +public: + static MachineCode *Cast(ObjectHeader *object) + { + ASSERT(JSTaggedValue(object).IsMachineCodeObject()); + return static_cast(object); + } + + MachineCode() = default; + ~MachineCode() = default; + + static constexpr size_t INS_SIZE_OFFSET = TaggedObjectSize(); + ACCESSORS(InstructionSizeInBytes, INS_SIZE_OFFSET, DATA_OFFSET); + static constexpr size_t SIZE = DATA_OFFSET; + + DECL_DUMP() + + uintptr_t GetDataOffsetAddress() + { + return reinterpret_cast(this) + DATA_OFFSET; + } + + void SetData(const uint8_t *stackMapData, size_t codeLength) + { + if (stackMapData == nullptr) { + LOG_ECMA_MEM(ERROR) << "data is null in creating new code object"; + return; + } + if (memcpy_s(reinterpret_cast(this->GetDataOffsetAddress()), + this->GetInstructionSizeInBytes().GetInt(), stackMapData, codeLength) != EOK) { + LOG_ECMA_MEM(ERROR) << "memcpy fail in creating new code object "; + return; + } + } + + void VisitRangeSlot([[maybe_unused]] const EcmaObjectRangeVisitor &v) + { + // left blank deliberately,only need to visit TaggedObject type object. + } + + void VisitObjects([[maybe_unused]] const EcmaObjectRangeVisitor &visitor) const + { + // left blank deliberately,only need to visit TaggedObject type object. + } + + size_t GetMachineCodeObjectSize() + { + return SIZE + this->GetInstructionSizeInBytes().GetInt(); + } + + NO_COPY_SEMANTIC(MachineCode); + NO_MOVE_SEMANTIC(MachineCode); +}; +} // namespace ecmascript +} // namespace panda + +#endif // PANDA_RUNTIME_ECMASCRIPT_MEM_MACHINE_CODE_H diff --git a/runtime/mem/mark_stack-inl.h b/runtime/mem/mark_stack-inl.h new file mode 100644 index 000000000..11cb4e9d4 --- /dev/null +++ b/runtime/mem/mark_stack-inl.h @@ -0,0 +1,64 @@ +/* + * Copyright (c) 2021-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_MEM_MARK_STACK_INL_H +#define ECMASCRIPT_MEM_MARK_STACK_INL_H + +#include "plugins/ecmascript/runtime/mem/mark_stack.h" +#include "plugins/ecmascript/runtime/mem/region_factory.h" + +namespace panda::ecmascript { +template +void ContinuousStack::BeginMarking(Heap *heap, ContinuousStack *other) +{ + heap_ = heap; + currentArea_ = other->currentArea_; + if (currentArea_ == nullptr) { + currentArea_ = RegionFactory::AllocateSpace(DEFAULT_MARK_STACK_SIZE); + } + ResetBegin(currentArea_->GetBegin(), currentArea_->GetEnd()); +} + +template +void ContinuousStack::FinishMarking(ContinuousStack *other) +{ + other->currentArea_ = currentArea_; + + while (!unusedList_.IsEmpty()) { + Area *node = unusedList_.PopBack(); + RegionFactory::FreeSpace(node); + } +} + +template +void ContinuousStack::Extend() +{ + auto area = RegionFactory::AllocateSpace(DEFAULT_MARK_STACK_SIZE); + areaList_.AddNode(currentArea_); + currentArea_ = area; + ResetBegin(currentArea_->GetBegin(), currentArea_->GetEnd()); +} + +template +void ContinuousStack::Destroy() +{ + if (currentArea_ != nullptr) { + RegionFactory::FreeSpace(currentArea_); + currentArea_ = nullptr; + } +} +} // namespace panda::ecmascript + +#endif // ECMASCRIPT_MEM_MARK_STACK_INL_H diff --git a/runtime/mem/mark_stack.h b/runtime/mem/mark_stack.h new file mode 100644 index 000000000..2cec86021 --- /dev/null +++ b/runtime/mem/mark_stack.h @@ -0,0 +1,147 @@ +/* + * Copyright (c) 2021-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_MEM_MARK_STACK_H +#define ECMASCRIPT_MEM_MARK_STACK_H + +#include "plugins/ecmascript/runtime/mem/ecma_list.h" +#include "plugins/ecmascript/runtime/mem/space.h" +#include "plugins/ecmascript/runtime/mem/area.h" +#include "plugins/ecmascript/runtime/mem/region_factory.h" +#include "plugins/ecmascript/runtime/js_tagged_value.h" + +namespace panda { +namespace ecmascript { +class Stack { +public: + Stack() = default; + virtual ~Stack() = default; + NO_COPY_SEMANTIC(Stack); + NO_MOVE_SEMANTIC(Stack); + uintptr_t GetBegin() const + { + return begin_; + } + + uintptr_t PopBackChecked() + { + if (UNLIKELY(top_ <= reinterpret_cast(begin_))) { + return 0; + } + // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-pointer-arithmetic) + return *--top_; + } + + void PushBackUnchecked(uintptr_t obj) + { + // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-pointer-arithmetic) + *top_++ = obj; + } + + uintptr_t PopBackUnchecked() + { + // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-pointer-arithmetic) + return *--top_; + } + + bool PushBackChecked(uintptr_t obj) + { + if (UNLIKELY(top_ >= end_)) { + return false; + } + // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-pointer-arithmetic) + *top_++ = obj; + return true; + } + + bool IsEmpty() const + { + return top_ == reinterpret_cast(begin_); + } + + void ResetBegin(uintptr_t begin, uintptr_t end) + { + begin_ = begin; + top_ = reinterpret_cast(begin); + end_ = reinterpret_cast(end); + } + + void ResetTop(uintptr_t begin, uintptr_t end) + { + begin_ = begin; + top_ = end_ = reinterpret_cast(end); + } + +private: + template + friend class ContinuousStack; + friend class WorkNode; + uintptr_t begin_{0}; + uintptr_t *end_{nullptr}; + uintptr_t *top_{nullptr}; +}; + +template +class ContinuousStack : public Stack { +public: + ContinuousStack() = default; + explicit ContinuousStack(Heap *heap) : heap_(heap) {} + ~ContinuousStack() override = default; + NO_COPY_SEMANTIC(ContinuousStack); + NO_MOVE_SEMANTIC(ContinuousStack); + + inline void BeginMarking(Heap *heap, ContinuousStack *other); + inline void FinishMarking(ContinuousStack *other); + + T *PopBack() + { + if (UNLIKELY(top_ <= reinterpret_cast(begin_))) { + if (!areaList_.IsEmpty()) { + unusedList_.AddNode(currentArea_); + Area *last = areaList_.PopBack(); + currentArea_ = last; + ResetTop(currentArea_->GetBegin(), currentArea_->GetEnd()); + } else { + return nullptr; + } + } + return reinterpret_cast(PopBackUnchecked()); + } + + void PushBack(T *obj) + { + if (UNLIKELY(top_ >= end_)) { + Extend(); + } + PushBackUnchecked(ToUintPtr(obj)); + } + + inline void Destroy(); + +private: + inline void Extend(); + + Heap *heap_{nullptr}; + Area *currentArea_{nullptr}; + EcmaList areaList_{}; + EcmaList unusedList_{}; +}; + +using MarkStack = ContinuousStack; +using ProcessQueue = ContinuousStack; +} // namespace ecmascript +} // namespace panda + +#endif // ECMASCRIPT_MEM_MARK_STACK_H diff --git a/runtime/mem/mark_word.h b/runtime/mem/mark_word.h new file mode 100644 index 000000000..a6bc442ce --- /dev/null +++ b/runtime/mem/mark_word.h @@ -0,0 +1,76 @@ +/* + * Copyright (c) 2021-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_MEM_MARK_WORD_H +#define ECMASCRIPT_MEM_MARK_WORD_H + +#include +#include + +#include "utils/bit_field.h" +#include "libpandabase/mem/mem.h" + +namespace panda { +namespace ecmascript { +class TaggedObject; +class JSHClass; + +using MarkWordType = uint64_t; + +class MarkWord { +public: + // ForwardingAddress mark, this object has been moved and the address points to the newly allocated space. + static constexpr MarkWordType TAG_MARK_BIT = 0x02ULL; + + explicit MarkWord(TaggedObject *header) + { + value_ = reinterpret_cast *>(header)->load(std::memory_order_acquire); + } + ~MarkWord() = default; + NO_COPY_SEMANTIC(MarkWord); + NO_MOVE_SEMANTIC(MarkWord); + + bool IsForwardingAddress() + { + return (value_ & TAG_MARK_BIT) != 0; + } + + TaggedObject *ToForwardingAddress() + { + return reinterpret_cast(value_ & (~TAG_MARK_BIT)); + } + + static MarkWordType FromForwardingAddress(MarkWordType forwardAddress) + { + return forwardAddress | TAG_MARK_BIT; + } + + MarkWordType GetValue() const + { + return value_; + } + + JSHClass *GetJSHClass() const + { + return reinterpret_cast(value_ & (~TAG_MARK_BIT)); + } + +private: + MarkWordType value_{0}; +}; +} // namespace ecmascript +} // namespace panda + +#endif // ECMASCRIPT_MEM_MARK_WORD_H diff --git a/runtime/mem/mem.h b/runtime/mem/mem.h new file mode 100644 index 000000000..f2279707f --- /dev/null +++ b/runtime/mem/mem.h @@ -0,0 +1,122 @@ +/* + * Copyright (c) 2021-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_MEM_MEM_H +#define ECMASCRIPT_MEM_MEM_H + +#include + +#include "libpandabase/mem/mem.h" +#include "plugins/ecmascript/runtime/mem/tagged_object.h" +#include "libpandabase/utils/logger.h" +#include "mem/gc/bitmap.h" + +// NOLINTNEXTLINE(cppcoreguidelines-macro-usage, bugprone-lambda-function-name) +#define LOG_ECMA_MEM(type) LOG(type, ECMASCRIPT) << __func__ << " Line:" << __LINE__ << " " + +namespace panda::ecmascript { +enum class MemAlignment : uint8_t { + MEM_ALIGN_OBJECT = 8, + MEM_ALIGN_REGION = 16, +}; + +static constexpr size_t DEFAULT_SEMI_SPACE_SIZE = 1024 * 1024; +static constexpr size_t MIN_AllOC_LIMIT_GROWING_STEP = 2 * 1024 * 1024; + +#if defined(IS_STANDARD_SYSTEM) +static constexpr size_t SEMI_SPACE_SIZE_CAPACITY = 3 * 1024 * 1024; +static constexpr size_t MAX_SEMI_SPACE_SIZE_STARTUP = 3 * 1024 * 1024; +static constexpr size_t SEMI_SPACE_TRIGGER_CONCURRENT_MARK = 2.5 * 1024 * 1024; +static constexpr size_t SEMI_SPACE_OVERSHOOT_SIZE = 2 * 1024 * 1024; +#else +static constexpr size_t SEMI_SPACE_SIZE_CAPACITY = 4 * 1024 * 1024; +static constexpr size_t MAX_SEMI_SPACE_SIZE_STARTUP = 16 * 1024 * 1024; +static constexpr size_t SEMI_SPACE_TRIGGER_CONCURRENT_MARK = 3.5 * 1024 * 1024; +static constexpr size_t SEMI_SPACE_OVERSHOOT_SIZE = 2 * 1024 * 1024; +#endif + +static constexpr size_t OLD_SPACE_LIMIT_BEGIN = 10 * 1024 * 1024; +static constexpr size_t GLOBAL_SPACE_LIMIT_BEGIN = 20 * 1024 * 1024; + +static constexpr size_t DEFAULT_OLD_SPACE_SIZE = 2 * 1024 * 1024; +static constexpr size_t MAX_OLD_SPACE_SIZE = 256 * 1024 * 1024; + +static constexpr size_t DEFAULT_NON_MOVABLE_SPACE_SIZE = 2 * 1024 * 1024; +static constexpr size_t MAX_NON_MOVABLE_SPACE_SIZE = 256 * 1024 * 1024; +static constexpr size_t DEFAULT_NON_MOVABLE_SPACE_LIMIT = 4 * 1024 * 1024; + +static constexpr size_t REGION_SIZE_LOG2 = 18U; +static constexpr size_t DEFAULT_SNAPSHOT_SPACE_SIZE = 1U << REGION_SIZE_LOG2; +static constexpr size_t MAX_SNAPSHOT_SPACE_SIZE = 8 * 1024 * 1024; + +static constexpr size_t DEFAULT_MACHINE_CODE_SPACE_SIZE = 256 * 1024; +static constexpr size_t DEFAULT_MACHINE_CODE_SPACE_LIMIT = 1024 * 1024; +static constexpr size_t MAX_MACHINE_CODE_SPACE_SIZE = 8 * 1024 * 1024; + +static constexpr size_t MAX_HEAP_SIZE = 256 * 1024 * 1024; +static constexpr size_t HALF_MAX_HEAP_SIZE = MAX_HEAP_SIZE / 2; +static constexpr size_t DEFAULT_HEAP_SIZE = 5 * 1024 * 1024; + +static constexpr size_t DEFAULT_REGION_SIZE = 1U << REGION_SIZE_LOG2; +static constexpr size_t DEFAULT_REGION_MASK = DEFAULT_REGION_SIZE - 1; + +static constexpr size_t DEFAULT_MARK_STACK_SIZE = 4 * 1024; + +// Objects which are larger than half of the region size are 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 +// region as the border of regular objects. +static constexpr size_t MAX_32BIT_OBJECT_SPACE_SIZE = 1 * 1024 * 1024 * 1024; +static constexpr size_t MAX_REGULAR_HEAP_OBJECT_SIZE = DEFAULT_REGION_SIZE * 2 / 3; +static constexpr size_t MAX_HUGE_OBJECT_SIZE = 256 * 1024 * 1024; +static constexpr size_t MAX_HUGE_OBJECT_SPACE_SIZE = 256 * 1024 * 1024; +static constexpr size_t LARGE_BITMAP_MIN_SIZE = static_cast(MemAlignment::MEM_ALIGN_OBJECT) + << mem::Bitmap::LOG_BITSPERWORD; + +static constexpr size_t SMALL_OBJECT_SIZE = 8 * 1024; + +// internal allocator +static constexpr size_t CHUNK_ALIGN_SIZE = 4 * 1024; +static constexpr size_t MIN_CHUNK_AREA_SIZE = 4 * 1024; +static constexpr size_t MAX_CACHED_CHUNK_AREA_SIZE = 16 * 1024; +static constexpr size_t MAX_CHUNK_AREA_SIZE = 1 * 1024 * 1024; + +static constexpr uintptr_t PANDA_32BITS_HEAP_START_ADDRESS_256 = 256_KB; +static constexpr double SEMI_SPACE_RETENTION_RATIO = 0.75; + +template +constexpr inline bool IsAligned(T value, size_t alignment) +{ + return (value & (alignment - 1U)) == 0; +} + +template +inline T AlignDown(T x, size_t alignment) +{ + ASSERT(std::is_integral::value); + // alignment must be a power of two. + ASSERT(alignment != 0 && ((alignment & (alignment - 1U)) == 0)); + return x & ~(alignment - 1U); +} + +template +inline T AlignUp(T x, size_t alignment) +{ + ASSERT(std::is_integral::value); + return AlignDown(static_cast(x + alignment - 1U), alignment); +} +} // namespace panda::ecmascript + +#endif // ECMASCRIPT_MEM_MEM_H diff --git a/runtime/mem/mem_controller.cpp b/runtime/mem/mem_controller.cpp new file mode 100644 index 000000000..8dcdae6cf --- /dev/null +++ b/runtime/mem/mem_controller.cpp @@ -0,0 +1,236 @@ +/* + * Copyright (c) 2021-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 "plugins/ecmascript/runtime/mem/mem_controller.h" + +#include "plugins/ecmascript/runtime/mem/concurrent_marker.h" +#include "plugins/ecmascript/runtime/mem/mem_manager.h" +#include "plugins/ecmascript/runtime/mem/heap-inl.h" +#include "plugins/ecmascript/runtime/mem/parallel_evacuation.h" + +namespace panda::ecmascript { +MemController::MemController(Heap *heap, bool isDelayGCMode) : heap_(heap), isDelayGCMode_(isDelayGCMode) {} + +double MemController::CalculateAllocLimit(size_t currentSize, size_t minSize, size_t maxSize, + [[maybe_unused]] size_t newSpaceCapacity, double factor) const +{ + const uint64_t limit = std::max(static_cast(currentSize * factor), + static_cast(currentSize) + MIN_AllOC_LIMIT_GROWING_STEP) + + newSpaceCapacity; + + const uint64_t limitAboveMinSize = std::max(limit, minSize); + const uint64_t halfToMaxSize = (static_cast(currentSize) + maxSize) / 2; + const auto result = static_cast(std::min(limitAboveMinSize, halfToMaxSize)); + return result; +} + +double MemController::CalculateGrowingFactor(double gcSpeed, double mutatorSpeed) +{ + static constexpr double minGrowingFactor = 1.3; + static constexpr double maxGrowingFactor = 2.0; + static constexpr double targetMutatorUtilization = 0.97; + if (gcSpeed == 0 || mutatorSpeed == 0) { + return maxGrowingFactor; + } + + const double speedRatio = gcSpeed / mutatorSpeed; + + const double a = speedRatio * (1 - targetMutatorUtilization); + const double b = speedRatio * (1 - targetMutatorUtilization) - targetMutatorUtilization; + + double factor = (a < b * maxGrowingFactor) ? a / b : maxGrowingFactor; + factor = std::min(maxGrowingFactor, factor); + factor = std::max(factor, minGrowingFactor); + return factor; +} + +void MemController::StartCalculationBeforeGC() +{ + startCounter_++; + if (startCounter_ != 1) { + return; + } + + auto heapManager = heap_->GetHeapManager(); + // It's unnecessary to calculate newSpaceAllocAccumulatorSize. newSpaceAllocBytesSinceGC can be calculated directly. + size_t newSpaceAllocBytesSinceGC = heap_->GetNewSpace()->GetAllocatedSizeSinceGC(); + size_t hugeObjectAllocSizeSinceGC = heap_->GetHugeObjectSpace()->GetHeapObjectSize() - hugeObjectAllocSizeSinceGC_; + size_t oldSpaceAllocAccumulatorSize = + heapManager->GetOldSpaceAllocator().GetAllocatedSize() + heap_->GetEvacuation()->GetPromotedAccumulatorSize(); + size_t nonMovableSpaceAllocAccumulatorSize = heapManager->GetNonMovableSpaceAllocator().GetAllocatedSize(); + size_t codeSpaceAllocAccumulatorSize = heapManager->GetMachineCodeSpaceAllocator().GetAllocatedSize(); + if (allocTimeMs_ == 0) { + allocTimeMs_ = GetSystemTimeInMs(); + oldSpaceAllocAccumulatorSize_ = oldSpaceAllocAccumulatorSize; + nonMovableSpaceAllocAccumulatorSize_ = nonMovableSpaceAllocAccumulatorSize; + codeSpaceAllocAccumulatorSize_ = codeSpaceAllocAccumulatorSize; + return; + } + double currentTimeInMs = GetSystemTimeInMs(); + gcStartTime_ = currentTimeInMs; + size_t oldSpaceAllocSize = oldSpaceAllocAccumulatorSize - oldSpaceAllocAccumulatorSize_; + size_t nonMovableSpaceAllocSize = nonMovableSpaceAllocAccumulatorSize - nonMovableSpaceAllocAccumulatorSize_; + size_t codeSpaceAllocSize = codeSpaceAllocAccumulatorSize - codeSpaceAllocAccumulatorSize_; + + double duration = currentTimeInMs - allocTimeMs_; + allocTimeMs_ = currentTimeInMs; + oldSpaceAllocAccumulatorSize_ = oldSpaceAllocAccumulatorSize; + nonMovableSpaceAllocAccumulatorSize_ = nonMovableSpaceAllocAccumulatorSize; + codeSpaceAllocAccumulatorSize_ = codeSpaceAllocAccumulatorSize; + allocDurationSinceGc_ += duration; + newSpaceAllocSizeSinceGC_ += newSpaceAllocBytesSinceGC; + oldSpaceAllocSizeSinceGC_ += oldSpaceAllocSize; + oldSpaceAllocSizeSinceGC_ += hugeObjectAllocSizeSinceGC; + nonMovableSpaceAllocSizeSinceGC_ += nonMovableSpaceAllocSize; + codeSpaceAllocSizeSinceGC_ += codeSpaceAllocSize; +} + +void MemController::StopCalculationAfterGC(TriggerGCType gcType) +{ + startCounter_--; + if (startCounter_ != 0) { + return; + } + + gcEndTime_ = GetSystemTimeInMs(); + allocTimeMs_ = gcEndTime_; + if (allocDurationSinceGc_ > 0) { + recordedNewSpaceAllocations_.Push(MakeBytesAndDuration(newSpaceAllocSizeSinceGC_, allocDurationSinceGc_)); + recordedOldSpaceAllocations_.Push(MakeBytesAndDuration(oldSpaceAllocSizeSinceGC_, allocDurationSinceGc_)); + recordedNonmovableSpaceAllocations_.Push( + MakeBytesAndDuration(nonMovableSpaceAllocSizeSinceGC_, allocDurationSinceGc_)); + recordedCodeSpaceAllocations_.Push(MakeBytesAndDuration(codeSpaceAllocSizeSinceGC_, allocDurationSinceGc_)); + } + allocDurationSinceGc_ = 0.0; + newSpaceAllocSizeSinceGC_ = 0; + oldSpaceAllocSizeSinceGC_ = 0; + nonMovableSpaceAllocSizeSinceGC_ = 0; + codeSpaceAllocSizeSinceGC_ = 0; + + hugeObjectAllocSizeSinceGC_ = heap_->GetHugeObjectSpace()->GetHeapObjectSize(); + + double duration = gcEndTime_ - gcStartTime_; + + switch (gcType) { + case TriggerGCType::SEMI_GC: + case TriggerGCType::NON_MOVE_GC: + case TriggerGCType::HUGE_GC: + case TriggerGCType::MACHINE_CODE_GC: + break; + case TriggerGCType::OLD_GC: + case TriggerGCType::COMPRESS_FULL_GC: { + size_t heapObjectSize = heap_->GetHeapObjectSize(); + recordedMarkCompacts_.Push(MakeBytesAndDuration(heapObjectSize, duration)); + break; + } + default: + UNREACHABLE(); + break; + } +} + +void MemController::RecordAfterConcurrentMark(const bool isSemi, const ConcurrentMarker *marker) +{ + double duration = marker->GetDuration(); + if (isSemi) { + recordedSemiConcurrentMarks_.Push(MakeBytesAndDuration(marker->GetHeapObjectSize(), duration)); + } else { + recordedConcurrentMarks_.Push(MakeBytesAndDuration(marker->GetHeapObjectSize(), duration)); + } +} + +double MemController::CalculateMarkCompactSpeedPerMS() +{ + if (markCompactSpeedCache_ > 0) { + return markCompactSpeedCache_; + } + markCompactSpeedCache_ = CalculateAverageSpeed(recordedMarkCompacts_); + if (markCompactSpeedCache_ > 0) { + return markCompactSpeedCache_; + } + return 0; +} + +double MemController::CalculateAverageSpeed(const base::GCRingBuffer &buffer, + const BytesAndDuration &initial, double timeMs) +{ + BytesAndDuration sum = buffer.Sum( + [timeMs](BytesAndDuration a, BytesAndDuration b) { + if (timeMs != 0 && a.second >= timeMs) { + return a; + } + return std::make_pair(a.first + b.first, a.second + b.second); + }, + initial); + uint64_t bytes = sum.first; + double durations = sum.second; + if (durations == 0.0) { + return 0; + } + double speed = bytes / durations; + const int maxSpeed = 1024 * 1024 * 1024; + const int minSpeed = 1; + if (speed >= maxSpeed) { + return maxSpeed; + } + if (speed <= minSpeed) { + return minSpeed; + } + return speed; +} + +double MemController::CalculateAverageSpeed(const base::GCRingBuffer &buffer) +{ + return CalculateAverageSpeed(buffer, MakeBytesAndDuration(0, 0), 0); +} + +double MemController::GetCurrentOldSpaceAllocationThroughtputPerMS(double timeMs) const +{ + size_t allocatedSize = oldSpaceAllocSizeSinceGC_; + double duration = allocDurationSinceGc_; + return CalculateAverageSpeed(recordedOldSpaceAllocations_, MakeBytesAndDuration(allocatedSize, duration), timeMs); +} + +double MemController::GetNewSpaceAllocationThroughtPerMS() const +{ + return CalculateAverageSpeed(recordedNewSpaceAllocations_); +} + +double MemController::GetNewSpaceConcurrentMarkSpeedPerMS() const +{ + return CalculateAverageSpeed(recordedSemiConcurrentMarks_); +} + +double MemController::GetOldSpaceAllocationThroughtPerMS() const +{ + return CalculateAverageSpeed(recordedOldSpaceAllocations_); +} + +double MemController::GetFullSpaceConcurrentMarkSpeedPerMS() const +{ + return CalculateAverageSpeed(recordedConcurrentMarks_); +} + +MemController *CreateMemController(Heap *heap, std::string_view gcTriggerType) +{ + MemController *ret {nullptr}; + if (gcTriggerType == "no-gc-for-start-up") { + ret = new MemController(heap, true); + } else if (gcTriggerType == "heap-trigger") { + ret = new MemController(heap, false); + } + return ret; +} +} // namespace panda::ecmascript diff --git a/runtime/mem/mem_controller.h b/runtime/mem/mem_controller.h new file mode 100644 index 000000000..526027080 --- /dev/null +++ b/runtime/mem/mem_controller.h @@ -0,0 +1,171 @@ +/* + * Copyright (c) 2021-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_MEM_MEM_CONTROLLER_H +#define ECMASCRIPT_MEM_MEM_CONTROLLER_H + +#include + +#include "plugins/ecmascript/runtime/base/gc_ring_buffer.h" +#include "plugins/ecmascript/runtime/mem/heap.h" +#include "plugins/ecmascript/runtime/mem/mem.h" + +namespace panda::ecmascript { +constexpr static int MILLISECONDS_PER_SECOND = 1000; + +using BytesAndDuration = std::pair; + +inline BytesAndDuration MakeBytesAndDuration(uint64_t bytes, double duration) +{ + return std::make_pair(bytes, duration); +} + +class MemController { +public: + explicit MemController(Heap *heap, bool isDelayGCMode = false); + MemController() = default; + ~MemController() = default; + NO_COPY_SEMANTIC(MemController); + NO_MOVE_SEMANTIC(MemController); + + static double GetSystemTimeInMs() + { + double currentTime = + std::chrono::duration(std::chrono::high_resolution_clock::now().time_since_epoch()).count(); + return currentTime * MILLISECOND_PER_SECOND; + } + + double CalculateAllocLimit(size_t currentSize, size_t minSize, size_t maxSize, size_t newSpaceCapacity, + double factor) const; + + double CalculateGrowingFactor(double gcSpeed, double mutatorSpeed); + + inline bool IsDelayGCMode() const + { + return isDelayGCMode_; + } + + void ResetDelayGCMode() + { + isDelayGCMode_ = false; + } + + void StartCalculationBeforeGC(); + void StopCalculationAfterGC(TriggerGCType gcType); + + void RecordAfterConcurrentMark(bool isSemi, const ConcurrentMarker *marker); + + double CalculateMarkCompactSpeedPerMS(); + double GetCurrentOldSpaceAllocationThroughtputPerMS(double timeMs = THROUGHPUT_TIME_FRAME_MS) const; + double GetNewSpaceAllocationThroughtPerMS() const; + double GetOldSpaceAllocationThroughtPerMS() const; + double GetNewSpaceConcurrentMarkSpeedPerMS() const; + double GetFullSpaceConcurrentMarkSpeedPerMS() const; + + double GetAllocTimeMs() const + { + return allocTimeMs_; + } + + size_t GetOldSpaceAllocAccumulatorSize() const + { + return oldSpaceAllocAccumulatorSize_; + } + + size_t GetNonMovableSpaceAllocAccumulatorSize() const + { + return nonMovableSpaceAllocAccumulatorSize_; + } + + size_t GetCodeSpaceAllocAccumulatorSize() const + { + return codeSpaceAllocAccumulatorSize_; + } + + double GetAllocDurationSinceGc() const + { + return allocDurationSinceGc_; + } + + size_t GetNewSpaceAllocSizeSinceGC() const + { + return newSpaceAllocSizeSinceGC_; + } + + size_t GetOldSpaceAllocSizeSinceGC() const + { + return oldSpaceAllocSizeSinceGC_; + } + + size_t GetNonMovableSpaceAllocSizeSinceGC() const + { + return nonMovableSpaceAllocSizeSinceGC_; + } + + size_t GetCodeSpaceAllocSizeSinceGC() const + { + return codeSpaceAllocSizeSinceGC_; + } + + size_t GetHugeObjectAllocSizeSinceGC() const + { + return hugeObjectAllocSizeSinceGC_; + } + +private: + static constexpr int LENGTH = 10; + static double CalculateAverageSpeed(const base::GCRingBuffer &buffer); + static double CalculateAverageSpeed(const base::GCRingBuffer &buffer, + const BytesAndDuration &initial, double timeMs); + + Heap *heap_ {nullptr}; + + double gcStartTime_ {0.0}; + double gcEndTime_ {0.0}; + + // Time and allocation accumulators. + double allocTimeMs_ {0.0}; + size_t oldSpaceAllocAccumulatorSize_ {0}; + size_t nonMovableSpaceAllocAccumulatorSize_ {0}; + size_t codeSpaceAllocAccumulatorSize_ {0}; + + // Duration and allocation size in last gc. + double allocDurationSinceGc_ {0.0}; + size_t newSpaceAllocSizeSinceGC_ {0}; + size_t oldSpaceAllocSizeSinceGC_ {0}; + size_t nonMovableSpaceAllocSizeSinceGC_ {0}; + size_t codeSpaceAllocSizeSinceGC_ {0}; + size_t hugeObjectAllocSizeSinceGC_ {0}; + + int startCounter_ {0}; + double markCompactSpeedCache_ {0.0}; + + base::GCRingBuffer recordedMarkCompacts_; + base::GCRingBuffer recordedNewSpaceAllocations_; + base::GCRingBuffer recordedOldSpaceAllocations_; + base::GCRingBuffer recordedNonmovableSpaceAllocations_; + base::GCRingBuffer recordedCodeSpaceAllocations_; + + base::GCRingBuffer recordedConcurrentMarks_; + base::GCRingBuffer recordedSemiConcurrentMarks_; + bool isDelayGCMode_ {false}; + + static constexpr double THROUGHPUT_TIME_FRAME_MS = 5000; + static constexpr int MILLISECOND_PER_SECOND = 1000; +}; + +MemController *CreateMemController(Heap *heap, std::string_view gcTriggerType); +} // namespace panda::ecmascript +#endif // ECMASCRIPT_MEM_MEM_CONTROLLER_H diff --git a/runtime/mem/mem_manager-inl.h b/runtime/mem/mem_manager-inl.h new file mode 100644 index 000000000..54054aff9 --- /dev/null +++ b/runtime/mem/mem_manager-inl.h @@ -0,0 +1,158 @@ +/* + * Copyright (c) 2021-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_MEM_HEAP_MANAGER_INL_H +#define ECMASCRIPT_MEM_HEAP_MANAGER_INL_H + +#include "plugins/ecmascript/runtime/mem/mem_manager.h" + +#include + +#include "plugins/ecmascript/runtime/mem/allocator-inl.h" +#include "plugins/ecmascript/runtime/mem/heap-inl.h" +#include "plugins/ecmascript/runtime/mem/object_xray.h" +#include "plugins/ecmascript/runtime/js_hclass.h" +#include "plugins/ecmascript/runtime/js_hclass.h" + +namespace panda::ecmascript { +TaggedObject *MemManager::AllocateYoungGenerationOrHugeObject(JSHClass *hclass) +{ + size_t size = hclass->GetObjectSize(); + return AllocateYoungGenerationOrHugeObject(hclass, size); +} + +TaggedObject *MemManager::AllocateYoungGenerationOrHugeObject(JSHClass *hclass, size_t size) +{ + auto object = heapManager_->AllocateObject(hclass->GetHClass(), size, DEFAULT_ALIGNMENT, thread_); + // OnAllocateEvent checks object for nullptr + // SUPPRESS_CSA_NEXTLINE(alpha.core.CheckObjHeaderTypeRef) + heap_->OnAllocateEvent(reinterpret_cast(object)); + // CSA detects that OnAllocateEven potentionally may trigger GC but in the real situation cannot. + // OnAllocateEven leads to HeapSnapShot class which may allocate an object in JSObject::GetProperty. + // Snapshots doesn't work now so this situation is impossible. + // SUPPRESS_CSA_NEXTLINE(alpha.core.WasteObjHeader) + return TaggedObject::Cast(object); +} + +TaggedObject *MemManager::AllocateDynClassClass(JSHClass *hclass, size_t size) +{ + return AllocateNonMovableOrHugeObject(hclass, size); +} + +TaggedObject *MemManager::AllocateNonMovableOrHugeObject(JSHClass *hclass, size_t size) +{ + ObjectHeader *object = nullptr; + if (hclass == nullptr) { + object = heapManager_->AllocateNonMovableObject(nullptr, size, DEFAULT_ALIGNMENT, thread_); + } else { + object = heapManager_->AllocateNonMovableObject(hclass->GetHClass(), size, DEFAULT_ALIGNMENT, thread_); + } + // OnAllocateEvent checks object for nullptr + // SUPPRESS_CSA_NEXTLINE(alpha.core.CheckObjHeaderTypeRef) + heap_->OnAllocateEvent(reinterpret_cast(object)); + // CSA detects that OnAllocateEven potentionally may trigger GC but in the real situation cannot. + // OnAllocateEven leads to HeapSnapShot class which may allocate an object in JSObject::GetProperty. + // Snapshots doesn't work now so this situation is impossible. + // SUPPRESS_CSA_NEXTLINE(alpha.core.WasteObjHeader) + return TaggedObject::Cast(object); +} + +uintptr_t MemManager::AllocateSnapShotSpace(size_t size) +{ + uintptr_t object = snapshotSpaceAllocator_.Allocate(size); + if (UNLIKELY(object == 0)) { + if (!heap_->FillSnapShotSpace(&snapshotSpaceAllocator_)) { + LOG_ECMA_MEM(FATAL) << "alloc failed"; + UNREACHABLE(); + } + object = snapshotSpaceAllocator_.Allocate(size); + if (UNLIKELY(object == 0)) { + LOG_ECMA_MEM(FATAL) << "alloc failed"; + UNREACHABLE(); + } + } + return object; +} + +void MemManager::SetClass(TaggedObject *header, JSHClass *hclass) +{ + header->SetClass(hclass); +} + +TaggedObject *MemManager::AllocateNonMovableOrHugeObject(JSHClass *hclass) +{ + size_t size = hclass->GetObjectSize(); + return AllocateNonMovableOrHugeObject(hclass, size); +} + +TaggedObject *MemManager::AllocateOldGenerationOrHugeObject(JSHClass *hclass, size_t size) +{ + auto object = heapManager_->AllocateObject(hclass->GetHClass(), size, DEFAULT_ALIGNMENT, thread_); + // OnAllocateEvent checks object for nullptr + // SUPPRESS_CSA_NEXTLINE(alpha.core.CheckObjHeaderTypeRef) + heap_->OnAllocateEvent(reinterpret_cast(object)); + // CSA detects that OnAllocateEven potentionally may trigger GC but in the real situation cannot. + // OnAllocateEven leads to HeapSnapShot class which may allocate an object in JSObject::GetProperty. + // Snapshots doesn't work now so this situation is impossible. + // SUPPRESS_CSA_NEXTLINE(alpha.core.WasteObjHeader) + return TaggedObject::Cast(object); +} + +TaggedObject *MemManager::AllocateHugeObject(JSHClass *hclass, size_t size) +{ + auto object = heapManager_->AllocateObject(hclass->GetHClass(), size, DEFAULT_ALIGNMENT, thread_); + // OnAllocateEvent checks object for nullptr + // SUPPRESS_CSA_NEXTLINE(alpha.core.CheckObjHeaderTypeRef) + heap_->OnAllocateEvent(reinterpret_cast(object)); + // CSA detects that OnAllocateEven potentionally may trigger GC but in the real situation cannot. + // OnAllocateEven leads to HeapSnapShot class which may allocate an object in JSObject::GetProperty. + // Snapshots doesn't work now so this situation is impossible. + // SUPPRESS_CSA_NEXTLINE(alpha.core.WasteObjHeader) + return TaggedObject::Cast(object); +} + +TaggedObject *MemManager::AllocateMachineCodeSpaceObject(JSHClass *hclass, size_t size) +{ + auto object = reinterpret_cast(GetMachineCodeSpaceAllocator().Allocate(size)); + if (UNLIKELY(object == nullptr)) { + if (heap_->CheckAndTriggerMachineCodeGC()) { + object = reinterpret_cast(GetMachineCodeSpaceAllocator().Allocate(size)); + } + if (UNLIKELY(object == nullptr)) { + if (!heap_->FillMachineCodeSpaceAndTryGC(&GetMachineCodeSpaceAllocator())) { + return nullptr; + } + object = reinterpret_cast(GetMachineCodeSpaceAllocator().Allocate(size)); + if (UNLIKELY(object == nullptr)) { + heap_->ThrowOutOfMemoryError(size, "AllocateMachineCodeSpaceObject"); + return nullptr; + } + } + } + // hclass is a non-movable object + // SUPPRESS_CSA_NEXTLINE(alpha.core.WasteObjHeader) + SetClass(object, hclass); + // OnAllocateEvent checks object for nullptr + // SUPPRESS_CSA_NEXTLINE(alpha.core.CheckObjHeaderTypeRef) + heap_->OnAllocateEvent(reinterpret_cast(object)); + // CSA detects that OnAllocateEven potentionally may trigger GC but in the real situation cannot. + // OnAllocateEven leads to HeapSnapShot class which may allocate an object in JSObject::GetProperty. + // Snapshots doesn't work now so this situation is impossible. + // SUPPRESS_CSA_NEXTLINE(alpha.core.WasteObjHeader) + return object; +} +} // namespace panda::ecmascript + +#endif // ECMASCRIPT_MEM_HEAP_MANAGER_INL_H diff --git a/runtime/mem/mem_manager.cpp b/runtime/mem/mem_manager.cpp new file mode 100644 index 000000000..142a4d36b --- /dev/null +++ b/runtime/mem/mem_manager.cpp @@ -0,0 +1,31 @@ +/* + * Copyright (c) 2021-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 "plugins/ecmascript/runtime/mem/mem_manager-inl.h" +#include "plugins/ecmascript/runtime/mem/heap.h" + +namespace panda::ecmascript { +MemManager::MemManager(Heap *heap) + : heap_(heap), + newSpaceAllocator_(heap->GetNewSpace()), + freeListAllocator_ { FreeListAllocator(heap->GetOldSpace()), FreeListAllocator(heap_->GetNonMovableSpace()), + FreeListAllocator(heap->GetMachineCodeSpace()) } +{ + ASSERT(heap != nullptr); + heap->SetHeapManager(this); + heapManager_ = heap->GetEcmaVM()->GetHeapManager(); + thread_ = heap->GetEcmaVM()->GetAssociatedJSThread(); +} +} // namespace panda::ecmascript diff --git a/runtime/mem/mem_manager.h b/runtime/mem/mem_manager.h new file mode 100644 index 000000000..bfa1bfc92 --- /dev/null +++ b/runtime/mem/mem_manager.h @@ -0,0 +1,90 @@ +/* + * Copyright (c) 2021-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_MEM_JS_MEM_MANAGER_H +#define ECMASCRIPT_MEM_JS_MEM_MANAGER_H + +#include "plugins/ecmascript/runtime/mem/allocator-inl.h" +#include "plugins/ecmascript/runtime/js_hclass.h" + +namespace panda::ecmascript { +class Heap; + +class MemManager { +public: + explicit MemManager(Heap *heap); + ~MemManager() = default; + + NO_COPY_SEMANTIC(MemManager); + NO_MOVE_SEMANTIC(MemManager); + + inline TaggedObject *AllocateYoungGenerationOrHugeObject(JSHClass *hclass); + inline TaggedObject *AllocateYoungGenerationOrHugeObject(JSHClass *hclass, size_t size); + inline TaggedObject *AllocateNonMovableOrHugeObject(JSHClass *hclass, size_t size); + inline TaggedObject *AllocateDynClassClass(JSHClass *hclass, size_t size); + inline TaggedObject *AllocateNonMovableOrHugeObject(JSHClass *hclass); + inline TaggedObject *AllocateHugeObject(JSHClass *hclass, size_t size); + inline TaggedObject *AllocateOldGenerationOrHugeObject(JSHClass *hclass, size_t size); + inline TaggedObject *AllocateMachineCodeSpaceObject(JSHClass *hclass, size_t size); + inline uintptr_t AllocateSnapShotSpace(size_t size); + + inline void SetClass(TaggedObject *header, JSHClass *hclass); + + const Heap *GetHeap() const + { + return heap_; + } + + FreeListAllocator &GetFreeListAllocator(MemSpaceType type) + { + return freeListAllocator_[type]; + } + + inline FreeListAllocator &GetOldSpaceAllocator() + { + return freeListAllocator_[OLD_SPACE]; + } + + BumpPointerAllocator &GetNewSpaceAllocator() + { + return newSpaceAllocator_; + } + + inline FreeListAllocator &GetNonMovableSpaceAllocator() + { + return freeListAllocator_[NON_MOVABLE]; + } + + const BumpPointerAllocator &GetSnapShotSpaceAllocator() const + { + return snapshotSpaceAllocator_; + } + + inline FreeListAllocator &GetMachineCodeSpaceAllocator() + { + return freeListAllocator_[MACHINE_CODE_SPACE]; + } + +private: + Heap *heap_ {nullptr}; + mem::HeapManager *heapManager_; + ManagedThread *thread_; + BumpPointerAllocator newSpaceAllocator_; + std::array freeListAllocator_; + BumpPointerAllocator snapshotSpaceAllocator_; +}; +} // namespace panda::ecmascript + +#endif // ECMASCRIPT_MEM_JS_MEM_MANAGER_H diff --git a/runtime/mem/mix_space_collector.cpp b/runtime/mem/mix_space_collector.cpp new file mode 100644 index 000000000..1cdd249e6 --- /dev/null +++ b/runtime/mem/mix_space_collector.cpp @@ -0,0 +1,141 @@ +/* + * Copyright (c) 2021-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 "plugins/ecmascript/runtime/mem/mix_space_collector.h" + +#include "plugins/ecmascript/runtime/ecma_vm.h" +#include "plugins/ecmascript/runtime/mem/barriers-inl.h" +#include "plugins/ecmascript/runtime/mem/clock_scope.h" +#include "plugins/ecmascript/runtime/mem/concurrent_marker.h" +#include "plugins/ecmascript/runtime/mem/mem_manager.h" +#include "plugins/ecmascript/runtime/mem/heap-inl.h" +#include "plugins/ecmascript/runtime/mem/object_xray-inl.h" +#include "plugins/ecmascript/runtime/mem/mark_stack.h" +#include "plugins/ecmascript/runtime/mem/mem.h" +#include "plugins/ecmascript/runtime/mem/parallel_evacuation.h" +#include "plugins/ecmascript/runtime/mem/parallel_marker-inl.h" +#include "plugins/ecmascript/runtime/mem/space-inl.h" +#include "plugins/ecmascript/runtime/runtime_call_id.h" +#include "plugins/ecmascript/runtime/vmstat/runtime_stat.h" + +namespace panda::ecmascript { +MixSpaceCollector::MixSpaceCollector(Heap *heap) : heap_(heap), workList_(heap->GetWorkList()) {} + +void MixSpaceCollector::RunPhases() +{ + [[maybe_unused]] ecmascript::JSThread *thread = heap_->GetEcmaVM()->GetJSThread(); + INTERPRETER_TRACE(thread, MixSpaceCollector_RunPhases); + ClockScope clockScope; + + ECMA_BYTRACE_NAME(BYTRACE_TAG_ARK, "MixSpaceCollector::RunPhases"); + concurrentMark_ = heap_->CheckConcurrentMark(thread); + ECMA_GC_LOG() << "concurrentMark_" << concurrentMark_; + InitializePhase(); + MarkingPhase(); + EvacuaPhases(); + SweepPhases(); + heap_->GetEvacuation()->Finalize(); + FinishPhase(); + heap_->GetEcmaVM()->GetEcmaGCStats()->StatisticOldCollector( + clockScope.GetPauseTime(), freeSize_, oldSpaceCommitSize_, nonMoveSpaceCommitSize_); + ECMA_GC_LOG() << "MixSpaceCollector::RunPhases " << clockScope.TotalSpentTime(); +} + +void MixSpaceCollector::InitializePhase() +{ + ECMA_BYTRACE_NAME(BYTRACE_TAG_ARK, "MixSpaceCollector::InitializePhase"); + if (!concurrentMark_) { + heap_->Prepare(); + if (!heap_->IsSemiMarkNeeded() && heap_->GetSweeper()->CanSelectCset()) { + const_cast(heap_->GetOldSpace())->SelectCSet(); + } + heap_->EnumerateRegions([](Region *current) { + // ensure mark bitmap + auto bitmap = current->GetMarkBitmap(); + if (bitmap == nullptr) { + current->GetOrCreateMarkBitmap(); + } else { + bitmap->ClearAllBits(); + } + auto rememberset = current->GetCrossRegionRememberedSet(); + if (rememberset != nullptr) { + rememberset->ClearAllBits(); + } + current->SetMarking(false); + }); + if (heap_->IsSemiMarkNeeded()) { + heap_->EnumerateNewSpaceRegions([](Region *current) { + current->ResetAliveObject(); + }); + } else { + heap_->EnumerateRegions([](Region *current) { + current->ResetAliveObject(); + }); + } + workList_->Initialize(TriggerGCType::OLD_GC, ParallelGCTaskPhase::OLD_HANDLE_GLOBAL_POOL_TASK); + + freeSize_ = 0; + hugeSpaceFreeSize_ = 0; + oldSpaceCommitSize_ = heap_->GetOldSpace()->GetCommittedSize(); + nonMoveSpaceCommitSize_ = heap_->GetNonMovableSpace()->GetCommittedSize(); + } +} + +void MixSpaceCollector::FinishPhase() +{ + ECMA_BYTRACE_NAME(BYTRACE_TAG_ARK, "MixSpaceCollector::FinishPhase"); + if (concurrentMark_) { + auto marker = heap_->GetConcurrentMarker(); + marker->Reset(false); + } else { + size_t aliveSize = 0; + workList_->Finish(aliveSize); + heap_->EnumerateRegions([](Region *current) { + current->ClearFlag(RegionFlags::IS_IN_PROMOTE_SET); + }); + } +} + +void MixSpaceCollector::MarkingPhase() +{ + ECMA_BYTRACE_NAME(BYTRACE_TAG_ARK, "MixSpaceCollector::MarkingPhase"); + if (concurrentMark_) { + [[maybe_unused]] ClockScope scope; + heap_->GetConcurrentMarker()->ReMarking(); + return; + } + heap_->GetNonMovableMarker()->MarkRoots(0); + if (heap_->IsSemiMarkNeeded()) { + heap_->GetNonMovableMarker()->ProcessOldToNew(0); + } else { + heap_->GetNonMovableMarker()->ProcessMarkStack(0); + } + heap_->WaitRunningTaskFinished(); +} + +void MixSpaceCollector::SweepPhases() +{ + ECMA_BYTRACE_NAME(BYTRACE_TAG_ARK, "MixSpaceCollector::SweepPhases"); + if (!heap_->IsSemiMarkNeeded()) { + heap_->GetSweeper()->SweepPhases(); + } +} + +void MixSpaceCollector::EvacuaPhases() +{ + ECMA_BYTRACE_NAME(BYTRACE_TAG_ARK, "MixSpaceCollector::EvacuaPhases"); + heap_->GetEvacuation()->Evacuate(); +} +} // namespace panda::ecmascript diff --git a/runtime/mem/mix_space_collector.h b/runtime/mem/mix_space_collector.h new file mode 100644 index 000000000..6160e68dc --- /dev/null +++ b/runtime/mem/mix_space_collector.h @@ -0,0 +1,70 @@ +/* + * Copyright (c) 2021-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_MEM_OLD_SAPACE_COLLECTOR_H +#define ECMASCRIPT_MEM_OLD_SAPACE_COLLECTOR_H + +#include "plugins/ecmascript/runtime/mem/mem.h" +#include "plugins/ecmascript/runtime/mem/heap.h" +#include "plugins/ecmascript/runtime/mem/allocator.h" +#include "plugins/ecmascript/runtime/mem/mark_stack-inl.h" +#include "plugins/ecmascript/runtime/mem/mark_word.h" +#include "plugins/ecmascript/runtime/mem/parallel_work_helper.h" +#include "plugins/ecmascript/runtime/mem/slots.h" +#include "plugins/ecmascript/runtime/mem/object_xray.h" +#include "plugins/ecmascript/runtime/mem/remembered_set.h" +#include "plugins/ecmascript/runtime/mem/semi_space_collector.h" + +namespace panda { +namespace ecmascript { +class Heap; +class JSHClass; + +class MixSpaceCollector : public GarbageCollector { +public: + explicit MixSpaceCollector(Heap *heap); + ~MixSpaceCollector() override = default; + NO_COPY_SEMANTIC(MixSpaceCollector); + NO_MOVE_SEMANTIC(MixSpaceCollector); + void RunPhases(); + + Heap *GetHeap() const + { + return heap_; + } + +private: + void InitializePhase(); + void MarkingPhase(); + void SweepPhases(); + void EvacuaPhases(); + void FinishPhase(); + + Heap *heap_; + size_t freeSize_ {0}; + size_t hugeSpaceFreeSize_ = 0; + size_t oldSpaceCommitSize_ = 0; + size_t nonMoveSpaceCommitSize_ = 0; + bool concurrentMark_ {false}; + // obtain from heap + WorkerHelper *workList_ {nullptr}; + + friend class WorkerHelper; + friend class Heap; +}; +} // namespace ecmascript +} // namespace panda + +#endif // ECMASCRIPT_MEM_OLD_SAPACE_COLLECTOR_H diff --git a/runtime/mem/object_helpers.h b/runtime/mem/object_helpers.h new file mode 100644 index 000000000..160cc19fa --- /dev/null +++ b/runtime/mem/object_helpers.h @@ -0,0 +1,16 @@ +/** + * Copyright (c) Huawei Technologies Co., Ltd. 2019-2021. All rights reserved. + */ + +#ifndef PANDA_RUNTIME_ECMASCRIPT_MEM_OBJECT_HELPERS_H +#define PANDA_RUNTIME_ECMASCRIPT_MEM_OBJECT_HELPERS_H + +#include + +namespace panda::ecmascript { + +size_t GetObjectSize(const void *mem); + +} // namespace panda::ecmascript + +#endif // PANDA_RUNTIME_ECMASCRIPT_MEM_OBJECT_HELPERS_H \ No newline at end of file diff --git a/runtime/mem/object_xray-inl.h b/runtime/mem/object_xray-inl.h new file mode 100644 index 000000000..16aae1064 --- /dev/null +++ b/runtime/mem/object_xray-inl.h @@ -0,0 +1,333 @@ +/* + * Copyright (c) 2021-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_MEM_HEAP_ROOTS_INL_H +#define ECMASCRIPT_MEM_HEAP_ROOTS_INL_H + +#include +#include "plugins/ecmascript/runtime/class_info_extractor.h" +#include "plugins/ecmascript/runtime/class_linker/program_object.h" +#include "plugins/ecmascript/runtime/ecma_module.h" +#include "plugins/ecmascript/runtime/ecma_vm.h" +#include "plugins/ecmascript/runtime/global_env.h" +#include "plugins/ecmascript/runtime/ic/ic_handler.h" +#include "plugins/ecmascript/runtime/ic/proto_change_details.h" +#include "plugins/ecmascript/runtime/jobs/micro_job_queue.h" +#include "plugins/ecmascript/runtime/jobs/pending_job.h" +#include "plugins/ecmascript/runtime/js_arguments.h" +#include "plugins/ecmascript/runtime/js_array.h" +#include "plugins/ecmascript/runtime/js_arraylist.h" +#include "plugins/ecmascript/runtime/js_array_iterator.h" +#include "plugins/ecmascript/runtime/js_arraybuffer.h" +#include "plugins/ecmascript/runtime/js_async_function.h" +#include "plugins/ecmascript/runtime/js_async_from_sync_iterator_object.h" +#include "plugins/ecmascript/runtime/js_async_generator_object.h" +#include "plugins/ecmascript/runtime/js_collator.h" +#include "plugins/ecmascript/runtime/js_dataview.h" +#include "plugins/ecmascript/runtime/js_date.h" +#include "plugins/ecmascript/runtime/js_date_time_format.h" +#include "plugins/ecmascript/runtime/js_for_in_iterator.h" +#include "plugins/ecmascript/runtime/js_function.h" +#include "plugins/ecmascript/runtime/js_generator_object.h" +#include "plugins/ecmascript/runtime/js_hclass.h" +#include "plugins/ecmascript/runtime/js_intl.h" +#include "plugins/ecmascript/runtime/js_locale.h" +#include "plugins/ecmascript/runtime/js_map.h" +#include "plugins/ecmascript/runtime/js_map_iterator.h" +#include "plugins/ecmascript/runtime/js_number_format.h" +#include "plugins/ecmascript/runtime/js_object-inl.h" +#include "plugins/ecmascript/runtime/js_plural_rules.h" +#include "plugins/ecmascript/runtime/js_primitive_ref.h" +#include "plugins/ecmascript/runtime/js_promise.h" +#include "plugins/ecmascript/runtime/js_realm.h" +#include "plugins/ecmascript/runtime/js_regexp.h" +#include "plugins/ecmascript/runtime/js_relative_time_format.h" +#include "plugins/ecmascript/runtime/js_set.h" +#include "plugins/ecmascript/runtime/js_set_iterator.h" +#include "plugins/ecmascript/runtime/js_string_iterator.h" +#include "plugins/ecmascript/runtime/js_typed_array.h" +#include "plugins/ecmascript/runtime/js_weak_container.h" +#include "plugins/ecmascript/runtime/mem/object_xray.h" +#include "plugins/ecmascript/runtime/mem/machine_code.h" +#include "plugins/ecmascript/runtime/mem/mem.h" + +namespace panda::ecmascript { +void ObjectXRay::VisitVMRoots(const RootVisitor &visitor, const RootRangeVisitor &range_visitor) const +{ + ecmaVm_->Iterate(visitor); + ecmaVm_->GetJSThread()->Iterate(visitor, range_visitor); +} + +template +// NOLINTNEXTLINE(readability-function-size) +void ObjectXRay::VisitObjectBody(TaggedObject *object, JSHClass *klass, const EcmaObjectRangeVisitor &visitor) +{ + // handle body + JSType type = klass->GetObjectType(); + switch (type) { + case JSType::JS_OBJECT: + case JSType::JS_ERROR: + case JSType::JS_EVAL_ERROR: + case JSType::JS_RANGE_ERROR: + case JSType::JS_REFERENCE_ERROR: + case JSType::JS_TYPE_ERROR: + case JSType::JS_URI_ERROR: + case JSType::JS_SYNTAX_ERROR: + case JSType::JS_ITERATOR: + JSObject::Cast(object)->VisitRangeSlot(visitor); + break; + case JSType::JS_GLOBAL_OBJECT: + JSGlobalObject::Cast(object)->VisitRangeSlot(visitor); + break; + case JSType::JS_FUNCTION_BASE: + JSFunctionBase::Cast(object)->VisitRangeSlot(visitor); + break; + case JSType::JS_FUNCTION: + JSFunction::Cast(object)->VisitRangeSlot(visitor); + break; + case JSType::JS_GENERATOR_FUNCTION: + JSGeneratorFunction::Cast(object)->VisitRangeSlot(visitor); + break; + case JSType::JS_PROXY_REVOC_FUNCTION: + JSProxyRevocFunction::Cast(object)->VisitRangeSlot(visitor); + break; + case JSType::JS_PROMISE_REACTIONS_FUNCTION: + JSPromiseReactionsFunction::Cast(object)->VisitRangeSlot(visitor); + break; + case JSType::JS_PROMISE_EXECUTOR_FUNCTION: + JSPromiseExecutorFunction::Cast(object)->VisitRangeSlot(visitor); + break; + case JSType::JS_PROMISE_ALL_RESOLVE_ELEMENT_FUNCTION: + JSPromiseAllResolveElementFunction::Cast(object)->VisitRangeSlot(visitor); + break; + case JSType::JS_ASYNC_FUNCTION: + JSAsyncFunction::Cast(object)->VisitRangeSlot(visitor); + break; + case JSType::JS_ASYNC_GENERATOR_FUNCTION: + JSAsyncGeneratorFunction::Cast(object)->VisitRangeSlot(visitor); + break; + case JSType::JS_ASYNC_AWAIT_STATUS_FUNCTION: + JSAsyncAwaitStatusFunction::Cast(object)->VisitRangeSlot(visitor); + break; + case JSType::JS_ASYNC_GENERATOR_RESOLVE_NEXT_FUNCTION: + JSAsyncGeneratorResolveNextFunction::Cast(object)->VisitRangeSlot(visitor); + break; + case JSType::JS_ASYNC_FROM_SYNC_ITERATOR_VALUE_UNWRAP_FUNCTION: + JSAsyncFromSyncIteratorValueUnwrapFunction::Cast(object)->VisitRangeSlot(visitor); + break; + case JSType::JS_REG_EXP: + JSRegExp::Cast(object)->VisitRangeSlot(visitor); + break; + case JSType::JS_SET: + JSSet::Cast(object)->VisitRangeSlot(visitor); + break; + case JSType::JS_MAP: + JSMap::Cast(object)->VisitRangeSlot(visitor); + break; + case JSType::JS_WEAK_MAP: + JSWeakMap::Cast(object)->VisitRangeSlot(visitor); + break; + case JSType::JS_WEAK_SET: + JSWeakSet::Cast(object)->VisitRangeSlot(visitor); + break; + case JSType::JS_DATE: + JSDate::Cast(object)->VisitRangeSlot(visitor); + break; + case JSType::JS_FORIN_ITERATOR: + JSForInIterator::Cast(object)->VisitRangeSlot(visitor); + break; + case JSType::JS_MAP_ITERATOR: + JSMapIterator::Cast(object)->VisitRangeSlot(visitor); + break; + case JSType::JS_SET_ITERATOR: + JSSetIterator::Cast(object)->VisitRangeSlot(visitor); + break; + case JSType::JS_ARRAY_ITERATOR: + JSArrayIterator::Cast(object)->VisitRangeSlot(visitor); + break; + case JSType::JS_STRING_ITERATOR: + JSStringIterator::Cast(object)->VisitRangeSlot(visitor); + break; + case JSType::JS_ARRAY_BUFFER: + JSArrayBuffer::Cast(object)->VisitRangeSlot(visitor); + break; + case JSType::JS_PROMISE: + JSPromise::Cast(object)->VisitRangeSlot(visitor); + break; + case JSType::JS_DATA_VIEW: + JSDataView::Cast(object)->VisitRangeSlot(visitor); + break; + case JSType::JS_BOUND_FUNCTION: + JSBoundFunction::Cast(object)->VisitRangeSlot(visitor); + break; + case JSType::JS_ARGUMENTS: + JSArguments::Cast(object)->VisitRangeSlot(visitor); + break; + case JSType::JS_GENERATOR_OBJECT: + JSGeneratorObject::Cast(object)->VisitRangeSlot(visitor); + break; + case JSType::JS_ASYNC_GENERATOR_OBJECT: + JSAsyncGeneratorObject::Cast(object)->VisitRangeSlot(visitor); + break; + case JSType::JS_ASYNC_FROM_SYNC_ITERATOR_OBJECT: + JSAsyncFromSyncIteratorObject::Cast(object)->VisitRangeSlot(visitor); + break; + case JSType::JS_ASYNC_FUNC_OBJECT: + JSAsyncFuncObject::Cast(object)->VisitRangeSlot(visitor); + break; + case JSType::JS_ARRAY: + JSArray::Cast(object)->VisitRangeSlot(visitor); + break; + case JSType::JS_TYPED_ARRAY: + case JSType::JS_INT8_ARRAY: + case JSType::JS_UINT8_ARRAY: + case JSType::JS_UINT8_CLAMPED_ARRAY: + case JSType::JS_INT16_ARRAY: + case JSType::JS_UINT16_ARRAY: + case JSType::JS_INT32_ARRAY: + case JSType::JS_UINT32_ARRAY: + case JSType::JS_FLOAT32_ARRAY: + case JSType::JS_FLOAT64_ARRAY: + JSTypedArray::Cast(object)->VisitRangeSlot(visitor); + break; + case JSType::JS_PRIMITIVE_REF: + JSPrimitiveRef::Cast(object)->VisitRangeSlot(visitor); + break; + case JSType::JS_PROXY: + JSProxy::Cast(object)->VisitRangeSlot(visitor); + break; + case JSType::HCLASS: + // semi gc is not needed to visit dyn class + if (gc_type != GCType::SEMI_GC) { + JSHClass::Cast(object)->VisitRangeSlot(visitor); + } + break; + case JSType::STRING: + case JSType::JS_NATIVE_POINTER: + break; + case JSType::TAGGED_ARRAY: + case JSType::LINKED_HASH_SET: + case JSType::LINKED_HASH_MAP: + case JSType::TAGGED_DICTIONARY: + case JSType::TEMPLATE_MAP: + TaggedArray::Cast(object)->VisitRangeSlot(visitor); + break; + case JSType::GLOBAL_ENV: + GlobalEnv::Cast(object)->VisitRangeSlot(visitor); + break; + case JSType::ACCESSOR_DATA: + case JSType::INTERNAL_ACCESSOR: + AccessorData::Cast(object)->VisitRangeSlot(visitor); + break; + case JSType::SYMBOL: + JSSymbol::Cast(object)->VisitRangeSlot(visitor); + break; + case JSType::JS_GENERATOR_CONTEXT: + GeneratorContext::Cast(object)->VisitRangeSlot(visitor); + break; + case JSType::PROTOTYPE_HANDLER: + PrototypeHandler::Cast(object)->VisitRangeSlot(visitor); + break; + case JSType::TRANSITION_HANDLER: + TransitionHandler::Cast(object)->VisitRangeSlot(visitor); + break; + case JSType::PROPERTY_BOX: + PropertyBox::Cast(object)->VisitRangeSlot(visitor); + break; + case JSType::PROTO_CHANGE_MARKER: + break; + case JSType::PROTOTYPE_INFO: + ProtoChangeDetails::Cast(object)->VisitRangeSlot(visitor); + break; + case JSType::PROMISE_CAPABILITY: + PromiseCapability::Cast(object)->VisitRangeSlot(visitor); + break; + case JSType::PROMISE_RECORD: + PromiseRecord::Cast(object)->VisitRangeSlot(visitor); + break; + case JSType::RESOLVING_FUNCTIONS_RECORD: + ResolvingFunctionsRecord::Cast(object)->VisitRangeSlot(visitor); + break; + case JSType::PROMISE_REACTIONS: + PromiseReaction::Cast(object)->VisitRangeSlot(visitor); + break; + case JSType::PROMISE_ITERATOR_RECORD: + PromiseIteratorRecord::Cast(object)->VisitRangeSlot(visitor); + break; + case JSType::MICRO_JOB_QUEUE: + job::MicroJobQueue::Cast(object)->VisitRangeSlot(visitor); + break; + case JSType::PENDING_JOB: + job::PendingJob::Cast(object)->VisitRangeSlot(visitor); + break; + case JSType::FUNCTION_EXTRA_INFO: + JSFunctionExtraInfo::Cast(object)->VisitRangeSlot(visitor); + break; + case JSType::COMPLETION_RECORD: + CompletionRecord::Cast(object)->VisitRangeSlot(visitor); + break; + case JSType::ECMA_MODULE: + EcmaModule::Cast(object)->VisitRangeSlot(visitor); + break; + case JSType::PROGRAM: + Program::Cast(object)->VisitRangeSlot(visitor); + break; + case JSType::LEXICAL_FUNCTION: + LexicalFunction::Cast(object)->VisitRangeSlot(visitor); + break; + case JSType::JS_INTL: + JSIntl::Cast(object)->VisitRangeSlot(visitor); + break; + case JSType::JS_NUMBER_FORMAT: + JSNumberFormat::Cast(object)->VisitRangeSlot(visitor); + break; + case JSType::JS_LOCALE: + JSLocale::Cast(object)->VisitRangeSlot(visitor); + break; + case JSType::JS_DATE_TIME_FORMAT: + JSDateTimeFormat::Cast(object)->VisitRangeSlot(visitor); + break; + case JSType::JS_RELATIVE_TIME_FORMAT: + JSRelativeTimeFormat::Cast(object)->VisitRangeSlot(visitor); + break; + case JSType::JS_INTL_BOUND_FUNCTION: + JSIntlBoundFunction::Cast(object)->VisitRangeSlot(visitor); + break; + case JSType::JS_REALM: + JSRealm::Cast(object)->VisitRangeSlot(visitor); + break; + case JSType::JS_COLLATOR: + JSCollator::Cast(object)->VisitRangeSlot(visitor); + break; + case JSType::JS_PLURAL_RULES: + JSPluralRules::Cast(object)->VisitRangeSlot(visitor); + break; + case JSType::MACHINE_CODE_OBJECT: + MachineCode::Cast(object)->VisitRangeSlot(visitor); + break; + case JSType::CLASS_INFO_EXTRACTOR: + ClassInfoExtractor::Cast(object)->VisitRangeSlot(visitor); + break; + case JSType::JS_QUEUE: + case JSType::JS_ARRAY_LIST: + JSArrayList::Cast(object)->VisitRangeSlot(visitor); + break; + default: + UNREACHABLE(); + } +} +} // namespace panda::ecmascript + +#endif // ECMASCRIPT_MEM_HEAP_ROOTS_INL_H diff --git a/runtime/mem/object_xray.h b/runtime/mem/object_xray.h new file mode 100644 index 000000000..ddbadd33c --- /dev/null +++ b/runtime/mem/object_xray.h @@ -0,0 +1,62 @@ +/* + * Copyright (c) 2021-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_MEM_HEAP_ROOTS_H +#define ECMASCRIPT_MEM_HEAP_ROOTS_H + +#include + +#include "plugins/ecmascript/runtime/mem/slots.h" +#include "mem/mem.h" + +namespace panda::ecmascript { +class EcmaVM; +class JSHClass; + +enum class Root { + ROOT_FRAME, + ROOT_HANDLE, + ROOT_VM, + ROOT_STRING, + ROOT_INTERNAL_CALL_PARAMS, +}; + +enum class GCType : size_t { SEMI_GC, OLD_GC }; + +using RootVisitor = std::function; +using RootRangeVisitor = std::function; +using EcmaObjectVisitor = std::function; +using EcmaObjectRangeVisitor = std::function; + +using WeakRootVisitor = std::function; + +class ObjectXRay { +public: + explicit ObjectXRay(EcmaVM *ecmaVm) : ecmaVm_(ecmaVm) {} + ~ObjectXRay() = default; + + inline void VisitVMRoots(const RootVisitor &visitor, const RootRangeVisitor &range_visitor) const; + template + inline void VisitObjectBody(TaggedObject *object, JSHClass *klass, const EcmaObjectRangeVisitor &visitor); + + DEFAULT_MOVE_SEMANTIC(ObjectXRay); + DEFAULT_COPY_SEMANTIC(ObjectXRay); + +private: + EcmaVM *ecmaVm_ {nullptr}; +}; +} // namespace panda::ecmascript + +#endif // ECMASCRIPT_MEM_HEAP_ROOTS_H diff --git a/runtime/mem/parallel_evacuation-inl.h b/runtime/mem/parallel_evacuation-inl.h new file mode 100644 index 000000000..5219e799f --- /dev/null +++ b/runtime/mem/parallel_evacuation-inl.h @@ -0,0 +1,129 @@ +/* + * Copyright (c) 2021-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_MEM_PARALLEL_EVACUATION_INL_H +#define ECMASCRIPT_MEM_PARALLEL_EVACUATION_INL_H + +#include "plugins/ecmascript/runtime/mem/parallel_evacuation.h" + +#include "plugins/ecmascript/runtime/mem/evacuation_allocator-inl.h" +#include "plugins/ecmascript/runtime/mem/heap.h" +#include "plugins/ecmascript/runtime/mem/region-inl.h" +#include "plugins/ecmascript/runtime/mem/remembered_set.h" +#include "plugins/ecmascript/runtime/platform/platform.h" +#include "mark_word.h" + +namespace panda::ecmascript { +bool ParallelEvacuation::IsPromoteComplete(Region *region) +{ + return (static_cast(region->AliveObject()) / DEFAULT_REGION_SIZE) > MIN_OBJECT_SURVIVAL_RATE && + !region->HasAgeMark(); +} + +bool ParallelEvacuation::UpdateObjectSlot(ObjectSlot &slot) +{ + JSTaggedValue value(slot.GetTaggedType()); + if (value.IsHeapObject()) { + if (value.IsWeak()) { + return UpdateWeakObjectSlot(value.GetTaggedWeakRef(), slot); + } + TaggedObject *object = value.GetTaggedObject(); + MarkWord markWord(object); + if (markWord.IsForwardingAddress()) { + TaggedObject *dst = markWord.ToForwardingAddress(); + slot.Update(dst); + } + return true; + } + return false; +} + +bool ParallelEvacuation::UpdateWeakObjectSlot(TaggedObject *value, ObjectSlot &slot) +{ + Region *objectRegion = Region::ObjectAddressToRange(value); + if (objectRegion->InYoungAndCSetGeneration() && !objectRegion->InPromoteSet()) { + MarkWord markWord(value); + if (markWord.IsForwardingAddress()) { + TaggedObject *dst = markWord.ToForwardingAddress(); + auto weakRef = JSTaggedValue(JSTaggedValue(dst).CreateAndGetWeakRef()).GetRawTaggedObject(); + slot.Update(weakRef); + return true; + } + slot.Update(static_cast(JSTaggedValue::Undefined().GetRawData())); + return false; + } + if (!heap_->IsSemiMarkNeeded() || objectRegion->InPromoteSet()) { + auto markBitmap = objectRegion->GetOrCreateMarkBitmap(); + if (!markBitmap->Test(value)) { + slot.Update(static_cast(JSTaggedValue::Undefined().GetRawData())); + return false; + } + } + return true; +} + +std::unique_ptr ParallelEvacuation::GetFragmentSafe() +{ + os::memory::LockHolder holder(mutex_); + std::unique_ptr unit; + if (!fragments_.empty()) { + unit = std::move(fragments_.back()); + fragments_.pop_back(); + } + return unit; +} + +void ParallelEvacuation::AddFragment(std::unique_ptr region) +{ + fragments_.emplace_back(std::move(region)); +} + +void ParallelEvacuation::AddSweptRegionSafe(Region *region) +{ + os::memory::LockHolder holder(mutex_); + sweptList_.emplace_back(region); +} + +void ParallelEvacuation::FillSweptRegion() +{ + while (!sweptList_.empty()) { + Region *region = sweptList_.back(); + sweptList_.pop_back(); + region->EnumerateKinds([this](FreeObjectKind *kind) { + if (kind == nullptr || kind->Empty()) { + return; + } + evacuationAllocator_->FillFreeList(kind); + }); + } +} + +int ParallelEvacuation::CalculateEvacuationThreadNum() +{ + int length = fragments_.size(); + int regionPerThread = 8; + return std::min(std::max(1, length / regionPerThread), + static_cast(Platform::GetCurrentPlatform()->GetTotalThreadNum())); +} + +int ParallelEvacuation::CalculateUpdateThreadNum() +{ + int length = fragments_.size(); + double regionPerThread = 1.0 / 4; + length = static_cast(std::pow(length, regionPerThread)); + return std::min(std::max(1, length), static_cast(Platform::GetCurrentPlatform()->GetTotalThreadNum())); +} +} // namespace panda::ecmascript +#endif // ECMASCRIPT_MEM_PARALLEL_EVACUATION_INL_H diff --git a/runtime/mem/parallel_evacuation.cpp b/runtime/mem/parallel_evacuation.cpp new file mode 100644 index 000000000..81e1da519 --- /dev/null +++ b/runtime/mem/parallel_evacuation.cpp @@ -0,0 +1,521 @@ +/* + * Copyright (c) 2021-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 "plugins/ecmascript/runtime/mem/parallel_evacuation-inl.h" + +#include "plugins/ecmascript/runtime/js_hclass-inl.h" +#include "plugins/ecmascript/runtime/mem/barriers-inl.h" +#include "plugins/ecmascript/runtime/mem/heap.h" +#include "plugins/ecmascript/runtime/mem/object_xray-inl.h" +#include "plugins/ecmascript/runtime/mem/space-inl.h" +#include "plugins/ecmascript/runtime/mem/mem.h" +#include "plugins/ecmascript/runtime/mem/tlab_allocator-inl.h" + +#ifndef __clang_analyzer__ + +namespace panda::ecmascript { +void ParallelEvacuation::Initialize() +{ + evacuationAllocator_->Initialize(TriggerGCType::OLD_GC); + allocator_ = new TlabAllocator(heap_, TriggerGCType::COMPRESS_FULL_GC); + ageMark_ = heap_->GetFromSpace()->GetAgeMark(); +} + +void ParallelEvacuation::Finalize() +{ + delete allocator_; + evacuationAllocator_->Finalize(TriggerGCType::OLD_GC); +} + +void ParallelEvacuation::Evacuate() +{ + ClockScope clockScope; + Initialize(); + EvacuateSpace(); + UpdateReference(); +} + +void ParallelEvacuation::EvacuateSpace() +{ + heap_->GetFromSpace()->EnumerateRegions( + [this](Region *current) { AddFragment(std::make_unique(this, current)); }); + if (!heap_->IsSemiMarkNeeded()) { + heap_->GetOldSpace()->EnumerateCollectRegionSet( + [this](Region *current) { AddFragment(std::make_unique(this, current)); }); + } + if (heap_->IsParallelGCEnabled()) { + os::memory::LockHolder holder(mutex_); + parallel_ = CalculateEvacuationThreadNum(); + for (int i = 0; i < parallel_; i++) { + Platform::GetCurrentPlatform()->PostTask(std::make_unique(this)); + } + } + + EvacuateSpace(allocator_, true); + WaitFinished(); +} + +bool ParallelEvacuation::EvacuateSpace(TlabAllocator *allocator, bool isMain) +{ + std::unique_ptr region = GetFragmentSafe(); + while (region != nullptr) { + EvacuateRegion(allocator, region->GetRegion()); + region = GetFragmentSafe(); + } + allocator->Finalize(); + if (!isMain) { + os::memory::LockHolder holder(mutex_); + if (--parallel_ <= 0) { + condition_.SignalAll(); + } + } + return true; +} + +void ParallelEvacuation::EvacuateRegion(TlabAllocator *allocator, Region *region) +{ + bool isPromoted = region->BelowAgeMark() || region->InOldGeneration(); + if (IsPromoteComplete(region)) { + bool ret = false; + if (isPromoted) { + if (region->InYoungGeneration()) { + promotedAccumulatorSize_.fetch_add(region->AliveObject()); + } + ret = evacuationAllocator_->AddRegionToOld(region); + } else { + ret = evacuationAllocator_->AddRegionToYoung(region); + if (!ret) { + if (region->InYoungGeneration()) { + promotedAccumulatorSize_.fetch_add(region->AliveObject()); + } + ret = evacuationAllocator_->AddRegionToOld(region); + } + } + if (!ret) { + LOG(FATAL, RUNTIME) << "Add Region failed:"; + } + } else { + auto markBitmap = region->GetOrCreateMarkBitmap(); + ASSERT(markBitmap != nullptr); + markBitmap->IterateOverMarkedChunks([this, ®ion, &isPromoted, &allocator](void *mem) { + ASSERT(region->InRange(ToUintPtr(mem))); + auto header = reinterpret_cast(mem); + auto klass = header->GetClass(); + auto size = klass->SizeFromJSHClass(header); + + uintptr_t address = 0; + if (isPromoted || (region->HasAgeMark() && ToUintPtr(mem) < ageMark_)) { + address = allocator->Allocate(size, SpaceAlloc::OLD_SPACE); + promotedAccumulatorSize_.fetch_add(size); + } else { + address = allocator->Allocate(size, SpaceAlloc::YOUNG_SPACE); + if (address == 0) { + address = allocator->Allocate(size, SpaceAlloc::OLD_SPACE); + promotedAccumulatorSize_.fetch_add(size); + } + } + LOG_IF(address == 0, FATAL, RUNTIME) << "Evacuate object failed:" << size; + + // NOLINTNEXTLINE(cppcoreguidelines-pro-type-vararg) + if (memcpy_sp(ToVoidPtr(address), size, ToVoidPtr(ToUintPtr(mem)), size) != EOK) { + LOG_ECMA(FATAL) << "memcpy_s failed"; + } + + ObjectAccessor::SetDynValueWithoutBarrier( + header, 0, JSTaggedValue(MarkWord::FromForwardingAddress(address)).GetRawData()); +#if ECMASCRIPT_ENABLE_HEAP_VERIFY + VerifyHeapObject(reinterpret_cast(address)); +#endif + }); + } +} + +void ParallelEvacuation::VerifyHeapObject(TaggedObject *object) +{ + auto klass = object->GetClass(); + objXRay_.VisitObjectBody( + object, klass, [&]([[maybe_unused]] TaggedObject *root, ObjectSlot start, ObjectSlot end) { + for (ObjectSlot slot = start; slot < end; slot++) { + JSTaggedValue value(slot.GetTaggedType()); + if (value.IsHeapObject()) { + if (value.IsWeak()) { + continue; + } + Region *object_region = Region::ObjectAddressToRange(value.GetTaggedObject()); + if (heap_->IsSemiMarkNeeded() && !object_region->InYoungGeneration()) { + continue; + } + auto reset = object_region->GetMarkBitmap(); + if (!reset->Test(value.GetTaggedObject())) { + LOG(FATAL, RUNTIME) << "Miss mark value: " << value.GetTaggedObject() + << ", body address:" << slot.SlotAddress() << ", header address:" << object; + } + } + } + }); +} + +void ParallelEvacuation::UpdateReference() +{ + // Update reference pointers + uint32_t youngeRegionMoveCount = 0; + uint32_t oldRegionMoveCount = 0; + uint32_t youngeRegionCopyCount = 0; + uint32_t oldRegionCopyCount = 0; + heap_->GetNewSpace()->EnumerateRegions([&](Region *current) { + if (current->InPromoteSet()) { + AddFragment(std::make_unique(this, current)); + youngeRegionMoveCount++; + } else { + AddFragment(std::make_unique(this, current)); + youngeRegionCopyCount++; + } + }); + heap_->GetCompressSpace()->EnumerateRegions([&](Region *current) { + if (current->InPromoteSet()) { + AddFragment(std::make_unique(this, current)); + oldRegionMoveCount++; + } else { + AddFragment(std::make_unique(this, current)); + oldRegionCopyCount++; + } + }); + heap_->EnumerateOldSpaceRegions([this](Region *current) { + if (current->InCollectSet()) { + return; + } + AddFragment(std::make_unique(this, current)); + }); + LOG(INFO, RUNTIME) << "UpdatePointers statistic: younge space region compact moving count:" << youngeRegionMoveCount + << "younge space region compact coping count:" << youngeRegionCopyCount + << "old space region compact moving count:" << oldRegionMoveCount + << "old space region compact coping count:" << oldRegionCopyCount; + + // Optimization weak reference for native pointer + UpdateWeakReference(); + if (heap_->IsParallelGCEnabled()) { + os::memory::LockHolder holder(mutex_); + parallel_ = CalculateUpdateThreadNum(); + for (int i = 0; i < parallel_; i++) { + Platform::GetCurrentPlatform()->PostTask(std::make_unique(this)); + } + } + + UpdateRoot(); + ProcessFragments(true); + WaitFinished(); + FillSweptRegion(); +} + +void ParallelEvacuation::UpdateRoot() +{ + RootVisitor gcUpdateYoung = [this]([[maybe_unused]] Root type, ObjectSlot slot) { UpdateObjectSlot(slot); }; + RootRangeVisitor gcUpdateRangeYoung = [this]([[maybe_unused]] Root type, ObjectSlot start, ObjectSlot end) { + for (ObjectSlot slot = start; slot < end; slot++) { + UpdateObjectSlot(slot); + } + }; + + objXRay_.VisitVMRoots(gcUpdateYoung, gcUpdateRangeYoung); +} + +void ParallelEvacuation::UpdateWeakReference() +{ + bool isOnlySemi = heap_->IsSemiMarkNeeded(); + auto stringTable = heap_->GetEcmaVM()->GetEcmaStringTable(); + WeakRootVisitor gcUpdateWeak = [&](TaggedObject *header) { + Region *objectRegion = Region::ObjectAddressToRange(reinterpret_cast(header)); + if (objectRegion->InYoungAndCSetGeneration() && !objectRegion->InPromoteSet()) { + MarkWord markWord(header); + if (markWord.IsForwardingAddress()) { + return markWord.ToForwardingAddress(); + } + return reinterpret_cast(ToUintPtr(nullptr)); + } + if (!isOnlySemi || objectRegion->InPromoteSet()) { + auto markBitmap = objectRegion->GetOrCreateMarkBitmap(); + if (!markBitmap->Test(header)) { + return reinterpret_cast(ToUintPtr(nullptr)); + } + } + return header; + }; + stringTable->SweepWeakReference(gcUpdateWeak); + heap_->GetEcmaVM()->GetJSThread()->IterateWeakEcmaGlobalStorage(gcUpdateWeak); + heap_->GetEcmaVM()->ProcessReferences(gcUpdateWeak); +} + +void ParallelEvacuation::UpdateRSet(Region *region) +{ + auto rememberedSet = region->GetOldToNewRememberedSet(); + if (LIKELY(rememberedSet != nullptr)) { + rememberedSet->IterateOverMarkedChunks([this, rememberedSet](void *mem) -> bool { + ObjectSlot slot(ToUintPtr(mem)); + if (UpdateObjectSlot(slot)) { + Region *valueRegion = Region::ObjectAddressToRange(slot.GetTaggedObjectHeader()); + if (!valueRegion->InYoungGeneration()) { + rememberedSet->Clear(slot.SlotAddress()); + } + } + return true; + }); + } + if (!heap_->IsSemiMarkNeeded()) { + rememberedSet = region->GetCrossRegionRememberedSet(); + if (LIKELY(rememberedSet != nullptr)) { + rememberedSet->IterateOverMarkedChunks([this](void *mem) -> bool { + ObjectSlot slot(ToUintPtr(mem)); + UpdateObjectSlot(slot); + return true; + }); + rememberedSet->ClearAllBits(); + } + } +} + +void ParallelEvacuation::UpdateCompressRegionReference(Region *region) +{ + auto curPtr = region->GetBegin(); + auto endPtr = region->GetEnd(); + auto rset = region->GetOldToNewRememberedSet(); + if (rset != nullptr) { + rset->ClearAllBits(); + } + + size_t objSize = 0; + while (curPtr < endPtr) { + auto freeObject = FreeObject::Cast(curPtr); + if (!freeObject->IsFreeObject()) { + auto obj = reinterpret_cast(curPtr); + auto klass = obj->GetClass(); + if (klass->HasReferenceField()) { + UpdateCompressObjectField(region, obj, klass); + } + objSize = klass->SizeFromJSHClass(obj); + } else { + objSize = freeObject->Available(); + } + curPtr += objSize; + CHECK_OBJECT_SIZE(objSize); + } + CHECK_REGION_END(curPtr, endPtr); +} + +void ParallelEvacuation::UpdateNewRegionReference(Region *region) +{ + Region *current = heap_->GetNewSpace()->GetCurrentRegion(); + auto curPtr = region->GetBegin(); + uintptr_t endPtr; + if (region == current) { + auto top = evacuationAllocator_->GetNewSpaceTop(); + endPtr = curPtr + region->GetAllocatedBytes(top); + } else { + endPtr = curPtr + region->GetAllocatedBytes(); + } + + size_t objSize = 0; + while (curPtr < endPtr) { + auto freeObject = FreeObject::Cast(curPtr); + if (!freeObject->IsFreeObject()) { + auto obj = reinterpret_cast(curPtr); + auto klass = obj->GetClass(); + if (klass->HasReferenceField()) { + UpdateNewObjectField(obj, klass); + } + objSize = klass->SizeFromJSHClass(obj); + } else { + objSize = freeObject->Available(); + } + curPtr += objSize; + CHECK_OBJECT_SIZE(objSize); + } + CHECK_REGION_END(curPtr, endPtr); +} + +void ParallelEvacuation::UpdateAndSweepNewRegionReference(Region *region) +{ + auto markBitmap = region->GetMarkBitmap(); + uintptr_t freeStart = region->GetBegin(); + uintptr_t freeEnd = freeStart + region->GetAllocatedBytes(); + if (markBitmap != nullptr) { + markBitmap->IterateOverMarkedChunks([this, ®ion, &freeStart](void *mem) { + ASSERT(region->InRange(ToUintPtr(mem))); + (void)region; + auto header = reinterpret_cast(mem); + JSHClass *klass = header->GetClass(); + if (klass->HasReferenceField()) { + UpdateNewObjectField(header, klass); + } + + uintptr_t fEnd = ToUintPtr(mem); + if (freeStart != fEnd) { + FreeObject::FillFreeObject(heap_->GetEcmaVM(), freeStart, fEnd - freeStart); + } + + freeStart = fEnd + klass->SizeFromJSHClass(header); + }); + } + CHECK_REGION_END(freeStart, freeEnd); + if (freeStart < freeEnd) { + FreeObject::FillFreeObject(heap_->GetEcmaVM(), freeStart, freeEnd - freeStart); + } +} + +void ParallelEvacuation::UpdateAndSweepCompressRegionReference(Region *region, bool isMain) +{ + auto markBitmap = region->GetMarkBitmap(); + auto rset = region->GetOldToNewRememberedSet(); + if (rset != nullptr) { + rset->ClearAllBits(); + } + uintptr_t freeStart = region->GetBegin(); + if (markBitmap != nullptr) { + markBitmap->IterateOverMarkedChunks([this, ®ion, &freeStart, &isMain](void *mem) { + ASSERT(region->InRange(ToUintPtr(mem))); + ASSERT(!FreeObject::Cast(ToUintPtr(mem))->IsFreeObject()); + auto header = reinterpret_cast(mem); + auto klass = header->GetClass(); + ObjectSlot slot(ToUintPtr(&klass)); + UpdateObjectSlot(slot); + if (klass->HasReferenceField()) { + UpdateCompressObjectField(region, header, klass); + } + + uintptr_t freeEnd = ToUintPtr(mem); + if (freeStart != freeEnd) { + evacuationAllocator_->Free(freeStart, freeEnd, isMain); + } + freeStart = freeEnd + klass->SizeFromJSHClass(header); + }); + } + uintptr_t freeEnd = region->GetEnd(); + CHECK_REGION_END(freeStart, freeEnd); + if (freeStart < freeEnd) { + evacuationAllocator_->Free(freeStart, freeEnd, isMain); + } + if (!isMain) { + AddSweptRegionSafe(region); + } +} + +void ParallelEvacuation::UpdateNewObjectField(TaggedObject *object, JSHClass *cls) +{ + objXRay_.VisitObjectBody( + object, cls, [this]([[maybe_unused]] TaggedObject *root, ObjectSlot start, ObjectSlot end) { + for (ObjectSlot slot = start; slot < end; slot++) { + UpdateObjectSlot(slot); + } + }); +} + +void ParallelEvacuation::UpdateCompressObjectField(Region *region, TaggedObject *object, JSHClass *cls) +{ + objXRay_.VisitObjectBody( + object, cls, [this, region]([[maybe_unused]] TaggedObject *root, ObjectSlot start, ObjectSlot end) { + for (ObjectSlot slot = start; slot < end; slot++) { + if (UpdateObjectSlot(slot)) { + Region *valueRegion = Region::ObjectAddressToRange(slot.GetTaggedObjectHeader()); + if (valueRegion->InYoungGeneration()) { + region->InsertOldToNewRememberedSet(slot.SlotAddress()); + } + } + } + }); +} + +void ParallelEvacuation::WaitFinished() +{ + if (parallel_ > 0) { + os::memory::LockHolder holder(mutex_); + while (parallel_ > 0) { + condition_.Wait(&mutex_); + } + } +} + +bool ParallelEvacuation::ProcessFragments(bool isMain) +{ + std::unique_ptr region = GetFragmentSafe(); + while (region != nullptr) { + region->Process(isMain); + region = GetFragmentSafe(); + } + if (!isMain) { + os::memory::LockHolder holder(mutex_); + if (--parallel_ <= 0) { + condition_.SignalAll(); + } + } + return true; +} + +ParallelEvacuation::EvacuationTask::EvacuationTask(ParallelEvacuation *evacuation) : evacuation_(evacuation) +{ + allocator_ = new TlabAllocator(evacuation->heap_, TriggerGCType::COMPRESS_FULL_GC); +} + +ParallelEvacuation::EvacuationTask::~EvacuationTask() +{ + allocator_->Finalize(); + delete allocator_; +} + +bool ParallelEvacuation::EvacuationTask::Run([[maybe_unused]] uint32_t threadIndex) +{ + return evacuation_->EvacuateSpace(allocator_); +} + +bool ParallelEvacuation::UpdateReferenceTask::Run([[maybe_unused]] uint32_t threadIndex) +{ + evacuation_->ProcessFragments(false); + return true; +} + +bool ParallelEvacuation::EvacuationFragment::Process([[maybe_unused]] bool isMain) +{ + return true; +} + +bool ParallelEvacuation::UpdateRSetFragment::Process([[maybe_unused]] bool isMain) +{ + GetEvacuation()->UpdateRSet(GetRegion()); + return true; +} + +bool ParallelEvacuation::UpdateNewRegionFragment::Process([[maybe_unused]] bool isMain) +{ + GetEvacuation()->UpdateNewRegionReference(GetRegion()); + return true; +} + +bool ParallelEvacuation::UpdateCompressRegionFragment::Process([[maybe_unused]] bool isMain) +{ + GetEvacuation()->UpdateCompressRegionReference(GetRegion()); + return true; +} + +bool ParallelEvacuation::UpdateAndSweepNewRegionFragment::Process([[maybe_unused]] bool isMain) +{ + GetEvacuation()->UpdateAndSweepNewRegionReference(GetRegion()); + return true; +} + +bool ParallelEvacuation::UpdateAndSweepCompressRegionFragment::Process(bool isMain) +{ + GetEvacuation()->UpdateAndSweepCompressRegionReference(GetRegion(), isMain); + return true; +} +} // namespace panda::ecmascript +#endif // __clang_analyzer__ diff --git a/runtime/mem/parallel_evacuation.h b/runtime/mem/parallel_evacuation.h new file mode 100644 index 000000000..7ac8170d4 --- /dev/null +++ b/runtime/mem/parallel_evacuation.h @@ -0,0 +1,196 @@ +/* + * Copyright (c) 2021-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_MEM_PARALLEL_EVACUATION_H +#define ECMASCRIPT_MEM_PARALLEL_EVACUATION_H + +#include +#include + +#include "plugins/ecmascript/runtime/mem/heap.h" +#include "plugins/ecmascript/runtime/mem/object_xray.h" +#include "plugins/ecmascript/runtime/mem/region.h" +#include "plugins/ecmascript/runtime/mem/remembered_set.h" +#include "plugins/ecmascript/runtime/mem/space.h" +#include "plugins/ecmascript/runtime/mem/tagged_object.h" +#include "plugins/ecmascript/runtime/platform/task.h" +#include "os/mutex.h" + +namespace panda::ecmascript { +class JSHClass; +class TlabAllocator; + +class ParallelEvacuation { +public: + explicit ParallelEvacuation(Heap *heap) + : heap_(heap), objXRay_(heap->GetEcmaVM()), evacuationAllocator_(heap_->GetEvacuationAllocator()) + { + } + ~ParallelEvacuation() = default; + void Initialize(); + void Finalize(); + void Evacuate(); + + size_t GetPromotedAccumulatorSize() const + { + return promotedAccumulatorSize_; + } + + NO_COPY_SEMANTIC(ParallelEvacuation); + NO_MOVE_SEMANTIC(ParallelEvacuation); + +private: + static constexpr double MIN_OBJECT_SURVIVAL_RATE = 0.8; + + class EvacuationTask : public Task { + public: + explicit EvacuationTask(ParallelEvacuation *evacuation); + ~EvacuationTask() override; + bool Run(uint32_t threadIndex) override; + + NO_COPY_SEMANTIC(EvacuationTask); + NO_MOVE_SEMANTIC(EvacuationTask); + + private: + ParallelEvacuation *evacuation_ {nullptr}; + TlabAllocator *allocator_ {nullptr}; + }; + + class UpdateReferenceTask : public Task { + public: + explicit UpdateReferenceTask(ParallelEvacuation *evacuation) : evacuation_(evacuation) {}; + ~UpdateReferenceTask() override = default; + + bool Run(uint32_t threadIndex) override; + + NO_COPY_SEMANTIC(UpdateReferenceTask); + NO_MOVE_SEMANTIC(UpdateReferenceTask); + + private: + ParallelEvacuation *evacuation_; + }; + + class Fragment { + public: + Fragment(ParallelEvacuation *evacuation, Region *region) : evacuation_(evacuation), region_(region) {}; + virtual ~Fragment() = default; + virtual bool Process(bool isMain) = 0; + inline Region *GetRegion() + { + return region_; + } + + inline ParallelEvacuation *GetEvacuation() + { + return evacuation_; + } + + NO_COPY_SEMANTIC(Fragment); + NO_MOVE_SEMANTIC(Fragment); + + protected: + ParallelEvacuation *evacuation_; // NOLINT(misc-non-private-member-variables-in-classes) + Region *region_; // NOLINT(misc-non-private-member-variables-in-classes) + }; + + class EvacuationFragment : public Fragment { + public: + EvacuationFragment(ParallelEvacuation *evacuation, Region *region) : Fragment(evacuation, region) {} + bool Process(bool isMain) override; + }; + + class UpdateRSetFragment : public Fragment { + public: + UpdateRSetFragment(ParallelEvacuation *evacuation, Region *region) : Fragment(evacuation, region) {} + bool Process(bool isMain) override; + }; + + class UpdateNewRegionFragment : public Fragment { + public: + UpdateNewRegionFragment(ParallelEvacuation *evacuation, Region *region) : Fragment(evacuation, region) {} + bool Process(bool isMain) override; + }; + + class UpdateCompressRegionFragment : public Fragment { + public: + UpdateCompressRegionFragment(ParallelEvacuation *evacuation, Region *region) : Fragment(evacuation, region) {} + bool Process(bool isMain) override; + }; + + class UpdateAndSweepNewRegionFragment : public Fragment { + public: + UpdateAndSweepNewRegionFragment(ParallelEvacuation *evacuation, Region *region) : Fragment(evacuation, region) + { + } + bool Process(bool isMain) override; + }; + + class UpdateAndSweepCompressRegionFragment : public Fragment { + public: + UpdateAndSweepCompressRegionFragment(ParallelEvacuation *evacuation, Region *region) + : Fragment(evacuation, region) + { + } + bool Process(bool isMain) override; + }; + + bool ProcessFragments(bool isMain = false); + + void EvacuateSpace(); + bool EvacuateSpace(TlabAllocator *allocator, bool isMain = false); + void EvacuateRegion(TlabAllocator *allocator, Region *region); + + inline bool IsPromoteComplete(Region *region); + void VerifyHeapObject(TaggedObject *object); + + void UpdateReference(); + void UpdateRoot(); + void UpdateWeakReference(); + void UpdateRSet(Region *region); + void UpdateNewRegionReference(Region *region); + void UpdateCompressRegionReference(Region *region); + void UpdateAndSweepNewRegionReference(Region *region); + void UpdateAndSweepCompressRegionReference(Region *region, bool isMain); + void UpdateNewObjectField(TaggedObject *object, JSHClass *cls); + void UpdateCompressObjectField(Region *region, TaggedObject *object, JSHClass *cls); + + inline bool UpdateObjectSlot(ObjectSlot &slot); + inline bool UpdateWeakObjectSlot(TaggedObject *value, ObjectSlot &slot); + + inline std::unique_ptr GetFragmentSafe(); + inline void AddFragment(std::unique_ptr region); + + inline void AddSweptRegionSafe(Region *region); + inline void FillSweptRegion(); + + inline int CalculateEvacuationThreadNum(); + inline int CalculateUpdateThreadNum(); + void WaitFinished(); + + Heap *heap_; + TlabAllocator *allocator_ {nullptr}; + ObjectXRay objXRay_; + EvacuationAllocator *evacuationAllocator_; + + uintptr_t ageMark_ {0}; + std::vector> fragments_; + std::vector sweptList_; + std::atomic_int parallel_ = 0; + std::atomic promotedAccumulatorSize_ = 0; + os::memory::Mutex mutex_; + os::memory::ConditionVariable condition_; +}; +} // namespace panda::ecmascript +#endif // ECMASCRIPT_MEM_PARALLEL_EVACUATION_H diff --git a/runtime/mem/parallel_marker-inl.h b/runtime/mem/parallel_marker-inl.h new file mode 100644 index 000000000..8ce6b1e4f --- /dev/null +++ b/runtime/mem/parallel_marker-inl.h @@ -0,0 +1,330 @@ +/* + * Copyright (c) 2021-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_MEM_PARALLEL_MARKER_INL_H +#define ECMASCRIPT_MEM_PARALLEL_MARKER_INL_H + +#include "plugins/ecmascript/runtime/mem/parallel_marker.h" + +#include "plugins/ecmascript/runtime/js_hclass-inl.h" +#include "plugins/ecmascript/runtime/mem/heap.h" +#include "plugins/ecmascript/runtime/mem/parallel_work_helper.h" +#include "plugins/ecmascript/runtime/mem/region-inl.h" +#include "plugins/ecmascript/runtime/mem/tlab_allocator-inl.h" + +#include "mem/gc/bitmap.h" + +#ifndef __clang_analyzer__ + +namespace panda::ecmascript { +constexpr int HEAD_SIZE = TaggedObject::TaggedObjectSize(); + +inline void Marker::HandleObjectVisitor(uint32_t threadId, Region *objectRegion, bool needBarrier, + [[maybe_unused]] TaggedObject *root, ObjectSlot start, ObjectSlot end) +{ + for (ObjectSlot slot = start; slot < end; slot++) { + JSTaggedValue value(slot.GetTaggedType()); + if (!value.IsHeapObject()) { + continue; + } + + TaggedObject *obj = nullptr; + if (!value.IsWeak()) { + obj = value.GetTaggedObject(); + MarkObject(threadId, obj); + } else { + obj = value.GetWeakReferentUnChecked(); + } + + if (!needBarrier) { + continue; + } + + Region *valueRegion = Region::ObjectAddressToRange(obj); + if (valueRegion->InCollectSet()) { + objectRegion->AtomicInsertCrossRegionRememberedSet(slot.SlotAddress()); + } + } +} + +inline void Marker::HandleMoveObjectVisitor(uint32_t threadId, bool promoted, TaggedObject *root, ObjectSlot start, + ObjectSlot end) +{ + for (ObjectSlot slot = start; slot < end; slot++) { + JSTaggedValue value(slot.GetTaggedType()); + if (value.IsHeapObject()) { + if (value.IsWeak()) { + RecordWeakReference(threadId, reinterpret_cast(slot.SlotAddress())); + continue; + } + auto slotStatus = MarkObject(threadId, value.GetTaggedObject(), slot); + if (promoted && slotStatus == SlotStatus::KEEP_SLOT) { + SlotNeedUpdate waitUpdate(reinterpret_cast(root), slot); + heap_->GetWorkList()->PushWaitUpdateSlot(threadId, waitUpdate); + } + } + } +} + +inline void NonMovableMarker::MarkObject(uint32_t threadId, TaggedObject *object) +{ + Region *objectRegion = Region::ObjectAddressToRange(object); + + if (heap_->IsSemiMarkNeeded() && !objectRegion->InYoungGeneration()) { + return; + } + + auto markBitmap = objectRegion->GetOrCreateMarkBitmap(); + if (!markBitmap->AtomicTestAndSet(object)) { + heap_->GetWorkList()->Push(threadId, object, objectRegion); + } +} + +inline void NonMovableMarker::HandleRoots(uint32_t threadId, [[maybe_unused]] Root type, ObjectSlot slot) +{ + JSTaggedValue value(slot.GetTaggedType()); + if (value.IsHeapObject()) { + MarkObject(threadId, value.GetTaggedObject()); + } +} + +inline void NonMovableMarker::HandleRangeRoots(uint32_t threadId, [[maybe_unused]] Root type, ObjectSlot start, + ObjectSlot end) +{ + for (ObjectSlot slot = start; slot < end; slot++) { + JSTaggedValue value(slot.GetTaggedType()); + if (value.IsHeapObject()) { + MarkObject(threadId, value.GetTaggedObject()); + } + } +} + +inline void NonMovableMarker::HandleOldToNewRSet(uint32_t threadId, Region *region) +{ + auto oldRSet = region->GetOldToNewRememberedSet(); + if (LIKELY(oldRSet != nullptr)) { + oldRSet->IterateOverMarkedChunks([this, threadId](void *mem) -> bool { + ObjectSlot slot(ToUintPtr(mem)); + JSTaggedValue value(slot.GetTaggedType()); + if (value.IsHeapObject() && !value.IsWeak()) { + MarkObject(threadId, value.GetTaggedObject()); + } + return true; + }); + } +} + +inline void NonMovableMarker::RecordWeakReference(uint32_t threadId, JSTaggedType *ref) +{ + Region *objectRegion = Region::ObjectAddressToRange(reinterpret_cast(ref)); + if (!objectRegion->InYoungGeneration()) { + heap_->GetWorkList()->PushWeakReference(threadId, ref); + } +} + +inline void MovableMarker::HandleRoots(uint32_t threadId, [[maybe_unused]] Root type, ObjectSlot slot) +{ + JSTaggedValue value(slot.GetTaggedType()); + if (value.IsHeapObject()) { + MarkObject(threadId, value.GetTaggedObject(), slot); + } +} + +inline void MovableMarker::HandleRangeRoots(uint32_t threadId, [[maybe_unused]] Root type, ObjectSlot start, + ObjectSlot end) +{ + for (ObjectSlot slot = start; slot < end; slot++) { + JSTaggedValue value(slot.GetTaggedType()); + if (value.IsHeapObject()) { + MarkObject(threadId, value.GetTaggedObject(), slot); + } + } +} + +inline void MovableMarker::HandleOldToNewRSet(uint32_t threadId, Region *region) +{ + auto oldRSet = region->GetOldToNewRememberedSet(); + if (LIKELY(oldRSet != nullptr)) { + oldRSet->IterateOverMarkedChunks([this, &oldRSet, threadId](void *mem) -> bool { + ObjectSlot slot(ToUintPtr(mem)); + JSTaggedValue value(slot.GetTaggedType()); + if (value.IsHeapObject()) { + if (value.IsWeak()) { + RecordWeakReference(threadId, reinterpret_cast(mem)); + return true; + } + auto slotStatus = MarkObject(threadId, value.GetTaggedObject(), slot); + if (slotStatus == SlotStatus::CLEAR_SLOT) { + oldRSet->Clear(ToUintPtr(mem)); + } + } + return true; + }); + } +} + +inline uintptr_t MovableMarker::AllocateDstSpace(uint32_t threadId, size_t size, bool &shouldPromote) +{ + uintptr_t forwardAddress = 0; + if (shouldPromote) { + forwardAddress = heap_->GetWorkList()->GetTlabAllocator(threadId)->Allocate(size, SpaceAlloc::OLD_SPACE); + if (UNLIKELY(forwardAddress == 0)) { + LOG_ECMA_MEM(FATAL) << "EvacuateObject alloc failed: " + << " size: " << size; + UNREACHABLE(); + } + } else { + forwardAddress = heap_->GetWorkList()->GetTlabAllocator(threadId)->Allocate(size, SpaceAlloc::YOUNG_SPACE); + if (UNLIKELY(forwardAddress == 0)) { + forwardAddress = heap_->GetWorkList()->GetTlabAllocator(threadId)->Allocate(size, SpaceAlloc::OLD_SPACE); + if (UNLIKELY(forwardAddress == 0)) { + LOG_ECMA_MEM(FATAL) << "EvacuateObject alloc failed: " + << " size: " << size; + UNREACHABLE(); + } + shouldPromote = true; + } + } + return forwardAddress; +} + +inline void MovableMarker::UpdateForwardAddressIfSuccess(uint32_t threadId, TaggedObject *object, JSHClass *klass, + uintptr_t toAddress, size_t size, const MarkWord &markWord, + ObjectSlot slot) +{ + CopyObjectWithoutHeader(object, toAddress, size); + heap_->GetWorkList()->AddAliveSize(threadId, size); + *reinterpret_cast(toAddress) = markWord.GetValue(); + heap_->OnMoveEvent(reinterpret_cast(object), toAddress); + if (klass->HasReferenceField()) { + heap_->GetWorkList()->Push(threadId, reinterpret_cast(toAddress)); + } + slot.Update(reinterpret_cast(toAddress)); +} + +inline bool MovableMarker::UpdateForwardAddressIfFailed(TaggedObject *object, uintptr_t toAddress, size_t size, + ObjectSlot slot) +{ + FreeObject::FillFreeObject(heap_->GetEcmaVM(), toAddress, size); + TaggedObject *dst = MarkWord(object).ToForwardingAddress(); + slot.Update(dst); + return Region::ObjectAddressToRange(dst)->InYoungGeneration(); +} + +inline void MovableMarker::CopyObjectWithoutHeader(TaggedObject *object, uintptr_t toAddress, size_t size) +{ + if (memcpy_s(ToVoidPtr(toAddress + HEAD_SIZE), size - HEAD_SIZE, ToVoidPtr(ToUintPtr(object) + HEAD_SIZE), + size - HEAD_SIZE) != EOK) { + LOG_ECMA_MEM(FATAL) << "CopyObjectWithoutHeader memcpy_s failed: " + << " dst: " << toAddress << " src: " << ToUintPtr(object) << " size: " << size; + UNREACHABLE(); + } +} + +inline SlotStatus SemiGcMarker::MarkObject(uint32_t threadId, TaggedObject *object, ObjectSlot slot) +{ + Region *objectRegion = Region::ObjectAddressToRange(object); + if (!objectRegion->InYoungGeneration()) { + return SlotStatus::CLEAR_SLOT; + } + + MarkWord markWord(object); + if (markWord.IsForwardingAddress()) { + TaggedObject *dst = markWord.ToForwardingAddress(); + slot.Update(dst); + Region *valueRegion = Region::ObjectAddressToRange(dst); + return valueRegion->InYoungGeneration() ? SlotStatus::KEEP_SLOT : SlotStatus::CLEAR_SLOT; + } + return EvacuateObject(threadId, object, markWord, slot); +} + +inline SlotStatus SemiGcMarker::EvacuateObject(uint32_t threadId, TaggedObject *object, const MarkWord &markWord, + ObjectSlot slot) +{ + JSHClass *klass = markWord.GetJSHClass(); + size_t size = klass->SizeFromJSHClass(object); + bool isPromoted = ShouldBePromoted(object); + + uintptr_t forwardAddress = AllocateDstSpace(threadId, size, isPromoted); + bool result = Barriers::AtomicSetDynPrimitive(object, 0, markWord.GetValue(), + MarkWord::FromForwardingAddress(forwardAddress)); + if (result) { + UpdateForwardAddressIfSuccess(threadId, object, klass, forwardAddress, size, markWord, slot); + return isPromoted ? SlotStatus::CLEAR_SLOT : SlotStatus::KEEP_SLOT; + } + bool keepSlot = UpdateForwardAddressIfFailed(object, forwardAddress, size, slot); + return keepSlot ? SlotStatus::KEEP_SLOT : SlotStatus::CLEAR_SLOT; +} + +inline bool SemiGcMarker::ShouldBePromoted(TaggedObject *object) +{ + Region *region = Region::ObjectAddressToRange(object); + return (region->BelowAgeMark() || (region->HasAgeMark() && ToUintPtr(object) < ageMark_)); +} + +inline void SemiGcMarker::RecordWeakReference(uint32_t threadId, JSTaggedType *ref) +{ + auto value = JSTaggedValue(*ref); + Region *objectRegion = Region::ObjectAddressToRange(value.GetTaggedWeakRef()); + if (objectRegion->InYoungGeneration()) { + heap_->GetWorkList()->PushWeakReference(threadId, ref); + } +} + +inline SlotStatus CompressGcMarker::MarkObject(uint32_t threadId, TaggedObject *object, ObjectSlot slot) +{ + Region *objectRegion = Region::ObjectAddressToRange(object); + if (!objectRegion->InYoungAndOldGeneration()) { + auto markBitmap = objectRegion->GetMarkBitmap(); + if (!markBitmap->AtomicTestAndSet(object)) { + heap_->GetWorkList()->Push(threadId, object); + } + return SlotStatus::CLEAR_SLOT; + } + + MarkWord markWord(object); + if (markWord.IsForwardingAddress()) { + TaggedObject *dst = markWord.ToForwardingAddress(); + slot.Update(dst); + return SlotStatus::CLEAR_SLOT; + } + return EvacuateObject(threadId, object, markWord, slot); +} + +inline SlotStatus CompressGcMarker::EvacuateObject(uint32_t threadId, TaggedObject *object, const MarkWord &markWord, + ObjectSlot slot) +{ + JSHClass *klass = markWord.GetJSHClass(); + size_t size = klass->SizeFromJSHClass(object); + bool isPromoted = true; + + uintptr_t forwardAddress = AllocateDstSpace(threadId, size, isPromoted); + bool result = Barriers::AtomicSetDynPrimitive(object, 0, markWord.GetValue(), + MarkWord::FromForwardingAddress(forwardAddress)); + if (result) { + UpdateForwardAddressIfSuccess(threadId, object, klass, forwardAddress, size, markWord, slot); + return SlotStatus::CLEAR_SLOT; + } + UpdateForwardAddressIfFailed(object, forwardAddress, size, slot); + return SlotStatus::CLEAR_SLOT; +} + +inline void CompressGcMarker::RecordWeakReference(uint32_t threadId, JSTaggedType *ref) +{ + heap_->GetWorkList()->PushWeakReference(threadId, ref); +} +} // namespace panda::ecmascript +#endif // __clang_analyzer__ +#endif // ECMASCRIPT_MEM_PARALLEL_MARKER_INL_H diff --git a/runtime/mem/parallel_marker.cpp b/runtime/mem/parallel_marker.cpp new file mode 100644 index 000000000..c210b215e --- /dev/null +++ b/runtime/mem/parallel_marker.cpp @@ -0,0 +1,122 @@ +/* + * Copyright (c) 2021-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 "plugins/ecmascript/runtime/mem/parallel_marker-inl.h" + +#include "plugins/ecmascript/runtime/mem/object_xray-inl.h" + +#ifndef __clang_analyzer__ + +namespace panda::ecmascript { +Marker::Marker(Heap *heap) : heap_(heap), objXRay_(heap_->GetEcmaVM()) {} + +void Marker::MarkRoots(uint32_t threadId) +{ + objXRay_.VisitVMRoots( + std::bind(&Marker::HandleRoots, this, threadId, std::placeholders::_1, std::placeholders::_2), + std::bind(&Marker::HandleRangeRoots, this, threadId, std::placeholders::_1, std::placeholders::_2, + std::placeholders::_3)); + heap_->GetWorkList()->PushWorkNodeToGlobal(threadId, false); +} + +void Marker::ProcessOldToNew(uint32_t threadId) +{ + heap_->EnumerateOldSpaceRegions(std::bind(&Marker::HandleOldToNewRSet, this, threadId, std::placeholders::_1)); + ProcessMarkStack(threadId); +} + +void Marker::ProcessOldToNew(uint32_t threadId, Region *region) +{ + heap_->EnumerateOldSpaceRegions(std::bind(&Marker::HandleOldToNewRSet, this, threadId, std::placeholders::_1), + region); + ProcessMarkStack(threadId); +} + +void Marker::ProcessSnapshotRSet(uint32_t threadId) +{ + heap_->EnumerateSnapShotSpaceRegions(std::bind(&Marker::HandleOldToNewRSet, this, threadId, std::placeholders::_1)); + ProcessMarkStack(threadId); +} + +void NonMovableMarker::ProcessMarkStack(uint32_t threadId) +{ + WorkerHelper *worklist = heap_->GetWorkList(); + TaggedObject *obj = nullptr; + bool isOnlySemi = heap_->IsSemiMarkNeeded(); + while (true) { + obj = nullptr; + if (!worklist->Pop(threadId, &obj)) { + break; + } + + JSHClass *jsHclass = obj->GetClass(); + MarkObject(threadId, jsHclass); + + Region *objectRegion = Region::ObjectAddressToRange(obj); + bool needBarrier = !isOnlySemi && !objectRegion->InYoungAndCSetGeneration(); + objXRay_.VisitObjectBody(obj, jsHclass, + std::bind(&Marker::HandleObjectVisitor, this, threadId, + objectRegion, needBarrier, std::placeholders::_1, + std::placeholders::_2, std::placeholders::_3)); + } +} + +void SemiGcMarker::Initialized() +{ + ageMark_ = heap_->GetNewSpace()->GetAgeMark(); +} + +void SemiGcMarker::ProcessMarkStack(uint32_t threadId) +{ + WorkerHelper *worklist = heap_->GetWorkList(); + TaggedObject *obj = nullptr; + while (true) { + obj = nullptr; + if (!worklist->Pop(threadId, &obj)) { + break; + } + + auto jsHclass = obj->GetClass(); + Region *objectRegion = Region::ObjectAddressToRange(obj); + bool promoted = !objectRegion->InYoungGeneration(); + objXRay_.VisitObjectBody(obj, jsHclass, + std::bind(&Marker::HandleMoveObjectVisitor, this, threadId, + promoted, std::placeholders::_1, std::placeholders::_2, + std::placeholders::_3)); + } +} + +void CompressGcMarker::ProcessMarkStack(uint32_t threadId) +{ + WorkerHelper *worklist = heap_->GetWorkList(); + TaggedObject *obj = nullptr; + while (true) { + obj = nullptr; + if (!worklist->Pop(threadId, &obj)) { + break; + } + + auto jsHclass = obj->GetClass(); + ObjectSlot objectSlot(ToUintPtr(obj)); + MarkObject(threadId, jsHclass, objectSlot); + + objXRay_.VisitObjectBody(obj, jsHclass, + std::bind(&Marker::HandleMoveObjectVisitor, this, threadId, false, + std::placeholders::_1, std::placeholders::_2, + std::placeholders::_3)); + } +} +} // namespace panda::ecmascript +#endif // __clang_analyzer__ diff --git a/runtime/mem/parallel_marker.h b/runtime/mem/parallel_marker.h new file mode 100644 index 000000000..e21d89c0c --- /dev/null +++ b/runtime/mem/parallel_marker.h @@ -0,0 +1,152 @@ +/* + * Copyright (c) 2021-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_MEM_PARALLEL_MARKER_H +#define ECMASCRIPT_MEM_PARALLEL_MARKER_H + +#include "plugins/ecmascript/runtime/js_hclass.h" +#include "plugins/ecmascript/runtime/mem/object_xray.h" +#include "plugins/ecmascript/runtime/mem/remembered_set.h" +#include "plugins/ecmascript/runtime/mem/slots.h" +#include "libpandabase/utils/logger.h" + +namespace panda::ecmascript { +class Heap; +class TaggedObject; +class Region; + +class Marker { +public: + explicit Marker(Heap *heap); + virtual ~Marker() = default; + + virtual void Initialized() + { + ECMA_GC_LOG() << "Marker::Initialize do nothing"; + } + + void MarkRoots(uint32_t threadId); + void ProcessOldToNew(uint32_t threadId); // for HPPGC only semi mode + void ProcessOldToNew(uint32_t threadId, Region *region); // for SemiGC + void ProcessSnapshotRSet(uint32_t threadId); // for SemiGC + + virtual void ProcessMarkStack([[maybe_unused]] uint32_t threadId) + { + LOG(FATAL, ECMASCRIPT) << "can not call this method"; + } + + inline void HandleObjectVisitor(uint32_t threadId, Region *objectRegion, bool needBarrier, + [[maybe_unused]] TaggedObject *root, ObjectSlot start, ObjectSlot end); + inline void HandleMoveObjectVisitor(uint32_t threadId, bool promoted, TaggedObject *root, ObjectSlot start, + ObjectSlot end); + + NO_COPY_SEMANTIC(Marker); + NO_MOVE_SEMANTIC(Marker); + +protected: + virtual inline void MarkObject([[maybe_unused]] uint32_t threadId, + [[maybe_unused]] TaggedObject *object) // non move + { + LOG(FATAL, ECMASCRIPT) << "can not call this method"; + } + + virtual inline SlotStatus MarkObject([[maybe_unused]] uint32_t threadId, [[maybe_unused]] TaggedObject *object, + [[maybe_unused]] ObjectSlot slot) // move + { + LOG(FATAL, ECMASCRIPT) << "can not call this method"; + return SlotStatus::KEEP_SLOT; + } + + virtual void HandleOldToNewRSet(uint32_t threadId, Region *region) = 0; + virtual void HandleRoots(uint32_t threadId, [[maybe_unused]] Root type, ObjectSlot slot) = 0; + virtual void HandleRangeRoots(uint32_t threadId, [[maybe_unused]] Root type, ObjectSlot start, ObjectSlot end) = 0; + virtual inline void RecordWeakReference([[maybe_unused]] uint32_t threadId, [[maybe_unused]] JSTaggedType *ref) + { + LOG(FATAL, ECMASCRIPT) << "can not call this method"; + } + + Heap *heap_; // NOLINT(misc-non-private-member-variables-in-classes) + ObjectXRay objXRay_; // NOLINT(misc-non-private-member-variables-in-classes) +}; + +class NonMovableMarker : public Marker { +public: + explicit NonMovableMarker(Heap *heap) : Marker(heap) {} + +protected: + void ProcessMarkStack(uint32_t threadId) override; + void MarkObject(uint32_t threadId, TaggedObject *object) override; + void HandleRoots(uint32_t threadId, [[maybe_unused]] Root type, ObjectSlot slot) override; + void HandleRangeRoots(uint32_t threadId, [[maybe_unused]] Root type, ObjectSlot start, + ObjectSlot end) override; + + void HandleOldToNewRSet(uint32_t threadId, Region *region) override; + void RecordWeakReference(uint32_t threadId, JSTaggedType *ref) override; +}; + +class MovableMarker : public Marker { +public: + explicit MovableMarker(Heap *heap) : Marker(heap) {} + +protected: + void HandleRoots(uint32_t threadId, [[maybe_unused]] Root type, ObjectSlot slot) override; + void HandleRangeRoots(uint32_t threadId, [[maybe_unused]] Root type, ObjectSlot start, + ObjectSlot end) override; + virtual inline SlotStatus EvacuateObject(uint32_t threadId, TaggedObject *object, const MarkWord &markWord, + ObjectSlot slot) = 0; + + void HandleOldToNewRSet(uint32_t threadId, Region *region) override; + + inline uintptr_t AllocateDstSpace(uint32_t threadId, size_t size, bool &shouldPromote); + inline void UpdateForwardAddressIfSuccess(uint32_t threadId, TaggedObject *object, JSHClass *klass, + uintptr_t toAddress, size_t size, const MarkWord &markWord, + ObjectSlot slot); + inline bool UpdateForwardAddressIfFailed(TaggedObject *object, uintptr_t toAddress, size_t size, ObjectSlot slot); + inline void CopyObjectWithoutHeader(TaggedObject *object, uintptr_t toAddress, size_t size); +}; + +class SemiGcMarker : public MovableMarker { +public: + explicit SemiGcMarker(Heap *heap) : MovableMarker(heap) {} + + void Initialized() override; + +protected: + void ProcessMarkStack(uint32_t threadId) override; + SlotStatus MarkObject(uint32_t threadId, TaggedObject *object, ObjectSlot slot) override; + SlotStatus EvacuateObject(uint32_t threadId, TaggedObject *object, const MarkWord &markWord, + ObjectSlot slot) override; + void RecordWeakReference(uint32_t threadId, JSTaggedType *ref) override; + +private: + inline bool ShouldBePromoted(TaggedObject *object); + + uintptr_t ageMark_ {0}; +}; + +class CompressGcMarker : public MovableMarker { +public: + explicit CompressGcMarker(Heap *heap) : MovableMarker(heap) {} + +protected: + void ProcessMarkStack(uint32_t threadId) override; + SlotStatus MarkObject(uint32_t threadId, TaggedObject *object, ObjectSlot slot) override; + + SlotStatus EvacuateObject(uint32_t threadId, TaggedObject *object, const MarkWord &markWord, + ObjectSlot slot) override; + void RecordWeakReference(uint32_t threadId, JSTaggedType *ref) override; +}; +} // namespace panda::ecmascript +#endif // ECMASCRIPT_MEM_PARALLEL_MARKER_H diff --git a/runtime/mem/parallel_work_helper.cpp b/runtime/mem/parallel_work_helper.cpp new file mode 100644 index 000000000..df32528e2 --- /dev/null +++ b/runtime/mem/parallel_work_helper.cpp @@ -0,0 +1,182 @@ +/* + * Copyright (c) 2021-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 "plugins/ecmascript/runtime/mem/parallel_work_helper.h" + +#include "plugins/ecmascript/runtime/js_hclass-inl.h" +#include "plugins/ecmascript/runtime/mem/area.h" +#include "plugins/ecmascript/runtime/mem/compress_collector.h" +#include "plugins/ecmascript/runtime/mem/heap.h" +#include "plugins/ecmascript/runtime/mem/mark_stack.h" +#include "plugins/ecmascript/runtime/mem/mix_space_collector.h" +#include "plugins/ecmascript/runtime/mem/region.h" +#include "plugins/ecmascript/runtime/mem/region_factory.h" +#include "plugins/ecmascript/runtime/mem/tlab_allocator-inl.h" + +namespace panda::ecmascript { +WorkerHelper::WorkerHelper(Heap *heap, uint32_t threadNum) + : heap_(heap), threadNum_(threadNum), markSpace_(0), spaceTop_(0), markSpaceEnd_(0) +{ + for (uint32_t i = 0; i < threadNum_; i++) { + continuousQueue_[i] = new ProcessQueue(heap); + } + markSpace_ = ToUintPtr(const_cast(heap_->GetRegionFactory())->AllocateBuffer(SPACE_SIZE)); +} + +WorkerHelper::~WorkerHelper() +{ + for (uint32_t i = 0; i < threadNum_; i++) { + continuousQueue_[i]->Destroy(); + delete continuousQueue_[i]; + continuousQueue_[i] = nullptr; + } + const_cast(heap_->GetRegionFactory())->FreeBuffer(reinterpret_cast(markSpace_)); +} + +bool WorkerHelper::Push(uint32_t threadId, TaggedObject *object) +{ + WorkNode *&pushNode = workList_[threadId].pushNode_; + if (!pushNode->Push(ToUintPtr(object))) { + PushWorkNodeToGlobal(threadId); + return pushNode->Push(ToUintPtr(object)); + } + return true; +} + +bool WorkerHelper::Push(uint32_t threadId, TaggedObject *object, Region *region) +{ + if (Push(threadId, object)) { + auto klass = object->GetClass(); + auto size = klass->SizeFromJSHClass(object); + region->IncrementAliveObject(size); + return true; + } + return false; +} + +void WorkerHelper::PushWorkNodeToGlobal(uint32_t threadId, bool postTask) +{ + WorkNode *&pushNode = workList_[threadId].pushNode_; + if (!pushNode->IsEmpty()) { + globalWork_.Push(pushNode); + pushNode = AllocalWorkNode(); + if (postTask && heap_->IsParallelGCEnabled() && heap_->CheckCanDistributeTask()) { + heap_->PostParallelGCTask(parallelTask_); + } + } +} + +bool WorkerHelper::Pop(uint32_t threadId, TaggedObject **object) +{ + WorkNode *&popNode = workList_[threadId].popNode_; + WorkNode *&pushNode = workList_[threadId].pushNode_; + if (!popNode->Pop(reinterpret_cast(object))) { + if (!pushNode->IsEmpty()) { + WorkNode *tmp = popNode; + popNode = pushNode; + pushNode = tmp; + } else if (!PopWorkNodeFromGlobal(threadId)) { + return false; + } + return popNode->Pop(reinterpret_cast(object)); + } + return true; +} + +bool WorkerHelper::PopWorkNodeFromGlobal(uint32_t threadId) +{ + return globalWork_.Pop(&workList_[threadId].popNode_); +} + +void WorkerHelper::Finish(size_t &aliveSize) +{ + for (uint32_t i = 0; i < threadNum_; i++) { + WorkNodeHolder &holder = workList_[i]; + holder.weakQueue_->FinishMarking(continuousQueue_[i]); + delete holder.weakQueue_; + holder.weakQueue_ = nullptr; + delete holder.allocator_; + holder.allocator_ = nullptr; + holder.waitUpdate_.clear(); + aliveSize += holder.aliveSize_; + } + + while (!unuseSpace_.empty()) { + const_cast(heap_->GetRegionFactory())->FreeBuffer(reinterpret_cast( + unuseSpace_.back())); + unuseSpace_.pop_back(); + } +} + +void WorkerHelper::Finish(size_t &aliveSize, size_t &promoteSize) +{ + Finish(aliveSize); + for (uint32_t i = 0; i < threadNum_; i++) { + WorkNodeHolder &holder = workList_[i]; + promoteSize += holder.aliveSize_; + } +} + +void WorkerHelper::Initialize(TriggerGCType gcType, ParallelGCTaskPhase parallelTask) +{ + parallelTask_ = parallelTask; + spaceTop_ = markSpace_; + markSpaceEnd_ = markSpace_ + SPACE_SIZE; + for (uint32_t i = 0; i < threadNum_; i++) { + WorkNodeHolder &holder = workList_[i]; + holder.pushNode_ = AllocalWorkNode(); + holder.popNode_ = AllocalWorkNode(); + holder.weakQueue_ = new ProcessQueue(); + holder.weakQueue_->BeginMarking(heap_, continuousQueue_[i]); + holder.aliveSize_ = 0; + holder.promoteSize_ = 0; + if (gcType == TriggerGCType::SEMI_GC) { + holder.allocator_ = new TlabAllocator(heap_, TriggerGCType::SEMI_GC); + } else if (gcType == TriggerGCType::COMPRESS_FULL_GC) { + holder.allocator_ = new TlabAllocator(heap_, TriggerGCType::COMPRESS_FULL_GC); + } + } +} + +WorkNode *WorkerHelper::AllocalWorkNode() +{ + size_t totalSize = sizeof(WorkNode) + sizeof(Stack) + STACK_AREA_SIZE; + // CAS + volatile auto atomicField = reinterpret_cast *>(&spaceTop_); + bool result = false; + uintptr_t begin = 0; + do { + begin = atomicField->load(std::memory_order_acquire); + if (begin + totalSize >= markSpaceEnd_) { + os::memory::LockHolder lock(mtx_); + begin = atomicField->load(std::memory_order_acquire); + if (begin + totalSize >= markSpaceEnd_) { + unuseSpace_.emplace_back(markSpace_); + markSpace_ = + ToUintPtr(const_cast(heap_->GetRegionFactory())->AllocateBuffer(SPACE_SIZE)); + spaceTop_ = markSpace_; + markSpaceEnd_ = markSpace_ + SPACE_SIZE; + begin = spaceTop_; + } + } + result = std::atomic_compare_exchange_strong_explicit(atomicField, &begin, begin + totalSize, + std::memory_order_release, std::memory_order_relaxed); + } while (!result); + auto *stack = reinterpret_cast(begin + sizeof(WorkNode)); + stack->ResetBegin(begin + sizeof(WorkNode) + sizeof(Stack), begin + totalSize); + auto *work = reinterpret_cast(begin); + return new (work) WorkNode(stack); +} +} // namespace panda::ecmascript diff --git a/runtime/mem/parallel_work_helper.h b/runtime/mem/parallel_work_helper.h new file mode 100644 index 000000000..61062f22d --- /dev/null +++ b/runtime/mem/parallel_work_helper.h @@ -0,0 +1,218 @@ +/* + * Copyright (c) 2021-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_MEM_PARALLEL_WORK_HELPER_H +#define ECMASCRIPT_MEM_PARALLEL_WORK_HELPER_H + +#include "plugins/ecmascript/runtime/mem/mark_stack-inl.h" +#include "plugins/ecmascript/runtime/mem/slots.h" +#include "plugins/ecmascript/runtime/platform/platform.h" + +namespace panda::ecmascript { +using SlotNeedUpdate = std::pair; + +static constexpr uint32_t MARKSTACK_MAX_SIZE = 100; +static constexpr uint32_t STACK_AREA_SIZE = sizeof(uintptr_t) * MARKSTACK_MAX_SIZE; +static constexpr uint32_t SPACE_SIZE = 8 * 1024; + +class Heap; +class Stack; +class SemiSpaceCollector; +class TlabAllocator; +class Region; + +enum ParallelGCTaskPhase { + SEMI_HANDLE_THREAD_ROOTS_TASK, + SEMI_HANDLE_SNAPSHOT_TASK, + SEMI_HANDLE_GLOBAL_POOL_TASK, + OLD_HANDLE_GLOBAL_POOL_TASK, + COMPRESS_HANDLE_GLOBAL_POOL_TASK, + CONCURRENT_HANDLE_GLOBAL_POOL_TASK, + CONCURRENT_HANDLE_OLD_TO_NEW_TASK, + TASK_LAST // Count of different Task phase +}; + +class WorkNode { +public: + explicit WorkNode(Stack *stack) : next_(nullptr), stack_(stack) {} + ~WorkNode() + { + delete stack_; + stack_ = nullptr; + } + + NO_COPY_SEMANTIC(WorkNode); + NO_MOVE_SEMANTIC(WorkNode); + + bool Push(uintptr_t obj) + { + return stack_->PushBackChecked(obj); + } + + bool Pop(uintptr_t *obj) + { + if (IsEmpty()) { + return false; + } + auto object = ToVoidPtr(*obj); + if (object != nullptr) { + delete reinterpret_cast(object); + } + *obj = stack_->PopBackUnchecked(); + return true; + } + + bool IsEmpty() const + { + return stack_->IsEmpty(); + } + + WorkNode *Next() const + { + return next_; + } + + void SetNext(WorkNode *node) + { + next_ = node; + } + +private: + WorkNode *next_; + Stack *stack_; +}; + +class GlobalWorkList { +public: + GlobalWorkList() : top_(nullptr) {} + ~GlobalWorkList() = default; + + NO_COPY_SEMANTIC(GlobalWorkList); + NO_MOVE_SEMANTIC(GlobalWorkList); + + void Push(WorkNode *node) + { + if (node == nullptr) { + return; + } + os::memory::LockHolder lock(mtx_); + node->SetNext(top_); + top_ = node; + } + + bool Pop(WorkNode **node) + { + os::memory::LockHolder lock(mtx_); + if (top_ == nullptr) { + return false; + } + *node = top_; + top_ = top_->Next(); + return true; + } + +private: + WorkNode *top_ {nullptr}; + os::memory::Mutex mtx_; +}; + +struct WorkNodeHolder { + WorkNode *pushNode_ {nullptr}; + WorkNode *popNode_ {nullptr}; + ProcessQueue *weakQueue_ {nullptr}; + std::vector waitUpdate_; + TlabAllocator *allocator_ {nullptr}; + size_t aliveSize_ = 0; + size_t promoteSize_ = 0; +}; + +class WorkerHelper final { +public: + WorkerHelper() = delete; + explicit WorkerHelper(Heap *heap, uint32_t threadNum); + ~WorkerHelper(); + + void Initialize(TriggerGCType gcType, ParallelGCTaskPhase parallelTask); + void Finish(size_t &aliveSize); + void Finish(size_t &aliveSize, size_t &promoteSize); + + bool Push(uint32_t threadId, TaggedObject *object); + bool Push(uint32_t threadId, TaggedObject *object, Region *region); + bool Pop(uint32_t threadId, TaggedObject **object); + + bool PopWorkNodeFromGlobal(uint32_t threadId); + void PushWorkNodeToGlobal(uint32_t threadId, bool postTask = true); + + inline void PushWeakReference(uint32_t threadId, JSTaggedType *weak) + { + workList_[threadId].weakQueue_->PushBack(weak); + } + + inline void AddAliveSize(uint32_t threadId, size_t size) + { + workList_[threadId].aliveSize_ += size; + } + + inline void AddPromoteSize(uint32_t threadId, size_t size) + { + workList_[threadId].promoteSize_ += size; + } + + inline ProcessQueue *GetWeakReferenceQueue(uint32_t threadId) const + { + return workList_[threadId].weakQueue_; + } + + inline TlabAllocator *GetTlabAllocator(uint32_t threadId) const + { + return workList_[threadId].allocator_; + } + + inline void PushWaitUpdateSlot(uint32_t threadId, SlotNeedUpdate slot) + { + workList_[threadId].waitUpdate_.emplace_back(slot); + } + + inline bool GetSlotNeedUpdate(uint32_t threadId, SlotNeedUpdate *slot) + { + std::vector &waitUpdate = workList_[threadId].waitUpdate_; + if (waitUpdate.empty()) { + return false; + } + *slot = waitUpdate.back(); + waitUpdate.pop_back(); + return true; + } + +private: + NO_COPY_SEMANTIC(WorkerHelper); + NO_MOVE_SEMANTIC(WorkerHelper); + + WorkNode *AllocalWorkNode(); + + Heap *heap_; + uint32_t threadNum_; + std::array workList_; + std::array *, MAX_PLATFORM_THREAD_NUM + 1> continuousQueue_{}; + GlobalWorkList globalWork_; + uintptr_t markSpace_; + uintptr_t spaceTop_; + uintptr_t markSpaceEnd_; + std::vector unuseSpace_; + os::memory::Mutex mtx_; + ParallelGCTaskPhase parallelTask_{}; +}; +} // namespace panda::ecmascript +#endif // ECMASCRIPT_MEM_PARALLEL_WORK_HELPER_H diff --git a/runtime/mem/region-inl.h b/runtime/mem/region-inl.h new file mode 100644 index 000000000..076c52d2f --- /dev/null +++ b/runtime/mem/region-inl.h @@ -0,0 +1,150 @@ +/* + * Copyright (c) 2021-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_MEM_REGION_INL_H +#define ECMASCRIPT_MEM_REGION_INL_H + +#include "plugins/ecmascript/runtime/mem/region.h" + +#include "plugins/ecmascript/runtime/mem/heap.h" +#include "plugins/ecmascript/runtime/mem/region_factory.h" +#include "plugins/ecmascript/runtime/mem/remembered_set.h" +#include "plugins/ecmascript/runtime/mem/space.h" + +namespace panda::ecmascript { +inline void Region::SetSpace(Space *space) +{ + space_ = space; + heap_ = space_->GetHeap(); +} + +inline RangeBitmap *Region::GetOrCreateMarkBitmap() +{ + if (UNLIKELY(markBitmap_ == nullptr)) { + os::memory::LockHolder lock(lock_); + if (markBitmap_ == nullptr) { + markBitmap_ = CreateMarkBitmap(); + } + } + return markBitmap_; +} + +RangeBitmap *Region::CreateMarkBitmap() +{ + size_t heapSize = IsFlagSet(RegionFlags::IS_HUGE_OBJECT) ? LARGE_BITMAP_MIN_SIZE : GetCapacity(); + // Only one huge object is stored in a region. The BitmapSize of a huge region will always be 8 Bytes. + size_t bitmapSize = RangeBitmap::GetBitMapSizeInByte(heapSize); + + auto bitmapData = const_cast(heap_->GetRegionFactory())->Allocate(bitmapSize); + auto *ret = new RangeBitmap(this, heapSize, bitmapData); + + ret->ClearAllBits(); + return ret; +} + +RememberedSet *Region::CreateRememberedSet() +{ + auto setSize = RememberedSet::GetSizeInByte(GetCapacity()); + auto setAddr = const_cast(heap_->GetRegionFactory())->Allocate(setSize); + uintptr_t setData = ToUintPtr(setAddr); + auto ret = new RememberedSet(ToUintPtr(this), GetCapacity(), setData); + ret->ClearAllBits(); + return ret; +} + +RememberedSet *Region::GetOrCreateCrossRegionRememberedSet() +{ + if (UNLIKELY(crossRegionSet_ == nullptr)) { + os::memory::LockHolder lock(lock_); + if (crossRegionSet_ == nullptr) { + crossRegionSet_ = CreateRememberedSet(); + } + } + return crossRegionSet_; +} + +RememberedSet *Region::GetOrCreateOldToNewRememberedSet() +{ + if (UNLIKELY(oldToNewSet_ == nullptr)) { + os::memory::LockHolder lock(lock_); + if (oldToNewSet_ == nullptr) { + oldToNewSet_ = CreateRememberedSet(); + } + } + return oldToNewSet_; +} + +void Region::InsertCrossRegionRememberedSet(uintptr_t addr) +{ + auto set = GetOrCreateCrossRegionRememberedSet(); + set->Insert(addr); +} + +void Region::AtomicInsertCrossRegionRememberedSet(uintptr_t addr) +{ + auto set = GetOrCreateCrossRegionRememberedSet(); + set->AtomicInsert(addr); +} + +void Region::InsertOldToNewRememberedSet(uintptr_t addr) +{ + auto set = GetOrCreateOldToNewRememberedSet(); + set->Insert(addr); +} + +WorkerHelper *Region::GetWorkList() const +{ + return heap_->GetWorkList(); +} + +void Region::AtomicInsertOldToNewRememberedSet(uintptr_t addr) +{ + auto set = GetOrCreateOldToNewRememberedSet(); + set->AtomicInsert(addr); +} + +void Region::ClearMarkBitmap() +{ + if (markBitmap_ != nullptr) { + auto size = RangeBitmap::GetBitMapSizeInByte(GetCapacity()); + const_cast(heap_->GetRegionFactory())->Free(markBitmap_->GetBitMap().Data(), size); + delete markBitmap_; + markBitmap_ = nullptr; + } +} + +void Region::ClearCrossRegionRememberedSet() +{ + if (crossRegionSet_ != nullptr) { + auto size = RememberedSet::GetSizeInByte(GetCapacity()); + const_cast(heap_->GetRegionFactory())->Free( + crossRegionSet_->GetBitMap().Data(), size); + delete crossRegionSet_; + crossRegionSet_ = nullptr; + } +} + +void Region::ClearOldToNewRememberedSet() +{ + if (oldToNewSet_ != nullptr) { + auto size = RememberedSet::GetSizeInByte(GetCapacity()); + const_cast(heap_->GetRegionFactory())->Free( + oldToNewSet_->GetBitMap().Data(), size); + delete oldToNewSet_; + oldToNewSet_ = nullptr; + } +} +} // namespace panda::ecmascript +#endif // ECMASCRIPT_MEM_REGION_INL_H diff --git a/runtime/mem/region.h b/runtime/mem/region.h new file mode 100644 index 000000000..8c2de222b --- /dev/null +++ b/runtime/mem/region.h @@ -0,0 +1,352 @@ +/* + * Copyright (c) 2021-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_MEM_REGION_H +#define ECMASCRIPT_MEM_REGION_H + +#include "plugins/ecmascript/runtime/mem/free_object_list.h" +#include "plugins/ecmascript/runtime/mem/mem.h" +#include "mem/gc/bitmap.h" + +namespace panda { +using RangeBitmap = mem::MemBitmap(ecmascript::MemAlignment::MEM_ALIGN_OBJECT)>; + +namespace ecmascript { +class Space; +class Heap; +class RememberedSet; +class WorkerHelper; + +enum RegionFlags : uint32_t { + NEVER_EVACUATE = 1, + HAS_AGE_MARK = 1 << 1, // NOLINT(hicpp-signed-bitwise) + BELOW_AGE_MARK = 1 << 2, // NOLINT(hicpp-signed-bitwise) + IS_IN_YOUNG_GENERATION = 1 << 3, // NOLINT(hicpp-signed-bitwise) + IS_IN_SNAPSHOT_GENERATION = 1 << 4, // NOLINT(hicpp-signed-bitwise) + IS_HUGE_OBJECT = 1 << 5, // NOLINT(hicpp-signed-bitwise) + IS_IN_OLD_GENERATION = 1 << 6, // NOLINT(hicpp-signed-bitwise) + IS_IN_NON_MOVABLE_GENERATION = 1 << 7, // NOLINT(hicpp-signed-bitwise) + IS_IN_YOUNG_OR_OLD_GENERATION = IS_IN_YOUNG_GENERATION | IS_IN_OLD_GENERATION, + IS_IN_COLLECT_SET = 1 << 8, // NOLINT(hicpp-signed-bitwise) + IS_IN_PROMOTE_SET = 1 << 9, // NOLINT(hicpp-signed-bitwise) + IS_IN_YOUNG_OR_CSET_GENERATION = IS_IN_YOUNG_GENERATION | IS_IN_COLLECT_SET, + IS_INVALID = 1 << 10, // NOLINT(hicpp-signed-bitwise) +}; + +class Region { +public: + Region(Space *space, Heap *heap, uintptr_t allocateBase, uintptr_t begin, uintptr_t end) + : space_(space), heap_(heap), + flags_(0), + allocateBase_(allocateBase), + // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-pointer-arithmetic) + begin_(begin), + end_(end), + highWaterMark_(end), + aliveObject_(0) + { + } + ~Region() = default; + NO_COPY_SEMANTIC(Region); + NO_MOVE_SEMANTIC(Region); + + void Reset() + { + flags_ = 0; + highWaterMark_ = end_; + memset_s(reinterpret_cast(begin_), GetSize(), 0, GetSize()); + } + + void LinkNext(Region *next) + { + next_ = next; + } + + Region *GetNext() const + { + return next_; + } + + void LinkPrev(Region *prev) + { + prev_ = prev; + } + + Region *GetPrev() const + { + return prev_; + } + + uintptr_t GetBegin() const + { + return begin_; + } + + uintptr_t GetEnd() const + { + return end_; + } + + size_t GetCapacity() const + { + return end_ - reinterpret_cast(this); + } + + size_t GetSize() const + { + return end_ - begin_; + } + + inline void SetSpace(Space *space); + + Heap *GetHeap() const + { + return heap_; + } + + void ResetFlag() + { + flags_ = 0; + } + + void SetFlag(RegionFlags flag) + { + flags_ |= flag; + } + + void ClearFlag(RegionFlags flag) + { + // NOLINTNEXTLINE(hicpp-signed-bitwise) + flags_ &= ~flag; + } + + bool IsFlagSet(RegionFlags flag) const + { + return (flags_ & flag) != 0; + } + + RangeBitmap *GetMarkBitmap() + { + return markBitmap_; + } + + RememberedSet *GetCrossRegionRememberedSet() + { + return crossRegionSet_; + } + + RememberedSet *GetOldToNewRememberedSet() + { + return oldToNewSet_; + } + + static Region *ObjectAddressToRange(TaggedObject *obj) + { + return reinterpret_cast(ToUintPtr(obj) & ~DEFAULT_REGION_MASK); + } + + static Region *ObjectAddressToRange(uintptr_t objAddress) + { + return reinterpret_cast(objAddress & ~DEFAULT_REGION_MASK); + } + + bool InYoungGeneration() const + { + return IsFlagSet(RegionFlags::IS_IN_YOUNG_GENERATION); + } + + bool InOldGeneration() const + { + return IsFlagSet(RegionFlags::IS_IN_OLD_GENERATION); + } + + bool InYoungAndOldGeneration() const + { + return IsFlagSet(RegionFlags::IS_IN_YOUNG_OR_OLD_GENERATION); + } + + bool InYoungAndCSetGeneration() const + { + return IsFlagSet(RegionFlags::IS_IN_YOUNG_OR_CSET_GENERATION); + } + + bool InPromoteSet() const + { + return IsFlagSet(RegionFlags::IS_IN_PROMOTE_SET); + } + + bool InCollectSet() const + { + return IsFlagSet(RegionFlags::IS_IN_COLLECT_SET); + } + + bool HasAgeMark() const + { + return IsFlagSet(RegionFlags::HAS_AGE_MARK); + } + + bool BelowAgeMark() const + { + return IsFlagSet(RegionFlags::BELOW_AGE_MARK); + } + + bool InRange(uintptr_t address) + { + return address >= begin_ && address <= end_; + } + + inline RangeBitmap *GetOrCreateMarkBitmap(); + inline RangeBitmap *CreateMarkBitmap(); + inline void ClearMarkBitmap(); + inline RememberedSet *CreateRememberedSet(); + inline RememberedSet *GetOrCreateCrossRegionRememberedSet(); + inline RememberedSet *GetOrCreateOldToNewRememberedSet(); + inline void InsertCrossRegionRememberedSet(uintptr_t addr); + inline void AtomicInsertCrossRegionRememberedSet(uintptr_t addr); + inline void InsertOldToNewRememberedSet(uintptr_t addr); + inline void AtomicInsertOldToNewRememberedSet(uintptr_t addr); + inline void ClearCrossRegionRememberedSet(); + inline void ClearOldToNewRememberedSet(); + + uintptr_t GetAllocateBase() const + { + return allocateBase_; + } + + size_t GetAllocatedBytes(uintptr_t top = 0) + { + ASSERT(top == 0 || InRange(top)); + return (top == 0) ? (highWaterMark_ - begin_) : (top - begin_); + } + + void SetHighWaterMark(uintptr_t mark) + { + ASSERT(InRange(mark)); + highWaterMark_ = mark; + } + + int SetCodeExecutableAndReadable() + { + // NOLINTNEXTLINE(hicpp-signed-bitwise) + int res = mprotect(reinterpret_cast(allocateBase_), GetCapacity(), PROT_EXEC | PROT_READ | PROT_WRITE); + return res; + } + + void InitializeKind() + { + kinds_ = Span(new FreeObjectKind *[FreeObjectList::NumberOfKinds()](), + FreeObjectList::NumberOfKinds()); + } + + void RebuildKind() + { + EnumerateKinds([](FreeObjectKind *kind) { + if (kind != nullptr) { + kind->Rebuild(); + } + }); + } + + void DestroyKind() + { + for (auto kind : kinds_) { + delete kind; + } + delete[] kinds_.data(); + } + + FreeObjectKind *GetFreeObjectKind(KindType type) + { + // Thread safe + if (kinds_[type] == nullptr) { + kinds_[type] = new FreeObjectKind(type); + } + return kinds_[type]; + } + + template + void EnumerateKinds(Callback cb) + { + for (auto kind : kinds_) { + cb(kind); + } + } + + bool IsMarking() const + { + return marking_; + } + + void SetMarking(bool isMarking) + { + marking_ = isMarking; + } + + inline WorkerHelper *GetWorkList() const; + + void IncrementAliveObject(size_t size) + { + aliveObject_ += size; + } + + void DecreaseAliveObject(size_t size) + { + aliveObject_ -= size; + } + + void SetAliveObject(size_t size) + { + aliveObject_ = size; + } + + void ResetAliveObject() + { + aliveObject_ = 0; + } + + size_t AliveObject() const + { + return aliveObject_; + } + + bool MostObjectAlive() const + { + return aliveObject_ > MOST_OBJECT_ALIVE_THRESHOLD_PERCENT * GetSize(); + } + +private: + static constexpr double MOST_OBJECT_ALIVE_THRESHOLD_PERCENT = 0.8; + Space *space_; + Heap *heap_; + uintptr_t flags_; // Memory alignment, only low 32bits are used now + uintptr_t allocateBase_; + uintptr_t begin_; + uintptr_t end_; + uintptr_t highWaterMark_; + bool marking_ {false}; + std::atomic_size_t aliveObject_ {0}; + Region *next_ {nullptr}; + Region *prev_ {nullptr}; + RangeBitmap *markBitmap_ {nullptr}; + RememberedSet *crossRegionSet_ {nullptr}; + RememberedSet *oldToNewSet_ {nullptr}; + Span kinds_; + os::memory::Mutex lock_; + friend class SnapShot; +}; +} // namespace ecmascript +} // namespace panda + +#endif // ECMASCRIPT_MEM_REGION_H diff --git a/runtime/mem/region_factory.cpp b/runtime/mem/region_factory.cpp new file mode 100644 index 000000000..047c65beb --- /dev/null +++ b/runtime/mem/region_factory.cpp @@ -0,0 +1,171 @@ +/* + * Copyright (c) 2021-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_MM_REGION_FACTORY_H +#define ECMASCRIPT_MM_REGION_FACTORY_H + +#include "plugins/ecmascript/runtime/mem/region_factory.h" + +#include + +#include "plugins/ecmascript/runtime/mem/mark_stack.h" +#include "plugins/ecmascript/runtime/mem/region.h" +#include "libpandabase/mem/pool_manager.h" +#include "os/mem.h" + +namespace panda::ecmascript { +Region *RegionFactory::AllocateAlignedRegion(Space *space, size_t capacity) +{ + if (capacity == 0) { + LOG_ECMA_MEM(FATAL) << "capacity must have a size bigger than 0"; + UNREACHABLE(); + } + size_t commitSize = capacity; + + auto pool = PoolManager::GetMmapMemPool()->AllocPool(commitSize, panda::SpaceType::SPACE_TYPE_OBJECT, + AllocatorType::RUNSLOTS_ALLOCATOR, nullptr); + void *mapMem = pool.GetMem(); +#if ECMASCRIPT_ENABLE_ZAP_MEM + memset_s(mapMem, commitSize, 0, commitSize); +#endif + if (mapMem == nullptr) { + LOG_ECMA_MEM(FATAL) << "pool is empty " << annoMemoryUsage_.load(std::memory_order_relaxed); + UNREACHABLE(); + } + IncreaseAnnoMemoryUsage(capacity); + + uintptr_t mem = ToUintPtr(mapMem); + // Check that the address is 256K byte aligned + LOG_IF(AlignUp(mem, PANDA_POOL_ALIGNMENT_IN_BYTES) != mem, FATAL, RUNTIME) << "region not align by 256KB"; + + // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-pointer-arithmetic) + uintptr_t begin = AlignUp(mem + sizeof(Region), static_cast(MemAlignment::MEM_ALIGN_REGION)); + uintptr_t end = mem + capacity; + + return new (ToVoidPtr(mem)) Region(space, space->GetHeap(), mem, begin, end); +} + +void RegionFactory::FreeRegion(Region *region) +{ + auto size = region->GetCapacity(); + DecreaseAnnoMemoryUsage(size); +#if ECMASCRIPT_ENABLE_ZAP_MEM + memset_s(ToVoidPtr(region->GetAllocateBase()), size, INVALID_VALUE, size); +#endif + PoolManager::GetMmapMemPool()->FreePool(ToVoidPtr(region->GetAllocateBase()), size); +} + +Area *RegionFactory::AllocateArea(size_t capacity) +{ + size_t headerSize = sizeof(Area); + if (capacity < headerSize) { + LOG_ECMA_MEM(FATAL) << "capacity must have a size not less than sizeof Area."; + UNREACHABLE(); + } + if (cachedArea_ != nullptr && capacity <= cachedArea_->GetSize()) { + auto result = cachedArea_; + cachedArea_ = nullptr; + return result; + } + // NOLINTNEXTLINE(cppcoreguidelines-no-malloc) + void *mem = malloc(capacity); +#if ECMASCRIPT_ENABLE_ZAP_MEM + memset_s(mem, capacity, 0, capacity); +#endif + if (mem == nullptr) { + LOG_ECMA_MEM(FATAL) << "malloc failed"; + UNREACHABLE(); + } + IncreaseNativeMemoryUsage(capacity); + // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-pointer-arithmetic) + uintptr_t begin = reinterpret_cast(mem) + headerSize; + capacity -= headerSize; + return new (mem) Area(begin, capacity); +} + +void RegionFactory::FreeArea(Area *area) +{ + if (area == nullptr) { + return; + } + if (cachedArea_ == nullptr && area->GetSize() <= MAX_CACHED_CHUNK_AREA_SIZE) { + cachedArea_ = area; + return; + } + auto size = area->GetSize() + sizeof(Area); + DecreaseNativeMemoryUsage(size); +#if ECMASCRIPT_ENABLE_ZAP_MEM + memset_s(area, size, INVALID_VALUE, size); +#endif + // NOLINTNEXTLINE(cppcoreguidelines-no-malloc) + free(reinterpret_cast(area)); +} + +void RegionFactory::Free(void *mem, size_t size) +{ + if (mem == nullptr) { + return; + } + DecreaseNativeMemoryUsage(size); +#if ECMASCRIPT_ENABLE_ZAP_MEM + memset_s(mem, size, INVALID_VALUE, size); +#endif + // NOLINTNEXTLINE(cppcoreguidelines-no-malloc) + free(mem); +} + +void *RegionFactory::AllocateBuffer(size_t size) +{ + if (size == 0) { + LOG_ECMA_MEM(FATAL) << "size must have a size bigger than 0"; + UNREACHABLE(); + } + // NOLINTNEXTLINE(cppcoreguidelines-no-malloc) + void *ptr = malloc(size); + if (ptr == nullptr) { + LOG_ECMA_MEM(FATAL) << "malloc failed"; + UNREACHABLE(); + } +#if ECMASCRIPT_ENABLE_ZAP_MEM + memset_s(ptr, size, INVALID_VALUE, size); +#endif + IncreaseNativeMemoryUsage(size); + return ptr; +} + +void RegionFactory::FreeBuffer(void *mem) +{ + if (mem == nullptr) { + return; + } + DecreaseNativeMemoryUsage(malloc_usable_size(mem)); +#if ECMASCRIPT_ENABLE_ZAP_MEM + memset_s(mem, size, INVALID_VALUE, size); +#endif + // NOLINTNEXTLINE(cppcoreguidelines-no-malloc) + free(mem); +} + +void RegionFactory::FreeBufferFunc(void *buffer, void* data) +{ + if (buffer == nullptr || data == nullptr) { + return; + } + auto* factory = reinterpret_cast(data); + factory->FreeBuffer(buffer); +} +} // namespace panda::ecmascript + +#endif // ECMASCRIPT_MM_REGION_FACTORY_H diff --git a/runtime/mem/region_factory.h b/runtime/mem/region_factory.h new file mode 100644 index 000000000..465a58592 --- /dev/null +++ b/runtime/mem/region_factory.h @@ -0,0 +1,181 @@ +/* + * Copyright (c) 2021-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_MEM_REGION_FACTORY_H +#define ECMASCRIPT_MEM_REGION_FACTORY_H + +#include + +#include "plugins/ecmascript/runtime/mem/mem.h" +#include "plugins/ecmascript/runtime/mem/area.h" +#include "libpandabase/utils/logger.h" + +namespace panda::ecmascript { +class Region; +class Space; +class Area; + +class RegionFactory { +public: + RegionFactory() = default; + virtual ~RegionFactory() + { + if (cachedArea_ != nullptr) { + FreeArea(cachedArea_); + cachedArea_ = nullptr; + } + } + + Region *AllocateAlignedRegion(Space *space, size_t capacity); + void FreeRegion(Region *region); + Area *AllocateArea(size_t capacity); + void FreeArea(Area *area); + void Free(void *mem, size_t size); + void *AllocateBuffer(size_t size); + void FreeBuffer(void *mem); + + static void FreeBufferFunc(void* buffer, void* data); + + // implemented by AllocateBuffer + template + std::enable_if_t, T *> New(Args &&... args) + { + void *p = AllocateBuffer(sizeof(T)); + if (UNLIKELY(p == nullptr)) { + return nullptr; + } + new (p) T(std::forward(args)...); // NOLINT(bugprone-throw-keyword-missing) + return reinterpret_cast(p); + } + + template + void Delete(T *ptr) + { + if (ptr == nullptr) { + return; + } + // NOLINTNEXTLINE(readability-braces-around-statements,bugprone-suspicious-semicolon) + if constexpr (std::is_class_v) { + ptr->~T(); + } + FreeBuffer(ptr); + } + + void IncreaseAnnoMemoryUsage(size_t bytes) + { + size_t current = annoMemoryUsage_.fetch_add(bytes, std::memory_order_relaxed) + bytes; + size_t max = maxAnnoMemoryUsage_.load(std::memory_order_relaxed); + while (current > max && !maxAnnoMemoryUsage_.compare_exchange_weak(max, current, std::memory_order_relaxed)) { + } + } + + void DecreaseAnnoMemoryUsage(size_t bytes) + { + annoMemoryUsage_.fetch_sub(bytes, std::memory_order_relaxed); + } + + size_t GetAnnoMemoryUsage() const + { + return annoMemoryUsage_.load(std::memory_order_relaxed); + } + + size_t GetMaxAnnoMemoryUsage() const + { + return maxAnnoMemoryUsage_.load(std::memory_order_relaxed); + } + + void IncreaseNativeMemoryUsage(size_t bytes) + { + size_t current = nativeMemoryUsage_.fetch_add(bytes, std::memory_order_relaxed) + bytes; + size_t max = maxNativeMemoryUsage_.load(std::memory_order_relaxed); + while (current > max && !maxNativeMemoryUsage_.compare_exchange_weak(max, current, std::memory_order_relaxed)) { + } + } + + void DecreaseNativeMemoryUsage(size_t bytes) + { + nativeMemoryUsage_.fetch_sub(bytes, std::memory_order_relaxed); + } + + size_t GetNativeMemoryUsage() const + { + return nativeMemoryUsage_.load(std::memory_order_relaxed); + } + + size_t GetMaxNativeMemoryUsage() const + { + return maxNativeMemoryUsage_.load(std::memory_order_relaxed); + } + + void *Allocate(size_t size) + { + if (size == 0) { + LOG_ECMA_MEM(FATAL) << "size must have a size bigger than 0"; + UNREACHABLE(); + } + // NOLINTNEXTLINE(cppcoreguidelines-no-malloc) + void *ptr = malloc(size); + if (ptr == nullptr) { + LOG_ECMA_MEM(FATAL) << "malloc failed"; + UNREACHABLE(); + } + IncreaseNativeMemoryUsage(size); + return ptr; + } + + static inline Area *AllocateSpace(size_t capacity) + { + size_t headerSize = sizeof(Area); + if (capacity < headerSize) { + LOG_ECMA_MEM(FATAL) << "capacity must have a size not less than sizeof Area."; + UNREACHABLE(); + } + // NOLINTNEXTLINE(cppcoreguidelines-no-malloc) + void *mem = malloc(capacity); + if (mem == nullptr) { + LOG_ECMA_MEM(FATAL) << "malloc failed"; + UNREACHABLE(); + } + // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-pointer-arithmetic) + uintptr_t begin = reinterpret_cast(mem) + headerSize; + capacity -= headerSize; + return new (mem) Area(begin, capacity); + } + + static inline void FreeSpace(Area *area) + { + if (area == nullptr) { + return; + } + // NOLINTNEXTLINE(cppcoreguidelines-no-malloc) + free(reinterpret_cast(area)); + } + +private: + NO_COPY_SEMANTIC(RegionFactory); + NO_MOVE_SEMANTIC(RegionFactory); + +#if ECMASCRIPT_ENABLE_ZAP_MEM + static constexpr int INVALID_VALUE = 0x7; +#endif + Area *cachedArea_ {nullptr}; + std::atomic annoMemoryUsage_ {0}; + std::atomic maxAnnoMemoryUsage_ {0}; + std::atomic nativeMemoryUsage_ {0}; + std::atomic maxNativeMemoryUsage_ {0}; +}; +} // namespace panda::ecmascript + +#endif // ECMASCRIPT_MEM_REGION_FACTORY_H diff --git a/runtime/mem/remembered_set.h b/runtime/mem/remembered_set.h new file mode 100644 index 000000000..3059bcdf3 --- /dev/null +++ b/runtime/mem/remembered_set.h @@ -0,0 +1,95 @@ +/* + * Copyright (c) 2021-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_MEM_REMEMBERED_SET_H +#define ECMASCRIPT_MEM_REMEMBERED_SET_H + +#include "plugins/ecmascript/runtime/mem/mem.h" +#include "mem/gc/bitmap.h" + +namespace panda::ecmascript { +enum class SlotStatus : bool { + KEEP_SLOT, + CLEAR_SLOT, +}; + +class RememberedSet : public mem::Bitmap { +public: + using RememberedWordType = uintptr_t; + static const size_t BYTESPERCHUNK = static_cast(MemAlignment::MEM_ALIGN_OBJECT); + + RememberedSet(uintptr_t begin_addr, size_t range_size, uintptr_t bitset_addr) + : mem::Bitmap(reinterpret_cast(bitset_addr), range_size / BYTESPERCHUNK), + begin_addr_(begin_addr) + { + } + ~RememberedSet() = default; + NO_COPY_SEMANTIC(RememberedSet); + NO_MOVE_SEMANTIC(RememberedSet); + + void Insert(uintptr_t address) + { + SetBit(AddrToBitOffset(address)); + } + + void AtomicInsert(uintptr_t address) + { + AtomicTestAndSetBit(AddrToBitOffset(address)); + } + + template + void IterateOverSetBits(VisitorType visitor) + { + IterateOverSetBitsInRange(0, Size(), visitor); + } + + template + void IterateOverMarkedChunks(MemVisitor visitor) + { + IterateOverSetBits( + [&visitor, this](size_t bit_offset) -> bool { return visitor(BitOffsetToAddr(bit_offset)); }); + } + + void Clear(uintptr_t address) + { + ClearBit(AddrToBitOffset(address)); + } + + void ClearRange(uintptr_t begin, uintptr_t end) + { + ClearBitsInRange(AddrToBitOffset(begin), AddrToBitOffset(end)); + } + + static size_t GetSizeInByte(size_t range_size) + { + return (range_size >> mem::Bitmap::LOG_BITSPERWORD) / BYTESPERCHUNK * sizeof(mem::Bitmap::BitmapWordType); + } + +private: + void *BitOffsetToAddr(size_t bit_offset) const + { + return ToVoidPtr(begin_addr_ + bit_offset * BYTESPERCHUNK); + } + + size_t AddrToBitOffset(uintptr_t addr) const + { + return (addr - begin_addr_) / BYTESPERCHUNK; + } + + uintptr_t begin_addr_; +}; +} // namespace panda::ecmascript + +#endif // ECMASCRIPT_MEM_REMEMBERED_SET_H diff --git a/runtime/mem/semi_space_collector-inl.h b/runtime/mem/semi_space_collector-inl.h new file mode 100644 index 000000000..a3e75d419 --- /dev/null +++ b/runtime/mem/semi_space_collector-inl.h @@ -0,0 +1,37 @@ +/* + * Copyright (c) 2021-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_MEM_SEMI_SAPACE_COLLECTOR_INL_H +#define ECMASCRIPT_MEM_SEMI_SAPACE_COLLECTOR_INL_H + +#include "plugins/ecmascript/runtime/mem/semi_space_collector.h" + +#include "plugins/ecmascript/runtime/js_hclass-inl.h" +#include "plugins/ecmascript/runtime/mem/parallel_work_helper.h" + +namespace panda::ecmascript { +void SemiSpaceCollector::UpdatePromotedSlot(TaggedObject *object, ObjectSlot slot) +{ +#ifndef NDEBUG + JSTaggedValue value(slot.GetTaggedType()); + ASSERT(value.IsHeapObject()); +#endif + Region *objectRegion = Region::ObjectAddressToRange(object); + ASSERT(!objectRegion->InYoungGeneration()); + objectRegion->InsertOldToNewRememberedSet(slot.SlotAddress()); +} +} // namespace panda::ecmascript + +#endif // ECMASCRIPT_MEM_SEMI_SAPACE_COLLECTOR_INL_H diff --git a/runtime/mem/semi_space_collector.cpp b/runtime/mem/semi_space_collector.cpp new file mode 100644 index 000000000..acc7d2905 --- /dev/null +++ b/runtime/mem/semi_space_collector.cpp @@ -0,0 +1,148 @@ +/* + * Copyright (c) 2021-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 "plugins/ecmascript/runtime/mem/semi_space_collector-inl.h" + +#include "plugins/ecmascript/runtime/ecma_vm.h" +#include "plugins/ecmascript/runtime/mem/clock_scope.h" +#include "plugins/ecmascript/runtime/mem/concurrent_marker.h" +#include "plugins/ecmascript/runtime/mem/mem_manager.h" +#include "plugins/ecmascript/runtime/mem/heap-inl.h" +#include "plugins/ecmascript/runtime/mem/object_xray-inl.h" +#include "plugins/ecmascript/runtime/mem/mark_stack.h" +#include "plugins/ecmascript/runtime/mem/mem.h" +#include "plugins/ecmascript/runtime/mem/parallel_marker-inl.h" +#include "plugins/ecmascript/runtime/mem/space-inl.h" +#include "plugins/ecmascript/runtime/mem/tlab_allocator-inl.h" +#include "plugins/ecmascript/runtime/runtime_call_id.h" +#include "plugins/ecmascript/runtime/vmstat/runtime_stat.h" + +namespace panda::ecmascript { +SemiSpaceCollector::SemiSpaceCollector(Heap *heap, bool paralledGc) + : heap_(heap), paralledGc_(paralledGc), workList_(heap->GetWorkList()) +{ +} + +void SemiSpaceCollector::RunPhases() +{ + [[maybe_unused]] ecmascript::JSThread *thread = heap_->GetEcmaVM()->GetJSThread(); + INTERPRETER_TRACE(thread, SemiSpaceCollector_RunPhases); + [[maybe_unused]] ClockScope clockScope; + + ECMA_BYTRACE_NAME(BYTRACE_TAG_ARK, "SemiSpaceCollector::RunPhases"); + bool concurrentMark = heap_->CheckConcurrentMark(thread); + if (concurrentMark) { + ECMA_GC_LOG() << "SemiSpaceCollector after ConcurrentMarking"; + heap_->GetConcurrentMarker()->Reset(); // HPPGC use mark result to move TaggedObject. + } + InitializePhase(); + ParallelMarkingPhase(); + SweepPhases(); + FinishPhase(); + heap_->GetEcmaVM()->GetEcmaGCStats()->StatisticSemiCollector(clockScope.GetPauseTime(), semiCopiedSize_, + promotedSize_, commitSize_); + ECMA_GC_LOG() << "SemiSpaceCollector::RunPhases " << clockScope.TotalSpentTime(); +} + +void SemiSpaceCollector::InitializePhase() +{ + ECMA_BYTRACE_NAME(BYTRACE_TAG_ARK, "SemiSpaceCollector::InitializePhase"); + heap_->Prepare(); + workList_->Initialize(TriggerGCType::SEMI_GC, ParallelGCTaskPhase::SEMI_HANDLE_GLOBAL_POOL_TASK); + heap_->GetSemiGcMarker()->Initialized(); + heap_->GetEvacuationAllocator()->Initialize(TriggerGCType::SEMI_GC); + promotedSize_ = 0; + semiCopiedSize_ = 0; + commitSize_ = heap_->GetFromSpace()->GetCommittedSize(); +} + +void SemiSpaceCollector::ParallelMarkingPhase() +{ + ECMA_BYTRACE_NAME(BYTRACE_TAG_ARK, "SemiSpaceCollector::ParallelMarkingPhase"); + auto region = heap_->GetOldSpace()->GetCurrentRegion(); + + if (paralledGc_) { + heap_->PostParallelGCTask(ParallelGCTaskPhase::SEMI_HANDLE_THREAD_ROOTS_TASK); + heap_->PostParallelGCTask(ParallelGCTaskPhase::SEMI_HANDLE_SNAPSHOT_TASK); + heap_->GetSemiGcMarker()->ProcessOldToNew(0, region); + } else { + heap_->GetSemiGcMarker()->ProcessOldToNew(0, region); + heap_->GetSemiGcMarker()->ProcessSnapshotRSet(0); + heap_->GetSemiGcMarker()->MarkRoots(0); + heap_->GetSemiGcMarker()->ProcessMarkStack(0); + } + heap_->WaitRunningTaskFinished(); + + auto totalThreadCount = Platform::GetCurrentPlatform()->GetTotalThreadNum() + 1; // gc thread and main thread + for (uint32_t i = 0; i < totalThreadCount; i++) { + SlotNeedUpdate needUpdate(nullptr, ObjectSlot(0)); + while (workList_->GetSlotNeedUpdate(i, &needUpdate)) { + UpdatePromotedSlot(needUpdate.first, needUpdate.second); + } + } +} + +void SemiSpaceCollector::SweepPhases() +{ + ECMA_BYTRACE_NAME(BYTRACE_TAG_ARK, "SemiSpaceCollector::SweepPhases"); + auto totalThreadCount = Platform::GetCurrentPlatform()->GetTotalThreadNum() + 1; // gc thread and main thread + for (uint32_t i = 0; i < totalThreadCount; i++) { + ProcessQueue *queue = workList_->GetWeakReferenceQueue(i); + while (true) { + auto obj = queue->PopBack(); + if (UNLIKELY(obj == nullptr)) { + break; + } + ObjectSlot slot(ToUintPtr(obj)); + JSTaggedValue value(slot.GetTaggedType()); + auto header = value.GetTaggedWeakRef(); + MarkWord markWord(header); + if (markWord.IsForwardingAddress()) { + TaggedObject *dst = markWord.ToForwardingAddress(); + auto weakRef = JSTaggedValue(JSTaggedValue(dst).CreateAndGetWeakRef()).GetRawTaggedObject(); + slot.Update(weakRef); + } else { + slot.Update(static_cast(JSTaggedValue::Undefined().GetRawData())); + } + } + } + + auto stringTable = heap_->GetEcmaVM()->GetEcmaStringTable(); + WeakRootVisitor gcUpdateWeak = [](TaggedObject *header) { + Region *objectRegion = Region::ObjectAddressToRange(reinterpret_cast(header)); + if (!objectRegion->InYoungGeneration()) { + return header; + } + + MarkWord markWord(header); + if (markWord.IsForwardingAddress()) { + TaggedObject *dst = markWord.ToForwardingAddress(); + return dst; + } + return reinterpret_cast(ToUintPtr(nullptr)); + }; + stringTable->SweepWeakReference(gcUpdateWeak); + heap_->GetEcmaVM()->GetJSThread()->IterateWeakEcmaGlobalStorage(gcUpdateWeak); + heap_->GetEcmaVM()->ProcessReferences(gcUpdateWeak); + heap_->UpdateDerivedObjectInStack(); +} + +void SemiSpaceCollector::FinishPhase() +{ + ECMA_BYTRACE_NAME(BYTRACE_TAG_ARK, "SemiSpaceCollector::FinishPhase"); + workList_->Finish(semiCopiedSize_, promotedSize_); + heap_->GetEvacuationAllocator()->Finalize(TriggerGCType::SEMI_GC); +} +} // namespace panda::ecmascript diff --git a/runtime/mem/semi_space_collector.h b/runtime/mem/semi_space_collector.h new file mode 100644 index 000000000..2ac14c26f --- /dev/null +++ b/runtime/mem/semi_space_collector.h @@ -0,0 +1,80 @@ +/* + * Copyright (c) 2021-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_MEM_SEMI_SAPACE_COLLECTOR_H +#define ECMASCRIPT_MEM_SEMI_SAPACE_COLLECTOR_H + +#include "plugins/ecmascript/runtime/mem/clock_scope.h" +#include "plugins/ecmascript/runtime/mem/mem.h" +#include "plugins/ecmascript/runtime/mem/heap.h" +#include "plugins/ecmascript/runtime/mem/allocator.h" +#include "plugins/ecmascript/runtime/mem/mark_stack-inl.h" +#include "plugins/ecmascript/runtime/mem/mark_word.h" +#include "plugins/ecmascript/runtime/mem/slots.h" +#include "plugins/ecmascript/runtime/mem/object_xray.h" +#include "plugins/ecmascript/runtime/mem/remembered_set.h" +#include "plugins/ecmascript/runtime/mem/chunk_containers.h" +#include "plugins/ecmascript/runtime/mem/tlab_allocator.h" + +#include "os/mutex.h" + +namespace panda { +namespace ecmascript { +class Heap; +class JSHClass; +class WorkerHelper; + +class GarbageCollector { +public: + GarbageCollector() = default; + virtual ~GarbageCollector() = default; + DEFAULT_COPY_SEMANTIC(GarbageCollector); + DEFAULT_MOVE_SEMANTIC(GarbageCollector); +}; + +class SemiSpaceCollector : public GarbageCollector { +public: + explicit SemiSpaceCollector(Heap *heap, bool paralledGc); + ~SemiSpaceCollector() override = default; + NO_COPY_SEMANTIC(SemiSpaceCollector); + NO_MOVE_SEMANTIC(SemiSpaceCollector); + + void RunPhases(); + +private: + void InitializePhase(); + void ParallelMarkingPhase(); + void SweepPhases(); + void FinishPhase(); + + inline void UpdatePromotedSlot(TaggedObject *object, ObjectSlot slot); + + Heap *heap_; + size_t promotedSize_{0}; + size_t semiCopiedSize_{0}; + size_t commitSize_ = 0; + + // obtain from heap + bool paralledGc_ {false}; + WorkerHelper *workList_ {nullptr}; + + friend class TlabAllocator; + friend class WorkerHelper; + friend class Heap; +}; +} // namespace ecmascript +} // namespace panda + +#endif // ECMASCRIPT_MEM_SEMI_SAPACE_COLLECTOR_H diff --git a/runtime/mem/slots.h b/runtime/mem/slots.h new file mode 100644 index 000000000..a2d3c37ef --- /dev/null +++ b/runtime/mem/slots.h @@ -0,0 +1,102 @@ +/* + * Copyright (c) 2021-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_MEM_SLOTS_H +#define ECMASCRIPT_MEM_SLOTS_H + +#include "plugins/ecmascript/runtime/js_tagged_value.h" +#include "plugins/ecmascript/runtime/mem/mem.h" + +namespace panda::ecmascript { +class ObjectSlot { +public: + explicit ObjectSlot(uintptr_t slotAddr) : slotAddress_(slotAddr) {} + ~ObjectSlot() = default; + + DEFAULT_COPY_SEMANTIC(ObjectSlot); + DEFAULT_MOVE_SEMANTIC(ObjectSlot); + + void Update(TaggedObject *header) + { + Update(static_cast(ToUintPtr(header))); + } + + void Update(JSTaggedType value) + { + // NOLINTNEXTLINE(clang-analyzer-core.NullDereference) + *reinterpret_cast(slotAddress_) = value; + } + + TaggedObject *GetTaggedObjectHeader() const + { + return reinterpret_cast(GetTaggedType()); + } + + JSTaggedType GetTaggedType() const + { + // NOLINTNEXTLINE(clang-analyzer-core.NullDereference) + return *reinterpret_cast(slotAddress_); + } + + ObjectSlot &operator++() + { + slotAddress_ += sizeof(JSTaggedType); + return *this; + } + + // NOLINTNEXTLINE(cert-dcl21-cpp) + ObjectSlot operator++(int) + { + ObjectSlot ret = *this; + slotAddress_ += sizeof(JSTaggedType); + return ret; + } + + uintptr_t SlotAddress() const + { + return slotAddress_; + } + + bool operator<(const ObjectSlot &other) const + { + return slotAddress_ < other.slotAddress_; + } + bool operator<=(const ObjectSlot &other) const + { + return slotAddress_ <= other.slotAddress_; + } + bool operator>(const ObjectSlot &other) const + { + return slotAddress_ > other.slotAddress_; + } + bool operator>=(const ObjectSlot &other) const + { + return slotAddress_ >= other.slotAddress_; + } + bool operator==(const ObjectSlot &other) const + { + return slotAddress_ == other.slotAddress_; + } + bool operator!=(const ObjectSlot &other) const + { + return slotAddress_ != other.slotAddress_; + } + +private: + uintptr_t slotAddress_; +}; +} // namespace panda::ecmascript + +#endif // ECMASCRIPT_MEM_SLOTS_H diff --git a/runtime/mem/space-inl.h b/runtime/mem/space-inl.h new file mode 100644 index 000000000..107030095 --- /dev/null +++ b/runtime/mem/space-inl.h @@ -0,0 +1,62 @@ +/* + * Copyright (c) 2021-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_MEM_SPACE_INL_H +#define ECMASCRIPT_MEM_SPACE_INL_H + +#include "plugins/ecmascript/runtime/mem/space.h" + +namespace panda::ecmascript { +template +void Space::EnumerateRegions(const Callback &cb, Region *end) const +{ + Region *current = regionList_.GetFirst(); + if (end == nullptr) { + end = regionList_.GetLast(); + } + while (current != end) { + auto next = current->GetNext(); + cb(current); + current = next; + } + + if (current != nullptr) { + cb(current); + } +} + +template +void OldSpace::EnumerateCollectRegionSet(const Callback &cb) const +{ + for (Region *current : collectRegionSet_) { + if (current != nullptr) { + ASSERT(current->InCollectSet()); + cb(current); + } + } +} + +template +void OldSpace::EnumerateNonCollectRegionSet(const Callback &cb) const +{ + EnumerateRegions([&cb](Region *region) { + if (!region->InCollectSet()) { + cb(region); + } + }); +} +} // namespace panda::ecmascript + +#endif // ECMASCRIPT_MEM_SPACE_INL_H diff --git a/runtime/mem/space.cpp b/runtime/mem/space.cpp new file mode 100644 index 000000000..8ed18a659 --- /dev/null +++ b/runtime/mem/space.cpp @@ -0,0 +1,632 @@ +/* + * Copyright (c) 2021-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 "plugins/ecmascript/runtime/mem/space-inl.h" + +#include "plugins/ecmascript/runtime/class_linker/program_object.h" +#include "plugins/ecmascript/runtime/mem/heap.h" +#include "plugins/ecmascript/runtime/mem/mem_controller.h" +#include "plugins/ecmascript/runtime/mem/region-inl.h" +#include "plugins/ecmascript/runtime/mem/region_factory.h" +#include "plugins/ecmascript/runtime/mem/remembered_set.h" +#include "libpandabase/utils/logger.h" + +namespace panda::ecmascript { +Space::Space(Heap *heap, MemSpaceType spaceType, size_t initialCapacity, size_t maximumCapacity) + : heap_(heap), + vm_(heap_->GetEcmaVM()), + thread_(vm_->GetJSThread()), + regionFactory_(vm_->GetRegionFactory()), + spaceType_(spaceType), + initialCapacity_(initialCapacity), + maximumCapacity_(maximumCapacity), + committedSize_(0) +{ +} + +void Space::AddRegion(Region *region) +{ + LOG_ECMA_MEM(DEBUG) << "Add region:" << region << " to " << ToSpaceTypeName(spaceType_); + regionList_.AddNode(region); + IncrementCommitted(region->GetCapacity()); +} + +void Space::AddRegionToFirst(Region *region) +{ + LOG_ECMA_MEM(DEBUG) << "Add region to first:" << region << " to " << ToSpaceTypeName(spaceType_); + regionList_.AddNodeToFirst(region); + IncrementCommitted(region->GetCapacity()); +} + +void Space::RemoveRegion(Region *region) +{ + LOG_ECMA_MEM(DEBUG) << "Remove region:" << region << " to " << ToSpaceTypeName(spaceType_); + if (regionList_.HasNode(region)) { + if (spaceType_ == MemSpaceType::OLD_SPACE || spaceType_ == MemSpaceType::NON_MOVABLE || + spaceType_ == MemSpaceType::MACHINE_CODE_SPACE) { + region->RebuildKind(); + } + regionList_.RemoveNode(region); + DecrementCommitted(region->GetCapacity()); + } +} + +void Space::Initialize() +{ + Region *region = regionFactory_->AllocateAlignedRegion(this, DEFAULT_REGION_SIZE); + if (spaceType_ == MemSpaceType::SEMI_SPACE) { + region->SetFlag(RegionFlags::IS_IN_YOUNG_GENERATION); + } else if (spaceType_ == MemSpaceType::SNAPSHOT_SPACE) { + region->SetFlag(RegionFlags::IS_IN_SNAPSHOT_GENERATION); + } else if (spaceType_ == MemSpaceType::OLD_SPACE) { + region->InitializeKind(); + region->SetFlag(RegionFlags::IS_IN_OLD_GENERATION); + } else if (spaceType_ == MemSpaceType::MACHINE_CODE_SPACE) { + region->InitializeKind(); + region->SetFlag(RegionFlags::IS_IN_NON_MOVABLE_GENERATION); + // NOLINTNEXTLINE(clang-analyzer-deadcode.DeadStores) + [[maybe_unused]]int res = region->SetCodeExecutableAndReadable(); + LOG_ECMA_MEM(DEBUG) << "Initialize SetCodeExecutableAndReadable" << res; + } else if (spaceType_ == MemSpaceType::NON_MOVABLE) { + region->InitializeKind(); + region->SetFlag(RegionFlags::IS_IN_NON_MOVABLE_GENERATION); + } + + AddRegion(region); +} + +void Space::ReclaimRegions() +{ + EnumerateRegions([this](Region *current) { ClearAndFreeRegion(current); }); + regionList_.Clear(); + committedSize_ = 0; +} + +void Space::ClearAndFreeRegion(Region *region) +{ + LOG_ECMA_MEM(DEBUG) << "Clear region from:" << region << " to " << ToSpaceTypeName(spaceType_); + region->ClearMarkBitmap(); + region->ClearCrossRegionRememberedSet(); + region->ClearOldToNewRememberedSet(); + DecrementCommitted(region->GetCapacity()); + if (spaceType_ == MemSpaceType::OLD_SPACE || spaceType_ == MemSpaceType::NON_MOVABLE || + spaceType_ == MemSpaceType::MACHINE_CODE_SPACE) { + region->DestroyKind(); + } + regionFactory_->FreeRegion(region); +} + +size_t Space::GetHeapObjectSize() const +{ + Region *last = GetCurrentRegion(); + auto top = heap_->GetHeapManager()->GetNewSpaceAllocator().GetTop(); + size_t result = last->GetAllocatedBytes(top); + + EnumerateRegions([&result, &last](Region *current) { + if (current != last) { + result += current->GetAllocatedBytes(); + } + }); + + return result; +} + +SemiSpace::SemiSpace(Heap *heap, size_t initialCapacity, size_t maximumCapacity) + : Space(heap, MemSpaceType::SEMI_SPACE, initialCapacity, maximumCapacity), ageMark_(0) +{ +} + +bool SemiSpace::Expand(uintptr_t top) +{ + if (committedSize_ >= maximumCapacity_) { + return false; + } + GetCurrentRegion()->SetHighWaterMark(top); + Region *region = regionFactory_->AllocateAlignedRegion(this, DEFAULT_REGION_SIZE); + region->SetFlag(RegionFlags::IS_IN_YOUNG_GENERATION); + if (!thread_->IsReadyToMark()) { + region->SetMarking(true); + } + + AddRegion(region); + return true; +} + +bool SemiSpace::AddRegionToList(Region *region) +{ + ASSERT(region != nullptr); + if (GetCommittedSize() >= GetMaximumCapacity()) { + return false; + } + region->ResetFlag(); + region->SetFlag(RegionFlags::IS_IN_YOUNG_GENERATION); + region->SetFlag(RegionFlags::IS_IN_PROMOTE_SET); + region->SetSpace(this); + AddRegionToFirst(region); + return true; +} + +void SemiSpace::Swap([[maybe_unused]] SemiSpace *other) {} + +void SemiSpace::SetAgeMark(uintptr_t mark) +{ + ageMark_ = mark; + Region *last = GetCurrentRegion(); + last->SetFlag(RegionFlags::HAS_AGE_MARK); + + EnumerateRegions([&last](Region *current) { + if (current != last) { + current->SetFlag(RegionFlags::BELOW_AGE_MARK); + } + }); +} + +bool SemiSpace::ContainObject(TaggedObject *object) const +{ + auto region = GetRegionList().GetFirst(); + while (region != nullptr) { + if (region->InRange(ToUintPtr(object))) { + return true; + } + region = region->GetNext(); + } + + return false; +} + +bool SemiSpace::IsLive(TaggedObject *object) const +{ + auto region = GetRegionList().GetFirst(); + while (region != nullptr) { + if (region->InRange(ToUintPtr(object))) { + auto freeObject = FreeObject::Cast(ToUintPtr(object)); + return !freeObject->IsFreeObject(); + } + region = region->GetNext(); + } + return false; +} + +void SemiSpace::IterateOverObjects(const std::function &visitor) const +{ + auto current = GetCurrentRegion(); + EnumerateRegions([&](Region *region) { + auto curPtr = region->GetBegin(); + uintptr_t endPtr; + if (region == current) { + auto top = heap_->GetHeapManager()->GetNewSpaceAllocator().GetTop(); + endPtr = curPtr + region->GetAllocatedBytes(top); + } else { + endPtr = curPtr + region->GetAllocatedBytes(); + } + + size_t objSize; + while (curPtr < endPtr) { + auto freeObject = FreeObject::Cast(curPtr); + if (!freeObject->IsFreeObject()) { + auto obj = reinterpret_cast(curPtr); + visitor(obj); + objSize = obj->GetClass()->SizeFromJSHClass(obj); + } else { + objSize = freeObject->Available(); + } + curPtr += objSize; + // NOLINTNEXTLINE(bugprone-lambda-function-name) + CHECK_OBJECT_SIZE(objSize); + } + // NOLINTNEXTLINE(bugprone-lambda-function-name) + CHECK_REGION_END(curPtr, endPtr); + }); +} + +size_t SemiSpace::GetAllocatedSizeSinceGC() const +{ + Region *last = GetCurrentRegion(); + if (last->BelowAgeMark()) { + return 0; + } + auto top = GetHeap()->GetHeapManager()->GetNewSpaceAllocator().GetTop(); + size_t result = 0; + if (last->HasAgeMark()) { + result = last->GetAllocatedBytes(top) - last->GetAllocatedBytes(ageMark_); + return result; + } + result = last->GetAllocatedBytes(top); + EnumerateRegions([this, &result, &last](Region *current) { + if (!current->HasAgeMark() && !current->BelowAgeMark()) { + if (current != last) { + result += current->GetAllocatedBytes(); + } + } else if (current->HasAgeMark()) { + result += current->GetAllocatedBytes(); + result -= current->GetAllocatedBytes(this->GetAgeMark()); + } + }); + return result; +} + +OldSpace::OldSpace(Heap *heap, size_t initialCapacity, size_t maximumCapacity) + : Space(heap, MemSpaceType::OLD_SPACE, initialCapacity, maximumCapacity) +{ +} + +bool OldSpace::Expand() +{ + if (committedSize_ >= maximumCapacity_) { + LOG_ECMA_MEM(FATAL) << "Committed size " << committedSize_ << " of old space is too big. "; + return false; + } + Region *region = regionFactory_->AllocateAlignedRegion(this, DEFAULT_REGION_SIZE); + region->SetFlag(RegionFlags::IS_IN_OLD_GENERATION); + if (!thread_->IsReadyToMark()) { + region->SetMarking(true); + } + region->InitializeKind(); + AddRegion(region); + return true; +} + +bool OldSpace::AddRegionToList(Region *region) +{ + ASSERT(region != nullptr); + if (GetCommittedSize() >= GetMaximumCapacity()) { + LOG_ECMA_MEM(FATAL) << "Committed size " << GetCommittedSize() << " of old space is too big. "; + return false; + } + if (!region->InOldGeneration()) { + region->InitializeKind(); + } + region->ResetFlag(); + region->SetFlag(RegionFlags::IS_IN_OLD_GENERATION); + region->SetFlag(RegionFlags::IS_IN_PROMOTE_SET); + region->SetSpace(this); + AddRegionToFirst(region); + return true; +} + +bool OldSpace::ContainObject(TaggedObject *object) const +{ + auto region = GetRegionList().GetFirst(); + while (region != nullptr) { + if (region->InRange(ToUintPtr(object))) { + return true; + } + region = region->GetNext(); + } + return false; +} + +bool OldSpace::IsLive(TaggedObject *object) const +{ + auto region = GetRegionList().GetFirst(); + while (region != nullptr) { + if (region->InRange(ToUintPtr(object))) { + if (region->InCollectSet()) { + return false; + } + auto freeObject = FreeObject::Cast(ToUintPtr(object)); + return !freeObject->IsFreeObject(); + } + region = region->GetNext(); + } + return false; +} + +void OldSpace::IterateOverObjects(const std::function &visitor) const +{ + EnumerateRegions([&](Region *region) { + if (region->InCollectSet()) { + return; + } + uintptr_t curPtr = region->GetBegin(); + uintptr_t endPtr = region->GetEnd(); + while (curPtr < endPtr) { + auto freeObject = FreeObject::Cast(curPtr); + size_t objSize; + if (!freeObject->IsFreeObject()) { + auto obj = reinterpret_cast(curPtr); + visitor(obj); + objSize = obj->GetClass()->SizeFromJSHClass(obj); + } else { + objSize = freeObject->Available(); + } + curPtr += objSize; + // NOLINTNEXTLINE(bugprone-lambda-function-name) + CHECK_OBJECT_SIZE(objSize); + } + // NOLINTNEXTLINE(bugprone-lambda-function-name) + CHECK_REGION_END(curPtr, endPtr); + }); +} + +size_t OldSpace::GetHeapObjectSize() const +{ + size_t result; + size_t availableSize = heap_->GetHeapManager()->GetOldSpaceAllocator().GetAvailableSize(); + result = committedSize_ - availableSize; + return result; +} + +void OldSpace::Merge(Space *fromSpace) +{ + ASSERT(GetSpaceType() == fromSpace->GetSpaceType()); + fromSpace->EnumerateRegions([&](Region *region) { + fromSpace->DecrementCommitted(region->GetCapacity()); + AddRegion(region); + }); + fromSpace->GetRegionList().Clear(); +} + +void OldSpace::AddRegionToCSet(Region *region) +{ + region->SetFlag(RegionFlags::IS_IN_COLLECT_SET); + collectRegionSet_.emplace_back(region); +} + +void OldSpace::ClearRegionFromCSet() +{ + EnumerateCollectRegionSet([](Region *region) { region->ClearFlag(RegionFlags::IS_IN_COLLECT_SET); }); + collectRegionSet_.clear(); +} + +void OldSpace::RemoveRegionFromCSetAndList(Region *region) +{ + for (auto current = collectRegionSet_.begin(); current != collectRegionSet_.end(); current++) { + if (*current == region) { + region->ClearFlag(RegionFlags::IS_IN_COLLECT_SET); + current = collectRegionSet_.erase(current); + break; + } + } + RemoveRegion(region); +} + +void OldSpace::RemoveCSetFromList() +{ + EnumerateCollectRegionSet([this](Region *current) { GetRegionList().RemoveNode(current); }); +} + +void OldSpace::ReclaimRegionCSet() +{ + EnumerateCollectRegionSet([this](Region *current) { ClearAndFreeRegion(current); }); + collectRegionSet_.clear(); +} + +void OldSpace::SelectCSet() +{ + EnumerateRegions([this](Region *region) { + if (!region->MostObjectAlive()) { + collectRegionSet_.emplace_back(region); + } + }); + if (collectRegionSet_.size() < PARTIAL_GC_MIN_COLLECT_REGION_SIZE) { + heap_->SetOnlyMarkSemi(true); + collectRegionSet_.clear(); + return; + } + // sort + std::sort(collectRegionSet_.begin(), collectRegionSet_.end(), + [](Region *first, Region *second) { return first->AliveObject() < second->AliveObject(); }); + uint64_t selectedRegionNumber = GetSelectedRegionNumber(); + if (collectRegionSet_.size() > selectedRegionNumber) { + collectRegionSet_.resize(selectedRegionNumber); + } + for (Region *region : collectRegionSet_) { + region->SetFlag(RegionFlags::IS_IN_COLLECT_SET); + } +} + +NonMovableSpace::NonMovableSpace(Heap *heap, size_t initialCapacity, size_t maximumCapacity) + : Space(heap, MemSpaceType::NON_MOVABLE, initialCapacity, maximumCapacity) +{ +} + +bool NonMovableSpace::Expand() +{ + if (committedSize_ >= maximumCapacity_) { + LOG_ECMA_MEM(FATAL) << "Committed size " << committedSize_ << " of non movable space is too big. "; + return false; + } + Region *region = regionFactory_->AllocateAlignedRegion(this, DEFAULT_REGION_SIZE); + region->SetFlag(IS_IN_NON_MOVABLE_GENERATION); + if (!thread_->IsReadyToMark()) { + region->SetMarking(true); + } + region->InitializeKind(); + AddRegion(region); + return true; +} + +size_t NonMovableSpace::GetHeapObjectSize() const +{ + size_t result; + size_t availableSize = heap_->GetHeapManager()->GetNonMovableSpaceAllocator().GetAvailableSize(); + result = GetCommittedSize() - availableSize; + return result; +} + +SnapShotSpace::SnapShotSpace(Heap *heap, size_t initialCapacity, size_t maximumCapacity) + : Space(heap, MemSpaceType::SNAPSHOT_SPACE, initialCapacity, maximumCapacity) +{ +} + +bool SnapShotSpace::Expand(uintptr_t top) +{ + if (committedSize_ >= maximumCapacity_) { + return false; + } + Region *current = GetCurrentRegion(); + if (current != nullptr) { + current->SetHighWaterMark(top); + } + Region *region = regionFactory_->AllocateAlignedRegion(this, DEFAULT_SNAPSHOT_SPACE_SIZE); + region->SetFlag(RegionFlags::IS_IN_SNAPSHOT_GENERATION); + if (!thread_->IsReadyToMark()) { + region->SetMarking(true); + } + AddRegion(region); + return true; +} + +void Space::Destroy() +{ + ReclaimRegions(); +} + +void NonMovableSpace::IterateOverObjects(const std::function &visitor) const +{ + EnumerateRegions([&](Region *region) { + uintptr_t curPtr = region->GetBegin(); + uintptr_t endPtr = region->GetEnd(); + while (curPtr < endPtr) { + auto freeObject = FreeObject::Cast(curPtr); + size_t objSize; + if (!freeObject->IsFreeObject()) { + auto obj = reinterpret_cast(curPtr); + visitor(obj); + objSize = obj->GetClass()->SizeFromJSHClass(obj); + } else { + objSize = freeObject->Available(); + } + curPtr += objSize; + // NOLINTNEXTLINE(bugprone-lambda-function-name) + CHECK_OBJECT_SIZE(objSize); + } + // NOLINTNEXTLINE(bugprone-lambda-function-name) + CHECK_REGION_END(curPtr, endPtr); + }); +} + +bool NonMovableSpace::ContainObject(TaggedObject *object) const +{ + auto region = GetRegionList().GetFirst(); + while (region != nullptr) { + if (region->InRange(ToUintPtr(object))) { + return true; + } + region = region->GetNext(); + } + return false; +} + +bool NonMovableSpace::IsLive(TaggedObject *object) const +{ + auto region = GetRegionList().GetFirst(); + while (region != nullptr) { + if (region->InRange(ToUintPtr(object))) { + auto freeObject = FreeObject::Cast(ToUintPtr(object)); + return !freeObject->IsFreeObject(); + } + region = region->GetNext(); + } + return false; +} + +HugeObjectSpace::HugeObjectSpace(Heap *heap, size_t initialCapacity, size_t maximumCapacity) + : Space(heap, MemSpaceType::HUGE_OBJECT_SPACE, initialCapacity, maximumCapacity) +{ +} + +uintptr_t HugeObjectSpace::Allocate(size_t objectSize) +{ + if (committedSize_ >= maximumCapacity_) { + LOG_ECMA_MEM(FATAL) << "Committed size " << committedSize_ << " of huge object space is too big. " + << " old space committed: " << heap_->GetOldSpace()->GetCommittedSize() + << " old space limit: " << heap_->GetOldSpaceAllocLimit() + << " length: " << GetRegionList().GetLength(); + return 0; + } + size_t alignedSize = AlignUp(objectSize + sizeof(Region), PANDA_POOL_ALIGNMENT_IN_BYTES); + if (UNLIKELY(alignedSize > MAX_HUGE_OBJECT_SIZE)) { + LOG_ECMA_MEM(FATAL) << "The size is too big for this allocator. Return nullptr."; + return 0; + } + Region *region = regionFactory_->AllocateAlignedRegion(this, alignedSize); + region->SetFlag(RegionFlags::IS_HUGE_OBJECT); + if (!thread_->IsReadyToMark()) { + region->SetMarking(true); + } + AddRegion(region); + return region->GetBegin(); +} + +void HugeObjectSpace::Free(Region *region) +{ + GetRegionList().RemoveNode(region); + ClearAndFreeRegion(region); +} + +bool HugeObjectSpace::ContainObject(TaggedObject *object) const +{ + auto region = GetRegionList().GetFirst(); + while (region != nullptr) { + if (region->InRange(ToUintPtr(object))) { + return true; + } + region = region->GetNext(); + } + return false; +} + +bool HugeObjectSpace::IsLive(TaggedObject *object) const +{ + return ContainObject(object); +} + +size_t HugeObjectSpace::GetHeapObjectSize() const +{ + return committedSize_; +} + +void HugeObjectSpace::IterateOverObjects(const std::function &objectVisitor) const +{ + EnumerateRegions([&](Region *region) { + uintptr_t curPtr = region->GetBegin(); + objectVisitor(reinterpret_cast(curPtr)); + }); +} + +MachineCodeSpace::MachineCodeSpace(Heap *heap, size_t initialCapacity, size_t maximumCapacity) + : Space(heap, MemSpaceType::MACHINE_CODE_SPACE, initialCapacity, maximumCapacity) +{ +} + +bool MachineCodeSpace::Expand() +{ + if (committedSize_ >= maximumCapacity_) { + LOG_ECMA_MEM(FATAL) << "Committed size " << committedSize_ << " of machine Code space is too big. "; + return false; + } + Region *region = regionFactory_->AllocateAlignedRegion(this, DEFAULT_REGION_SIZE); + region->SetFlag(IS_IN_NON_MOVABLE_GENERATION); + region->InitializeKind(); + AddRegion(region); + if (!thread_->IsReadyToMark()) { + region->SetMarking(true); + } + // NOLINTNEXTLINE(clang-analyzer-deadcode.DeadStores) + [[maybe_unused]]int res = region->SetCodeExecutableAndReadable(); + LOG_ECMA_MEM(DEBUG) << "MachineCodeSpace::Expand() SetCodeExecutableAndReadable" << res; + return true; +} + +size_t MachineCodeSpace::GetHeapObjectSize() const +{ + size_t result = 0; + size_t availableSize = GetHeap()->GetHeapManager()->GetMachineCodeSpaceAllocator().GetAvailableSize(); + result = committedSize_ - availableSize; + return result; +} +} // namespace panda::ecmascript diff --git a/runtime/mem/space.h b/runtime/mem/space.h new file mode 100644 index 000000000..24737c138 --- /dev/null +++ b/runtime/mem/space.h @@ -0,0 +1,310 @@ +/* + * Copyright (c) 2021-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_MEM_SPACE_H +#define ECMASCRIPT_MEM_SPACE_H + +#include "mem/gc/bitmap.h" +#include "plugins/ecmascript/runtime/mem/c_containers.h" +#include "plugins/ecmascript/runtime/mem/ecma_list.h" +#include "plugins/ecmascript/runtime/mem/mem.h" +#include "plugins/ecmascript/runtime/mem/region.h" +#include "libpandabase/utils/type_helpers.h" +#include "libpandafile/file.h" + +namespace panda::ecmascript { +class EcmaVM; +class Heap; +class Program; + +enum MemSpaceType { + OLD_SPACE = 0, + NON_MOVABLE, + MACHINE_CODE_SPACE, + HUGE_OBJECT_SPACE, + SEMI_SPACE, + SNAPSHOT_SPACE, + COMPRESS_SPACE, + SPACE_TYPE_LAST, // Count of different types + + FREE_LIST_NUM = MACHINE_CODE_SPACE - OLD_SPACE + 1, +}; + +enum TriggerGCType { + SEMI_GC, + OLD_GC, + NON_MOVE_GC, + HUGE_GC, + MACHINE_CODE_GC, + COMPRESS_FULL_GC, + GC_TYPE_LAST // Count of different types +}; + +static inline std::string ToSpaceTypeName(MemSpaceType type) +{ + switch (type) { + case OLD_SPACE: + return "old space"; + case NON_MOVABLE: + return "non movable space"; + case MACHINE_CODE_SPACE: + return "machine code space"; + case HUGE_OBJECT_SPACE: + return "huge object space"; + case SEMI_SPACE: + return "semi space"; + case SNAPSHOT_SPACE: + return "snapshot space"; + case COMPRESS_SPACE: + return "compress space"; + default: + return "unknow space"; + } +} + +static constexpr size_t SPACE_TYPE_SIZE = helpers::ToUnderlying(MemSpaceType::SPACE_TYPE_LAST); + +class Space { +public: + Space(Heap *heap, MemSpaceType spaceType, size_t initialCapacity, size_t maximumCapacity); + virtual ~Space() = default; + NO_COPY_SEMANTIC(Space); + NO_MOVE_SEMANTIC(Space); + + Heap *GetHeap() const + { + return heap_; + } + + size_t GetMaximumCapacity() const + { + return maximumCapacity_; + } + + void SetMaximumCapacity(size_t maximumCapacity) + { + maximumCapacity_ = maximumCapacity; + } + + size_t GetInitialCapacity() const + { + return initialCapacity_; + } + + size_t GetCommittedSize() const + { + return committedSize_; + } + + void IncrementCommitted(size_t bytes) + { + committedSize_ += bytes; + } + + void DecrementCommitted(size_t bytes) + { + committedSize_ -= bytes; + } + + MemSpaceType GetSpaceType() const + { + return spaceType_; + } + + uintptr_t GetAllocateAreaBegin() const + { + return regionList_.GetLast()->GetBegin(); + } + + uintptr_t GetAllocateAreaEnd() const + { + return regionList_.GetLast()->GetEnd(); + } + + Region *GetCurrentRegion() const + { + return regionList_.GetLast(); + } + + uint32_t GetRegionCount() + { + return regionList_.GetLength(); + } + + EcmaList &GetRegionList() + { + return regionList_; + } + + const EcmaList &GetRegionList() const + { + return regionList_; + } + + size_t GetHeapObjectSize() const; + + template + void EnumerateRegions(const Callback &cb, Region *end = nullptr) const; + + void AddRegion(Region *region); + void AddRegionToFirst(Region *region); + void RemoveRegion(Region *region); + + void Initialize(); + void Destroy(); + + void ReclaimRegions(); + + void ClearAndFreeRegion(Region *region); + +protected: + // NOLINTNEXTLINE(misc-non-private-member-variables-in-classes) + Heap *heap_ {nullptr}; + // NOLINTNEXTLINE(misc-non-private-member-variables-in-classes) + EcmaVM *vm_ {nullptr}; + // NOLINTNEXTLINE(misc-non-private-member-variables-in-classes) + JSThread *thread_ {nullptr}; + // NOLINTNEXTLINE(misc-non-private-member-variables-in-classes) + RegionFactory *regionFactory_ {nullptr}; + // NOLINTNEXTLINE(misc-non-private-member-variables-in-classes) + EcmaList regionList_ {}; + // NOLINTNEXTLINE(misc-non-private-member-variables-in-classes) + MemSpaceType spaceType_ {}; + // NOLINTNEXTLINE(misc-non-private-member-variables-in-classes) + size_t initialCapacity_ {0}; + // NOLINTNEXTLINE(misc-non-private-member-variables-in-classes) + size_t maximumCapacity_ {0}; + // NOLINTNEXTLINE(misc-non-private-member-variables-in-classes) + size_t committedSize_ {0}; + // NOLINTNEXTLINE(misc-non-private-member-variables-in-classes) +}; + +class SemiSpace : public Space { +public: + explicit SemiSpace(Heap *heap, size_t initialCapacity = DEFAULT_SEMI_SPACE_SIZE, + size_t maximumCapacity = SEMI_SPACE_SIZE_CAPACITY); + ~SemiSpace() override = default; + NO_COPY_SEMANTIC(SemiSpace); + NO_MOVE_SEMANTIC(SemiSpace); + + void SetAgeMark(uintptr_t mark); + + uintptr_t GetAgeMark() const + { + return ageMark_; + } + + bool Expand(uintptr_t top); + bool AddRegionToList(Region *region); + + void Swap(SemiSpace *other); + + bool ContainObject(TaggedObject *object) const; + bool IsLive(TaggedObject *object) const; + void IterateOverObjects(const std::function &objectVisitor) const; + + size_t GetAllocatedSizeSinceGC() const; + +private: + uintptr_t ageMark_; +}; + +class OldSpace : public Space { +public: + explicit OldSpace(Heap *heap, size_t initialCapacity = DEFAULT_OLD_SPACE_SIZE, + size_t maximumCapacity = MAX_OLD_SPACE_SIZE); + ~OldSpace() override = default; + NO_COPY_SEMANTIC(OldSpace); + NO_MOVE_SEMANTIC(OldSpace); + bool Expand(); + bool AddRegionToList(Region *region); + bool ContainObject(TaggedObject *object) const; + bool IsLive(TaggedObject *object) const; + void IterateOverObjects(const std::function &objectVisitor) const; + size_t GetHeapObjectSize() const; + void Merge(Space *fromSpace); + void AddRegionToCSet(Region *region); + void RemoveRegionFromCSetAndList(Region *region); + void ClearRegionFromCSet(); + void RemoveCSetFromList(); + void ReclaimRegionCSet(); + template + void EnumerateCollectRegionSet(const Callback &cb) const; + template + void EnumerateNonCollectRegionSet(const Callback &cb) const; + void SelectCSet(); + size_t GetSelectedRegionNumber() const + { + return std::max(committedSize_ / PARTIAL_GC_MAX_COLLECT_REGION_RATE, PARTIAL_GC_INITIAL_COLLECT_REGION_SIZE); + } +private: + static constexpr size_t PARTIAL_GC_MAX_COLLECT_REGION_RATE = 1024 * 1024 * 2; + static constexpr size_t PARTIAL_GC_INITIAL_COLLECT_REGION_SIZE = 16; + static constexpr size_t PARTIAL_GC_MIN_COLLECT_REGION_SIZE = 5; + CVector collectRegionSet_; +}; + +class NonMovableSpace : public Space { +public: + explicit NonMovableSpace(Heap *heap, size_t initialCapacity = DEFAULT_NON_MOVABLE_SPACE_SIZE, + size_t maximumCapacity = MAX_NON_MOVABLE_SPACE_SIZE); + ~NonMovableSpace() override = default; + NO_COPY_SEMANTIC(NonMovableSpace); + NO_MOVE_SEMANTIC(NonMovableSpace); + bool Expand(); + bool ContainObject(TaggedObject *object) const; + bool IsLive(TaggedObject *object) const; + void IterateOverObjects(const std::function &objectVisitor) const; + size_t GetHeapObjectSize() const; +}; + +class SnapShotSpace : public Space { +public: + explicit SnapShotSpace(Heap *heap, size_t initialCapacity = DEFAULT_SNAPSHOT_SPACE_SIZE, + size_t maximumCapacity = MAX_SNAPSHOT_SPACE_SIZE); + ~SnapShotSpace() override = default; + NO_COPY_SEMANTIC(SnapShotSpace); + NO_MOVE_SEMANTIC(SnapShotSpace); + bool Expand(uintptr_t top); +}; + +class HugeObjectSpace : public Space { +public: + explicit HugeObjectSpace(Heap *heap, size_t initialCapacity = DEFAULT_OLD_SPACE_SIZE, + size_t maximumCapacity = MAX_HUGE_OBJECT_SPACE_SIZE); + ~HugeObjectSpace() override = default; + NO_COPY_SEMANTIC(HugeObjectSpace); + NO_MOVE_SEMANTIC(HugeObjectSpace); + uintptr_t Allocate(size_t objectSize); + void Free(Region *region); + size_t GetHeapObjectSize() const; + bool ContainObject(TaggedObject *object) const; + bool IsLive(TaggedObject *object) const; + void IterateOverObjects(const std::function &objectVisitor) const; +}; + +class MachineCodeSpace : public Space { +public: + explicit MachineCodeSpace(Heap *heap, size_t initialCapacity = DEFAULT_MACHINE_CODE_SPACE_SIZE, + size_t maximumCapacity = MAX_MACHINE_CODE_SPACE_SIZE); + ~MachineCodeSpace() override = default; + NO_COPY_SEMANTIC(MachineCodeSpace); + NO_MOVE_SEMANTIC(MachineCodeSpace); // Note: Expand(), ContainObject(), IsLive() left for define + bool Expand(); + size_t GetHeapObjectSize() const; +}; +} // namespace panda::ecmascript + +#endif // ECMASCRIPT_MEM_SPACE_H diff --git a/runtime/mem/tagged_object-inl.h b/runtime/mem/tagged_object-inl.h new file mode 100644 index 000000000..1bc2c457f --- /dev/null +++ b/runtime/mem/tagged_object-inl.h @@ -0,0 +1,53 @@ +/* + * Copyright (c) 2021-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_TAGGED_OBJECT_HEADER_INL_H +#define ECMASCRIPT_TAGGED_OBJECT_HEADER_INL_H + +#include "plugins/ecmascript/runtime/mem/tagged_object.h" + +#include +#include "plugins/ecmascript/runtime/js_hclass.h" +#include "plugins/ecmascript/runtime/js_handle.h" +#include "heap.h" + +namespace panda::ecmascript { +inline void TaggedObject::SetClassWithoutBarrier(JSHClass *hclass) +{ + ObjectHeader::SetClass(hclass->GetHClass()); +} + +inline void TaggedObject::SetClass(JSHClass *hclass) +{ + ObjectHeader::SetClass(hclass->GetHClass()); +} + +inline void TaggedObject::SetClass(JSHandle hclass) +{ + ObjectHeader::SetClass(hclass->GetHClass()); +} + +inline JSHClass *TaggedObject::GetClass() const +{ + return JSHClass::FromHClass(ObjectHeader::ClassAddr()); +} + +inline JSThread *TaggedObject::GetJSThread() const +{ + return JSThread::Cast(ManagedThread::GetCurrent()); +} +} // namespace panda::ecmascript + +#endif // ECMASCRIPT_TAGGED_OBJECT_HEADER_INL_H diff --git a/runtime/mem/tagged_object.h b/runtime/mem/tagged_object.h new file mode 100644 index 000000000..531bf9d29 --- /dev/null +++ b/runtime/mem/tagged_object.h @@ -0,0 +1,51 @@ +/* + * Copyright (c) 2021-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_TAGGED_OBJECT_HEADER_H +#define ECMASCRIPT_TAGGED_OBJECT_HEADER_H + +#include "plugins/ecmascript/runtime/mem/mark_word.h" +#include "include/object_header.h" + +namespace panda::ecmascript { +class JSHClass; +template +class JSHandle; +class JSThread; + +class TaggedObject : public ObjectHeader { +public: + static TaggedObject *Cast(ObjectHeader *header) + { + return static_cast(header); + } + + void SetClassWithoutBarrier(JSHClass *hclass); + void SetClass(JSHandle hclass); + void SetClass(JSHClass *hclass); + JSHClass *GetClass() const; + + // Size of object header + static constexpr int TaggedObjectSize() + { + return sizeof(TaggedObject); + } + + JSThread* GetJSThread() const; +}; +static_assert(TaggedObject::TaggedObjectSize() == sizeof(ObjectHeader)); +} // namespace panda::ecmascript + +#endif // ECMASCRIPT_TAGGED_OBJECT_HEADER_H diff --git a/runtime/mem/tlab_allocator-inl.h b/runtime/mem/tlab_allocator-inl.h new file mode 100644 index 000000000..d9b2e597e --- /dev/null +++ b/runtime/mem/tlab_allocator-inl.h @@ -0,0 +1,157 @@ +/* + * Copyright (c) 2021-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_MEM_TLAB_ALLOCATOR_INL_H +#define ECMASCRIPT_MEM_TLAB_ALLOCATOR_INL_H + +#include "plugins/ecmascript/runtime/mem/tlab_allocator.h" + +#include + +#include "plugins/ecmascript/runtime/free_object.h" +#include "plugins/ecmascript/runtime/mem/compress_collector.h" +#include "plugins/ecmascript/runtime/mem/evacuation_allocator-inl.h" + +namespace panda::ecmascript { +static constexpr size_t YOUNG_BUFFER_SIZE = 31 * 1024; +static constexpr size_t OLD_BUFFER_SIZE = 255 * 1024; + +TlabAllocator::TlabAllocator(Heap *heap, TriggerGCType gcType) + : heap_(heap), gcType_(gcType), youngEnable_(true), allocator_(heap_->GetEvacuationAllocator()) +{ +} + +TlabAllocator::~TlabAllocator() +{ + Finalize(); +} + +inline void TlabAllocator::Finalize() +{ + if (youngerAllocator_.Available() != 0) { + FreeObject::FillFreeObject(heap_->GetEcmaVM(), youngerAllocator_.GetTop(), youngerAllocator_.Available()); + youngerAllocator_.Reset(); + } + if (oldBumpPointerAllocator_.Available() != 0) { + allocator_->FreeSafe(oldBumpPointerAllocator_.GetTop(), oldBumpPointerAllocator_.GetEnd()); + Region *current = Region::ObjectAddressToRange(oldBumpPointerAllocator_.GetTop()); + current->DecreaseAliveObject(oldBumpPointerAllocator_.Available()); + oldBumpPointerAllocator_.Reset(); + } +} + +uintptr_t TlabAllocator::Allocate(size_t size, SpaceAlloc spaceAlloc) +{ + uintptr_t result = 0; + switch (spaceAlloc) { + case SpaceAlloc::YOUNG_SPACE: + result = TlabAllocatorYoungSpace(size); + break; + case SpaceAlloc::OLD_SPACE: + result = TlabAllocatorOldSpace(size); + break; + default: + UNREACHABLE(); + } + return result; +} + +uintptr_t TlabAllocator::TlabAllocatorYoungSpace(size_t size) +{ + size = AlignUp(size, static_cast(MemAlignment::MEM_ALIGN_OBJECT)); + if (UNLIKELY(size >= SMALL_OBJECT_SIZE)) { + uintptr_t address = allocator_->AllocateYoung(size); + LOG(DEBUG, RUNTIME) << "AllocatorYoungSpace:" << address; + return address; + } + uintptr_t result = youngerAllocator_.Allocate(size); + if (result != 0) { + return result; + } + if (youngerAllocator_.Available() != 0) { + FreeObject::FillFreeObject(heap_->GetEcmaVM(), youngerAllocator_.GetTop(), youngerAllocator_.Available()); + } + if (!youngEnable_ || !ExpandYoung()) { + youngEnable_ = false; + return 0; + } + return youngerAllocator_.Allocate(size); +} + +uintptr_t TlabAllocator::TlabAllocatorOldSpace(size_t size) +{ + size = AlignUp(size, static_cast(MemAlignment::MEM_ALIGN_OBJECT)); + uintptr_t result = oldBumpPointerAllocator_.Allocate(size); + if (result != 0) { + FreeObject::FillFreeObject(heap_->GetEcmaVM(), oldBumpPointerAllocator_.GetTop(), + oldBumpPointerAllocator_.Available()); + return result; + } + Region *current = Region::ObjectAddressToRange(oldBumpPointerAllocator_.GetTop()); + if (current != nullptr) { + current->DecreaseAliveObject(oldBumpPointerAllocator_.Available()); + } + allocator_->FreeSafe(oldBumpPointerAllocator_.GetTop(), oldBumpPointerAllocator_.GetEnd()); + if (!ExpandOld()) { + return 0; + } + result = oldBumpPointerAllocator_.Allocate(size); + if (result != 0) { + FreeObject::FillFreeObject(heap_->GetEcmaVM(), oldBumpPointerAllocator_.GetTop(), + oldBumpPointerAllocator_.Available()); + } + return result; +} + +bool TlabAllocator::ExpandYoung() +{ + uintptr_t buffer = 0; + buffer = allocator_->AllocateYoung(YOUNG_BUFFER_SIZE); + + if (buffer == 0) { + return false; + } + youngerAllocator_.Reset(buffer, buffer + YOUNG_BUFFER_SIZE); + return true; +} + +bool TlabAllocator::ExpandOld() +{ + uintptr_t buffer = 0; + if (gcType_ == TriggerGCType::SEMI_GC) { + buffer = allocator_->AllocateOld(YOUNG_BUFFER_SIZE); + if (buffer == 0) { + return false; + } + oldBumpPointerAllocator_.Reset(buffer, buffer + YOUNG_BUFFER_SIZE); + FreeObject::FillFreeObject(heap_->GetEcmaVM(), oldBumpPointerAllocator_.GetTop(), + oldBumpPointerAllocator_.Available()); + } else if (gcType_ == TriggerGCType::COMPRESS_FULL_GC) { + Region *region = allocator_->ExpandOldSpace(); + if (region == nullptr) { + return false; + } + region->SetAliveObject(region->GetSize()); + oldBumpPointerAllocator_.Reset(region->GetBegin(), region->GetEnd()); + FreeObject::FillFreeObject(heap_->GetEcmaVM(), oldBumpPointerAllocator_.GetTop(), + oldBumpPointerAllocator_.Available()); + } else { + UNREACHABLE(); + } + + return true; +} +} // namespace panda::ecmascript +#endif // ECMASCRIPT_MEM_TLAB_ALLOCATOR_INL_H diff --git a/runtime/mem/tlab_allocator.h b/runtime/mem/tlab_allocator.h new file mode 100644 index 000000000..70f5579fe --- /dev/null +++ b/runtime/mem/tlab_allocator.h @@ -0,0 +1,60 @@ +/* + * Copyright (c) 2021-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_MEM_TLAB_ALLOCATOR_H +#define ECMASCRIPT_MEM_TLAB_ALLOCATOR_H + +#include "plugins/ecmascript/runtime/mem/allocator-inl.h" + +namespace panda::ecmascript { +enum class SpaceAlloc : bool { + YOUNG_SPACE, + OLD_SPACE, +}; + +class Heap; +class EvacuationAllocator; + +class TlabAllocator { +public: + TlabAllocator() = delete; + inline ~TlabAllocator(); + NO_COPY_SEMANTIC(TlabAllocator); + NO_MOVE_SEMANTIC(TlabAllocator); + + inline void Finalize(); + + inline explicit TlabAllocator(Heap *heap, TriggerGCType gcType); + + inline uintptr_t Allocate(size_t size, SpaceAlloc spaceAlloc); + +private: + inline uintptr_t TlabAllocatorYoungSpace(size_t size); + inline uintptr_t TlabAllocatorOldSpace(size_t size); + + inline bool ExpandYoung(); + inline bool ExpandOld(); + + Heap *heap_; + TriggerGCType gcType_; + bool youngEnable_; + + BumpPointerAllocator youngerAllocator_; + BumpPointerAllocator oldBumpPointerAllocator_; + EvacuationAllocator *allocator_; +}; +} // namespace panda::ecmascript + +#endif // ECMASCRIPT_MEM_TLAB_ALLOCATOR_H diff --git a/runtime/mem/verification.cpp b/runtime/mem/verification.cpp new file mode 100644 index 000000000..990abbcbd --- /dev/null +++ b/runtime/mem/verification.cpp @@ -0,0 +1,90 @@ +/* + * Copyright (c) 2021-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 "verification.h" + +#include "plugins/ecmascript/runtime/js_tagged_value-inl.h" +#include "plugins/ecmascript/runtime/mem/object_xray-inl.h" +#include "plugins/ecmascript/runtime/mem/slots.h" + +namespace panda::ecmascript { +// Verify the object body +void VerifyObjectVisitor::VisitAllObjects(TaggedObject *obj) +{ + auto jsHclass = obj->GetClass(); + objXRay_.VisitObjectBody( + obj, jsHclass, [this]([[maybe_unused]] TaggedObject *root, ObjectSlot start, ObjectSlot end) { + for (ObjectSlot slot = start; slot < end; slot++) { + JSTaggedValue value(slot.GetTaggedType()); + if (value.IsWeak()) { + if (!heap_->IsLive(value.GetTaggedWeakRef())) { + LOG(ERROR, RUNTIME) << "Heap verify detected a dead object at " << value.GetTaggedObject() + << "at object:" << slot.SlotAddress(); + ++(*failCount_); + } + } else if (value.IsHeapObject()) { + if (!heap_->IsLive(value.GetTaggedObject())) { + LOG(ERROR, RUNTIME) << "Heap verify detected a dead object at " << value.GetTaggedObject() + << "at object:" << slot.SlotAddress(); + ++(*failCount_); + } + } + } + }); +} + +size_t Verification::VerifyRoot() const +{ + size_t failCount = 0; + RootVisitor visit1 = [this, &failCount]([[maybe_unused]] Root type, ObjectSlot slot) { + JSTaggedValue value(slot.GetTaggedType()); + if (value.IsWeak()) { + VerifyObjectVisitor(heap_, &failCount)(value.GetTaggedWeakRef()); + } else if (value.IsHeapObject()) { + VerifyObjectVisitor(heap_, &failCount)(value.GetTaggedObject()); + } + }; + RootRangeVisitor visit2 = [this, &failCount]([[maybe_unused]] Root type, ObjectSlot start, ObjectSlot end) { + for (ObjectSlot slot = start; slot < end; slot++) { + JSTaggedValue value(slot.GetTaggedType()); + if (value.IsWeak()) { + VerifyObjectVisitor(heap_, &failCount)(value.GetTaggedWeakRef()); + } else if (value.IsHeapObject()) { + VerifyObjectVisitor(heap_, &failCount)(value.GetTaggedObject()); + } + } + }; + objXRay_.VisitVMRoots(visit1, visit2); + if (failCount > 0) { + LOG(ERROR, RUNTIME) << "VerifyRoot detects deadObject count is " << failCount; + } + + return failCount; +} + +size_t Verification::VerifyHeap() const +{ + size_t failCount = heap_->VerifyHeapObjects(); + if (failCount > 0) { + LOG(ERROR, RUNTIME) << "VerifyHeap detects deadObject count is " << failCount; + } + return failCount; +} + +bool Verification::IsHeapAddress(void *addr) const +{ + return heap_->ContainObject(reinterpret_cast(addr)); +} +} // namespace panda::ecmascript diff --git a/runtime/mem/verification.h b/runtime/mem/verification.h new file mode 100644 index 000000000..c72bc5f63 --- /dev/null +++ b/runtime/mem/verification.h @@ -0,0 +1,79 @@ +/* + * Copyright (c) 2021-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_MEM_HEAP_VERIFICATION_H +#define ECMASCRIPT_MEM_HEAP_VERIFICATION_H + +#include + +#include "plugins/ecmascript/runtime/mem/heap.h" +#include "plugins/ecmascript/runtime/mem/object_xray.h" +#include "plugins/ecmascript/runtime/mem/mem.h" +#include "plugins/ecmascript/runtime/mem/slots.h" + +namespace panda::ecmascript { +// Verify the object body +// NOLINTNEXTLINE(cppcoreguidelines-special-member-functions, hicpp-special-member-functions) +class VerifyObjectVisitor { +public: + VerifyObjectVisitor(const Heap *heap, size_t *failCount) + : heap_(heap), failCount_(failCount), objXRay_(heap->GetEcmaVM()) + { + } + ~VerifyObjectVisitor() = default; + + void operator()(TaggedObject *obj) + { + VisitAllObjects(obj); + } + + size_t GetFailedCount() const + { + return *failCount_; + } + +private: + void VisitAllObjects(TaggedObject *obj); + + const Heap* const heap_ {nullptr}; + size_t* const failCount_ {nullptr}; + ObjectXRay objXRay_; +}; + +class Verification { +public: + explicit Verification(const Heap *heap) : heap_(heap), objXRay_(heap->GetEcmaVM()) {} + ~Verification() = default; + + size_t VerifyAll() const + { + size_t result = VerifyRoot(); + result += VerifyHeap(); + return result; + } + + bool IsHeapAddress(void *addr) const; + size_t VerifyRoot() const; + size_t VerifyHeap() const; +private: + NO_COPY_SEMANTIC(Verification); + NO_MOVE_SEMANTIC(Verification); + + const Heap *heap_ {nullptr}; + ObjectXRay objXRay_; +}; +} // namespace panda::ecmascript + +#endif // ECMASCRIPT_MEM_HEAP_VERIFICATION_H diff --git a/runtime/message_string.cpp b/runtime/message_string.cpp new file mode 100644 index 000000000..a4f8b4cda --- /dev/null +++ b/runtime/message_string.cpp @@ -0,0 +1,34 @@ +/* + * Copyright (c) 2021-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 "message_string.h" +#include +#include "libpandabase/macros.h" + +namespace panda::ecmascript { +// NOLINTNEXTLINE(fuchsia-statically-constructed-objects) +static std::array g_messageString = { +// NOLINTNEXTLINE(cppcoreguidelines-macro-usage) +#define DEF_MESSAGE_ID(name, string) #string, + MESSAGE_STRING_LIST(DEF_MESSAGE_ID) +#undef DEF_MESSAGE_ID +}; + +const std::string& MessageString::GetMessageString(int id) +{ + ASSERT(id < MessageString::MAX_MESSAGE_COUNT); + return g_messageString[id]; +} +} // namespace panda::ecmascript \ No newline at end of file diff --git a/runtime/message_string.h b/runtime/message_string.h new file mode 100644 index 000000000..6b521c29e --- /dev/null +++ b/runtime/message_string.h @@ -0,0 +1,45 @@ +/* + * Copyright (c) 2021-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_MESSAGE_STRING_H +#define ECMASCRIPT_MESSAGE_STRING_H + +#include + +namespace panda::ecmascript { +// NOLINTNEXTLINE(cppcoreguidelines-macro-usage) +#define MESSAGE_STRING_LIST(V) \ + V(SetReadOnlyProperty, "Cannot set readonly property") \ + V(FunctionCallNotConstructor, "class constructor cannot call") \ + V(SetPropertyWhenNotExtensible, "Cannot add property in prevent extensions ") \ + V(GetPropertyOutOfBounds, "Get Property index out-of-bounds") \ + V(CanNotSetPropertyOnContainer, "Cannot set property on Container") + +class MessageString { +public: + enum MessageId { + // NOLINTNEXTLINE(cppcoreguidelines-macro-usage) +#define DEF_MESSAGE_ID(name, string) Message_##name, + MESSAGE_STRING_LIST(DEF_MESSAGE_ID) +#undef DEF_MESSAGE_ID + MAX_MESSAGE_COUNT + }; + static const std::string& GetMessageString(int id); +}; + +// NOLINTNEXTLINE(cppcoreguidelines-macro-usage) +#define GET_MESSAGE_STRING_ID(name) static_cast((MessageString::MessageId::Message_##name)) +} // namespace panda::ecmascript +#endif // ECMASCRIPT_MESSAGE_STRING_H diff --git a/runtime/napi/include/jsnapi.h b/runtime/napi/include/jsnapi.h new file mode 100644 index 000000000..0646d45d6 --- /dev/null +++ b/runtime/napi/include/jsnapi.h @@ -0,0 +1,971 @@ +/* + * Copyright (c) 2021-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_NAPI_INCLUDE_JSNAPI_H +#define ECMASCRIPT_NAPI_INCLUDE_JSNAPI_H + +#include +#include +#include +#include + +#include "plugins/ecmascript/runtime/common.h" +#include "libpandabase/macros.h" + +namespace panda { +class JSNApiHelper; +class EscapeLocalScope; +class PromiseRejectInfo; +template +class Global; +class JSNApi; +class PrimitiveRef; +class ArrayRef; +class StringRef; +class ObjectRef; +class FunctionRef; +class NumberRef; +class BooleanRef; +class NativePointerRef; +namespace test { +class JSNApiTests; +} // namespace test + +namespace ecmascript { +class EcmaVM; +class JSRuntimeOptions; +} // namespace ecmascript + +using Deleter = void (*)(void *buffer, void *data); +using EcmaVM = ecmascript::EcmaVM; +using JSTaggedType = uint64_t; + +static constexpr uint32_t DEFAULT_GC_POOL_SIZE = 256 * 1024 * 1024; + +// NOLINTNEXTLINE(cppcoreguidelines-macro-usage) +#define ECMA_DISALLOW_COPY(className) \ + className(const className &) = delete; \ + className &operator=(const className &) = delete + +// NOLINTNEXTLINE(cppcoreguidelines-macro-usage) +#define ECMA_DISALLOW_MOVE(className) \ + className(className &&) = delete; \ + className &operator=(className &&) = delete + +template +class PUBLIC_API Local { // NOLINT(cppcoreguidelines-special-member-functions, hicpp-special-member-functions) +public: + inline Local() = default; + + template + // NOLINTNEXTLINE(google-explicit-constructor) + inline Local(const Local ¤t) : address_(reinterpret_cast(*current)) + { + // Check + } + + Local(const EcmaVM *vm, const Global ¤t); + + ~Local() = default; + + inline T *operator*() const + { + return GetAddress(); + } + + inline T *operator->() const + { + return GetAddress(); + } + + inline bool IsEmpty() const + { + return GetAddress() == nullptr; + } + + inline bool CheckException() const + { + return IsEmpty() || GetAddress()->IsException(); + } + + inline bool IsException() const + { + return !IsEmpty() && GetAddress()->IsException(); + } + + inline bool IsNull() const + { + return IsEmpty() || GetAddress()->IsHole(); + } + +private: + explicit inline Local(uintptr_t addr) : address_(addr) {} + inline T *GetAddress() const + { + return reinterpret_cast(address_); + }; + uintptr_t address_ = 0U; + friend JSNApiHelper; + friend EscapeLocalScope; +}; + +template +class PUBLIC_API Global { // NOLINTNEXTLINE(cppcoreguidelines-special-member-functions +public: + inline Global() = default; + + inline Global(const Global &that) + { + Update(that); + } + + // NOLINTNEXTLINE(bugprone-unhandled-self-assignment) + inline Global &operator=(const Global &that) + { + Update(that); + return *this; + } + + inline Global(Global &&that) + { + Update(that); + } + + inline Global &operator=(Global &&that) + { + Update(that); + return *this; + } + // Non-empty initial value. + explicit Global(const EcmaVM *vm); + + template + Global(const EcmaVM *vm, const Local ¤t); + template + Global(const EcmaVM *vm, const Global ¤t); + ~Global() = default; + + Local ToLocal(const EcmaVM *vm) const + { + return Local(vm, *this); + } + + void Empty() + { + address_ = 0; + } + + // This method must be called before Global is released. + void FreeGlobalHandleAddr(); + + inline T *operator*() const + { + return GetAddress(); + } + + inline T *operator->() const + { + return GetAddress(); + } + + inline bool IsEmpty() const + { + return GetAddress() == nullptr; + } + + inline bool CheckException() const + { + return IsEmpty() || GetAddress()->IsException(); + } + + void SetWeak(); + + bool IsWeak() const; + +private: + inline T *GetAddress() const + { + return reinterpret_cast(address_); + }; + inline void Update(const Global &that); + uintptr_t address_ = 0U; + const EcmaVM *vm_ {nullptr}; +}; + +// NOLINTNEXTLINE(cppcoreguidelines-special-member-functions, hicpp-special-member-functions) +class PUBLIC_API LocalScope { +public: + explicit LocalScope(const EcmaVM *vm); + virtual ~LocalScope(); + +protected: + inline LocalScope(const EcmaVM *vm, JSTaggedType value); + +private: + void *prevNext_ = nullptr; + void *prevEnd_ = nullptr; + int prevHandleStorageIndex_ {-1}; + void *thread_ = nullptr; +}; + +class PUBLIC_API EscapeLocalScope final : public LocalScope { +public: + explicit EscapeLocalScope(const EcmaVM *vm); + ~EscapeLocalScope() override = default; + + ECMA_DISALLOW_COPY(EscapeLocalScope); + ECMA_DISALLOW_MOVE(EscapeLocalScope); + + template + inline Local Escape(Local current) + { + ASSERT(!alreadyEscape_); + alreadyEscape_ = true; + *(reinterpret_cast(escapeHandle_)) = **current; + return Local(escapeHandle_); + } + +private: + bool alreadyEscape_ = false; + uintptr_t escapeHandle_ = 0U; +}; + +class PUBLIC_API JSExecutionScope { +public: + explicit JSExecutionScope(const EcmaVM *vm); + ~JSExecutionScope(); + ECMA_DISALLOW_COPY(JSExecutionScope); + ECMA_DISALLOW_MOVE(JSExecutionScope); + +private: + void *last_current_thread_ = nullptr; + bool is_revert_ = false; +}; + +class PUBLIC_API JSValueRef { +public: + static Local Undefined(const EcmaVM *vm); + static Local Null(const EcmaVM *vm); + static Local True(const EcmaVM *vm); + static Local False(const EcmaVM *vm); + static Local Exception(const EcmaVM *vm); + + bool BooleaValue(); + int64_t IntegerValue(const EcmaVM *vm); + uint32_t Uint32Value(const EcmaVM *vm); + int32_t Int32Value(const EcmaVM *vm); + + Local ToNumber(const EcmaVM *vm); + Local ToBoolean(const EcmaVM *vm); + Local ToString(const EcmaVM *vm); + Local ToObject(const EcmaVM *vm); + Local ToNativePointer(const EcmaVM *vm); + + bool IsUndefined(); + bool IsNull(); + bool IsHole(); + bool IsTrue(); + bool IsFalse(); + bool IsNumber(); + bool IsInt(); + bool WithinInt32(); + bool IsBoolean(); + bool IsString(); + bool IsSymbol(); + bool IsObject(); + bool IsArray(const EcmaVM *vm); + bool IsConstructor(); + bool IsFunction(); + bool IsProxy(); + bool IsException(); + bool IsPromise(); + bool IsDataView(); + bool IsTypedArray(); + bool IsNativePointer(); + bool IsDate(); + bool IsError(); + bool IsMap(); + bool IsSet(); + bool IsWeakMap(); + bool IsWeakSet(); + bool IsRegExp(); + bool IsArrayIterator(); + bool IsStringIterator(); + bool IsSetIterator(); + bool IsMapIterator(); + bool IsArrayBuffer(); + bool IsUint8Array(); + bool IsInt8Array(); + bool IsUint8ClampedArray(); + bool IsInt16Array(); + bool IsUint16Array(); + bool IsInt32Array(); + bool IsUint32Array(); + bool IsFloat32Array(); + bool IsFloat64Array(); + bool IsJSPrimitiveRef(); + bool IsJSPrimitiveNumber(); + bool IsJSPrimitiveInt(); + bool IsJSPrimitiveBoolean(); + bool IsJSPrimitiveString(); + + bool IsGeneratorObject(); + bool IsJSPrimitiveSymbol(); + + bool IsArgumentsObject(); + bool IsGeneratorFunction(); + bool IsAsyncFunction(); + + bool IsStrictEquals(const EcmaVM *vm, Local value); + Local Typeof(const EcmaVM *vm); + bool InstanceOf(const EcmaVM *vm, Local value); + +private: + JSTaggedType value_; + friend JSNApi; + template + friend class Global; + template + friend class Local; +}; + +class PUBLIC_API PrimitiveRef : public JSValueRef { +}; + +class PUBLIC_API IntegerRef : public PrimitiveRef { +public: + static Local New(const EcmaVM *vm, int input); + static Local NewFromUnsigned(const EcmaVM *vm, unsigned int input); + int Value(); +}; + +class PUBLIC_API NumberRef : public PrimitiveRef { +public: + static Local New(const EcmaVM *vm, double input); + double Value(); +}; + +class PUBLIC_API BooleanRef : public PrimitiveRef { +public: + static Local New(const EcmaVM *vm, bool input); + bool Value(); +}; + +class PUBLIC_API StringRef : public PrimitiveRef { +public: + static inline StringRef *Cast(JSValueRef *value) + { + // check + return static_cast(value); + } + static Local NewFromUtf8(const EcmaVM *vm, const char *utf8, int length = -1); + std::string ToString(); + int32_t Length(); + int32_t Utf8Length(); + int WriteUtf8(char *buffer, int length); +}; + +class PUBLIC_API SymbolRef : public PrimitiveRef { +public: + static Local New(const EcmaVM *vm, Local description); + Local GetDescription(const EcmaVM *vm); +}; + +using NativePointerCallback = void (*)(void *value, void *hint); +class PUBLIC_API NativePointerRef : public JSValueRef { +public: + static Local New(const EcmaVM *vm, void *nativePointer); + static Local New(const EcmaVM *vm, void *nativePointer, NativePointerCallback callBack, + void *data); + void *Value(); +}; + +// NOLINTNEXTLINE(cppcoreguidelines-special-member-functions, hicpp-special-member-functions) +class PUBLIC_API PropertyAttribute { +public: + static PropertyAttribute Default() + { + return PropertyAttribute(); + } + PropertyAttribute() = default; + PropertyAttribute(Local value, bool w, bool e, bool c) + : value_(value), + writable_(w), + enumerable_(e), + configurable_(c), + hasWritable_(true), + hasEnumerable_(true), + hasConfigurable_(true) + { + } + ~PropertyAttribute() = default; + + bool IsWritable() const + { + return writable_; + } + void SetWritable(bool flag) + { + writable_ = flag; + hasWritable_ = true; + } + bool IsEnumerable() const + { + return enumerable_; + } + void SetEnumerable(bool flag) + { + enumerable_ = flag; + hasEnumerable_ = true; + } + bool IsConfigurable() const + { + return configurable_; + } + void SetConfigurable(bool flag) + { + configurable_ = flag; + hasConfigurable_ = true; + } + bool HasWritable() const + { + return hasWritable_; + } + bool HasConfigurable() const + { + return hasConfigurable_; + } + bool HasEnumerable() const + { + return hasEnumerable_; + } + Local GetValue(const EcmaVM *vm) const + { + if (value_.IsEmpty()) { + return Local(JSValueRef::Undefined(vm)); + } + return value_; + } + void SetValue(Local value) + { + value_ = value; + } + inline bool HasValue() const + { + return !value_.IsEmpty(); + } + Local GetGetter(const EcmaVM *vm) const + { + if (getter_.IsEmpty()) { + return Local(JSValueRef::Undefined(vm)); + } + return getter_; + } + void SetGetter(Local value) + { + getter_ = value; + } + bool HasGetter() const + { + return !getter_.IsEmpty(); + } + Local GetSetter(const EcmaVM *vm) const + { + if (setter_.IsEmpty()) { + return Local(JSValueRef::Undefined(vm)); + } + return setter_; + } + void SetSetter(Local value) + { + setter_ = value; + } + bool HasSetter() const + { + return !setter_.IsEmpty(); + } + +private: + Local value_; + Local getter_; + Local setter_; + bool writable_ = false; + bool enumerable_ = false; + bool configurable_ = false; + bool hasWritable_ = false; + bool hasEnumerable_ = false; + bool hasConfigurable_ = false; +}; + +class PUBLIC_API ObjectRef : public JSValueRef { +public: + static inline ObjectRef *Cast(JSValueRef *value) + { + // check + return static_cast(value); + } + static Local New(const EcmaVM *vm); + bool Set(const EcmaVM *vm, Local key, Local value); + bool Set(const EcmaVM *vm, uint32_t key, Local value); + bool SetAccessorProperty(const EcmaVM *vm, Local key, Local getter, + Local setter, PropertyAttribute attribute = PropertyAttribute::Default()); + Local Get(const EcmaVM *vm, Local key); + Local Get(const EcmaVM *vm, int32_t key); + + bool GetOwnProperty(const EcmaVM *vm, Local key, PropertyAttribute &property); + Local GetOwnPropertyNames(const EcmaVM *vm); + Local GetOwnEnumerablePropertyNames(const EcmaVM *vm); + Local GetPrototype(const EcmaVM *vm); + + bool DefineProperty(const EcmaVM *vm, Local key, PropertyAttribute attribute); + + bool Has(const EcmaVM *vm, Local key); + bool Has(const EcmaVM *vm, uint32_t key); + + bool Delete(const EcmaVM *vm, Local key); + bool Delete(const EcmaVM *vm, uint32_t key); + + void SetNativePointerFieldCount(int32_t count); + int32_t GetNativePointerFieldCount(); + void *GetNativePointerField(int32_t index); + void SetNativePointerField(int32_t index, void *nativePointer = nullptr, NativePointerCallback callBack = nullptr, + void *data = nullptr); +}; + +using FunctionCallback = Local (*)(EcmaVM *, Local, + const Local[], // NOLINTNEXTLINE(modernize-avoid-c-arrays) + int32_t, void *); +using FunctionCallbackWithNewTarget = + Local (*)(EcmaVM *, Local, Local, + const Local[], // NOLINTNEXTLINE(modernize-avoid-c-arrays) + int32_t, void *); +class PUBLIC_API FunctionRef : public ObjectRef { +public: + static Local New(EcmaVM *vm, FunctionCallback nativeFunc, void *data); + static Local New(EcmaVM *vm, FunctionCallback nativeFunc, Deleter deleter, void *data); + static Local NewWithProperty(EcmaVM *vm, FunctionCallback nativeFunc, void *data); + static Local NewClassFunction(EcmaVM *vm, FunctionCallbackWithNewTarget nativeFunc, Deleter deleter, + void *data); + Local Call(const EcmaVM *vm, Local thisObj, + const Local argv[], // NOLINTNEXTLINE(modernize-avoid-c-arrays) + int32_t length); + Local Constructor(const EcmaVM *vm, + const Local argv[], // NOLINTNEXTLINE(modernize-avoid-c-arrays) + int32_t length); + Local GetFunctionPrototype(const EcmaVM *vm); + // Inherit Prototype from parent function + // set this.Prototype.__proto__ to parent.Prototype, set this.__proto__ to parent function + bool Inherit(const EcmaVM *vm, Local parent); + void SetName(const EcmaVM *vm, Local name); + Local GetName(const EcmaVM *vm); + bool IsNative(const EcmaVM *vm); +}; + +class PUBLIC_API ArrayRef : public ObjectRef { +public: + static Local New(const EcmaVM *vm, int32_t length = 0); + int32_t Length(const EcmaVM *vm); + static bool SetValueAt(const EcmaVM *vm, Local obj, uint32_t index, Local value); + static Local GetValueAt(const EcmaVM *vm, Local obj, uint32_t index); +}; + +class PUBLIC_API PromiseRef : public ObjectRef { +public: + Local Catch(const EcmaVM *vm, Local handler); + Local Then(const EcmaVM *vm, Local handler); + Local Then(const EcmaVM *vm, Local onFulfilled, Local onRejected); +}; + +class PUBLIC_API PromiseCapabilityRef : public ObjectRef { +public: + static Local New(const EcmaVM *vm); + bool Resolve(const EcmaVM *vm, Local value); + bool Reject(const EcmaVM *vm, Local reason); + Local GetPromise(const EcmaVM *vm); +}; + +class PUBLIC_API ArrayBufferRef : public ObjectRef { +public: + static Local New(const EcmaVM *vm, int32_t length); + static Local New(const EcmaVM *vm, void *buffer, int32_t length, const Deleter &deleter, + void *data); + + int32_t ByteLength(const EcmaVM *vm); + void *GetBuffer(); +}; + +class PUBLIC_API DataViewRef : public ObjectRef { +public: + static Local New(const EcmaVM *vm, Local arrayBuffer, int32_t byteOffset, + int32_t byteLength); + int32_t ByteLength(); + int32_t ByteOffset(); + Local GetArrayBuffer(const EcmaVM *vm); +}; + +class PUBLIC_API TypedArrayRef : public ObjectRef { +public: + int32_t ByteLength(const EcmaVM *vm); + int32_t ByteOffset(const EcmaVM *vm); + int32_t ArrayLength(const EcmaVM *vm); + Local GetArrayBuffer(const EcmaVM *vm); +}; + +class PUBLIC_API Int8ArrayRef : public TypedArrayRef { +public: + static Local New(const EcmaVM *vm, Local buffer, int32_t byteOffset, int32_t length); +}; + +class PUBLIC_API Uint8ArrayRef : public TypedArrayRef { +public: + static Local New(const EcmaVM *vm, Local buffer, int32_t byteOffset, int32_t length); +}; + +class PUBLIC_API Uint8ClampedArrayRef : public TypedArrayRef { +public: + static Local New(const EcmaVM *vm, Local buffer, int32_t byteOffset, + int32_t length); +}; + +class PUBLIC_API Int16ArrayRef : public TypedArrayRef { +public: + static Local New(const EcmaVM *vm, Local buffer, int32_t byteOffset, int32_t length); +}; + +class PUBLIC_API Uint16ArrayRef : public TypedArrayRef { +public: + static Local New(const EcmaVM *vm, Local buffer, int32_t byteOffset, + int32_t length); +}; + +class PUBLIC_API Int32ArrayRef : public TypedArrayRef { +public: + static Local New(const EcmaVM *vm, Local buffer, int32_t byteOffset, int32_t length); +}; + +class PUBLIC_API Uint32ArrayRef : public TypedArrayRef { +public: + static Local New(const EcmaVM *vm, Local buffer, int32_t byteOffset, + int32_t length); +}; + +class PUBLIC_API Float32ArrayRef : public TypedArrayRef { +public: + static Local New(const EcmaVM *vm, Local buffer, int32_t byteOffset, + int32_t length); +}; + +class PUBLIC_API Float64ArrayRef : public TypedArrayRef { +public: + static Local New(const EcmaVM *vm, Local buffer, int32_t byteOffset, + int32_t length); +}; + +class PUBLIC_API RegExpRef : public ObjectRef { +public: + Local GetOriginalSource(const EcmaVM *vm); +}; + +class PUBLIC_API DateRef : public ObjectRef { +public: + static Local New(const EcmaVM *vm, double time); + Local ToString(const EcmaVM *vm); + double GetTime(); +}; + +class PUBLIC_API MapRef : public ObjectRef { +public: + int32_t GetSize(); +}; + +class PUBLIC_API SetRef : public ObjectRef { +public: + int32_t GetSize(); +}; + +class PUBLIC_API JSON { +public: + static Local Parse(const EcmaVM *vm, Local string); + static Local Stringify(const EcmaVM *vm, Local json); +}; + +class PUBLIC_API Exception { +public: + static Local Error(const EcmaVM *vm, Local message); + static Local RangeError(const EcmaVM *vm, Local message); + static Local ReferenceError(const EcmaVM *vm, Local message); + static Local SyntaxError(const EcmaVM *vm, Local message); + static Local TypeError(const EcmaVM *vm, Local message); + static Local EvalError(const EcmaVM *vm, Local message); +}; + +using LOG_PRINT = int (*)(int id, int level, const char *tag, const char *fmt, const char *message); + +class PUBLIC_API RuntimeOption { +public: + enum class PUBLIC_API GC_TYPE : uint8_t { EPSILON, GEN_GC, STW, G1_GC }; + enum class PUBLIC_API LOG_LEVEL : uint8_t { + DEBUG = 3, + INFO = 4, + WARN = 5, + ERROR = 6, + FATAL = 7, + }; + + void SetGcType(GC_TYPE type) + { + gcType_ = type; + } + + void SetGcPoolSize(uint32_t size) + { + gcPoolSize_ = size; + } + + void SetLogLevel(LOG_LEVEL logLevel) + { + logLevel_ = logLevel; + } + + void SetLogBufPrint(LOG_PRINT out) + { + logBufPrint_ = out; + } + + void SetDebuggerLibraryPath(const std::string &path) + { + debuggerLibraryPath_ = path; + } + + void SetEnableArkTools(bool value) + { + enableArkTools_ = value; + } + + void SetArkProperties(int prop) + { + arkProperties_ = prop; + } + +private: + std::string GetGcType() const + { + std::string gcType; + switch (gcType_) { + case GC_TYPE::GEN_GC: + gcType = "gen-gc"; + break; + case GC_TYPE::STW: + gcType = "stw"; + break; + case GC_TYPE::EPSILON: + gcType = "epsilon"; + break; + case GC_TYPE::G1_GC: + gcType = "g1-gc"; + break; + default: + UNREACHABLE(); + } + return gcType; + } + + std::string GetLogLevel() const + { + std::string logLevel; + switch (logLevel_) { + case LOG_LEVEL::INFO: + case LOG_LEVEL::WARN: + logLevel = "info"; + break; + case LOG_LEVEL::ERROR: + logLevel = "error"; + break; + case LOG_LEVEL::FATAL: + logLevel = "fatal"; + break; + case LOG_LEVEL::DEBUG: + default: + logLevel = "debug"; + break; + } + + return logLevel; + } + + uint32_t GetGcPoolSize() const + { + return gcPoolSize_; + } + + LOG_PRINT GetLogBufPrint() const + { + return logBufPrint_; + } + + std::string GetDebuggerLibraryPath() const + { + return debuggerLibraryPath_; + } + + bool GetEnableArkTools() const + { + return enableArkTools_; + } + + int GetArkProperties() const + { + return arkProperties_; + } + + GC_TYPE gcType_ = GC_TYPE::EPSILON; + LOG_LEVEL logLevel_ = LOG_LEVEL::DEBUG; + uint32_t gcPoolSize_ = DEFAULT_GC_POOL_SIZE; + LOG_PRINT logBufPrint_ {nullptr}; + std::string debuggerLibraryPath_ {}; + bool enableArkTools_ {false}; + int arkProperties_ {-1}; + friend JSNApi; +}; + +class PUBLIC_API PromiseRejectInfo { +public: + enum class PUBLIC_API PROMISE_REJECTION_EVENT : uint32_t { REJECT = 0, HANDLE }; + PromiseRejectInfo(Local promise, Local reason, + PromiseRejectInfo::PROMISE_REJECTION_EVENT operation, void *data); + ~PromiseRejectInfo() = default; + Local GetPromise() const; + Local GetReason() const; + PromiseRejectInfo::PROMISE_REJECTION_EVENT GetOperation() const; + void *GetData() const; + + DEFAULT_MOVE_SEMANTIC(PromiseRejectInfo); + DEFAULT_COPY_SEMANTIC(PromiseRejectInfo); + +private: + Local promise_ {}; + Local reason_ {}; + PROMISE_REJECTION_EVENT operation_ = PROMISE_REJECTION_EVENT::REJECT; + void *data_ {nullptr}; +}; + +class PUBLIC_API JSNApi { +public: + // JSVM + enum class PUBLIC_API TRIGGER_GC_TYPE : uint8_t { SEMI_GC, OLD_GC, COMPRESS_FULL_GC }; + static EcmaVM *CreateJSVM(const RuntimeOption &option); + static void DestroyJSVM(EcmaVM *ecmaVm); + + // JS code + static bool Execute(EcmaVM *vm, const std::string &fileName, const std::string &entry); + static bool Execute(EcmaVM *vm, const uint8_t *data, int32_t size, const std::string &entry); + static bool ExecuteModuleFromBuffer(EcmaVM *vm, const void *data, int32_t size, const std::string &file); + static Local GetExportObject(EcmaVM *vm, const std::string &file, const std::string &itemName); + + // ObjectRef Operation + static Local GetGlobalObject(const EcmaVM *vm); + static void ExecutePendingJob(const EcmaVM *vm); + + // Memory + static void TriggerGC(const EcmaVM *vm, TRIGGER_GC_TYPE gcType = TRIGGER_GC_TYPE::SEMI_GC); + // Exception + static void ThrowException(const EcmaVM *vm, Local error); + static Local GetUncaughtException(const EcmaVM *vm); + static void EnableUserUncaughtErrorHandler(EcmaVM *vm); + + static bool StartDebugger(const char *library_path, EcmaVM *vm, bool isDebugMode); + static bool StopDebugger(const char *library_path); + // Serialize & Deserialize. + static void *SerializeValue(const EcmaVM *vm, Local value, Local transfer); + static Local DeserializeValue(const EcmaVM *vm, void *recoder); + static void DeleteSerializationData(void *data); + static void SetOptions(const ecmascript::JSRuntimeOptions &options); + static void SetHostPromiseRejectionTracker(EcmaVM *vm, void *cb, void *data); + static void SetHostEnqueueJob(const EcmaVM *vm, Local cb); + +private: + static bool CreateRuntime(const RuntimeOption &option); + static bool DestroyRuntime(); + + static uintptr_t GetHandleAddr(const EcmaVM *vm, uintptr_t localAddress); + static uintptr_t GetGlobalHandleAddr(const EcmaVM *vm, uintptr_t localAddress); + static uintptr_t SetWeak(const EcmaVM *vm, uintptr_t localAddress); + static bool IsWeak(const EcmaVM *vm, uintptr_t localAddress); + static void DisposeGlobalHandleAddr(const EcmaVM *vm, uintptr_t addr); + template + friend class Global; + template + friend class Local; + friend class test::JSNApiTests; +}; + +template +template +Global::Global(const EcmaVM *vm, const Local ¤t) : vm_(vm) +{ + if (!current.IsEmpty()) { + address_ = JSNApi::GetGlobalHandleAddr(vm_, reinterpret_cast(*current)); + } +} + +template +template +Global::Global(const EcmaVM *vm, const Global ¤t) : vm_(vm) +{ + if (!current.IsEmpty()) { + address_ = JSNApi::GetGlobalHandleAddr(vm_, reinterpret_cast(*current)); + } +} + +template +void Global::Update(const Global &that) +{ + if (address_ != 0) { + JSNApi::DisposeGlobalHandleAddr(vm_, address_); + } + address_ = that.address_; + vm_ = that.vm_; +} + +template +void Global::FreeGlobalHandleAddr() +{ + if (address_ == 0) { + return; + } + JSNApi::DisposeGlobalHandleAddr(vm_, address_); + address_ = 0; +} + +template +void Global::SetWeak() +{ + address_ = JSNApi::SetWeak(vm_, address_); +} + +template +bool Global::IsWeak() const +{ + return JSNApi::IsWeak(vm_, address_); +} + +// ---------------------------------- Local -------------------------------------------- +template +Local::Local(const EcmaVM *vm, const Global ¤t) +{ + address_ = JSNApi::GetHandleAddr(vm, reinterpret_cast(*current)); +} +} // namespace panda +#endif // ECMASCRIPT_NAPI_INCLUDE_JSNAPI_H diff --git a/runtime/napi/jsnapi.cpp b/runtime/napi/jsnapi.cpp new file mode 100644 index 000000000..df698c84b --- /dev/null +++ b/runtime/napi/jsnapi.cpp @@ -0,0 +1,2029 @@ +/* + * Copyright (c) 2021-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 "jsnapi_helper-inl.h" + +#include +#include + +#include "runtime/include/thread_scopes.h" +#include "plugins/ecmascript/runtime/base/builtins_base.h" +#include "plugins/ecmascript/runtime/base/json_parser.h" +#include "plugins/ecmascript/runtime/base/json_stringifier.h" +#include "plugins/ecmascript/runtime/base/string_helper.h" +#include "plugins/ecmascript/runtime/base/typed_array_helper-inl.h" +#include "plugins/ecmascript/runtime/ecma_global_storage-inl.h" +#include "plugins/ecmascript/runtime/ecma_language_context.h" +#include "plugins/ecmascript/runtime/ecma_module.h" +#include "plugins/ecmascript/runtime/ecma_string.h" +#include "plugins/ecmascript/runtime/ecma_vm.h" +#include "plugins/ecmascript/runtime/global_env.h" +#include "plugins/ecmascript/runtime/internal_call_params.h" +#include "plugins/ecmascript/runtime/interpreter/fast_runtime_stub-inl.h" +#include "plugins/ecmascript/runtime/jobs/micro_job_queue.h" +#include "plugins/ecmascript/runtime/js_array.h" +#include "plugins/ecmascript/runtime/js_arraybuffer.h" +#include "plugins/ecmascript/runtime/js_dataview.h" +#include "plugins/ecmascript/runtime/js_function.h" +#include "plugins/ecmascript/runtime/js_map.h" +#include "plugins/ecmascript/runtime/js_primitive_ref.h" +#include "plugins/ecmascript/runtime/js_promise.h" +#include "plugins/ecmascript/runtime/js_regexp.h" +#include "plugins/ecmascript/runtime/js_runtime_options.h" +#include "plugins/ecmascript/runtime/js_serializer.h" +#include "plugins/ecmascript/runtime/js_set.h" +#include "plugins/ecmascript/runtime/js_tagged_number.h" +#include "plugins/ecmascript/runtime/js_thread.h" +#include "plugins/ecmascript/runtime/js_typed_array.h" +#include "plugins/ecmascript/runtime/mem/region.h" +#include "plugins/ecmascript/runtime/object_factory.h" +#include "plugins/ecmascript/runtime/tagged_array.h" + +#include "generated/base_options.h" +#include "include/runtime_notification.h" +#include "libpandabase/os/library_loader.h" +#include "utils/pandargs.h" + +namespace panda { +using ecmascript::CString; +using ecmascript::EcmaString; +using ecmascript::ErrorType; +using ecmascript::FastRuntimeStub; +using ecmascript::GlobalEnv; +using ecmascript::GlobalEnvConstants; +using ecmascript::InternalCallParams; +using ecmascript::JSArray; +using ecmascript::JSArrayBuffer; +using ecmascript::JSDataView; +using ecmascript::JSDate; +using ecmascript::JSFunction; +using ecmascript::JSFunctionBase; +using ecmascript::JSFunctionExtraInfo; +using ecmascript::JSHClass; +using ecmascript::JSMap; +using ecmascript::JSMethod; +using ecmascript::JSNativePointer; +using ecmascript::JSObject; +using ecmascript::JSPrimitiveRef; +using ecmascript::JSPromise; +using ecmascript::JSRegExp; +using ecmascript::JSRuntimeOptions; +using ecmascript::JSSet; +using ecmascript::JSSymbol; +using ecmascript::JSTaggedNumber; +using ecmascript::JSTaggedType; +using ecmascript::JSTaggedValue; +using ecmascript::JSThread; +using ecmascript::JSTypedArray; +using ecmascript::ObjectFactory; +using ecmascript::OperationResult; +using ecmascript::PromiseCapability; +using ecmascript::PropertyDescriptor; +using ecmascript::Region; +using ecmascript::TaggedArray; +using ecmascript::base::BuiltinsBase; +using ecmascript::base::JsonParser; +using ecmascript::base::JsonStringifier; +using ecmascript::base::StringHelper; +using ecmascript::job::MicroJobQueue; +using ecmascript::job::QueueType; +template +using JSHandle = ecmascript::JSHandle; + +namespace { +constexpr uint32_t INTERNAL_POOL_SIZE = 0; +constexpr uint32_t CODE_POOL_SIZE = 2000000; +constexpr uint32_t COMPILER_POOL_SIZE = 2000000; +// NOLINTNEXTLINE(fuchsia-statically-constructed-objects) +constexpr std::string_view ENTRY_POINTER = "_GLOBAL::func_main_0"; + +class JSNDebuggerAgent : public LibraryAgent, public LibraryAgentLoader { +public: + JSNDebuggerAgent(os::memory::Mutex &mutex, const char *libraryPath, EcmaVM *vm, bool isDebugMode) + : LibraryAgent(mutex, libraryPath, "StartDebug", "StopDebug"), vm_(vm), isDebugMode_(isDebugMode) + { + } + +private: + bool CallLoadCallback(void *resolvedFunction) override + { + ASSERT(resolvedFunction); + + using StartDebugger = bool (*)(const std::string &, EcmaVM *, bool); + if (!reinterpret_cast(resolvedFunction)("PandaDebugger", vm_, isDebugMode_)) { + LOG_ECMA(ERROR) << "'StartDebug' has failed"; + return false; + } + + return true; + } + + bool CallUnloadCallback(void *resolvedFunction) override + { + ASSERT(resolvedFunction); + + using StopDebug = void (*)(const std::string &); + reinterpret_cast(resolvedFunction)("PandaDebugger"); + + return true; + } + + EcmaVM *vm_; + bool isDebugMode_; +}; + +LoadableAgentHandle s_debugger_agent; // NOLINT(fuchsia-statically-constructed-objects) +} // namespace + +// ------------------------------------ Panda ----------------------------------------------- +bool JSNApi::CreateRuntime(const RuntimeOption &option) +{ + JSRuntimeOptions runtimeOptions; + runtimeOptions.SetRuntimeType("ecmascript"); + + // GC + runtimeOptions.SetGcType(option.GetGcType()); + runtimeOptions.SetRunGcInPlace(true); + runtimeOptions.SetArkProperties(option.GetArkProperties()); + // Mem + runtimeOptions.SetHeapSizeLimit(option.GetGcPoolSize()); + runtimeOptions.SetInternalMemorySizeLimit(INTERNAL_POOL_SIZE); + runtimeOptions.SetCodeCacheSizeLimit(CODE_POOL_SIZE); + runtimeOptions.SetCompilerMemorySizeLimit(COMPILER_POOL_SIZE); + runtimeOptions.SetInternalAllocatorType("malloc"); + + // Boot + runtimeOptions.SetBootPandaFiles({}); + runtimeOptions.SetLoadRuntimes({"ecmascript"}); + runtimeOptions.SetBootIntrinsicSpaces({"ecmascript"}); + + // Dfx + base_options::Options base_options(""); + base_options.SetLogLevel(option.GetLogLevel()); + arg_list_t logComponents; + logComponents.emplace_back("all"); + base_options.SetLogComponents(logComponents); + Logger::Initialize(base_options); + + if (option.GetLogBufPrint() != nullptr) { + runtimeOptions.SetMobileLog(reinterpret_cast(option.GetLogBufPrint())); + } + + runtimeOptions.SetEnableArkTools(option.GetEnableArkTools()); + static EcmaLanguageContext lcEcma; + if (!Runtime::Create(runtimeOptions, {&lcEcma})) { + std::cerr << "Error: cannot create runtime" << std::endl; + return false; + } + return true; +} + +bool JSNApi::DestroyRuntime() +{ + return Runtime::Destroy(); +} + +EcmaVM *JSNApi::CreateJSVM(const RuntimeOption &option) +{ + auto runtime = Runtime::GetCurrent(); + if (runtime == nullptr) { + // Only Ark js app + if (!CreateRuntime(option)) { + return nullptr; + } + runtime = Runtime::GetCurrent(); + return EcmaVM::Cast(runtime->GetPandaVM()); + } + JSRuntimeOptions runtimeOptions; + runtimeOptions.SetArkProperties(option.GetArkProperties()); + // GC + runtimeOptions.SetGcType("g1-gc"); + runtimeOptions.SetGcTriggerType("no-gc-for-start-up"); // A non-production gc strategy. Prohibit stw-gc 10 times. + + return EcmaVM::Cast(EcmaVM::Create(runtimeOptions)); +} + +void JSNApi::DestroyJSVM(EcmaVM *ecmaVm) +{ + ecmaVm->GetNotificationManager()->VmDeathEvent(); + auto runtime = Runtime::GetCurrent(); + if (runtime != nullptr) { + PandaVM *mainVm = runtime->GetPandaVM(); + // Only Ark js app + if (mainVm == ecmaVm) { + DestroyRuntime(); + } else { + EcmaVM::Destroy(ecmaVm); + } + } +} + +void JSNApi::TriggerGC(const EcmaVM *vm, TRIGGER_GC_TYPE gcType) +{ + if (vm->GetJSThread() != nullptr && vm->IsInitialized()) { + ScopedManagedCodeThread s(vm->GetAssociatedThread()); + switch (gcType) { + case TRIGGER_GC_TYPE::SEMI_GC: + vm->CollectGarbage(ecmascript::TriggerGCType::SEMI_GC); + break; + case TRIGGER_GC_TYPE::OLD_GC: + vm->CollectGarbage(ecmascript::TriggerGCType::OLD_GC); + break; + case TRIGGER_GC_TYPE::COMPRESS_FULL_GC: + vm->CollectGarbage(ecmascript::TriggerGCType::COMPRESS_FULL_GC); + break; + default: + break; + } + } +} + +void JSNApi::ThrowException(const EcmaVM *vm, Local error) +{ + auto thread = vm->GetJSThread(); + thread->SetException(JSNApiHelper::ToJSTaggedValue(*error)); +} + +bool JSNApi::StartDebugger(const char *library_path, EcmaVM *vm, bool isDebugMode) +{ + auto agent = JSNDebuggerAgent::LoadInstance(library_path, vm, isDebugMode); + if (!agent) { + LOG_ECMA(ERROR) << "Could not load JSN debugger agent"; + return false; + } + + s_debugger_agent = std::move(agent); + + return true; +} + +bool JSNApi::StopDebugger([[maybe_unused]] const char *library_path) +{ + s_debugger_agent.reset(); + return true; +} + +bool JSNApi::Execute(EcmaVM *vm, const std::string &fileName, const std::string &entry) +{ + std::vector argv; + LOG_ECMA(DEBUG) << "start to execute ark file" << fileName; + if (!vm->ExecuteFromPf(fileName, entry, argv)) { + LOG_ECMA(ERROR) << "Cannot execute ark file" << fileName; + LOG_ECMA(ERROR) << "Cannot execute ark file '" << fileName << "' with entry '" << entry << "'" << std::endl; + return false; + } + return true; +} + +bool JSNApi::Execute(EcmaVM *vm, const uint8_t *data, int32_t size, const std::string &entry) +{ + std::vector argv; + if (!vm->ExecuteFromBuffer(data, size, entry, argv)) { + LOG_ECMA(ERROR) << "Cannot execute panda file from memory " + << "' with entry '" << entry << "'" << std::endl; + return false; + } + return true; +} + +Local JSNApi::GetUncaughtException(const EcmaVM *vm) +{ + return JSNApiHelper::ToLocal(vm->GetEcmaUncaughtException()); +} + +void JSNApi::EnableUserUncaughtErrorHandler(EcmaVM *vm) +{ + return vm->EnableUserUncaughtErrorHandler(); +} + +Local JSNApi::GetGlobalObject(const EcmaVM *vm) +{ + JSHandle globalEnv = vm->GetGlobalEnv(); + JSHandle global(vm->GetJSThread(), globalEnv->GetGlobalObject()); + return JSNApiHelper::ToLocal(global); +} + +void JSNApi::ExecutePendingJob(const EcmaVM *vm) +{ + ScopedManagedCodeThread s(vm->GetAssociatedThread()); + vm->ExecutePromisePendingJob(); +} + +uintptr_t JSNApi::GetHandleAddr(const EcmaVM *vm, uintptr_t localAddress) +{ + if (localAddress == 0) { + return 0; + } + JSTaggedType value = *(reinterpret_cast(localAddress)); + return ecmascript::EcmaHandleScope::NewHandle(vm->GetJSThread(), value); +} + +uintptr_t JSNApi::GetGlobalHandleAddr(const EcmaVM *vm, uintptr_t localAddress) +{ + if (localAddress == 0) { + return 0; + } + JSTaggedType value = *(reinterpret_cast(localAddress)); + return vm->GetJSThread()->GetEcmaGlobalStorage()->NewGlobalHandle(value); +} + +uintptr_t JSNApi::SetWeak(const EcmaVM *vm, uintptr_t localAddress) +{ + if (localAddress == 0) { + return 0; + } + return vm->GetJSThread()->GetEcmaGlobalStorage()->SetWeak(localAddress); +} + +bool JSNApi::IsWeak(const EcmaVM *vm, uintptr_t localAddress) +{ + if (localAddress == 0) { + return false; + } + return vm->GetJSThread()->GetEcmaGlobalStorage()->IsWeak(localAddress); +} + +void JSNApi::DisposeGlobalHandleAddr(const EcmaVM *vm, uintptr_t addr) +{ + if (addr == 0) { + return; + } + vm->GetJSThread()->GetEcmaGlobalStorage()->DisposeGlobalHandle(addr); +} + +void *JSNApi::SerializeValue(const EcmaVM *vm, Local value, Local transfer) +{ + ecmascript::JSThread *thread = vm->GetJSThread(); + ecmascript::Serializer serializer(thread); + JSHandle arkValue = JSNApiHelper::ToJSHandle(value); + JSHandle arkTransfer = JSNApiHelper::ToJSHandle(transfer); + std::unique_ptr data; + if (serializer.WriteValue(thread, arkValue, arkTransfer)) { + data = serializer.Release(); + } + return reinterpret_cast(data.release()); +} + +Local JSNApi::DeserializeValue(const EcmaVM *vm, void *recoder) +{ + ecmascript::JSThread *thread = vm->GetJSThread(); + std::unique_ptr data(reinterpret_cast(recoder)); + ecmascript::Deserializer deserializer(thread, data.release()); + JSHandle result = deserializer.ReadValue(); + return Local(JSNApiHelper::ToLocal(result)); +} + +void JSNApi::DeleteSerializationData(void *data) +{ + auto *value = reinterpret_cast(data); + delete value; +} + +void HostPromiseRejectionTracker(const EcmaVM *vm, const JSHandle promise, + const JSHandle reason, + const ecmascript::PromiseRejectionEvent operation, void *data) +{ + ecmascript::PromiseRejectCallback promiseRejectCallback = vm->GetPromiseRejectCallback(); + if (promiseRejectCallback != nullptr) { + Local promiseVal = JSNApiHelper::ToLocal(JSHandle::Cast(promise)); + PromiseRejectInfo promiseRejectInfo(promiseVal, JSNApiHelper::ToLocal(reason), + static_cast(operation), data); + promiseRejectCallback(reinterpret_cast(&promiseRejectInfo)); + } +} + +void JSNApi::SetHostPromiseRejectionTracker(EcmaVM *vm, void *cb, void *data) +{ + vm->SetHostPromiseRejectionTracker(HostPromiseRejectionTracker); + vm->SetPromiseRejectCallback(reinterpret_cast(cb)); + vm->SetData(data); +} + +void JSNApi::SetHostEnqueueJob(const EcmaVM *vm, Local cb) +{ + JSHandle fun = JSHandle::Cast(JSNApiHelper::ToJSHandle(cb)); + JSHandle array = vm->GetFactory()->EmptyArray(); + JSHandle job = vm->GetMicroJobQueue(); + MicroJobQueue::EnqueueJob(vm->GetJSThread(), job, QueueType::QUEUE_PROMISE, fun, array); +} + +PromiseRejectInfo::PromiseRejectInfo(Local promise, Local reason, + PromiseRejectInfo::PROMISE_REJECTION_EVENT operation, void *data) + : promise_(promise), reason_(reason), operation_(operation), data_(data) +{ +} + +Local PromiseRejectInfo::GetPromise() const +{ + return promise_; +} + +Local PromiseRejectInfo::GetReason() const +{ + return reason_; +} + +PromiseRejectInfo::PROMISE_REJECTION_EVENT PromiseRejectInfo::GetOperation() const +{ + return operation_; +} + +void *PromiseRejectInfo::GetData() const +{ + return data_; +} + +bool JSNApi::ExecuteModuleFromBuffer(EcmaVM *vm, const void *data, int32_t size, const std::string &file) +{ + auto moduleManager = vm->GetModuleManager(); + moduleManager->SetCurrentExportModuleName(file); + // Update Current Module + std::vector argv; + if (!vm->ExecuteFromBuffer(data, size, ENTRY_POINTER, argv)) { + std::cerr << "Cannot execute panda file from memory" << std::endl; + moduleManager->RestoreCurrentExportModuleName(); + return false; + } + + // Restore Current Module + moduleManager->RestoreCurrentExportModuleName(); + return true; +} + +Local JSNApi::GetExportObject(EcmaVM *vm, const std::string &file, const std::string &itemName) +{ + ScopedManagedCodeThread s(vm->GetAssociatedThread()); + auto moduleManager = vm->GetModuleManager(); + ObjectFactory *factory = vm->GetFactory(); + JSHandle moduleName(factory->NewFromStdStringUnCheck(file, true)); + JSHandle moduleObj = moduleManager->GetModule(vm->GetJSThread(), moduleName); + JSHandle itemString(factory->NewFromStdString(itemName)); + JSHandle exportObj = moduleManager->GetModuleItem(vm->GetJSThread(), moduleObj, itemString); + return JSNApiHelper::ToLocal(exportObj); +} + +// ----------------------------------- HandleScope ------------------------------------- +LocalScope::LocalScope(const EcmaVM *vm) : thread_(vm->GetJSThread()) +{ + auto thread = reinterpret_cast(thread_); + prevNext_ = thread->GetHandleScopeStorageNext(); + prevEnd_ = thread->GetHandleScopeStorageEnd(); + prevHandleStorageIndex_ = thread->GetCurrentHandleStorageIndex(); + thread->HandleScopeCountAdd(); +} + +LocalScope::LocalScope(const EcmaVM *vm, JSTaggedType value) : thread_(vm->GetJSThread()) +{ + auto thread = reinterpret_cast(thread_); + ecmascript::EcmaHandleScope::NewHandle(thread, value); + prevNext_ = thread->GetHandleScopeStorageNext(); + prevEnd_ = thread->GetHandleScopeStorageEnd(); + prevHandleStorageIndex_ = thread->GetCurrentHandleStorageIndex(); + thread->HandleScopeCountAdd(); +} + +LocalScope::~LocalScope() +{ + auto thread = reinterpret_cast(thread_); + thread->HandleScopeCountDec(); + thread->SetHandleScopeStorageNext(static_cast(prevNext_)); + if (thread->GetHandleScopeStorageEnd() != prevEnd_) { + thread->SetHandleScopeStorageEnd(static_cast(prevEnd_)); + thread->ShrinkHandleStorage(prevHandleStorageIndex_); + } +} + +// ----------------------------------- EscapeLocalScope ------------------------------ +EscapeLocalScope::EscapeLocalScope(const EcmaVM *vm) : LocalScope(vm, 0U) +{ + auto thread = vm->GetJSThread(); + // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-pointer-arithmetic) + escapeHandle_ = ToUintPtr(thread->GetHandleScopeStorageNext() - 1); +} + +// ----------------------------------- NumberRef --------------------------------------- +Local NumberRef::New(const EcmaVM *vm, double input) +{ + JSThread *thread = vm->GetJSThread(); + JSHandle number(thread, JSTaggedValue(input)); + return JSNApiHelper::ToLocal(number); +} + +double NumberRef::Value() +{ + return JSTaggedNumber(JSNApiHelper::ToJSTaggedValue(this)).GetNumber(); +} + +// ----------------------------------- BooleanRef --------------------------------------- +Local BooleanRef::New(const EcmaVM *vm, bool input) +{ + JSThread *thread = vm->GetJSThread(); + JSHandle boolean(thread, JSTaggedValue(input)); + return JSNApiHelper::ToLocal(boolean); +} + +bool BooleanRef::Value() +{ + return JSNApiHelper::ToJSTaggedValue(this).IsTrue(); +} + +// ----------------------------------- IntegerRef --------------------------------------- +Local IntegerRef::New(const EcmaVM *vm, int input) +{ + JSThread *thread = vm->GetJSThread(); + JSHandle integer(thread, JSTaggedValue(input)); + return JSNApiHelper::ToLocal(integer); +} + +Local IntegerRef::NewFromUnsigned(const EcmaVM *vm, unsigned int input) +{ + JSThread *thread = vm->GetJSThread(); + JSHandle integer(thread, JSTaggedValue(input)); + return JSNApiHelper::ToLocal(integer); +} + +int IntegerRef::Value() +{ + return JSNApiHelper::ToJSTaggedValue(this).GetInt(); +} + +// ----------------------------------- StringRef ---------------------------------------- +Local StringRef::NewFromUtf8(const EcmaVM *vm, const char *utf8, int length) +{ + ScopedManagedCodeThread s(vm->GetAssociatedThread()); + ObjectFactory *factory = vm->GetFactory(); + if (length < 0) { + JSHandle current(factory->NewFromString(utf8)); + return JSNApiHelper::ToLocal(current); + } + JSHandle current(factory->NewFromUtf8(reinterpret_cast(utf8), length)); + return JSNApiHelper::ToLocal(current); +} + +std::string StringRef::ToString() +{ + return StringHelper::ToStdString(EcmaString::Cast(JSNApiHelper::ToJSTaggedValue(this).GetTaggedObject())); +} + +int32_t StringRef::Length() +{ + return EcmaString::Cast(JSNApiHelper::ToJSTaggedValue(this).GetTaggedObject())->GetLength(); +} + +int32_t StringRef::Utf8Length() +{ + return EcmaString::Cast(JSNApiHelper::ToJSTaggedValue(this).GetTaggedObject())->GetUtf8Length(); +} + +int StringRef::WriteUtf8(char *buffer, int length) +{ + return EcmaString::Cast(JSNApiHelper::ToJSTaggedValue(this).GetTaggedObject()) + ->CopyDataUtf8(reinterpret_cast(buffer), length); +} + +// ----------------------------------- SymbolRef ----------------------------------------- +Local SymbolRef::New(const EcmaVM *vm, Local description) +{ + ScopedManagedCodeThread s(vm->GetAssociatedThread()); + ObjectFactory *factory = vm->GetFactory(); + JSHandle symbol = factory->NewJSSymbol(); + JSTaggedValue desc = JSNApiHelper::ToJSTaggedValue(*description); + symbol->SetDescription(vm->GetJSThread(), desc); + return JSNApiHelper::ToLocal(JSHandle(symbol)); +} + +Local SymbolRef::GetDescription(const EcmaVM *vm) +{ + JSTaggedValue description = JSSymbol::Cast(JSNApiHelper::ToJSTaggedValue(this).GetTaggedObject())->GetDescription(); + if (!description.IsString()) { + auto constants = vm->GetJSThread()->GlobalConstants(); + return JSNApiHelper::ToLocal(constants->GetHandledEmptyString()); + } + JSHandle descriptionHandle(vm->GetJSThread(), description); + return JSNApiHelper::ToLocal(descriptionHandle); +} + +// -------------------------------- NativePointerRef ------------------------------------ +Local NativePointerRef::New(const EcmaVM *vm, void *nativePointer) +{ + ScopedManagedCodeThread s(vm->GetAssociatedThread()); + ObjectFactory *factory = vm->GetFactory(); + JSHandle obj = factory->NewJSNativePointer(nativePointer); + return JSNApiHelper::ToLocal(JSHandle(obj)); +} + +Local NativePointerRef::New(const EcmaVM *vm, void *nativePointer, NativePointerCallback callBack, + void *data) +{ + ScopedManagedCodeThread s(vm->GetAssociatedThread()); + ObjectFactory *factory = vm->GetFactory(); + JSHandle obj = factory->NewJSNativePointer(nativePointer, callBack, data); + return JSNApiHelper::ToLocal(JSHandle(obj)); +} + +void *NativePointerRef::Value() +{ + JSHandle nativePointer = JSNApiHelper::ToJSHandle(this); + return JSHandle(nativePointer)->GetExternalPointer(); +} + +// ----------------------------------- ObjectRef ---------------------------------------- +Local ObjectRef::New(const EcmaVM *vm) +{ + ScopedManagedCodeThread s(vm->GetAssociatedThread()); + ObjectFactory *factory = vm->GetFactory(); + JSHandle globalEnv = vm->GetGlobalEnv(); + JSHandle constructor = globalEnv->GetObjectFunction(); + JSHandle object(factory->NewJSObjectByConstructor(JSHandle(constructor), constructor)); + RETURN_VALUE_IF_ABRUPT(vm->GetJSThread(), Local(JSValueRef::Exception(vm))); + return JSNApiHelper::ToLocal(object); +} + +bool ObjectRef::Set(const EcmaVM *vm, Local key, Local value) +{ + ScopedManagedCodeThread s(vm->GetAssociatedThread()); + JSHandle obj = JSNApiHelper::ToJSHandle(this); + JSHandle keyValue = JSNApiHelper::ToJSHandle(key); + JSHandle valueValue = JSNApiHelper::ToJSHandle(value); + bool result = JSTaggedValue::SetProperty(vm->GetJSThread(), obj, keyValue, valueValue); + RETURN_VALUE_IF_ABRUPT(vm->GetJSThread(), false); + return result; +} + +bool ObjectRef::Set(const EcmaVM *vm, uint32_t key, Local value) +{ + Local keyValue = Local(NumberRef::New(vm, key)); + return Set(vm, keyValue, value); +} + +bool ObjectRef::SetAccessorProperty(const EcmaVM *vm, Local key, Local getter, + Local setter, PropertyAttribute attribute) +{ + JSThread *thread = vm->GetJSThread(); + ScopedManagedCodeThread s(thread); + JSHandle getterValue = JSNApiHelper::ToJSHandle(getter); + JSHandle setterValue = JSNApiHelper::ToJSHandle(setter); + PropertyDescriptor desc(thread, attribute.IsWritable(), attribute.IsEnumerable(), attribute.IsConfigurable()); + desc.SetValue(JSNApiHelper::ToJSHandle(attribute.GetValue(vm))); + desc.SetSetter(setterValue); + desc.SetGetter(getterValue); + JSHandle obj = JSNApiHelper::ToJSHandle(this); + JSHandle keyValue = JSNApiHelper::ToJSHandle(key); + bool result = JSTaggedValue::DefineOwnProperty(thread, obj, keyValue, desc); + RETURN_VALUE_IF_ABRUPT(thread, false); + return result; +} + +Local ObjectRef::Get(const EcmaVM *vm, Local key) +{ + JSThread *thread = vm->GetJSThread(); + ScopedManagedCodeThread s(thread); + JSHandle obj = JSNApiHelper::ToJSHandle(this); + JSHandle keyValue = JSNApiHelper::ToJSHandle(key); + OperationResult ret = JSTaggedValue::GetProperty(thread, obj, keyValue); + RETURN_VALUE_IF_ABRUPT(thread, JSValueRef::Exception(vm)); + if (!ret.GetPropertyMetaData().IsFound()) { + return JSValueRef::Undefined(vm); + } + return JSNApiHelper::ToLocal(ret.GetValue()); +} + +Local ObjectRef::Get(const EcmaVM *vm, int32_t key) +{ + Local keyValue = IntegerRef::New(vm, key); + return Get(vm, keyValue); +} + +bool ObjectRef::GetOwnProperty(const EcmaVM *vm, Local key, PropertyAttribute &property) +{ + JSThread *thread = vm->GetJSThread(); + ScopedManagedCodeThread s(thread); + JSHandle obj = JSNApiHelper::ToJSHandle(this); + JSHandle keyValue = JSNApiHelper::ToJSHandle(key); + PropertyDescriptor desc(thread); + bool ret = JSObject::GetOwnProperty(thread, JSHandle(obj), keyValue, desc); + if (!ret) { + return false; + } + property.SetValue(JSNApiHelper::ToLocal(desc.GetValue())); + if (desc.HasGetter()) { + property.SetGetter(JSNApiHelper::ToLocal(desc.GetGetter())); + } + if (desc.HasSetter()) { + property.SetSetter(JSNApiHelper::ToLocal(desc.GetSetter())); + } + if (desc.HasWritable()) { + property.SetWritable(desc.IsWritable()); + } + if (desc.HasEnumerable()) { + property.SetEnumerable(desc.IsEnumerable()); + } + if (desc.HasConfigurable()) { + property.SetConfigurable(desc.IsConfigurable()); + } + + return true; +} + +Local ObjectRef::GetOwnPropertyNames(const EcmaVM *vm) +{ + JSThread *thread = vm->GetJSThread(); + ScopedManagedCodeThread s(thread); + JSHandle obj(JSNApiHelper::ToJSHandle(this)); + JSHandle array(JSTaggedValue::GetOwnPropertyKeys(thread, obj)); + JSHandle jsArray(JSArray::CreateArrayFromList(thread, array)); + RETURN_VALUE_IF_ABRUPT(thread, JSValueRef::Exception(vm)); + return JSNApiHelper::ToLocal(jsArray); +} + +Local ObjectRef::GetOwnEnumerablePropertyNames(const EcmaVM *vm) +{ + JSThread *thread = vm->GetJSThread(); + ScopedManagedCodeThread s(thread); + JSHandle obj(JSNApiHelper::ToJSHandle(this)); + JSHandle array(JSObject::EnumerableOwnNames(thread, obj)); + JSHandle jsArray(JSArray::CreateArrayFromList(thread, array)); + RETURN_VALUE_IF_ABRUPT(thread, JSValueRef::Exception(vm)); + return JSNApiHelper::ToLocal(jsArray); +} + +Local ObjectRef::GetPrototype(const EcmaVM *vm) +{ + JSThread *thread = vm->GetJSThread(); + ScopedManagedCodeThread s(thread); + JSHandle object(JSNApiHelper::ToJSHandle(this)); + JSHandle prototype(thread, object->GetPrototype(thread)); + RETURN_VALUE_IF_ABRUPT(thread, JSValueRef::Exception(vm)); + return JSNApiHelper::ToLocal(prototype); +} + +bool ObjectRef::DefineProperty(const EcmaVM *vm, Local key, PropertyAttribute attribute) +{ + JSThread *thread = vm->GetJSThread(); + ScopedManagedCodeThread s(thread); + JSHandle object(JSNApiHelper::ToJSHandle(this)); + JSHandle keyValue(JSNApiHelper::ToJSHandle(key)); + PropertyDescriptor desc(thread, attribute.IsWritable(), attribute.IsEnumerable(), attribute.IsConfigurable()); + desc.SetValue(JSNApiHelper::ToJSHandle(attribute.GetValue(vm))); + bool result = object->DefinePropertyOrThrow(thread, object, keyValue, desc); + RETURN_VALUE_IF_ABRUPT(thread, false); + return result; +} + +bool ObjectRef::Has(const EcmaVM *vm, Local key) +{ + JSThread *thread = vm->GetJSThread(); + JSHandle object(JSNApiHelper::ToJSHandle(this)); + JSHandle keyValue(JSNApiHelper::ToJSHandle(key)); + bool result = object->HasProperty(thread, object, keyValue); + RETURN_VALUE_IF_ABRUPT(thread, false); + return result; +} + +bool ObjectRef::Has(const EcmaVM *vm, uint32_t key) +{ + JSThread *thread = vm->GetJSThread(); + JSHandle object(JSNApiHelper::ToJSHandle(this)); + bool result = object->HasProperty(thread, object, key); + RETURN_VALUE_IF_ABRUPT(thread, false); + return result; +} + +bool ObjectRef::Delete(const EcmaVM *vm, Local key) +{ + JSThread *thread = vm->GetJSThread(); + ScopedManagedCodeThread s(thread); + JSHandle object(JSNApiHelper::ToJSHandle(this)); + JSHandle keyValue(JSNApiHelper::ToJSHandle(key)); + bool result = object->DeleteProperty(thread, object, keyValue); + RETURN_VALUE_IF_ABRUPT(thread, false); + return result; +} + +bool ObjectRef::Delete(const EcmaVM *vm, uint32_t key) +{ + JSThread *thread = vm->GetJSThread(); + ScopedManagedCodeThread s(thread); + JSHandle object(JSNApiHelper::ToJSHandle(this)); + JSHandle keyHandle(thread, JSTaggedValue(key)); + bool result = object->DeleteProperty(thread, object, keyHandle); + RETURN_VALUE_IF_ABRUPT(thread, false); + return result; +} + +void ObjectRef::SetNativePointerFieldCount(int32_t count) +{ + JSHandle object(JSNApiHelper::ToJSHandle(this)); + object->SetNativePointerFieldCount(count); +} + +int32_t ObjectRef::GetNativePointerFieldCount() +{ + JSHandle object(JSNApiHelper::ToJSHandle(this)); + return object->GetNativePointerFieldCount(); +} + +void *ObjectRef::GetNativePointerField(int32_t index) +{ + JSHandle object(JSNApiHelper::ToJSHandle(this)); + return object->GetNativePointerField(index); +} + +void ObjectRef::SetNativePointerField(int32_t index, void *nativePointer, NativePointerCallback callBack, void *data) +{ + JSHandle object(JSNApiHelper::ToJSHandle(this)); + object->SetNativePointerField(index, nativePointer, callBack, data); +} + +// ----------------------------------- FunctionRef -------------------------------------- +Local FunctionRef::New(EcmaVM *vm, FunctionCallback nativeFunc, void *data) +{ + JSThread *thread = vm->GetJSThread(); + ScopedManagedCodeThread s(thread); + ObjectFactory *factory = vm->GetFactory(); + JSHandle env = vm->GetGlobalEnv(); + JSHandle current(factory->NewJSFunction(env, reinterpret_cast(Callback::RegisterCallback))); + JSHandle funcCallback = factory->NewJSNativePointer(reinterpret_cast(nativeFunc)); + JSHandle vmCaddress = factory->NewJSNativePointer(vm); + JSHandle dataCaddress = factory->NewJSNativePointer(data); + JSHandle extraInfo(factory->NewFunctionExtraInfo(funcCallback, vmCaddress, dataCaddress)); + current->SetFunctionExtraInfo(thread, extraInfo.GetTaggedValue()); + return JSNApiHelper::ToLocal(JSHandle(current)); +} + +Local FunctionRef::New(EcmaVM *vm, FunctionCallback nativeFunc, Deleter deleter, void *data) +{ + JSThread *thread = vm->GetJSThread(); + ScopedManagedCodeThread s(thread); + ObjectFactory *factory = vm->GetFactory(); + JSHandle env = vm->GetGlobalEnv(); + JSHandle current(factory->NewJSFunction(env, reinterpret_cast(Callback::RegisterCallback))); + JSHandle funcCallback = factory->NewJSNativePointer(reinterpret_cast(nativeFunc)); + JSHandle vmCaddress = factory->NewJSNativePointer(vm); + JSHandle dataCaddress = factory->NewJSNativePointer(data, deleter, nullptr); + vm->PushToArrayDataList(*dataCaddress); + JSHandle extraInfo(factory->NewFunctionExtraInfo(funcCallback, vmCaddress, dataCaddress)); + current->SetFunctionExtraInfo(thread, extraInfo.GetTaggedValue()); + return JSNApiHelper::ToLocal(JSHandle(current)); +} + +Local FunctionRef::NewWithProperty(EcmaVM *vm, FunctionCallback nativeFunc, void *data) +{ + JSThread *thread = vm->GetJSThread(); + ScopedManagedCodeThread s(thread); + ObjectFactory *factory = vm->GetFactory(); + JSHandle env = vm->GetGlobalEnv(); + JSHandle current = + factory->NewJSFunction(env, reinterpret_cast(Callback::RegisterCallbackWithProperty)); + JSHandle funcCallback = factory->NewJSNativePointer(reinterpret_cast(nativeFunc)); + JSHandle vmCaddress = factory->NewJSNativePointer(vm); + JSHandle dataCaddress = factory->NewJSNativePointer(data); + JSHandle extraInfo(factory->NewFunctionExtraInfo(funcCallback, vmCaddress, dataCaddress)); + current->SetFunctionExtraInfo(thread, extraInfo.GetTaggedValue()); + return JSNApiHelper::ToLocal(JSHandle(current)); +} + +Local FunctionRef::NewClassFunction(EcmaVM *vm, FunctionCallbackWithNewTarget nativeFunc, Deleter deleter, + void *data) +{ + JSThread *thread = vm->GetJSThread(); + ScopedManagedCodeThread s(thread); + ObjectFactory *factory = vm->GetFactory(); + JSHandle env = vm->GetGlobalEnv(); + JSHandle dynclass = JSHandle::Cast(env->GetFunctionClassWithoutName()); + JSMethod *method = + vm->GetMethodForNativeFunction(reinterpret_cast(Callback::RegisterCallbackWithNewTarget)); + JSHandle current = + factory->NewJSFunctionByDynClass(method, dynclass, ecmascript::FunctionKind::CLASS_CONSTRUCTOR); + + auto globalConst = thread->GlobalConstants(); + JSHandle accessor = globalConst->GetHandledFunctionPrototypeAccessor(); + current->SetPropertyInlinedProps(thread, JSFunction::CLASS_PROTOTYPE_INLINE_PROPERTY_INDEX, + accessor.GetTaggedValue()); + + JSHandle funcCallback = factory->NewJSNativePointer(reinterpret_cast(nativeFunc)); + JSHandle vmCaddress = factory->NewJSNativePointer(vm); + JSHandle dataCaddress(thread, JSTaggedValue::Undefined()); + if (deleter == nullptr) { + dataCaddress = factory->NewJSNativePointer(data); + } else { + dataCaddress = factory->NewJSNativePointer(data, deleter, nullptr); + vm->PushToArrayDataList(*dataCaddress); + } + JSHandle extraInfo(factory->NewFunctionExtraInfo(funcCallback, vmCaddress, dataCaddress)); + current->SetFunctionExtraInfo(thread, extraInfo.GetTaggedValue()); + + JSHandle clsPrototype = JSFunction::NewJSFunctionPrototype(thread, factory, current); + clsPrototype.GetTaggedValue().GetTaggedObject()->GetClass()->SetClassPrototype(true); + JSHandle::Cast(current)->GetTaggedObject()->GetClass()->SetClassConstructor(true); + current->SetClassConstructor(true); + JSHandle parent = env->GetFunctionPrototype(); + JSObject::SetPrototype(thread, JSHandle::Cast(current), parent); + current->SetHomeObject(thread, clsPrototype); + return JSNApiHelper::ToLocal(JSHandle(current)); +} + +Local FunctionRef::Call(const EcmaVM *vm, Local thisObj, + const Local argv[], // NOLINTNEXTLINE(modernize-avoid-c-arrays) + int32_t length) +{ + JSThread *thread = vm->GetJSThread(); + if (!IsFunction()) { + return JSValueRef::Undefined(vm); + } + ScopedManagedCodeThread s(thread); + JSHandle func = JSNApiHelper::ToJSHandle(this); + JSHandle thisValue = JSNApiHelper::ToJSHandle(thisObj); + ObjectFactory *factory = vm->GetFactory(); + JSHandle arguments = factory->NewTaggedArray(length); + Span> sp(argv, length); + for (int i = 0; i < length; ++i) { + arguments->Set(thread, i, JSNApiHelper::ToJSHandle(sp[i])); + } + InternalCallParams *args = thread->GetInternalCallParams(); + args->MakeArgList(*arguments); + JSTaggedValue result = JSFunction::Call(thread, func, thisValue, arguments->GetLength(), args->GetArgv()); + RETURN_VALUE_IF_ABRUPT_NOT_CLEAR_EXCEPTION(thread, JSValueRef::Exception(vm)); + JSHandle resultValue(thread, result); + + vm->ExecutePromisePendingJob(); + RETURN_VALUE_IF_ABRUPT_NOT_CLEAR_EXCEPTION(thread, JSValueRef::Exception(vm)); + + return JSNApiHelper::ToLocal(resultValue); +} + +Local FunctionRef::Constructor(const EcmaVM *vm, + const Local argv[], // NOLINTNEXTLINE(modernize-avoid-c-arrays) + int32_t length) +{ + JSThread *thread = vm->GetJSThread(); + if (!IsFunction()) { + return JSValueRef::Undefined(vm); + } + ScopedManagedCodeThread s(thread); + JSHandle func = JSNApiHelper::ToJSHandle(this); + JSHandle newTarget = func; + ObjectFactory *factory = vm->GetFactory(); + JSHandle arguments = factory->NewTaggedArray(length); + Span> sp(argv, length); + for (int i = 0; i < length; ++i) { + arguments->Set(thread, i, JSNApiHelper::ToJSHandle(sp[i])); + } + ecmascript::InternalCallParams *params = thread->GetInternalCallParams(); + params->MakeArgList(*arguments); + JSTaggedValue result = JSFunction::Construct(thread, func, length, params->GetArgv(), newTarget); + RETURN_VALUE_IF_ABRUPT(thread, JSValueRef::Exception(vm)); + JSHandle resultValue(vm->GetJSThread(), result); + return JSNApiHelper::ToLocal(resultValue); +} + +Local FunctionRef::GetFunctionPrototype(const EcmaVM *vm) +{ + JSThread *thread = vm->GetJSThread(); + ScopedManagedCodeThread s(thread); + JSHandle func = JSNApiHelper::ToJSHandle(this); + JSHandle prototype(thread, JSHandle(func)->GetFunctionPrototype()); + return JSNApiHelper::ToLocal(prototype); +} + +bool FunctionRef::Inherit(const EcmaVM *vm, Local parent) +{ + JSThread *thread = vm->GetJSThread(); + ScopedManagedCodeThread s(thread); + JSHandle parentValue = JSNApiHelper::ToJSHandle(parent); + JSHandle parentHandle = JSHandle::Cast(parentValue); + JSHandle thisHandle = JSHandle::Cast(JSNApiHelper::ToJSHandle(this)); + // Set this.__proto__ to parent + bool res = JSObject::SetPrototype(thread, thisHandle, parentValue); + if (!res) { + return false; + } + // Set this.Prototype.__proto__ to parent.Prototype + JSHandle parentProtoType(thread, JSFunction::PrototypeGetter(thread, parentHandle)); + JSHandle thisProtoType(thread, JSFunction::PrototypeGetter(thread, thisHandle)); + return JSObject::SetPrototype(thread, JSHandle::Cast(thisProtoType), parentProtoType); +} + +void FunctionRef::SetName(const EcmaVM *vm, Local name) +{ + JSThread *thread = vm->GetJSThread(); + JSFunction *func = JSFunction::Cast(JSNApiHelper::ToJSTaggedValue(this).GetTaggedObject()); + JSTaggedValue key = JSNApiHelper::ToJSTaggedValue(*name); + JSFunction::SetFunctionNameNoPrefix(thread, func, key); +} + +Local FunctionRef::GetName(const EcmaVM *vm) +{ + JSThread *thread = vm->GetJSThread(); + JSHandle func = JSHandle(thread, JSNApiHelper::ToJSTaggedValue(this)); + JSHandle name = JSFunctionBase::GetFunctionName(thread, func); + RETURN_VALUE_IF_ABRUPT(thread, JSValueRef::Exception(vm)); + return JSNApiHelper::ToLocal(name); +} + +bool FunctionRef::IsNative(const EcmaVM *vm) +{ + JSThread *thread = vm->GetJSThread(); + JSHandle func = JSHandle(thread, JSNApiHelper::ToJSTaggedValue(this)); + JSMethod *method = func->GetMethod(); + return method->IsNative(); +} + +// ----------------------------------- ArrayRef ---------------------------------------- +Local ArrayRef::New(const EcmaVM *vm, int32_t length) +{ + JSThread *thread = vm->GetJSThread(); + ScopedManagedCodeThread s(thread); + JSTaggedNumber arrayLen(length); + JSHandle array = JSArray::ArrayCreate(thread, arrayLen); + RETURN_VALUE_IF_ABRUPT(thread, JSValueRef::Exception(vm)); + return JSNApiHelper::ToLocal(array); +} + +int32_t ArrayRef::Length([[maybe_unused]] const EcmaVM *vm) +{ + return JSArray::Cast(JSNApiHelper::ToJSTaggedValue(this).GetTaggedObject())->GetArrayLength(); +} + +Local ArrayRef::GetValueAt(const EcmaVM *vm, Local obj, uint32_t index) +{ + JSThread *thread = vm->GetJSThread(); + JSHandle object = JSNApiHelper::ToJSHandle(obj); + JSHandle result = JSArray::FastGetPropertyByValue(thread, object, index); + return JSNApiHelper::ToLocal(result); +} + +bool ArrayRef::SetValueAt(const EcmaVM *vm, Local obj, uint32_t index, Local value) +{ + JSThread *thread = vm->GetJSThread(); + ScopedManagedCodeThread s(thread); + JSHandle objectHandle = JSNApiHelper::ToJSHandle(obj); + JSHandle valueHandle = JSNApiHelper::ToJSHandle(value); + return JSArray::FastSetPropertyByValue(thread, objectHandle, index, valueHandle); +} +// ---------------------------------- Promise -------------------------------------- +Local PromiseCapabilityRef::New(const EcmaVM *vm) +{ + JSThread *thread = vm->GetJSThread(); + ScopedManagedCodeThread s(thread); + JSHandle globalEnv = vm->GetGlobalEnv(); + JSHandle constructor(globalEnv->GetPromiseFunction()); + JSHandle capability(JSPromise::NewPromiseCapability(thread, constructor)); + return JSNApiHelper::ToLocal(capability); +} + +Local PromiseCapabilityRef::GetPromise(const EcmaVM *vm) +{ + JSThread *thread = vm->GetJSThread(); + JSHandle capacity(JSNApiHelper::ToJSHandle(this)); + return JSNApiHelper::ToLocal(JSHandle(thread, capacity->GetPromise())); +} + +bool PromiseCapabilityRef::Resolve(const EcmaVM *vm, Local value) +{ + JSThread *thread = vm->GetJSThread(); + ScopedManagedCodeThread s(thread); + const GlobalEnvConstants *constants = thread->GlobalConstants(); + + JSHandle arg = JSNApiHelper::ToJSHandle(value); + JSHandle capacity(JSNApiHelper::ToJSHandle(this)); + JSHandle resolve(thread, capacity->GetResolve()); + JSHandle undefined(thread, constants->GetUndefined()); + InternalCallParams *arguments = thread->GetInternalCallParams(); + arguments->MakeArgv(arg); + JSFunction::Call(thread, resolve, undefined, 1, arguments->GetArgv()); + RETURN_VALUE_IF_ABRUPT(thread, false); + + vm->ExecutePromisePendingJob(); + RETURN_VALUE_IF_ABRUPT(thread, false); + return true; +} + +bool PromiseCapabilityRef::Reject(const EcmaVM *vm, Local reason) +{ + JSThread *thread = vm->GetJSThread(); + ScopedManagedCodeThread s(thread); + const GlobalEnvConstants *constants = thread->GlobalConstants(); + + JSHandle arg = JSNApiHelper::ToJSHandle(reason); + JSHandle capacity(JSNApiHelper::ToJSHandle(this)); + JSHandle reject(thread, capacity->GetReject()); + JSHandle undefined(thread, constants->GetUndefined()); + InternalCallParams *arguments = thread->GetInternalCallParams(); + arguments->MakeArgv(arg); + JSFunction::Call(thread, reject, undefined, 1, arguments->GetArgv()); + RETURN_VALUE_IF_ABRUPT(thread, false); + + vm->ExecutePromisePendingJob(); + RETURN_VALUE_IF_ABRUPT(thread, false); + return true; +} + +Local PromiseRef::Catch(const EcmaVM *vm, Local handler) +{ + JSThread *thread = vm->GetJSThread(); + ScopedManagedCodeThread s(thread); + const GlobalEnvConstants *constants = thread->GlobalConstants(); + + JSHandle promise = JSNApiHelper::ToJSHandle(this); + JSHandle catchKey(thread, constants->GetPromiseCatchString()); + JSHandle reject = JSNApiHelper::ToJSHandle(handler); + ecmascript::InternalCallParams *arguments = thread->GetInternalCallParams(); + arguments->MakeArgv(reject); + JSTaggedValue result = JSFunction::Invoke(thread, promise, catchKey, 1, arguments->GetArgv()); + + RETURN_VALUE_IF_ABRUPT(thread, JSValueRef::Exception(vm)); + return JSNApiHelper::ToLocal(JSHandle(thread, result)); +} + +Local PromiseRef::Then(const EcmaVM *vm, Local handler) +{ + JSThread *thread = vm->GetJSThread(); + ScopedManagedCodeThread s(thread); + const GlobalEnvConstants *constants = thread->GlobalConstants(); + + JSHandle promise = JSNApiHelper::ToJSHandle(this); + JSHandle thenKey(thread, constants->GetPromiseThenString()); + JSHandle resolver = JSNApiHelper::ToJSHandle(handler); + ecmascript::InternalCallParams *arguments = thread->GetInternalCallParams(); + arguments->MakeArgv(resolver.GetTaggedValue(), constants->GetUndefined()); + JSTaggedValue result = JSFunction::Invoke(thread, promise, thenKey, 2, arguments->GetArgv()); // 2: two args + + RETURN_VALUE_IF_ABRUPT(thread, JSValueRef::Exception(vm)); + return JSNApiHelper::ToLocal(JSHandle(thread, result)); +} + +Local PromiseRef::Then(const EcmaVM *vm, Local onFulfilled, Local onRejected) +{ + JSThread *thread = vm->GetJSThread(); + ScopedManagedCodeThread s(thread); + const GlobalEnvConstants *constants = thread->GlobalConstants(); + + JSHandle promise = JSNApiHelper::ToJSHandle(this); + JSHandle thenKey(thread, constants->GetPromiseThenString()); + JSHandle resolver = JSNApiHelper::ToJSHandle(onFulfilled); + JSHandle reject = JSNApiHelper::ToJSHandle(onRejected); + ecmascript::InternalCallParams *arguments = thread->GetInternalCallParams(); + arguments->MakeArgv(resolver, reject); + JSTaggedValue result = JSFunction::Invoke(thread, promise, thenKey, 2, arguments->GetArgv()); // 2: two args + + RETURN_VALUE_IF_ABRUPT(thread, JSValueRef::Exception(vm)); + return JSNApiHelper::ToLocal(JSHandle(thread, result)); +} +// ---------------------------------- Promise ------------------------------------- + +// ---------------------------------- Buffer ----------------------------------- +Local ArrayBufferRef::New(const EcmaVM *vm, int32_t length) +{ + JSThread *thread = vm->GetJSThread(); + ScopedManagedCodeThread s(thread); + ObjectFactory *factory = vm->GetFactory(); + + JSHandle arrayBuffer = factory->NewJSArrayBuffer(length); + RETURN_VALUE_IF_ABRUPT(thread, JSValueRef::Exception(vm)); + return JSNApiHelper::ToLocal(JSHandle(arrayBuffer)); +} + +Local ArrayBufferRef::New(const EcmaVM *vm, void *buffer, int32_t length, const Deleter &deleter, + void *data) +{ + JSThread *thread = vm->GetJSThread(); + ScopedManagedCodeThread s(thread); + ObjectFactory *factory = vm->GetFactory(); + + JSHandle arrayBuffer = + factory->NewJSArrayBuffer(buffer, length, reinterpret_cast(deleter), data); + RETURN_VALUE_IF_ABRUPT(thread, JSValueRef::Exception(vm)); + return JSNApiHelper::ToLocal(JSHandle(arrayBuffer)); +} + +int32_t ArrayBufferRef::ByteLength(const EcmaVM *vm) +{ + JSThread *thread = vm->GetJSThread(); + JSHandle arrayBuffer(JSNApiHelper::ToJSHandle(this)); + JSHandle length(thread, arrayBuffer->GetArrayBufferByteLength()); + if (!length->IsNumber()) { + return 0; + } + return length->GetNumber(); +} + +void *ArrayBufferRef::GetBuffer() +{ + JSHandle arrayBuffer(JSNApiHelper::ToJSHandle(this)); + JSTaggedValue bufferData = arrayBuffer->GetArrayBufferData(); + if (!bufferData.IsJSNativePointer()) { + return nullptr; + } + return JSNativePointer::Cast(bufferData.GetTaggedObject())->GetExternalPointer(); +} +// ---------------------------------- Buffer ----------------------------------- + +// ---------------------------------- DataView ----------------------------------- +Local DataViewRef::New(const EcmaVM *vm, Local arrayBuffer, int32_t byteOffset, + int32_t byteLength) +{ + JSThread *thread = vm->GetJSThread(); + ScopedManagedCodeThread s(thread); + ObjectFactory *factory = vm->GetFactory(); + + JSHandle buffer(JSNApiHelper::ToJSHandle(arrayBuffer)); + JSHandle dataView = factory->NewJSDataView(buffer, byteOffset, byteLength); + RETURN_VALUE_IF_ABRUPT(thread, JSValueRef::Exception(vm)); + return JSNApiHelper::ToLocal(JSHandle(dataView)); +} + +int32_t DataViewRef::ByteLength() +{ + JSHandle dataView(JSNApiHelper::ToJSHandle(this)); + JSTaggedValue length = dataView->GetByteLength(); + if (!length.IsNumber()) { + return 0; + } + return length.GetNumber(); +} + +int32_t DataViewRef::ByteOffset() +{ + JSHandle dataView(JSNApiHelper::ToJSHandle(this)); + JSTaggedValue offset = dataView->GetByteOffset(); + if (!offset.IsNumber()) { + return 0; + } + return offset.GetNumber(); +} + +Local DataViewRef::GetArrayBuffer(const EcmaVM *vm) +{ + JSThread *thread = vm->GetJSThread(); + JSHandle dataView(JSNApiHelper::ToJSHandle(this)); + JSHandle arrayBuffer(thread, dataView->GetViewedArrayBuffer()); + return JSNApiHelper::ToLocal(arrayBuffer); +} +// ---------------------------------- DataView ----------------------------------- + +// ---------------------------------- TypedArray ----------------------------------- +int32_t TypedArrayRef::ByteLength([[maybe_unused]] const EcmaVM *vm) +{ + JSHandle typedArray(JSNApiHelper::ToJSHandle(this)); + JSTaggedValue length = typedArray->GetByteLength(); + if (!length.IsNumber()) { + return 0; + } + return length.GetNumber(); +} + +int32_t TypedArrayRef::ByteOffset([[maybe_unused]] const EcmaVM *vm) +{ + JSHandle typedArray(JSNApiHelper::ToJSHandle(this)); + JSTaggedValue length = typedArray->GetByteOffset(); + if (!length.IsNumber()) { + return 0; + } + return length.GetNumber(); +} + +int32_t TypedArrayRef::ArrayLength([[maybe_unused]] const EcmaVM *vm) +{ + JSHandle typedArray(JSNApiHelper::ToJSHandle(this)); + JSTaggedValue length = typedArray->GetArrayLength(); + if (!length.IsNumber()) { + return 0; + } + return length.GetNumber(); +} + +Local TypedArrayRef::GetArrayBuffer(const EcmaVM *vm) +{ + JSThread *thread = vm->GetJSThread(); + JSHandle typeArray(JSNApiHelper::ToJSHandle(this)); + JSHandle arrayBuffer(thread, JSTypedArray::Cast(*typeArray)->GetViewedArrayBuffer()); + return JSNApiHelper::ToLocal(arrayBuffer); +} + +// NOLINTNEXTLINE(cppcoreguidelines-macro-usage) +#define TYPED_ARRAY_NEW(Type) \ + Local Type##Ref::New(const EcmaVM *vm, Local buffer, int32_t byteOffset, \ + int32_t length) \ + { \ + JSThread *thread = vm->GetJSThread(); \ + ScopedManagedCodeThread s(thread); \ + JSHandle env = vm->GetGlobalEnv(); \ + \ + JSHandle func = env->Get##Type##Function(); \ + JSHandle arrayBuffer(JSNApiHelper::ToJSHandle(buffer)); \ + ecmascript::InternalCallParams *argv = thread->GetInternalCallParams(); \ + argv->MakeArgv(arrayBuffer.GetTaggedValue(), JSTaggedValue(byteOffset), JSTaggedValue(length)); \ + uint32_t argc = 3; \ + JSTaggedValue result = JSFunction::Construct(thread, func, argc, argv->GetArgv(), func); \ + RETURN_VALUE_IF_ABRUPT(thread, JSValueRef::Exception(vm)); \ + JSHandle resultHandle(thread, result); \ + return JSNApiHelper::ToLocal(resultHandle); \ + } + +TYPED_ARRAY_ALL(TYPED_ARRAY_NEW) + +#undef TYPED_ARRAY_NEW +// ---------------------------------- TypedArray ----------------------------------- + +// ---------------------------------- Error --------------------------------------- +// NOLINTNEXTLINE(cppcoreguidelines-macro-usage) +#define EXCEPTION_ERROR_NEW(name, type) \ + Local Exception::name(const EcmaVM *vm, Local message) \ + { \ + JSThread *thread = vm->GetJSThread(); \ + ScopedManagedCodeThread s(thread); \ + ObjectFactory *factory = vm->GetFactory(); \ + \ + JSHandle messageValue(JSNApiHelper::ToJSHandle(message)); \ + JSHandle result(factory->NewJSError(ErrorType::type, messageValue)); \ + RETURN_VALUE_IF_ABRUPT(thread, JSValueRef::Exception(vm)); \ + return JSNApiHelper::ToLocal(result); \ + } + +EXCEPTION_ERROR_ALL(EXCEPTION_ERROR_NEW) + +#undef EXCEPTION_ERROR_NEW +// ---------------------------------- Error --------------------------------------- + +// ---------------------------------- JSON ------------------------------------------ +Local JSON::Parse(const EcmaVM *vm, Local string) +{ + JSThread *thread = vm->GetJSThread(); + ScopedManagedCodeThread s(thread); + auto ecmaStr = EcmaString::Cast(JSNApiHelper::ToJSTaggedValue(*string).GetTaggedObject()); + JSHandle result; + if (ecmaStr->IsUtf8()) { + JsonParser parser(thread); + result = parser.ParseUtf8(EcmaString::Cast(JSNApiHelper::ToJSTaggedValue(*string).GetTaggedObject())); + } else { + JsonParser parser(thread); + result = parser.ParseUtf16(EcmaString::Cast(JSNApiHelper::ToJSTaggedValue(*string).GetTaggedObject())); + } + RETURN_VALUE_IF_ABRUPT(thread, JSValueRef::Exception(vm)); + return JSNApiHelper::ToLocal(result); +} + +Local JSON::Stringify(const EcmaVM *vm, Local json) +{ + JSThread *thread = vm->GetJSThread(); + ScopedManagedCodeThread s(thread); + auto constants = thread->GlobalConstants(); + JsonStringifier stringifier(thread); + JSHandle str = stringifier.Stringify( + JSNApiHelper::ToJSHandle(json), constants->GetHandledUndefined(), constants->GetHandledUndefined()); + RETURN_VALUE_IF_ABRUPT(thread, JSValueRef::Exception(vm)); + return JSNApiHelper::ToLocal(str); +} + +Local RegExpRef::GetOriginalSource(const EcmaVM *vm) +{ + JSThread *thread = vm->GetJSThread(); + ScopedManagedCodeThread s(thread); + JSHandle regExp(JSNApiHelper::ToJSHandle(this)); + JSTaggedValue source = regExp->GetOriginalSource(); + if (!source.IsString()) { + auto constants = thread->GlobalConstants(); + return JSNApiHelper::ToLocal(constants->GetHandledEmptyString()); + } + JSHandle sourceHandle(thread, source); + return JSNApiHelper::ToLocal(sourceHandle); +} + +Local DateRef::New(const EcmaVM *vm, double time) +{ + JSThread *thread = vm->GetJSThread(); + ObjectFactory *factory = vm->GetFactory(); + JSHandle globalEnv = vm->GetGlobalEnv(); + JSHandle dateFunction = globalEnv->GetDateFunction(); + JSHandle dateObject = + JSHandle::Cast(factory->NewJSObjectByConstructor(JSHandle(dateFunction), dateFunction)); + dateObject->SetTimeValue(thread, JSTaggedValue(time)); + return JSNApiHelper::ToLocal(JSHandle(dateObject)); +} + +Local DateRef::ToString(const EcmaVM *vm) +{ + JSThread *thread = vm->GetJSThread(); + ScopedManagedCodeThread s(thread); + JSHandle date(JSNApiHelper::ToJSHandle(this)); + JSTaggedValue dateStr = date->ToString(thread); + if (!dateStr.IsString()) { + auto constants = thread->GlobalConstants(); + return JSNApiHelper::ToLocal(constants->GetHandledEmptyString()); + } + JSHandle dateStrHandle(thread, dateStr); + return JSNApiHelper::ToLocal(dateStrHandle); +} + +double DateRef::GetTime() +{ + JSHandle date(JSNApiHelper::ToJSHandle(this)); + if (!date->IsDate()) { + LOG(ERROR, RUNTIME) << "Not a Date Object"; + } + return date->GetTime().GetDouble(); +} + +int32_t MapRef::GetSize() +{ + JSHandle map(JSNApiHelper::ToJSHandle(this)); + return map->GetSize(); +} + +int32_t SetRef::GetSize() +{ + JSHandle set(JSNApiHelper::ToJSHandle(this)); + return set->GetSize(); +} + +// ----------------------------------- FunctionCallback --------------------------------- +JSTaggedValue Callback::RegisterCallback(ecmascript::EcmaRuntimeCallInfo *info) +{ + // Constructor + JSThread *thread = info->GetThread(); + JSHandle constructor = BuiltinsBase::GetConstructor(info); + if (!constructor->IsJSFunction()) { + return JSTaggedValue::False(); + } + JSHandle function(constructor); + JSHandle extraInfoValue(thread, function->GetFunctionExtraInfo()); + if (!extraInfoValue->IsJSFunctionExtraInfo()) { + return JSTaggedValue::False(); + } + JSHandle extraInfo(extraInfoValue); + // vm + JSHandle vmValue(thread, extraInfo->GetVm()); + if (!vmValue->IsHeapObject()) { + return JSTaggedValue::False(); + } + JSHandle vmObj(vmValue); + auto *vm = reinterpret_cast(vmObj->GetExternalPointer()); + + // data + JSHandle data(thread, extraInfo->GetData()); + if (!data->IsHeapObject()) { + return JSTaggedValue::False(); + } + JSHandle dataObj(data); + // callBack + JSHandle callBack(thread, extraInfo->GetCallback()); + if (!callBack->IsHeapObject()) { + return JSTaggedValue::False(); + } + JSHandle callBackObj(callBack); + FunctionCallback nativeFunc = (reinterpret_cast(callBackObj->GetExternalPointer())); + + // this + JSHandle thisValue(BuiltinsBase::GetThis(info)); + + // arguments + std::vector> arguments; + uint32_t length = info->GetArgsNumber(); + for (uint32_t i = 0; i < length; ++i) { + arguments.emplace_back(JSNApiHelper::ToLocal(BuiltinsBase::GetCallArg(info, i))); + } + + ScopedNativeCodeThread s(thread); + Local result = nativeFunc(vm, JSNApiHelper::ToLocal(thisValue), arguments.data(), + arguments.size(), dataObj->GetExternalPointer()); + return JSNApiHelper::ToJSHandle(result).GetTaggedValue(); +} + +JSTaggedValue Callback::RegisterCallbackWithProperty(ecmascript::EcmaRuntimeCallInfo *info) +{ + // Constructor + JSThread *thread = info->GetThread(); + JSHandle constructor = BuiltinsBase::GetConstructor(info); + if (!constructor->IsJSFunction()) { + return JSTaggedValue::False(); + } + JSHandle function(constructor); + JSHandle extraInfoValue(thread, function->GetFunctionExtraInfo()); + if (!extraInfoValue->IsJSFunctionExtraInfo()) { + return JSTaggedValue::False(); + } + JSHandle extraInfo(extraInfoValue); + // vm + Region *region = Region::ObjectAddressToRange(extraInfo.GetTaggedValue().GetTaggedObject()); + if (region == nullptr) { + return JSTaggedValue::False(); + } + EcmaVM *vm = region->GetHeap()->GetEcmaVM(); + // data + JSHandle data(thread, extraInfo->GetData()); + if (!data->IsHeapObject()) { + return JSTaggedValue::False(); + } + JSHandle dataObj(data); + // callBack + JSHandle callBack(thread, extraInfo->GetCallback()); + if (!callBack->IsHeapObject()) { + return JSTaggedValue::False(); + } + JSHandle callBackObj(callBack); + FunctionCallback nativeFunc = (reinterpret_cast(callBackObj->GetExternalPointer())); + + // constructor + JSHandle thisValue(BuiltinsBase::GetConstructor(info)); + + // arguments + std::vector> arguments; + uint32_t length = info->GetArgsNumber(); + for (uint32_t i = 0; i < length; ++i) { + arguments.emplace_back(JSNApiHelper::ToLocal(BuiltinsBase::GetCallArg(info, i))); + } + + ScopedNativeCodeThread s(thread); + Local result = nativeFunc(vm, JSNApiHelper::ToLocal(thisValue), arguments.data(), + arguments.size(), dataObj->GetExternalPointer()); + return JSNApiHelper::ToJSHandle(result).GetTaggedValue(); +} + +JSTaggedValue Callback::RegisterCallbackWithNewTarget(ecmascript::EcmaRuntimeCallInfo *info) +{ + // Constructor + JSThread *thread = info->GetThread(); + JSHandle constructor = BuiltinsBase::GetConstructor(info); + if (!constructor->IsJSFunction()) { + return JSTaggedValue::False(); + } + JSHandle function(constructor); + JSHandle extraInfoValue(thread, function->GetFunctionExtraInfo()); + if (!extraInfoValue->IsJSFunctionExtraInfo()) { + return JSTaggedValue::False(); + } + JSHandle extraInfo(extraInfoValue); + // vm + Region *region = Region::ObjectAddressToRange(extraInfo.GetTaggedValue().GetTaggedObject()); + if (region == nullptr) { + return JSTaggedValue::False(); + } + EcmaVM *vm = region->GetHeap()->GetEcmaVM(); + // data + JSHandle data(thread, extraInfo->GetData()); + if (!data->IsHeapObject()) { + return JSTaggedValue::False(); + } + JSHandle dataObj(data); + // callBack + JSHandle callBack(thread, extraInfo->GetCallback()); + if (!callBack->IsHeapObject()) { + return JSTaggedValue::False(); + } + JSHandle callBackObj(callBack); + FunctionCallbackWithNewTarget nativeFunc = + (reinterpret_cast(callBackObj->GetExternalPointer())); + + // newTarget + JSHandle newTarget(BuiltinsBase::GetNewTarget(info)); + + // this + JSHandle thisValue(BuiltinsBase::GetThis(info)); + + // arguments + std::vector> arguments; + uint32_t length = info->GetArgsNumber(); + for (uint32_t i = 0; i < length; ++i) { + arguments.emplace_back(JSNApiHelper::ToLocal(BuiltinsBase::GetCallArg(info, i))); + } + + ScopedNativeCodeThread s(thread); + Local result = + nativeFunc(vm, JSNApiHelper::ToLocal(thisValue), JSNApiHelper::ToLocal(newTarget), + arguments.data(), arguments.size(), dataObj->GetExternalPointer()); + return JSNApiHelper::ToJSHandle(result).GetTaggedValue(); +} + +// ------------------------------------- JSExecutionScope ------------------------------ +JSExecutionScope::JSExecutionScope(const EcmaVM *vm) +{ + (void)vm; +} + +JSExecutionScope::~JSExecutionScope() +{ + last_current_thread_ = nullptr; + is_revert_ = false; +} + +// ----------------------------------- JSValueRef -------------------------------------- +Local JSValueRef::Undefined(const EcmaVM *vm) +{ + return JSNApiHelper::ToLocal(JSHandle(vm->GetJSThread(), JSTaggedValue::Undefined())); +} + +Local JSValueRef::Null(const EcmaVM *vm) +{ + return JSNApiHelper::ToLocal(JSHandle(vm->GetJSThread(), JSTaggedValue::Null())); +} + +Local JSValueRef::True(const EcmaVM *vm) +{ + return JSNApiHelper::ToLocal(JSHandle(vm->GetJSThread(), JSTaggedValue::True())); +} + +Local JSValueRef::False(const EcmaVM *vm) +{ + return JSNApiHelper::ToLocal(JSHandle(vm->GetJSThread(), JSTaggedValue::False())); +} + +Local JSValueRef::Exception(const EcmaVM *vm) +{ + return JSNApiHelper::ToLocal(JSHandle(vm->GetJSThread(), JSTaggedValue::Exception())); +} + +Local JSValueRef::ToObject(const EcmaVM *vm) +{ + JSThread *thread = vm->GetJSThread(); + ScopedManagedCodeThread s(thread); + if (IsUndefined() || IsNull()) { + return Exception(vm); + } + JSHandle obj(JSTaggedValue::ToObject(thread, JSNApiHelper::ToJSHandle(this))); + RETURN_VALUE_IF_ABRUPT(thread, JSValueRef::Exception(vm)); + return JSNApiHelper::ToLocal(obj); +} + +Local JSValueRef::ToString(const EcmaVM *vm) +{ + JSThread *thread = vm->GetJSThread(); + ScopedManagedCodeThread s(thread); + JSHandle obj = JSNApiHelper::ToJSHandle(this); + if (!obj->IsString()) { + obj = JSHandle(JSTaggedValue::ToString(thread, obj)); + RETURN_VALUE_IF_ABRUPT(thread, JSValueRef::Exception(vm)); + } + return JSNApiHelper::ToLocal(obj); +} + +Local JSValueRef::ToNativePointer(const EcmaVM *vm) +{ + JSThread *thread = vm->GetJSThread(); + ScopedManagedCodeThread s(thread); + JSHandle obj = JSNApiHelper::ToJSHandle(this); + RETURN_VALUE_IF_ABRUPT(thread, JSValueRef::Exception(vm)); + return JSNApiHelper::ToLocal(obj); +} + +bool JSValueRef::BooleaValue() +{ + return JSNApiHelper::ToJSTaggedValue(this).ToBoolean(); +} + +int64_t JSValueRef::IntegerValue(const EcmaVM *vm) +{ + JSThread *thread = vm->GetJSThread(); + JSTaggedNumber number = JSTaggedValue::ToInteger(thread, JSNApiHelper::ToJSHandle(this)); + RETURN_VALUE_IF_ABRUPT(thread, 0); + return number.GetNumber(); +} + +uint32_t JSValueRef::Uint32Value(const EcmaVM *vm) +{ + JSThread *thread = vm->GetJSThread(); + uint32_t number = JSTaggedValue::ToUint32(thread, JSNApiHelper::ToJSHandle(this)); + RETURN_VALUE_IF_ABRUPT(thread, 0); + return number; +} + +int32_t JSValueRef::Int32Value(const EcmaVM *vm) +{ + JSThread *thread = vm->GetJSThread(); + int32_t number = JSTaggedValue::ToInt32(thread, JSNApiHelper::ToJSHandle(this)); + RETURN_VALUE_IF_ABRUPT(thread, 0); + return number; +} + +Local JSValueRef::ToBoolean(const EcmaVM *vm) +{ + JSThread *thread = vm->GetJSThread(); + JSHandle obj = JSNApiHelper::ToJSHandle(this); + JSHandle booleanObj = JSHandle(thread, JSTaggedValue(obj->ToBoolean())); + return JSNApiHelper::ToLocal(booleanObj); +} + +Local JSValueRef::ToNumber(const EcmaVM *vm) +{ + JSThread *thread = vm->GetJSThread(); + JSHandle obj = JSNApiHelper::ToJSHandle(this); + JSHandle number(thread, JSTaggedValue::ToNumber(thread, obj)); + RETURN_VALUE_IF_ABRUPT(thread, JSValueRef::Exception(vm)); + return JSNApiHelper::ToLocal(number); +} + +bool JSValueRef::IsStrictEquals(const EcmaVM *vm, Local value) +{ + JSThread *thread = vm->GetJSThread(); + JSHandle xValue = JSNApiHelper::ToJSHandle(this); + JSHandle yValue = JSNApiHelper::ToJSHandle(value); + return JSTaggedValue::StrictEqual(thread, xValue, yValue); +} + +Local JSValueRef::Typeof(const EcmaVM *vm) +{ + JSThread *thread = vm->GetJSThread(); + ScopedManagedCodeThread s(thread); + JSTaggedValue value = FastRuntimeStub::FastTypeOf(thread, JSNApiHelper::ToJSTaggedValue(this)); + return JSNApiHelper::ToLocal(JSHandle(thread, value)); +} + +bool JSValueRef::InstanceOf(const EcmaVM *vm, Local value) +{ + JSThread *thread = vm->GetJSThread(); + ScopedManagedCodeThread s(thread); + JSHandle origin = JSNApiHelper::ToJSHandle(this); + JSHandle target = JSNApiHelper::ToJSHandle(value); + bool result = JSObject::InstanceOf(thread, origin, target); + RETURN_VALUE_IF_ABRUPT(thread, false); + return result; +} + +bool JSValueRef::IsUndefined() +{ + return JSNApiHelper::ToJSTaggedValue(this).IsUndefined(); +} + +bool JSValueRef::IsNull() +{ + return JSNApiHelper::ToJSTaggedValue(this).IsNull(); +} + +bool JSValueRef::IsHole() +{ + return JSNApiHelper::ToJSTaggedValue(this).IsHole(); +} + +bool JSValueRef::IsTrue() +{ + return JSNApiHelper::ToJSTaggedValue(this).IsTrue(); +} + +bool JSValueRef::IsFalse() +{ + return JSNApiHelper::ToJSTaggedValue(this).IsFalse(); +} + +bool JSValueRef::IsNumber() +{ + return JSNApiHelper::ToJSTaggedValue(this).IsNumber(); +} + +bool JSValueRef::IsInt() +{ + return JSNApiHelper::ToJSTaggedValue(this).IsInt(); +} + +bool JSValueRef::WithinInt32() +{ + return JSNApiHelper::ToJSTaggedValue(this).WithinInt32(); +} + +bool JSValueRef::IsBoolean() +{ + return JSNApiHelper::ToJSTaggedValue(this).IsBoolean(); +} + +bool JSValueRef::IsString() +{ + return JSNApiHelper::ToJSTaggedValue(this).IsString(); +} + +bool JSValueRef::IsSymbol() +{ + return JSNApiHelper::ToJSTaggedValue(this).IsSymbol(); +} + +bool JSValueRef::IsObject() +{ + return JSNApiHelper::ToJSTaggedValue(this).IsECMAObject(); +} + +bool JSValueRef::IsArray(const EcmaVM *vm) +{ + JSThread *thread = vm->GetJSThread(); + return JSNApiHelper::ToJSTaggedValue(this).IsArray(thread); +} + +bool JSValueRef::IsConstructor() +{ + JSTaggedValue value = JSNApiHelper::ToJSTaggedValue(this); + return value.IsHeapObject() && value.IsConstructor(); +} + +bool JSValueRef::IsFunction() +{ + JSTaggedValue value = JSNApiHelper::ToJSTaggedValue(this); + return value.IsHeapObject() && value.IsCallable(); +} + +bool JSValueRef::IsProxy() +{ + return JSNApiHelper::ToJSTaggedValue(this).IsJSProxy(); +} + +bool JSValueRef::IsException() +{ + return JSNApiHelper::ToJSTaggedValue(this).IsException(); +} + +bool JSValueRef::IsPromise() +{ + return JSNApiHelper::ToJSTaggedValue(this).IsJSPromise(); +} + +bool JSValueRef::IsDataView() +{ + return JSNApiHelper::ToJSTaggedValue(this).IsDataView(); +} + +bool JSValueRef::IsTypedArray() +{ + return JSNApiHelper::ToJSTaggedValue(this).IsTypedArray(); +} + +bool JSValueRef::IsNativePointer() +{ + return JSNApiHelper::ToJSTaggedValue(this).IsJSNativePointer(); +} + +bool JSValueRef::IsDate() +{ + return JSNApiHelper::ToJSTaggedValue(this).IsDate(); +} + +bool JSValueRef::IsError() +{ + return JSNApiHelper::ToJSTaggedValue(this).IsJSError(); +} + +bool JSValueRef::IsMap() +{ + return JSNApiHelper::ToJSTaggedValue(this).IsJSMap(); +} + +bool JSValueRef::IsSet() +{ + return JSNApiHelper::ToJSTaggedValue(this).IsJSSet(); +} + +bool JSValueRef::IsWeakMap() +{ + return JSNApiHelper::ToJSTaggedValue(this).IsJSWeakMap(); +} + +bool JSValueRef::IsWeakSet() +{ + return JSNApiHelper::ToJSTaggedValue(this).IsJSWeakSet(); +} + +bool JSValueRef::IsRegExp() +{ + return JSNApiHelper::ToJSTaggedValue(this).IsJSRegExp(); +} + +bool JSValueRef::IsArrayIterator() +{ + return JSNApiHelper::ToJSTaggedValue(this).IsJSArrayIterator(); +} + +bool JSValueRef::IsStringIterator() +{ + return JSNApiHelper::ToJSTaggedValue(this).IsStringIterator(); +} + +bool JSValueRef::IsSetIterator() +{ + return JSNApiHelper::ToJSTaggedValue(this).IsJSSetIterator(); +} + +bool JSValueRef::IsMapIterator() +{ + return JSNApiHelper::ToJSTaggedValue(this).IsJSMapIterator(); +} + +bool JSValueRef::IsArrayBuffer() +{ + return JSNApiHelper::ToJSTaggedValue(this).IsArrayBuffer(); +} + +bool JSValueRef::IsUint8Array() +{ + return JSNApiHelper::ToJSTaggedValue(this).IsJSUint8Array(); +} + +bool JSValueRef::IsInt8Array() +{ + return JSNApiHelper::ToJSTaggedValue(this).IsJSInt8Array(); +} + +bool JSValueRef::IsUint8ClampedArray() +{ + return JSNApiHelper::ToJSTaggedValue(this).IsJSUint8ClampedArray(); +} + +bool JSValueRef::IsInt16Array() +{ + return JSNApiHelper::ToJSTaggedValue(this).IsJSInt16Array(); +} + +bool JSValueRef::IsUint16Array() +{ + return JSNApiHelper::ToJSTaggedValue(this).IsJSUint16Array(); +} + +bool JSValueRef::IsInt32Array() +{ + return JSNApiHelper::ToJSTaggedValue(this).IsJSInt32Array(); +} + +bool JSValueRef::IsUint32Array() +{ + return JSNApiHelper::ToJSTaggedValue(this).IsJSUint32Array(); +} + +bool JSValueRef::IsFloat32Array() +{ + return JSNApiHelper::ToJSTaggedValue(this).IsJSFloat32Array(); +} + +bool JSValueRef::IsFloat64Array() +{ + return JSNApiHelper::ToJSTaggedValue(this).IsJSFloat64Array(); +} + +bool JSValueRef::IsJSPrimitiveRef() +{ + return JSNApiHelper::ToJSTaggedValue(this).IsJSPrimitiveRef(); +} + +bool JSValueRef::IsJSPrimitiveNumber() +{ + JSHandle obj = JSNApiHelper::ToJSHandle(this); + return JSPrimitiveRef::Cast(obj->GetHeapObject())->IsNumber(); +} + +bool JSValueRef::IsJSPrimitiveInt() +{ + JSHandle obj = JSNApiHelper::ToJSHandle(this); + return JSPrimitiveRef::Cast(obj->GetHeapObject())->IsInt(); +} + +bool JSValueRef::IsJSPrimitiveBoolean() +{ + JSHandle obj = JSNApiHelper::ToJSHandle(this); + return JSPrimitiveRef::Cast(obj->GetHeapObject())->IsBoolean(); +} + +bool JSValueRef::IsJSPrimitiveString() +{ + JSHandle obj = JSNApiHelper::ToJSHandle(this); + return JSPrimitiveRef::Cast(obj->GetHeapObject())->IsString(); +} + +bool JSValueRef::IsJSPrimitiveSymbol() +{ + JSHandle obj = JSNApiHelper::ToJSHandle(this); + return JSPrimitiveRef::Cast(obj->GetHeapObject())->IsSymbol(); +} + +bool JSValueRef::IsGeneratorObject() +{ + JSHandle obj = JSNApiHelper::ToJSHandle(this); + bool rst = obj->IsGeneratorObject(); + return rst; +} + +bool JSValueRef::IsAsyncFunction() +{ + JSHandle obj = JSNApiHelper::ToJSHandle(this); + bool rst = obj->IsJSAsyncFunction(); + return rst; +} + +bool JSValueRef::IsArgumentsObject() +{ + JSHandle obj = JSNApiHelper::ToJSHandle(this); + bool rst = obj->IsArguments(); + return rst; +} + +bool JSValueRef::IsGeneratorFunction() +{ + JSHandle obj = JSNApiHelper::ToJSHandle(this); + bool rst = obj->IsGeneratorFunction(); + return rst; +} +} // namespace panda diff --git a/runtime/napi/jsnapi_helper-inl.h b/runtime/napi/jsnapi_helper-inl.h new file mode 100644 index 000000000..60647e4db --- /dev/null +++ b/runtime/napi/jsnapi_helper-inl.h @@ -0,0 +1,48 @@ +/* + * Copyright (c) 2021-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_NAPI_JSNAPI_HELPER_INL_H +#define ECMASCRIPT_NAPI_JSNAPI_HELPER_INL_H + +#include "plugins/ecmascript/runtime/js_tagged_value.h" +#include "jsnapi_helper.h" +#include "libpandabase/macros.h" + +namespace panda { +template +Local JSNApiHelper::ToLocal(ecmascript::JSHandle from) +{ + return Local(from.GetAddress()); +} + +ecmascript::JSTaggedValue JSNApiHelper::ToJSTaggedValue(JSValueRef *from) +{ + ASSERT(from != nullptr); + return *reinterpret_cast(from); +} + +ecmascript::JSHandle JSNApiHelper::ToJSHandle(Local from) +{ + ASSERT(!from.IsEmpty()); + return ecmascript::JSHandle(reinterpret_cast(*from)); +} + +ecmascript::JSHandle JSNApiHelper::ToJSHandle(JSValueRef *from) +{ + ASSERT(from != nullptr); + return ecmascript::JSHandle(reinterpret_cast(from)); +} +} // namespace panda +#endif // ECMASCRIPT_NAPI_JSNAPI_HELPER_INL_H diff --git a/runtime/napi/jsnapi_helper.h b/runtime/napi/jsnapi_helper.h new file mode 100644 index 000000000..cc5014d6c --- /dev/null +++ b/runtime/napi/jsnapi_helper.h @@ -0,0 +1,82 @@ +/* + * Copyright (c) 2021-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_NAPI_JSNAPI_HELPER_H +#define ECMASCRIPT_NAPI_JSNAPI_HELPER_H + +#include "plugins/ecmascript/runtime/ecma_runtime_call_info.h" +#include "plugins/ecmascript/runtime/js_handle.h" +#include "plugins/ecmascript/runtime/js_tagged_value.h" +#include "plugins/ecmascript/runtime/napi/include/jsnapi.h" + +// NOLINTNEXTLINE(cppcoreguidelines-macro-usage) +#define RETURN_VALUE_IF_ABRUPT(thread, value) \ + do { \ + if (thread->HasPendingException()) { \ + thread->ClearException(); \ + return value; \ + } \ + } while (false) + +// NOLINTNEXTLINE(cppcoreguidelines-macro-usage) +#define RETURN_VALUE_IF_ABRUPT_NOT_CLEAR_EXCEPTION(thread, value) \ + do { \ + if (thread->HasPendingException()) { \ + return value; \ + } \ + } while (false) + +// NOLINTNEXTLINE(cppcoreguidelines-macro-usage) +#define TYPED_ARRAY_ALL(V) \ + V(Int8Array) \ + V(Uint8Array) \ + V(Uint8ClampedArray) \ + V(Int16Array) \ + V(Uint16Array) \ + V(Int32Array) \ + V(Uint32Array) \ + V(Float32Array) \ + V(Float64Array) + +// NOLINTNEXTLINE(cppcoreguidelines-macro-usage) +#define EXCEPTION_ERROR_ALL(V) \ + V(Error, ERROR) \ + V(RangeError, RANGE_ERROR) \ + V(SyntaxError, SYNTAX_ERROR) \ + V(ReferenceError, REFERENCE_ERROR) \ + V(TypeError, TYPE_ERROR) \ + V(EvalError, EVAL_ERROR) + +namespace panda { +class JSNApiHelper { +public: + template + static inline Local ToLocal(ecmascript::JSHandle from); + + static inline ecmascript::JSTaggedValue ToJSTaggedValue(JSValueRef *from); + + static inline ecmascript::JSHandle ToJSHandle(Local from); + + static inline ecmascript::JSHandle ToJSHandle(JSValueRef *from); +}; + +class Callback { +public: + static ecmascript::JSTaggedValue RegisterCallback(ecmascript::EcmaRuntimeCallInfo *info); + static ecmascript::JSTaggedValue RegisterCallbackWithProperty(ecmascript::EcmaRuntimeCallInfo *info); + static ecmascript::JSTaggedValue RegisterCallbackWithNewTarget(ecmascript::EcmaRuntimeCallInfo *info); +}; +} // namespace panda +#endif // ECMASCRIPT_NAPI_JSNAPI_HELPER_H diff --git a/runtime/object_factory-inl.h b/runtime/object_factory-inl.h new file mode 100644 index 000000000..d32e1cf65 --- /dev/null +++ b/runtime/object_factory-inl.h @@ -0,0 +1,84 @@ +/* + * Copyright (c) 2021-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_OBJECT_FACTORY_INL_H +#define ECMASCRIPT_OBJECT_FACTORY_INL_H + +#include "object_factory.h" +#include "plugins/ecmascript/runtime/mem/mem_manager-inl.h" +#include "plugins/ecmascript/runtime/tagged_array-inl.h" +#include "plugins/ecmascript/runtime/lexical_env.h" + +namespace panda::ecmascript { +EcmaString *ObjectFactory::AllocNonMovableStringObject(size_t size) +{ + NewObjectHook(); + return reinterpret_cast(heapHelper_.AllocateNonMovableOrHugeObject(stringClass_, size)); +} + +EcmaString *ObjectFactory::AllocStringObject(size_t size) +{ + NewObjectHook(); + return reinterpret_cast(heapHelper_.AllocateYoungGenerationOrHugeObject(stringClass_, size)); +} + +JSHandle ObjectFactory::NewJSNativePointer(void *externalPointer, const DeleteEntryPoint &callBack, + void *data, bool nonMovable) +{ + NewObjectHook(); + TaggedObject *header; + if (nonMovable) { + header = heapHelper_.AllocateNonMovableOrHugeObject(jsNativePointerClass_); + } else { + header = heapHelper_.AllocateYoungGenerationOrHugeObject(jsNativePointerClass_); + } + JSHandle obj(thread_, header); + obj->SetExternalPointer(externalPointer); + obj->SetDeleter(callBack); + obj->SetData(data); + return obj; +} + +LexicalEnv *ObjectFactory::InlineNewLexicalEnv(int numSlots) +{ + NewObjectHook(); + size_t size = LexicalEnv::ComputeSize(numSlots); + auto header = heapHelper_.AllocateYoungGenerationOrHugeObject(envClass_, size); + if (UNLIKELY(header == nullptr)) { + return nullptr; + } + LexicalEnv *array = LexicalEnv::Cast(header); + array->InitializeWithSpecialValue(JSTaggedValue::Hole(), numSlots + LexicalEnv::RESERVED_ENV_LENGTH); + return array; +} + +template +void ObjectFactory::NewJSIntlIcuData(const JSHandle &obj, const S &icu, const DeleteEntryPoint &callback) +{ + S *icuPoint = vm_->GetRegionFactory()->New(icu); + ASSERT(icuPoint != nullptr); + JSTaggedValue data = obj->GetIcuField(); + if (data.IsHeapObject() && data.IsJSNativePointer()) { + JSNativePointer *native = JSNativePointer::Cast(data.GetTaggedObject()); + native->ResetExternalPointer(icuPoint); + return; + } + JSHandle pointer(thread_, NewJSNativePointer(icuPoint, callback, vm_).GetTaggedValue()); + obj->SetIcuField(thread_, pointer.GetTaggedValue()); + // push uint8_t* to ecma array_data_list + vm_->PushToArrayDataList(*pointer); +} +} // namespace panda::ecmascript +#endif // ECMASCRIPT_OBJECT_FACTORY_INL_H diff --git a/runtime/object_factory.cpp b/runtime/object_factory.cpp new file mode 100644 index 000000000..dcb831ccf --- /dev/null +++ b/runtime/object_factory.cpp @@ -0,0 +1,2271 @@ +/* + * Copyright (c) 2021-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 "ecma_string_table.h" +#include "ecma_vm.h" +#include "include/stack_walker.h" +#include "js_method.h" +#include "mem/mark_word.h" +#include "plugins/ecmascript/runtime/accessor_data.h" +#include "plugins/ecmascript/runtime/base/error_helper.h" +#include "plugins/ecmascript/runtime/builtins.h" +#include "plugins/ecmascript/runtime/builtins/builtins_errors.h" +#include "plugins/ecmascript/runtime/builtins/builtins_global.h" +#include "plugins/ecmascript/runtime/class_info_extractor.h" +#include "plugins/ecmascript/runtime/class_linker/program_object.h" +#include "plugins/ecmascript/runtime/ecma_macros.h" +#include "plugins/ecmascript/runtime/ecma_module.h" +#include "plugins/ecmascript/runtime/free_object.h" +#include "plugins/ecmascript/runtime/global_env.h" +#include "plugins/ecmascript/runtime/global_env_constants-inl.h" +#include "plugins/ecmascript/runtime/global_env_constants.h" +#include "plugins/ecmascript/runtime/ic/ic_handler.h" +#include "plugins/ecmascript/runtime/ic/profile_type_info.h" +#include "plugins/ecmascript/runtime/ic/property_box.h" +#include "plugins/ecmascript/runtime/ic/proto_change_details.h" +#include "plugins/ecmascript/runtime/internal_call_params.h" +#include "plugins/ecmascript/runtime/jobs/micro_job_queue.h" +#include "plugins/ecmascript/runtime/jobs/pending_job.h" +#include "plugins/ecmascript/runtime/js_arguments.h" +#include "plugins/ecmascript/runtime/js_array.h" +#include "plugins/ecmascript/runtime/js_array_iterator.h" +#include "plugins/ecmascript/runtime/js_arraybuffer.h" +#include "plugins/ecmascript/runtime/js_arraylist.h" +#include "plugins/ecmascript/runtime/js_async_function.h" +#include "plugins/ecmascript/runtime/js_async_from_sync_iterator_object.h" +#include "plugins/ecmascript/runtime/js_async_generator_object.h" +#include "plugins/ecmascript/runtime/js_dataview.h" +#include "plugins/ecmascript/runtime/js_date.h" +#include "plugins/ecmascript/runtime/js_for_in_iterator.h" +#include "plugins/ecmascript/runtime/js_generator_object.h" +#include "plugins/ecmascript/runtime/js_hclass-inl.h" +#include "plugins/ecmascript/runtime/js_hclass.h" +#include "plugins/ecmascript/runtime/js_iterator.h" +#include "plugins/ecmascript/runtime/js_map.h" +#include "plugins/ecmascript/runtime/js_map_iterator.h" +#include "plugins/ecmascript/runtime/js_object-inl.h" +#include "plugins/ecmascript/runtime/js_primitive_ref.h" +#include "plugins/ecmascript/runtime/js_promise.h" +#include "plugins/ecmascript/runtime/js_proxy.h" +#include "plugins/ecmascript/runtime/js_realm.h" +#include "plugins/ecmascript/runtime/js_regexp.h" +#include "plugins/ecmascript/runtime/js_set.h" +#include "plugins/ecmascript/runtime/js_set_iterator.h" +#include "plugins/ecmascript/runtime/js_string_iterator.h" +#include "plugins/ecmascript/runtime/js_symbol.h" +#include "plugins/ecmascript/runtime/js_tagged_value-inl.h" +#include "plugins/ecmascript/runtime/js_thread.h" +#include "plugins/ecmascript/runtime/js_typed_array.h" +#include "plugins/ecmascript/runtime/js_weak_container.h" +#include "plugins/ecmascript/runtime/layout_info-inl.h" +#include "plugins/ecmascript/runtime/linked_hash_table-inl.h" +#include "plugins/ecmascript/runtime/mem/mem_manager.h" +#include "plugins/ecmascript/runtime/mem/heap-inl.h" +#include "plugins/ecmascript/runtime/mem/space.h" +#include "plugins/ecmascript/runtime/record.h" +#include "plugins/ecmascript/runtime/symbol_table-inl.h" +#include "plugins/ecmascript/runtime/template_map.h" +#include "plugins/ecmascript/runtime/interpreter/slow_runtime_stub.h" + +namespace panda::ecmascript { +using Error = builtins::BuiltinsError; +using RangeError = builtins::BuiltinsRangeError; +using ReferenceError = builtins::BuiltinsReferenceError; +using TypeError = builtins::BuiltinsTypeError; +using URIError = builtins::BuiltinsURIError; +using SyntaxError = builtins::BuiltinsSyntaxError; +using EvalError = builtins::BuiltinsEvalError; +using ErrorType = base::ErrorType; +using ErrorHelper = base::ErrorHelper; + +ObjectFactory::ObjectFactory(JSThread *thread, Heap *heap) + : thread_(thread), heapHelper_(heap), vm_(thread->GetEcmaVM()), heap_(heap) +{ +} + +JSHandle ObjectFactory::NewEcmaDynClassClass(JSHClass *hclass, uint32_t size, JSType type) +{ + NewObjectHook(); + uint32_t classSize = JSHClass::SIZE; + auto *newClass = static_cast(heapHelper_.AllocateDynClassClass(hclass, classSize)); + newClass->Initialize(thread_, size, type, 0, HClass::HCLASS); + + return JSHandle(thread_, newClass); +} + +JSHandle ObjectFactory::NewEcmaDynClass(JSHClass *hclass, uint32_t size, JSType type, uint32_t flags, + uint32_t inlinedProps) +{ + NewObjectHook(); + uint32_t classSize = JSHClass::SIZE; + auto *newClass = static_cast(heapHelper_.AllocateNonMovableOrHugeObject(hclass, classSize)); + newClass->Initialize(thread_, size, type, inlinedProps, flags); + + return JSHandle(thread_, newClass); +} + +JSHandle ObjectFactory::NewEcmaDynClass(uint32_t size, JSType type, uint32_t inlinedProps) +{ + return NewEcmaDynClass(hclassClass_, size, type, 0, inlinedProps); +} + +void ObjectFactory::ObtainRootClass([[maybe_unused]] const JSHandle &globalEnv) +{ + const GlobalEnvConstants *globalConst = thread_->GlobalConstants(); + hclassClass_ = JSHClass::Cast(globalConst->GetHClassClass().GetTaggedObject()); + stringClass_ = JSHClass::Cast(globalConst->GetStringClass().GetTaggedObject()); + arrayClass_ = JSHClass::Cast(globalConst->GetArrayClass().GetTaggedObject()); + dictionaryClass_ = JSHClass::Cast(globalConst->GetDictionaryClass().GetTaggedObject()); + jsNativePointerClass_ = JSHClass::Cast(globalConst->GetJSNativePointerClass().GetTaggedObject()); + freeObjectWithNoneFieldClass_ = JSHClass::Cast(globalConst->GetFreeObjectWithNoneFieldClass().GetTaggedObject()); + freeObjectWithOneFieldClass_ = JSHClass::Cast(globalConst->GetFreeObjectWithOneFieldClass().GetTaggedObject()); + freeObjectWithTwoFieldClass_ = JSHClass::Cast(globalConst->GetFreeObjectWithTwoFieldClass().GetTaggedObject()); + + completionRecordClass_ = JSHClass::Cast(globalConst->GetCompletionRecordClass().GetTaggedObject()); + generatorContextClass_ = JSHClass::Cast(globalConst->GetGeneratorContextClass().GetTaggedObject()); + programClass_ = JSHClass::Cast(globalConst->GetProgramClass().GetTaggedObject()); + ecmaModuleClass_ = JSHClass::Cast(globalConst->GetEcmaModuleClass().GetTaggedObject()); + envClass_ = JSHClass::Cast(globalConst->GetEnvClass().GetTaggedObject()); + symbolClass_ = JSHClass::Cast(globalConst->GetSymbolClass().GetTaggedObject()); + accessorDataClass_ = JSHClass::Cast(globalConst->GetAccessorDataClass().GetTaggedObject()); + internalAccessorClass_ = JSHClass::Cast(globalConst->GetInternalAccessorClass().GetTaggedObject()); + capabilityRecordClass_ = JSHClass::Cast(globalConst->GetCapabilityRecordClass().GetTaggedObject()); + reactionsRecordClass_ = JSHClass::Cast(globalConst->GetReactionsRecordClass().GetTaggedObject()); + promiseIteratorRecordClass_ = JSHClass::Cast(globalConst->GetPromiseIteratorRecordClass().GetTaggedObject()); + microJobQueueClass_ = JSHClass::Cast(globalConst->GetMicroJobQueueClass().GetTaggedObject()); + pendingJobClass_ = JSHClass::Cast(globalConst->GetPendingJobClass().GetTaggedObject()); + jsProxyOrdinaryClass_ = JSHClass::Cast(globalConst->GetJSProxyOrdinaryClass().GetTaggedObject()); + jsProxyCallableClass_ = JSHClass::Cast(globalConst->GetJSProxyCallableClass().GetTaggedObject()); + jsProxyConstructClass_ = JSHClass::Cast(globalConst->GetJSProxyConstructClass().GetTaggedObject()); + objectWrapperClass_ = JSHClass::Cast(globalConst->GetObjectWrapperClass().GetTaggedObject()); + PropertyBoxClass_ = JSHClass::Cast(globalConst->GetPropertyBoxClass().GetTaggedObject()); + protoChangeMarkerClass_ = JSHClass::Cast(globalConst->GetProtoChangeMarkerClass().GetTaggedObject()); + protoChangeDetailsClass_ = JSHClass::Cast(globalConst->GetProtoChangeDetailsClass().GetTaggedObject()); + promiseRecordClass_ = JSHClass::Cast(globalConst->GetPromiseRecordClass().GetTaggedObject()); + promiseResolvingFunctionsRecord_ = + JSHClass::Cast(globalConst->GetPromiseResolvingFunctionsRecordClass().GetTaggedObject()); + transitionHandlerClass_ = JSHClass::Cast(globalConst->GetTransitionHandlerClass().GetTaggedObject()); + prototypeHandlerClass_ = JSHClass::Cast(globalConst->GetPrototypeHandlerClass().GetTaggedObject()); + functionExtraInfo_ = JSHClass::Cast(globalConst->GetFunctionExtraInfoClass().GetTaggedObject()); + jsRealmClass_ = JSHClass::Cast(globalConst->GetJSRealmClass().GetTaggedObject()); + machineCodeClass_ = JSHClass::Cast(globalConst->GetMachineCodeClass().GetTaggedObject()); + classInfoExtractorHClass_ = JSHClass::Cast(globalConst->GetClassInfoExtractorHClass().GetTaggedObject()); + + linkedHashMapClass_ = JSHClass::Cast(globalConst->GetLinkedHashMapClass().GetTaggedObject()); + linkedHashSetClass_ = JSHClass::Cast(globalConst->GetLinkedHashSetClass().GetTaggedObject()); + weakLinkedHashMapClass_ = JSHClass::Cast(globalConst->GetWeakLinkedHashMapClass().GetTaggedObject()); + weakLinkedHashSetClass_ = JSHClass::Cast(globalConst->GetWeakLinkedHashSetClass().GetTaggedObject()); +} + +void ObjectFactory::InitObjectFields(TaggedObject *object) +{ + auto *klass = object->GetClass(); + auto objBodySize = klass->GetObjectSize() - TaggedObject::TaggedObjectSize(); + ASSERT(objBodySize % JSTaggedValue::TaggedTypeSize() == 0); + int numOfFields = static_cast(objBodySize / JSTaggedValue::TaggedTypeSize()); + for (int i = 0; i < numOfFields; i++) { + size_t field_offset = TaggedObject::TaggedObjectSize() + i * JSTaggedValue::TaggedTypeSize(); + // Without barrier because 'object' is a just allocated object and it doesn't contain references. + ObjectAccessor::SetDynValueWithoutBarrier(reinterpret_cast(object), field_offset, + JSTaggedValue::Undefined().GetRawData()); + } +} + +void ObjectFactory::NewJSArrayBufferData(const JSHandle &array, int32_t length) +{ + if (length == 0) { + return; + } + + JSTaggedValue data = array->GetArrayBufferData(); + if (data != JSTaggedValue::Undefined()) { + auto *pointer = JSNativePointer::Cast(data.GetTaggedObject()); + auto newData = vm_->GetRegionFactory()->AllocateBuffer(length * sizeof(uint8_t)); + if (memset_s(newData, length, 0, length) != EOK) { + LOG_ECMA(FATAL) << "memset_s failed"; + UNREACHABLE(); + } + pointer->ResetExternalPointer(newData); + return; + } + + auto newData = vm_->GetRegionFactory()->AllocateBuffer(length * sizeof(uint8_t)); + if (memset_s(newData, length, 0, length) != EOK) { + LOG_ECMA(FATAL) << "memset_s failed"; + UNREACHABLE(); + } + JSHandle pointer = + NewJSNativePointer(newData, RegionFactory::FreeBufferFunc, vm_->GetRegionFactory()); + array->SetArrayBufferData(thread_, pointer.GetTaggedValue()); + vm_->PushToArrayDataList(*pointer); +} + +JSHandle ObjectFactory::NewJSArrayBuffer(int32_t length) +{ + JSHandle env = vm_->GetGlobalEnv(); + + JSHandle constructor(env->GetArrayBufferFunction()); + JSHandle newTarget(constructor); + JSHandle arrayBuffer(NewJSObjectByConstructor(constructor, newTarget)); + arrayBuffer->SetArrayBufferByteLength(thread_, JSTaggedValue(length)); + if (length > 0) { + auto newData = vm_->GetRegionFactory()->AllocateBuffer(length); + if (memset_s(newData, length, 0, length) != EOK) { + LOG_ECMA(FATAL) << "memset_s failed"; + UNREACHABLE(); + } + JSHandle pointer = + NewJSNativePointer(newData, RegionFactory::FreeBufferFunc, vm_->GetRegionFactory()); + arrayBuffer->SetArrayBufferData(thread_, pointer.GetTaggedValue()); + arrayBuffer->SetShared(thread_, JSTaggedValue::False()); + vm_->PushToArrayDataList(*pointer); + } + return arrayBuffer; +} + +JSHandle ObjectFactory::NewJSArrayBuffer(void *buffer, int32_t length, const DeleteEntryPoint &deleter, + void *data, bool share) +{ + JSHandle env = vm_->GetGlobalEnv(); + + JSHandle constructor(env->GetArrayBufferFunction()); + JSHandle newTarget(constructor); + JSHandle arrayBuffer(NewJSObjectByConstructor(constructor, newTarget)); + length = buffer == nullptr ? 0 : length; + arrayBuffer->SetArrayBufferByteLength(thread_, JSTaggedValue(length)); + if (length > 0) { + JSHandle pointer = NewJSNativePointer(buffer, deleter, data); + arrayBuffer->SetArrayBufferData(thread_, pointer.GetTaggedValue()); + arrayBuffer->SetShared(thread_, JSTaggedValue(share)); + vm_->PushToArrayDataList(*pointer); + } + return arrayBuffer; +} + +JSHandle ObjectFactory::NewJSDataView(JSHandle buffer, int32_t offset, int32_t length) +{ + JSTaggedValue arrayLength = buffer->GetArrayBufferByteLength(); + if (!arrayLength.IsNumber()) { + THROW_TYPE_ERROR_AND_RETURN(thread_, "ArrayBuffer length error", + JSHandle(thread_, JSTaggedValue::Undefined())); + } + if (offset + length > arrayLength.GetNumber()) { + THROW_TYPE_ERROR_AND_RETURN(thread_, "offset or length error", + JSHandle(thread_, JSTaggedValue::Undefined())); + } + JSHandle env = vm_->GetGlobalEnv(); + + JSHandle constructor(env->GetDataViewFunction()); + JSHandle newTarget(constructor); + JSHandle arrayBuffer(NewJSObjectByConstructor(constructor, newTarget)); + arrayBuffer->SetDataView(thread_, JSTaggedValue::True()); + arrayBuffer->SetViewedArrayBuffer(thread_, buffer.GetTaggedValue()); + arrayBuffer->SetByteLength(thread_, JSTaggedValue(length)); + arrayBuffer->SetByteOffset(thread_, JSTaggedValue(offset)); + return arrayBuffer; +} + +void ObjectFactory::NewJSRegExpByteCodeData(const JSHandle ®exp, void *buffer, size_t size) +{ + if (buffer == nullptr) { + return; + } + + auto newBuffer = vm_->GetRegionFactory()->AllocateBuffer(size); + if (memcpy_s(newBuffer, size, buffer, size) != EOK) { + LOG_ECMA(FATAL) << "memcpy_s failed"; + UNREACHABLE(); + } + JSTaggedValue data = regexp->GetByteCodeBuffer(); + if (data != JSTaggedValue::Undefined()) { + JSNativePointer *native = JSNativePointer::Cast(data.GetTaggedObject()); + native->ResetExternalPointer(newBuffer); + return; + } + JSHandle pointer = + NewJSNativePointer(newBuffer, RegionFactory::FreeBufferFunc, vm_->GetRegionFactory()); + regexp->SetByteCodeBuffer(thread_, pointer.GetTaggedValue()); + regexp->SetLength(thread_, JSTaggedValue(static_cast(size))); + + // push uint8_t* to ecma array_data_list + vm_->PushToArrayDataList(*pointer); +} + +JSHandle ObjectFactory::NewEcmaDynClass(uint32_t size, JSType type, const JSHandle &prototype, + uint32_t flags) +{ + JSHandle newClass = + NewEcmaDynClass(hclassClass_, size, type, flags, JSHClass::DEFAULT_CAPACITY_OF_IN_OBJECTS); + newClass->SetPrototype(thread_, prototype.GetTaggedValue()); + return newClass; +} + +JSHandle ObjectFactory::NewJSObject(const JSHandle &jshclass) +{ + JSHandle obj(thread_, JSObject::Cast(NewDynObject(jshclass))); + JSHandle emptyArray = EmptyArray(); + obj->InitializeHash(); + obj->SetElements(thread_, emptyArray, SKIP_BARRIER); + obj->SetProperties(thread_, emptyArray, SKIP_BARRIER); + return obj; +} + +JSHandle ObjectFactory::CloneProperties(const JSHandle &old) +{ + uint32_t newLength = old->GetLength(); + if (newLength == 0) { + return EmptyArray(); + } + NewObjectHook(); + auto klass = old->GetClass(); + size_t size = TaggedArray::ComputeSize(JSTaggedValue::TaggedTypeSize(), newLength); + auto header = heapHelper_.AllocateYoungGenerationOrHugeObject(klass, size); + JSHandle newArray(thread_, header); + newArray->SetLength(newLength); + + for (uint32_t i = 0; i < newLength; i++) { + JSTaggedValue value = old->Get(i); + newArray->Set(thread_, i, value); + } + return newArray; +} + +JSHandle ObjectFactory::CloneObjectLiteral(JSHandle object) +{ + NewObjectHook(); + auto klass = JSHandle(thread_, object->GetClass()); + + JSHandle cloneObject = NewJSObject(klass); + + JSHandle elements(thread_, object->GetElements()); + auto newElements = CloneProperties(elements); + cloneObject->SetElements(thread_, newElements.GetTaggedValue()); + + JSHandle properties(thread_, object->GetProperties()); + auto newProperties = CloneProperties(properties); + cloneObject->SetProperties(thread_, newProperties.GetTaggedValue()); + + for (uint32_t i = 0; i < klass->GetInlinedProperties(); i++) { + cloneObject->SetPropertyInlinedProps(thread_, i, object->GetPropertyInlinedProps(i)); + } + return cloneObject; +} + +JSHandle ObjectFactory::CloneArrayLiteral(JSHandle object) +{ + NewObjectHook(); + auto klass = JSHandle(thread_, object->GetClass()); + + JSHandle cloneObject(NewJSObject(klass)); + cloneObject->SetArrayLength(thread_, object->GetArrayLength()); + + JSHandle elements(thread_, object->GetElements()); + auto newElements = CopyArray(elements, elements->GetLength(), elements->GetLength()); + cloneObject->SetElements(thread_, newElements.GetTaggedValue()); + + JSHandle properties(thread_, object->GetProperties()); + auto newProperties = CopyArray(properties, properties->GetLength(), properties->GetLength()); + cloneObject->SetProperties(thread_, newProperties.GetTaggedValue()); + + for (uint32_t i = 0; i < klass->GetInlinedProperties(); i++) { + cloneObject->SetPropertyInlinedProps(thread_, i, object->GetPropertyInlinedProps(i)); + } + return cloneObject; +} + +JSHandle ObjectFactory::CloneProperties(const JSHandle &old, + const JSHandle &env, const JSHandle &obj, + const JSHandle &constpool) +{ + uint32_t newLength = old->GetLength(); + if (newLength == 0) { + return EmptyArray(); + } + NewObjectHook(); + auto klass = old->GetClass(); + size_t size = TaggedArray::ComputeSize(JSTaggedValue::TaggedTypeSize(), newLength); + auto header = heapHelper_.AllocateYoungGenerationOrHugeObject(klass, size); + JSHandle newArray(thread_, header); + newArray->SetLength(newLength); + + for (uint32_t i = 0; i < newLength; i++) { + JSTaggedValue value = old->Get(i); + if (!value.IsJSFunction()) { + newArray->Set(thread_, i, value); + } else { + JSHandle valueHandle(thread_, value); + JSHandle newFunc = CloneJSFuction(valueHandle, valueHandle->GetFunctionKind()); + newFunc->SetLexicalEnv(thread_, env); + newFunc->SetHomeObject(thread_, obj); + newFunc->SetConstantPool(thread_, constpool); + newArray->Set(thread_, i, newFunc); + } + } + return newArray; +} + +JSHandle ObjectFactory::CloneObjectLiteral(JSHandle object, const JSHandle &env, + const JSHandle &constpool, bool canShareHClass) +{ + NewObjectHook(); + auto klass = JSHandle(thread_, object->GetClass()); + + if (!canShareHClass) { + klass = JSHClass::Clone(thread_, klass); + } + + JSHandle cloneObject = NewJSObject(klass); + + JSHandle elements(thread_, object->GetElements()); + auto newElements = CloneProperties(elements, env, cloneObject, constpool); + cloneObject->SetElements(thread_, newElements.GetTaggedValue()); + + JSHandle properties(thread_, object->GetProperties()); + auto newProperties = CloneProperties(properties, env, cloneObject, constpool); + cloneObject->SetProperties(thread_, newProperties.GetTaggedValue()); + + for (uint32_t i = 0; i < klass->GetInlinedProperties(); i++) { + JSTaggedValue value = object->GetPropertyInlinedProps(i); + if (!value.IsJSFunction()) { + cloneObject->SetPropertyInlinedProps(thread_, i, value); + } else { + JSHandle valueHandle(thread_, value); + JSHandle newFunc = CloneJSFuction(valueHandle, valueHandle->GetFunctionKind()); + newFunc->SetLexicalEnv(thread_, env); + newFunc->SetHomeObject(thread_, cloneObject); + newFunc->SetConstantPool(thread_, constpool); + cloneObject->SetPropertyInlinedProps(thread_, i, newFunc.GetTaggedValue()); + } + } + return cloneObject; +} + +JSHandle ObjectFactory::CloneJSFuction(JSHandle obj, FunctionKind kind) +{ + JSHandle env = vm_->GetGlobalEnv(); + JSHandle jshclass(thread_, obj->GetJSHClass()); + JSHandle cloneFunc = NewJSFunctionByDynClass(obj->GetCallTarget(), jshclass, kind); + if (kind == FunctionKind::GENERATOR_FUNCTION) { + JSHandle objFun = env->GetObjectFunction(); + JSHandle initialGeneratorFuncPrototype = + NewJSObjectByConstructor(JSHandle(objFun), objFun); + JSObject::SetPrototype(thread_, initialGeneratorFuncPrototype, env->GetGeneratorPrototype()); + cloneFunc->SetProtoOrDynClass(thread_, initialGeneratorFuncPrototype); + } else if (kind == FunctionKind::ASYNC_GENERATOR_FUNCTION) { + JSHandle objFun = env->GetObjectFunction(); + JSHandle initialAsyncGeneratorFuncPrototype = + NewJSObjectByConstructor(JSHandle(objFun), objFun); + JSObject::SetPrototype(thread_, initialAsyncGeneratorFuncPrototype, env->GetAsyncGeneratorPrototype()); + cloneFunc->SetProtoOrDynClass(thread_, initialAsyncGeneratorFuncPrototype); + } + + JSTaggedValue length = obj->GetPropertyInlinedProps(JSFunction::LENGTH_INLINE_PROPERTY_INDEX); + cloneFunc->SetPropertyInlinedProps(thread_, JSFunction::LENGTH_INLINE_PROPERTY_INDEX, length); + return cloneFunc; +} + +JSHandle ObjectFactory::CloneClassCtor(JSHandle ctor, const JSHandle &lexenv, + bool canShareHClass) +{ + NewObjectHook(); + JSHandle constpool(thread_, ctor->GetConstantPool()); + JSHandle hclass(thread_, ctor->GetClass()); + + if (!canShareHClass) { + hclass = JSHClass::Clone(thread_, hclass); + } + + FunctionKind kind = ctor->GetFunctionKind(); + ASSERT_PRINT(kind == FunctionKind::CLASS_CONSTRUCTOR || kind == FunctionKind::DERIVED_CONSTRUCTOR, + "cloned function is not class"); + JSHandle cloneCtor = NewJSFunctionByDynClass(ctor->GetCallTarget(), hclass, kind); + + for (uint32_t i = 0; i < hclass->GetInlinedProperties(); i++) { + JSTaggedValue value = ctor->GetPropertyInlinedProps(i); + if (!value.IsJSFunction()) { + cloneCtor->SetPropertyInlinedProps(thread_, i, value); + } else { + JSHandle valueHandle(thread_, value); + JSHandle newFunc = CloneJSFuction(valueHandle, valueHandle->GetFunctionKind()); + newFunc->SetLexicalEnv(thread_, lexenv); + newFunc->SetHomeObject(thread_, cloneCtor); + newFunc->SetConstantPool(thread_, constpool); + cloneCtor->SetPropertyInlinedProps(thread_, i, newFunc.GetTaggedValue()); + } + } + + JSHandle elements(thread_, ctor->GetElements()); + auto newElements = CloneProperties(elements, lexenv, JSHandle(cloneCtor), constpool); + cloneCtor->SetElements(thread_, newElements.GetTaggedValue()); + + JSHandle properties(thread_, ctor->GetProperties()); + auto newProperties = CloneProperties(properties, lexenv, JSHandle(cloneCtor), constpool); + cloneCtor->SetProperties(thread_, newProperties.GetTaggedValue()); + + cloneCtor->SetConstantPool(thread_, constpool); + + return cloneCtor; +} + +JSHandle ObjectFactory::NewNonMovableJSObject(const JSHandle &jshclass) +{ + JSHandle obj(thread_, JSObject::Cast(NewNonMovableDynObject(jshclass, jshclass->GetInlinedProperties()))); + obj->SetElements(thread_, EmptyArray(), SKIP_BARRIER); + obj->SetProperties(thread_, EmptyArray(), SKIP_BARRIER); + return obj; +} + +JSHandle ObjectFactory::NewJSPrimitiveRef(const JSHandle &dynKlass, + const JSHandle &object) +{ + JSHandle obj = JSHandle::Cast(NewJSObject(dynKlass)); + obj->SetValue(thread_, object); + return obj; +} + +JSHandle ObjectFactory::NewJSArray() +{ + JSHandle env = vm_->GetGlobalEnv(); + JSHandle function = env->GetArrayFunction(); + + return JSHandle(NewJSObjectByConstructor(JSHandle(function), function)); +} + +JSHandle ObjectFactory::NewJSForinIterator(const JSHandle &obj) +{ + JSHandle env = vm_->GetGlobalEnv(); + JSHandle dynclass(env->GetForinIteratorClass()); + + JSHandle it = JSHandle::Cast(NewJSObject(dynclass)); + it->SetObject(thread_, obj); + it->SetWasVisited(thread_, JSTaggedValue::False()); + it->SetVisitedKeys(thread_, env->GetEmptyTaggedQueue()); + it->SetRemainingKeys(thread_, env->GetEmptyTaggedQueue()); + return it; +} + +JSHandle ObjectFactory::CreateJSRegExpInstanceClass(JSHandle proto) +{ + const GlobalEnvConstants *globalConst = thread_->GlobalConstants(); + JSHandle regexpDynclass = NewEcmaDynClass(JSRegExp::SIZE, JSType::JS_REG_EXP, proto); + + uint32_t fieldOrder = 0; + JSHandle layoutInfoHandle = CreateLayoutInfo(1); + { + PropertyAttributes attributes = PropertyAttributes::Default(true, false, false); + attributes.SetIsInlinedProps(true); + attributes.SetRepresentation(Representation::MIXED); + attributes.SetOffset(fieldOrder++); + layoutInfoHandle->AddKey(thread_, 0, globalConst->GetLastIndexString(), attributes); + } + + { + regexpDynclass->SetLayout(thread_, layoutInfoHandle); + regexpDynclass->SetNumberOfProps(fieldOrder); + } + + return regexpDynclass; +} + +JSHandle ObjectFactory::CreateJSArrayInstanceClass(JSHandle proto) +{ + const GlobalEnvConstants *globalConst = thread_->GlobalConstants(); + JSHandle arrayDynclass = NewEcmaDynClass(JSArray::SIZE, JSType::JS_ARRAY, proto); + + uint32_t fieldOrder = 0; + ASSERT(JSArray::LENGTH_INLINE_PROPERTY_INDEX == fieldOrder); + JSHandle layoutInfoHandle = CreateLayoutInfo(1); + { + PropertyAttributes attributes = PropertyAttributes::DefaultAccessor(true, false, false); + attributes.SetIsInlinedProps(true); + attributes.SetRepresentation(Representation::MIXED); + attributes.SetOffset(fieldOrder++); + layoutInfoHandle->AddKey(thread_, 0, globalConst->GetLengthString(), attributes); + } + + { + arrayDynclass->SetLayout(thread_, layoutInfoHandle); + arrayDynclass->SetNumberOfProps(fieldOrder); + } + arrayDynclass->SetIsStableElements(true); + arrayDynclass->SetHasConstructor(false); + + return arrayDynclass; +} + +JSHandle ObjectFactory::CreateJSArguments() +{ + JSHandle env = thread_->GetEcmaVM()->GetGlobalEnv(); + const GlobalEnvConstants *globalConst = thread_->GlobalConstants(); + JSHandle proto = env->GetObjectFunctionPrototype(); + + JSHandle argumentsDynclass = NewEcmaDynClass(JSArguments::SIZE, JSType::JS_ARGUMENTS, proto); + + uint32_t fieldOrder = 0; + ASSERT(JSArguments::LENGTH_INLINE_PROPERTY_INDEX == fieldOrder); + JSHandle layoutInfoHandle = CreateLayoutInfo(JSArguments::LENGTH_OF_INLINE_PROPERTIES); + { + PropertyAttributes attributes = PropertyAttributes::Default(true, false, true); + attributes.SetIsInlinedProps(true); + attributes.SetRepresentation(Representation::MIXED); + attributes.SetOffset(fieldOrder++); + layoutInfoHandle->AddKey(thread_, JSArguments::LENGTH_INLINE_PROPERTY_INDEX, globalConst->GetLengthString(), + attributes); + } + + ASSERT(JSArguments::ITERATOR_INLINE_PROPERTY_INDEX == fieldOrder); + { + PropertyAttributes attributes = PropertyAttributes::Default(true, false, true); + attributes.SetIsInlinedProps(true); + attributes.SetRepresentation(Representation::MIXED); + attributes.SetOffset(fieldOrder++); + layoutInfoHandle->AddKey(thread_, JSArguments::ITERATOR_INLINE_PROPERTY_INDEX, + env->GetIteratorSymbol().GetTaggedValue(), attributes); + } + + { + ASSERT(JSArguments::CALLEE_INLINE_PROPERTY_INDEX == fieldOrder); + PropertyAttributes attributes = PropertyAttributes::Default(false, false, false); + attributes.SetIsInlinedProps(true); + attributes.SetIsAccessor(true); + attributes.SetRepresentation(Representation::MIXED); + attributes.SetOffset(fieldOrder++); + layoutInfoHandle->AddKey(thread_, JSArguments::CALLEE_INLINE_PROPERTY_INDEX, + thread_->GlobalConstants()->GetHandledCalleeString().GetTaggedValue(), attributes); + } + + { + argumentsDynclass->SetLayout(thread_, layoutInfoHandle); + argumentsDynclass->SetNumberOfProps(fieldOrder); + } + argumentsDynclass->SetIsStableElements(true); + return argumentsDynclass; +} + +JSHandle ObjectFactory::NewJSArguments() +{ + JSHandle env = vm_->GetGlobalEnv(); + JSHandle dynclass = JSHandle::Cast(env->GetArgumentsClass()); + JSHandle obj = JSHandle::Cast(NewJSObject(dynclass)); + return obj; +} + +JSHandle ObjectFactory::GetJSError(const ErrorType &errorType, const char *data) +{ + ASSERT_PRINT(errorType == ErrorType::ERROR || errorType == ErrorType::EVAL_ERROR || + errorType == ErrorType::RANGE_ERROR || errorType == ErrorType::REFERENCE_ERROR || + errorType == ErrorType::SYNTAX_ERROR || errorType == ErrorType::TYPE_ERROR || + errorType == ErrorType::URI_ERROR, + "The error type is not in the valid range."); + if (data != nullptr) { + JSHandle handleMsg = NewFromString(data); + return NewJSError(errorType, handleMsg); + } + JSHandle emptyString(thread_->GlobalConstants()->GetHandledEmptyString()); + return NewJSError(errorType, emptyString); +} + +JSHandle ObjectFactory::NewJSError(const ErrorType &errorType, const JSHandle &message) +{ + JSHandle env = vm_->GetGlobalEnv(); + const GlobalEnvConstants *globalConst = thread_->GlobalConstants(); + JSHandle nativeConstructor; + switch (errorType) { + case ErrorType::RANGE_ERROR: + nativeConstructor = env->GetRangeErrorFunction(); + break; + case ErrorType::EVAL_ERROR: + nativeConstructor = env->GetEvalErrorFunction(); + break; + case ErrorType::REFERENCE_ERROR: + nativeConstructor = env->GetReferenceErrorFunction(); + break; + case ErrorType::TYPE_ERROR: + nativeConstructor = env->GetTypeErrorFunction(); + break; + case ErrorType::URI_ERROR: + nativeConstructor = env->GetURIErrorFunction(); + break; + case ErrorType::SYNTAX_ERROR: + nativeConstructor = env->GetSyntaxErrorFunction(); + break; + default: + nativeConstructor = env->GetErrorFunction(); + break; + } + JSHandle nativeFunc = JSHandle::Cast(nativeConstructor); + JSHandle nativePrototype(thread_, nativeFunc->GetFunctionPrototype()); + JSHandle ctorKey = globalConst->GetHandledConstructorString(); + + InternalCallParams *arguments = thread_->GetInternalCallParams(); + arguments->MakeArgv(message.GetTaggedValue()); + JSTaggedValue obj = JSFunction::Invoke(thread_, nativePrototype, ctorKey, 1, arguments->GetArgv()); + JSHandle handleNativeInstanceObj(thread_, obj); + return handleNativeInstanceObj; +} + +JSHandle ObjectFactory::NewJSObjectByConstructor(const JSHandle &constructor, + const JSHandle &newTarget) +{ + JSHandle jshclass; + if (!constructor->HasFunctionPrototype() || + (constructor->GetProtoOrDynClass().IsHeapObject() && constructor->GetFunctionPrototype().IsECMAObject())) { + jshclass = JSFunction::GetInstanceJSHClass(thread_, constructor, newTarget); + } else { + JSHandle env = vm_->GetGlobalEnv(); + jshclass = JSFunction::GetInstanceJSHClass(thread_, JSHandle(env->GetObjectFunction()), newTarget); + } + // Check this exception elsewhere + RETURN_HANDLE_IF_ABRUPT_COMPLETION(JSObject, thread_); + + JSHandle obj = NewJSObject(jshclass); + { + JSType type = jshclass->GetObjectType(); + switch (type) { + case JSType::JS_OBJECT: + case JSType::JS_ERROR: + case JSType::JS_EVAL_ERROR: + case JSType::JS_RANGE_ERROR: + case JSType::JS_REFERENCE_ERROR: + case JSType::JS_TYPE_ERROR: + case JSType::JS_URI_ERROR: + case JSType::JS_SYNTAX_ERROR: + case JSType::JS_ITERATOR: + case JSType::JS_INTL: + case JSType::JS_LOCALE: + case JSType::JS_DATE_TIME_FORMAT: + case JSType::JS_NUMBER_FORMAT: + case JSType::JS_RELATIVE_TIME_FORMAT: + case JSType::JS_COLLATOR: + case JSType::JS_PLURAL_RULES: + break; + case JSType::JS_ARRAY: { + JSArray::Cast(*obj)->SetLength(thread_, JSTaggedValue(0)); + auto accessor = thread_->GlobalConstants()->GetArrayLengthAccessor(); + JSArray::Cast(*obj)->SetPropertyInlinedProps(thread_, JSArray::LENGTH_INLINE_PROPERTY_INDEX, accessor); + break; + } + case JSType::JS_DATE: + JSDate::Cast(*obj)->SetTimeValue(thread_, JSTaggedValue(0.0)); + JSDate::Cast(*obj)->SetLocalOffset(thread_, JSTaggedValue(JSDate::MAX_DOUBLE)); + break; + case JSType::JS_INT8_ARRAY: + case JSType::JS_UINT8_ARRAY: + case JSType::JS_UINT8_CLAMPED_ARRAY: + case JSType::JS_INT16_ARRAY: + case JSType::JS_UINT16_ARRAY: + case JSType::JS_INT32_ARRAY: + case JSType::JS_UINT32_ARRAY: + case JSType::JS_FLOAT32_ARRAY: + case JSType::JS_FLOAT64_ARRAY: + JSTypedArray::Cast(*obj)->SetViewedArrayBuffer(thread_, JSTaggedValue::Undefined()); + JSTypedArray::Cast(*obj)->SetTypedArrayName(thread_, JSTaggedValue::Undefined()); + JSTypedArray::Cast(*obj)->SetByteLength(thread_, JSTaggedValue(0)); + JSTypedArray::Cast(*obj)->SetByteOffset(thread_, JSTaggedValue(0)); + JSTypedArray::Cast(*obj)->SetArrayLength(thread_, JSTaggedValue(0)); + break; + case JSType::JS_REG_EXP: + JSRegExp::Cast(*obj)->SetByteCodeBuffer(thread_, JSTaggedValue::Undefined()); + JSRegExp::Cast(*obj)->SetOriginalSource(thread_, JSTaggedValue::Undefined()); + JSRegExp::Cast(*obj)->SetOriginalFlags(thread_, JSTaggedValue(0)); + JSRegExp::Cast(*obj)->SetLength(thread_, JSTaggedValue(0)); + break; + case JSType::JS_PRIMITIVE_REF: + JSPrimitiveRef::Cast(*obj)->SetValue(thread_, JSTaggedValue::Undefined()); + break; + case JSType::JS_SET: + JSSet::Cast(*obj)->SetLinkedSet(thread_, JSTaggedValue::Undefined()); + break; + case JSType::JS_MAP: + JSMap::Cast(*obj)->SetLinkedMap(thread_, JSTaggedValue::Undefined()); + break; + case JSType::JS_WEAK_MAP: + JSWeakMap::Cast(*obj)->SetLinkedMap(thread_, JSTaggedValue::Undefined()); + break; + case JSType::JS_WEAK_SET: + JSWeakSet::Cast(*obj)->SetLinkedSet(thread_, JSTaggedValue::Undefined()); + break; + case JSType::JS_ASYNC_GENERATOR_OBJECT: + JSAsyncGeneratorObject::Cast(*obj)->SetAsyncGeneratorQueue(thread_, + GetEmptyTaggedQueue().GetTaggedValue()); + [[fallthrough]]; + case JSType::JS_ASYNC_FUNC_OBJECT: + JSAsyncFuncObject::Cast(*obj)->SetPromise(thread_, JSTaggedValue::Undefined()); + [[fallthrough]]; + case JSType::JS_GENERATOR_OBJECT: + JSGeneratorObject::Cast(*obj)->SetGeneratorState(thread_, JSTaggedValue::Undefined()); + JSGeneratorObject::Cast(*obj)->SetGeneratorContext(thread_, JSTaggedValue::Undefined()); + JSGeneratorObject::Cast(*obj)->SetResumeResult(thread_, JSTaggedValue::Undefined()); + JSGeneratorObject::Cast(*obj)->SetResumeMode(thread_, JSTaggedValue::Undefined()); + break; + case JSType::JS_STRING_ITERATOR: + JSStringIterator::Cast(*obj)->SetStringIteratorNextIndex(thread_, JSTaggedValue(0)); + JSStringIterator::Cast(*obj)->SetIteratedString(thread_, JSTaggedValue::Undefined()); + break; + case JSType::JS_ARRAY_BUFFER: + JSArrayBuffer::Cast(*obj)->SetArrayBufferByteLength(thread_, JSTaggedValue(0)); + JSArrayBuffer::Cast(*obj)->SetArrayBufferData(thread_, JSTaggedValue::Undefined()); + break; + case JSType::JS_PROMISE: + JSPromise::Cast(*obj)->SetPromiseState(thread_, + JSTaggedValue(static_cast(PromiseStatus::PENDING))); + JSPromise::Cast(*obj)->SetPromiseResult(thread_, JSTaggedValue::Undefined()); + JSPromise::Cast(*obj)->SetPromiseRejectReactions(thread_, GetEmptyTaggedQueue().GetTaggedValue()); + JSPromise::Cast(*obj)->SetPromiseFulfillReactions(thread_, GetEmptyTaggedQueue().GetTaggedValue()); + + JSPromise::Cast(*obj)->SetPromiseIsHandled(thread_, JSTaggedValue::Undefined()); + break; + case JSType::JS_DATA_VIEW: + JSDataView::Cast(*obj)->SetDataView(thread_, JSTaggedValue(false)); + JSDataView::Cast(*obj)->SetViewedArrayBuffer(thread_, JSTaggedValue::Undefined()); + JSDataView::Cast(*obj)->SetByteLength(thread_, JSTaggedValue(0)); + JSDataView::Cast(*obj)->SetByteOffset(thread_, JSTaggedValue(0)); + break; + case JSType::JS_ARRAY_LIST: + JSArrayList::Cast(*obj)->SetLength(thread_, JSTaggedValue(0)); + break; + case JSType::JS_FUNCTION: + case JSType::JS_GENERATOR_FUNCTION: + case JSType::JS_FORIN_ITERATOR: + case JSType::JS_MAP_ITERATOR: + case JSType::JS_SET_ITERATOR: + case JSType::JS_ARRAY_ITERATOR: + default: + UNREACHABLE(); + } + } + return obj; +} + +FreeObject *ObjectFactory::FillFreeObject(uintptr_t address, size_t size, [[maybe_unused]] RemoveSlots removeSlots) +{ + FreeObject *object = nullptr; + if (size >= FreeObject::SIZE_OFFSET && size < FreeObject::SIZE) { + object = reinterpret_cast(address); + object->SetClassWithoutBarrier(freeObjectWithOneFieldClass_); + object->SetNext(nullptr); + } else if (size >= FreeObject::SIZE) { + object = reinterpret_cast(address); + object->SetClassWithoutBarrier(freeObjectWithTwoFieldClass_); + object->SetAvailable(size); + object->SetNext(nullptr); + } else if (size == FreeObject::NEXT_OFFSET) { + object = reinterpret_cast(address); + object->SetClassWithoutBarrier(freeObjectWithNoneFieldClass_); + } else { + LOG_ECMA(DEBUG) << "Fill free object size is smaller"; + } + + return object; +} + +TaggedObject *ObjectFactory::NewDynObject(const JSHandle &dynclass) +{ + NewObjectHook(); + TaggedObject *header = heapHelper_.AllocateYoungGenerationOrHugeObject(*dynclass); + uint32_t inobjPropCount = dynclass->GetInlinedProperties(); + if (inobjPropCount > 0) { + InitializeExtraProperties(dynclass, header, inobjPropCount); + } + return header; +} + +TaggedObject *ObjectFactory::NewNonMovableDynObject(const JSHandle &dynclass, int inobjPropCount) +{ + NewObjectHook(); + TaggedObject *header = heapHelper_.AllocateNonMovableOrHugeObject(*dynclass); + if (inobjPropCount > 0) { + InitializeExtraProperties(dynclass, header, inobjPropCount); + } + return header; +} + +void ObjectFactory::InitializeExtraProperties(const JSHandle &dynclass, TaggedObject *obj, int inobjPropCount) +{ + ASSERT(inobjPropCount * JSTaggedValue::TaggedTypeSize() < dynclass->GetObjectSize()); + auto offset = dynclass->GetObjectSize(); + JSTaggedType initVal = JSTaggedValue::Undefined().GetRawData(); + for (int i = 0; i < inobjPropCount; ++i) { + offset -= JSTaggedValue::TaggedTypeSize(); + // Without barrier because 'object' is a just allocated object and it doesn't contain references. + ObjectAccessor::SetDynValueWithoutBarrier(obj, offset, initVal); + } +} + +JSHandle ObjectFactory::OrdinaryNewJSObjectCreate(const JSHandle &proto) +{ + JSHandle protoValue(proto); + JSHandle protoDyn = NewEcmaDynClass(JSObject::SIZE, JSType::JS_OBJECT, protoValue); + JSHandle newObj = NewJSObject(protoDyn); + newObj->GetJSHClass()->SetExtensible(true); + return newObj; +} + +JSHandle ObjectFactory::NewJSFunction(const JSHandle &env, const void *nativeFunc, + FunctionKind kind) +{ + JSMethod *target = vm_->GetMethodForNativeFunction(nativeFunc); + return NewJSFunction(env, target, kind); +} + +JSHandle ObjectFactory::NewJSFunction(const JSHandle &env, JSMethod *method, FunctionKind kind) +{ + JSHandle dynclass; + if (kind == FunctionKind::BASE_CONSTRUCTOR) { + dynclass = JSHandle::Cast(env->GetFunctionClassWithProto()); + } else if (JSFunction::IsConstructorKind(kind)) { + dynclass = JSHandle::Cast(env->GetConstructorFunctionClass()); + } else { + dynclass = JSHandle::Cast(env->GetNormalFunctionClass()); + } + + return NewJSFunctionByDynClass(method, dynclass, kind); +} + +JSHandle ObjectFactory::CreateFunctionClass(FunctionKind kind, uint32_t size, JSType type, + const JSHandle &prototype) +{ + const GlobalEnvConstants *globalConst = thread_->GlobalConstants(); + JSHandle functionClass = NewEcmaDynClass(size, type, prototype, HClass::IS_CALLABLE); + { + // FunctionKind = BASE_CONSTRUCTOR + if (JSFunction::IsConstructorKind(kind)) { + functionClass->SetConstructor(true); + } + functionClass->SetExtensible(true); + functionClass->GetHClass()->MarkFieldAsNative(JSFunctionBase::METHOD_OFFSET); + } + + uint32_t fieldOrder = 0; + ASSERT(JSFunction::LENGTH_INLINE_PROPERTY_INDEX == fieldOrder); + JSHandle layoutInfoHandle = CreateLayoutInfo(JSFunction::LENGTH_OF_INLINE_PROPERTIES); + { + PropertyAttributes attributes = PropertyAttributes::Default(false, false, true); + attributes.SetIsInlinedProps(true); + attributes.SetRepresentation(Representation::MIXED); + attributes.SetOffset(fieldOrder); + layoutInfoHandle->AddKey(thread_, fieldOrder, globalConst->GetLengthString(), attributes); + fieldOrder++; + } + + ASSERT(JSFunction::NAME_INLINE_PROPERTY_INDEX == fieldOrder); + // not set name in-object property on class which may have a name() method + if (!JSFunction::IsClassConstructor(kind)) { + PropertyAttributes attributes = PropertyAttributes::DefaultAccessor(false, false, true); + attributes.SetIsInlinedProps(true); + attributes.SetRepresentation(Representation::MIXED); + attributes.SetOffset(fieldOrder); + layoutInfoHandle->AddKey(thread_, fieldOrder, + thread_->GlobalConstants()->GetHandledNameString().GetTaggedValue(), attributes); + fieldOrder++; + } + + if (JSFunction::HasPrototype(kind) && !JSFunction::IsClassConstructor(kind)) { + ASSERT(JSFunction::PROTOTYPE_INLINE_PROPERTY_INDEX == fieldOrder); + PropertyAttributes attributes = PropertyAttributes::DefaultAccessor(true, false, false); + attributes.SetIsInlinedProps(true); + attributes.SetRepresentation(Representation::MIXED); + attributes.SetOffset(fieldOrder); + layoutInfoHandle->AddKey(thread_, fieldOrder, globalConst->GetPrototypeString(), attributes); + fieldOrder++; + } else if (JSFunction::IsClassConstructor(kind)) { + ASSERT(JSFunction::CLASS_PROTOTYPE_INLINE_PROPERTY_INDEX == fieldOrder); + PropertyAttributes attributes = PropertyAttributes::DefaultAccessor(false, false, false); + attributes.SetIsInlinedProps(true); + attributes.SetRepresentation(Representation::MIXED); + attributes.SetOffset(fieldOrder); + layoutInfoHandle->AddKey(thread_, fieldOrder, globalConst->GetPrototypeString(), attributes); + fieldOrder++; + } + + { + functionClass->SetLayout(thread_, layoutInfoHandle); + functionClass->SetNumberOfProps(fieldOrder); + } + return functionClass; +} + +JSHandle ObjectFactory::NewJSFunctionByDynClass(JSMethod *method, const JSHandle &clazz, + FunctionKind kind) +{ + JSHandle function = JSHandle::Cast(NewJSObject(clazz)); + ASSERT(clazz->IsCallable()); + ASSERT(clazz->IsExtensible()); + JSFunction::InitializeJSFunction(thread_, function, kind); + function->SetCallTarget(thread_, method); + SlowRuntimeStub::NotifyInlineCache(thread_, *function, method); + return function; +} + +JSHandle ObjectFactory::NewJSNativeErrorFunction(const JSHandle &env, const void *nativeFunc) +{ + JSMethod *target = vm_->GetMethodForNativeFunction(nativeFunc); + JSHandle dynclass = JSHandle::Cast(env->GetNativeErrorFunctionClass()); + return NewJSFunctionByDynClass(target, dynclass, FunctionKind::BUILTIN_CONSTRUCTOR); +} + +JSHandle ObjectFactory::NewSpecificTypedArrayFunction(const JSHandle &env, + const void *nativeFunc) +{ + JSMethod *target = vm_->GetMethodForNativeFunction(nativeFunc); + JSHandle dynclass = JSHandle::Cast(env->GetSpecificTypedArrayFunctionClass()); + return NewJSFunctionByDynClass(target, dynclass, FunctionKind::BUILTIN_CONSTRUCTOR); +} + +JSHandle ObjectFactory::NewJSBoundFunction(const JSHandle &target, + const JSHandle &boundThis, + const JSHandle &args) +{ + JSHandle env = vm_->GetGlobalEnv(); + JSHandle proto = env->GetFunctionPrototype(); + JSHandle dynclass = + NewEcmaDynClass(JSBoundFunction::SIZE, JSType::JS_BOUND_FUNCTION, proto, HClass::IS_CALLABLE); + + JSHandle bundleFunction = JSHandle::Cast(NewJSObject(dynclass)); + bundleFunction->SetBoundTarget(thread_, target); + bundleFunction->SetBoundThis(thread_, boundThis); + bundleFunction->SetBoundArguments(thread_, args); + dynclass->GetHClass()->MarkFieldAsNative(JSFunctionBase::METHOD_OFFSET); + if (target.GetTaggedValue().IsConstructor()) { + bundleFunction->SetConstructor(true); + } + JSMethod *method = + vm_->GetMethodForNativeFunction(reinterpret_cast(builtins::BuiltinsGlobal::CallJsBoundFunction)); + bundleFunction->SetCallTarget(thread_, method); + return bundleFunction; +} + +JSHandle ObjectFactory::NewJSIntlBoundFunction(const void *nativeFunc, int functionLength) +{ + JSHandle env = vm_->GetGlobalEnv(); + JSHandle dynclass = JSHandle::Cast(env->GetJSIntlBoundFunctionClass()); + + JSHandle intlBoundFunc = JSHandle::Cast(NewJSObject(dynclass)); + JSMethod *method = vm_->GetMethodForNativeFunction(nativeFunc); + intlBoundFunc->SetCallTarget(thread_, method); + JSHandle function = JSHandle::Cast(intlBoundFunc); + JSFunction::InitializeJSFunction(thread_, function, FunctionKind::NORMAL_FUNCTION); + JSFunction::SetFunctionLength(thread_, function, JSTaggedValue(functionLength)); + const GlobalEnvConstants *globalConst = thread_->GlobalConstants(); + JSHandle emptyString = globalConst->GetHandledEmptyString(); + JSHandle nameKey = globalConst->GetHandledNameString(); + PropertyDescriptor nameDesc(thread_, emptyString, false, false, true); + JSTaggedValue::DefinePropertyOrThrow(thread_, JSHandle::Cast(function), nameKey, nameDesc); + return intlBoundFunc; +} + +JSHandle ObjectFactory::NewJSProxyRevocFunction(const JSHandle &proxy, + const void *nativeFunc) +{ + JSHandle env = vm_->GetGlobalEnv(); + const GlobalEnvConstants *globalConst = thread_->GlobalConstants(); + JSHandle dynclass = JSHandle::Cast(env->GetProxyRevocFunctionClass()); + + JSHandle revocFunction = JSHandle::Cast(NewJSObject(dynclass)); + revocFunction->SetRevocableProxy(thread_, proxy); + + JSMethod *target = vm_->GetMethodForNativeFunction(nativeFunc); + revocFunction->SetCallTarget(thread_, target); + JSHandle function = JSHandle::Cast(revocFunction); + JSFunction::InitializeJSFunction(thread_, function, FunctionKind::NORMAL_FUNCTION); + JSFunction::SetFunctionLength(thread_, function, JSTaggedValue(0)); + JSHandle emptyString = globalConst->GetHandledEmptyString(); + JSHandle nameKey = globalConst->GetHandledNameString(); + PropertyDescriptor nameDesc(thread_, emptyString, false, false, true); + JSTaggedValue::DefinePropertyOrThrow(thread_, JSHandle::Cast(function), nameKey, nameDesc); + return revocFunction; +} + +JSHandle ObjectFactory::NewJSAsyncAwaitStatusFunction(const void *nativeFunc) +{ + JSHandle env = vm_->GetGlobalEnv(); + JSHandle dynclass = JSHandle::Cast(env->GetAsyncAwaitStatusFunctionClass()); + + JSHandle awaitFunction = + JSHandle::Cast(NewJSObject(dynclass)); + + JSFunction::InitializeJSFunction(thread_, JSHandle::Cast(awaitFunction)); + JSMethod *target = vm_->GetMethodForNativeFunction(nativeFunc); + awaitFunction->SetCallTarget(thread_, target); + return awaitFunction; +} + +JSHandle ObjectFactory::NewJSAsyncGeneratorResolveNextFunction( + const void *nativeFunc) +{ + NewObjectHook(); + JSHandle env = vm_->GetGlobalEnv(); + JSHandle dynclass = JSHandle::Cast(env->GetAsyncGeneratorResolveNextFunctionClass()); + + JSHandle resolveNextFunction = + JSHandle::Cast(NewJSObject(dynclass)); + + JSFunction::InitializeJSFunction(thread_, env, JSHandle::Cast(resolveNextFunction)); + JSMethod *target = vm_->GetMethodForNativeFunction(nativeFunc); + resolveNextFunction->SetCallTarget(thread_, target); + return resolveNextFunction; +} + +JSHandle ObjectFactory::NewJSAsyncFromSyncIteratorValueUnwrapFunction( + const void *nativeFunc) +{ + NewObjectHook(); + JSHandle env = vm_->GetGlobalEnv(); + JSHandle dynclass = JSHandle::Cast(env->GetAsyncFromSyncIteratorValueUnwrapFunctionClass()); + + JSHandle asyncFromSyncIteratorUnwrapFunction = + JSHandle::Cast(NewJSObject(dynclass)); + + JSFunction::InitializeJSFunction(thread_, env, JSHandle::Cast(asyncFromSyncIteratorUnwrapFunction)); + JSMethod *target = vm_->GetMethodForNativeFunction(nativeFunc); + asyncFromSyncIteratorUnwrapFunction->SetCallTarget(thread_, target); + return asyncFromSyncIteratorUnwrapFunction; +} + +JSHandle ObjectFactory::NewJSGeneratorFunction(JSMethod *method) +{ + JSHandle env = vm_->GetGlobalEnv(); + + JSHandle dynclass = JSHandle::Cast(env->GetGeneratorFunctionClass()); + JSHandle generatorFunc = JSHandle::Cast(NewJSObject(dynclass)); + JSFunction::InitializeJSFunction(thread_, generatorFunc, FunctionKind::GENERATOR_FUNCTION); + generatorFunc->SetCallTarget(thread_, method); + SlowRuntimeStub::NotifyInlineCache(thread_, *generatorFunc, method); + return generatorFunc; +} + +JSHandle ObjectFactory::NewJSGeneratorObject(JSHandle generatorFunction) +{ + JSHandle proto(thread_, JSHandle::Cast(generatorFunction)->GetProtoOrDynClass()); + if (!proto->IsECMAObject()) { + JSHandle realmHandle = JSObject::GetFunctionRealm(thread_, generatorFunction); + proto = realmHandle->GetGeneratorPrototype(); + } + JSHandle dynclass = NewEcmaDynClass(JSGeneratorObject::SIZE, JSType::JS_GENERATOR_OBJECT, proto); + JSHandle generatorObject = JSHandle::Cast(NewJSObject(dynclass)); + return generatorObject; +} + +JSHandle ObjectFactory::NewJSAsyncGeneratorFunction(JSMethod *method) +{ + NewObjectHook(); + JSHandle env = vm_->GetGlobalEnv(); + + JSHandle dynclass = JSHandle::Cast(env->GetAsyncGeneratorFunctionClass()); + JSHandle generatorFunc = JSHandle::Cast(NewJSObject(dynclass)); + JSFunction::InitializeJSFunction(thread_, env, generatorFunc, FunctionKind::ASYNC_GENERATOR_FUNCTION); + generatorFunc->SetCallTarget(thread_, method); + return generatorFunc; +} + +JSHandle ObjectFactory::NewJSAsyncGeneratorObject( + JSHandle asyncGeneratorFunction) +{ + NewObjectHook(); + JSHandle proto(thread_, JSHandle::Cast(asyncGeneratorFunction)->GetProtoOrDynClass()); + if (!proto->IsECMAObject()) { + JSHandle realmHandle = JSObject::GetFunctionRealm(thread_, asyncGeneratorFunction); + proto = realmHandle->GetAsyncGeneratorPrototype(); + } + JSHandle dynclass = + NewEcmaDynClass(JSAsyncGeneratorObject::SIZE, JSType::JS_ASYNC_GENERATOR_OBJECT, proto); + JSHandle asyncgeneratorObject = + JSHandle::Cast(NewJSObject(dynclass)); + return asyncgeneratorObject; +} + +JSHandle ObjectFactory::NewJSAsyncFromSyncIteratorObject() +{ + NewObjectHook(); + JSHandle env = vm_->GetGlobalEnv(); + JSHandle proto = env->GetAsyncFromSyncIteratorPrototype(); + + JSHandle dynclass = + NewEcmaDynClass(JSAsyncFromSyncIteratorObject::SIZE, JSType::JS_ASYNC_FROM_SYNC_ITERATOR_OBJECT, proto); + JSHandle asyncFromSyncIteratorObject = + JSHandle::Cast(NewJSObject(dynclass)); + return asyncFromSyncIteratorObject; +} + +JSHandle ObjectFactory::NewAsyncFunction(JSMethod *method) +{ + JSHandle env = vm_->GetGlobalEnv(); + JSHandle dynclass = JSHandle::Cast(env->GetAsyncFunctionClass()); + JSHandle asyncFunction = JSHandle::Cast(NewJSObject(dynclass)); + JSFunction::InitializeJSFunction(thread_, JSHandle::Cast(asyncFunction)); + asyncFunction->SetCallTarget(thread_, method); + SlowRuntimeStub::NotifyInlineCache(thread_, *asyncFunction, method); + return asyncFunction; +} + +JSHandle ObjectFactory::NewJSAsyncFuncObject() +{ + JSHandle env = vm_->GetGlobalEnv(); + JSHandle proto = env->GetInitialGenerator(); + JSHandle dynclass = NewEcmaDynClass(JSAsyncFuncObject::SIZE, JSType::JS_ASYNC_FUNC_OBJECT, proto); + JSHandle asyncFuncObject = JSHandle::Cast(NewJSObject(dynclass)); + return asyncFuncObject; +} + +JSHandle ObjectFactory::NewCompletionRecord(uint8_t type, const JSHandle &value) +{ + NewObjectHook(); + TaggedObject *header = heapHelper_.AllocateYoungGenerationOrHugeObject(completionRecordClass_); + JSHandle obj(thread_, header); + obj->SetType(thread_, JSTaggedValue(static_cast(type))); + obj->SetValue(thread_, value.GetTaggedValue()); + return obj; +} + +JSHandle ObjectFactory::NewGeneratorContext() +{ + NewObjectHook(); + TaggedObject *header = heapHelper_.AllocateYoungGenerationOrHugeObject(generatorContextClass_); + JSHandle obj(thread_, header); + obj->SetRegsArray(thread_, JSTaggedValue::Undefined()); + obj->SetMethod(thread_, JSTaggedValue::Undefined()); + obj->SetAcc(thread_, JSTaggedValue::Undefined()); + obj->SetNRegs(thread_, JSTaggedValue::Undefined()); + obj->SetBCOffset(thread_, JSTaggedValue::Undefined()); + obj->SetLexicalEnv(thread_, JSTaggedValue::Undefined()); + obj->SetGeneratorObject(thread_, JSTaggedValue::Undefined()); + obj->SetLexicalEnv(thread_, JSTaggedValue::Undefined()); + return obj; +} + +JSHandle ObjectFactory::NewJSPrimitiveRef(const JSHandle &function, + const JSHandle &object) +{ + JSHandle obj(NewJSObjectByConstructor(function, JSHandle(function))); + obj->SetValue(thread_, object); + + JSHandle env = vm_->GetGlobalEnv(); + const GlobalEnvConstants *globalConst = thread_->GlobalConstants(); + if (function.GetTaggedValue() == env->GetStringFunction().GetTaggedValue()) { + JSHandle lengthStr = globalConst->GetHandledLengthString(); + + int32_t length = EcmaString::Cast(object.GetTaggedValue().GetTaggedObject())->GetLength(); + PropertyDescriptor desc(thread_, JSHandle(thread_, JSTaggedValue(length)), false, false, false); + JSTaggedValue::DefinePropertyOrThrow(thread_, JSHandle(obj), lengthStr, desc); + } + + return obj; +} + +JSHandle ObjectFactory::NewJSPrimitiveRef(PrimitiveType type, const JSHandle &object) +{ + ObjectFactory *factory = vm_->GetFactory(); + JSHandle env = vm_->GetGlobalEnv(); + JSHandle function; + switch (type) { + case PrimitiveType::PRIMITIVE_NUMBER: + function = env->GetNumberFunction(); + break; + case PrimitiveType::PRIMITIVE_STRING: + function = env->GetStringFunction(); + break; + case PrimitiveType::PRIMITIVE_SYMBOL: + function = env->GetSymbolFunction(); + break; + case PrimitiveType::PRIMITIVE_BOOLEAN: + function = env->GetBooleanFunction(); + break; + default: + break; + } + JSHandle funcHandle(function); + return factory->NewJSPrimitiveRef(funcHandle, object); +} + +JSHandle ObjectFactory::NewJSString(const JSHandle &str) +{ + JSHandle env = vm_->GetGlobalEnv(); + JSHandle stringFunc = env->GetStringFunction(); + + JSHandle obj = + JSHandle::Cast(NewJSObjectByConstructor(JSHandle(stringFunc), stringFunc)); + obj->SetValue(thread_, str); + return obj; +} + +JSHandle ObjectFactory::NewGlobalEnv(JSHClass *globalEnvClass) +{ + NewObjectHook(); + // Note: Global env must be allocated in non-movable heap, since its getters will directly return + // the offsets of the properties as the address of Handles. + TaggedObject *header = heapHelper_.AllocateNonMovableOrHugeObject(globalEnvClass); + InitObjectFields(header); + return JSHandle(thread_, GlobalEnv::Cast(header)); +} + +JSHandle ObjectFactory::NewLexicalEnv(int numSlots) +{ + NewObjectHook(); + size_t size = LexicalEnv::ComputeSize(numSlots); + auto header = heapHelper_.AllocateYoungGenerationOrHugeObject(envClass_, size); + JSHandle array(thread_, header); + array->InitializeWithSpecialValue(JSTaggedValue::Undefined(), numSlots + LexicalEnv::RESERVED_ENV_LENGTH); + return array; +} + +JSHandle ObjectFactory::NewJSSymbol() +{ + NewObjectHook(); + TaggedObject *header = heapHelper_.AllocateYoungGenerationOrHugeObject(symbolClass_); + JSHandle obj(thread_, JSSymbol::Cast(header)); + obj->SetDescription(thread_, JSTaggedValue::Undefined()); + obj->SetFlags(thread_, JSTaggedValue(0)); + auto result = JSTaggedValue(static_cast(SymbolTable::Hash(obj.GetTaggedValue()))); + obj->SetHashField(thread_, result); + return obj; +} + +JSHandle ObjectFactory::NewPrivateSymbol() +{ + JSHandle obj = NewJSSymbol(); + obj->SetPrivate(thread_); + return obj; +} + +JSHandle ObjectFactory::NewPrivateNameSymbol(const JSHandle &name) +{ + NewObjectHook(); + TaggedObject *header = heapHelper_.AllocateYoungGenerationOrHugeObject(symbolClass_); + JSHandle obj(thread_, JSSymbol::Cast(header)); + obj->SetFlags(thread_, JSTaggedValue(0)); + obj->SetPrivateNameSymbol(thread_); + obj->SetDescription(thread_, name); + auto result = JSTaggedValue(static_cast(SymbolTable::Hash(name.GetTaggedValue()))); + obj->SetHashField(thread_, result); + return obj; +} + +JSHandle ObjectFactory::NewWellKnownSymbol(const JSHandle &name) +{ + NewObjectHook(); + TaggedObject *header = heapHelper_.AllocateYoungGenerationOrHugeObject(symbolClass_); + JSHandle obj(thread_, JSSymbol::Cast(header)); + obj->SetFlags(thread_, JSTaggedValue(0)); + obj->SetWellKnownSymbol(thread_); + obj->SetDescription(thread_, name); + auto result = JSTaggedValue(static_cast(SymbolTable::Hash(name.GetTaggedValue()))); + obj->SetHashField(thread_, result); + return obj; +} + +JSHandle ObjectFactory::NewPublicSymbol(const JSHandle &name) +{ + NewObjectHook(); + TaggedObject *header = heapHelper_.AllocateYoungGenerationOrHugeObject(symbolClass_); + JSHandle obj(thread_, JSSymbol::Cast(header)); + obj->SetFlags(thread_, JSTaggedValue(0)); + obj->SetDescription(thread_, name); + auto result = JSTaggedValue(static_cast(SymbolTable::Hash(name.GetTaggedValue()))); + obj->SetHashField(thread_, result); + return obj; +} + +JSHandle ObjectFactory::NewSymbolWithTable(const JSHandle &name) +{ + JSHandle env = vm_->GetGlobalEnv(); + JSHandle tableHandle(env->GetRegisterSymbols()); + if (tableHandle->ContainsKey(thread_, name.GetTaggedValue())) { + JSTaggedValue objValue = tableHandle->GetSymbol(name.GetTaggedValue()); + return JSHandle(thread_, objValue); + } + + JSHandle obj = NewPublicSymbol(name); + JSHandle valueHandle(obj); + JSHandle keyHandle(name); + JSHandle table = SymbolTable::Insert(thread_, tableHandle, keyHandle, valueHandle); + env->SetRegisterSymbols(thread_, table); + return obj; +} + +JSHandle ObjectFactory::NewPrivateNameSymbolWithChar(const char *description) +{ + JSHandle string = NewFromString(description); + return NewPrivateNameSymbol(JSHandle(string)); +} + +JSHandle ObjectFactory::NewWellKnownSymbolWithChar(const char *description) +{ + JSHandle string = NewFromString(description); + return NewWellKnownSymbol(JSHandle(string)); +} + +JSHandle ObjectFactory::NewPublicSymbolWithChar(const char *description) +{ + JSHandle string = NewFromString(description); + return NewPublicSymbol(JSHandle(string)); +} + +JSHandle ObjectFactory::NewSymbolWithTableWithChar(const char *description) +{ + JSHandle string = NewFromString(description); + return NewSymbolWithTable(JSHandle(string)); +} + +JSHandle ObjectFactory::NewAccessorData() +{ + NewObjectHook(); + TaggedObject *header = heapHelper_.AllocateYoungGenerationOrHugeObject(accessorDataClass_); + JSHandle acc(thread_, AccessorData::Cast(header)); + acc->SetGetter(thread_, JSTaggedValue::Undefined()); + acc->SetSetter(thread_, JSTaggedValue::Undefined()); + return acc; +} + +JSHandle ObjectFactory::NewInternalAccessor(void *setter, void *getter) +{ + NewObjectHook(); + TaggedObject *header = heapHelper_.AllocateNonMovableOrHugeObject(internalAccessorClass_); + JSHandle obj(thread_, AccessorData::Cast(header)); + if (setter != nullptr) { + JSHandle setFunc = NewJSNativePointer(setter, nullptr, nullptr, true); + obj->SetSetter(thread_, setFunc.GetTaggedValue()); + } else { + JSTaggedValue setFunc = JSTaggedValue::Undefined(); + obj->SetSetter(thread_, setFunc); + ASSERT(!obj->HasSetter()); + } + JSHandle getFunc = NewJSNativePointer(getter, nullptr, nullptr, true); + obj->SetGetter(thread_, getFunc); + return obj; +} + +JSHandle ObjectFactory::NewPromiseCapability() +{ + NewObjectHook(); + TaggedObject *header = heapHelper_.AllocateYoungGenerationOrHugeObject(capabilityRecordClass_); + JSHandle obj(thread_, header); + obj->SetPromise(thread_, JSTaggedValue::Undefined()); + obj->SetResolve(thread_, JSTaggedValue::Undefined()); + obj->SetReject(thread_, JSTaggedValue::Undefined()); + return obj; +} + +JSHandle ObjectFactory::NewPromiseReaction() +{ + NewObjectHook(); + TaggedObject *header = heapHelper_.AllocateYoungGenerationOrHugeObject(reactionsRecordClass_); + JSHandle obj(thread_, header); + obj->SetPromiseCapability(thread_, JSTaggedValue::Undefined()); + obj->SetHandler(thread_, JSTaggedValue::Undefined()); + obj->SetType(thread_, JSTaggedValue::Undefined()); + return obj; +} + +JSHandle ObjectFactory::NewPromiseIteratorRecord(const JSHandle &itor, + const JSHandle &done) +{ + NewObjectHook(); + TaggedObject *header = heapHelper_.AllocateYoungGenerationOrHugeObject(promiseIteratorRecordClass_); + JSHandle obj(thread_, header); + obj->SetIterator(thread_, itor.GetTaggedValue()); + obj->SetDone(thread_, done.GetTaggedValue()); + return obj; +} + +JSHandle ObjectFactory::NewMicroJobQueue() +{ + NewObjectHook(); + TaggedObject *header = heapHelper_.AllocateNonMovableOrHugeObject(microJobQueueClass_); + JSHandle obj(thread_, header); + obj->SetPromiseJobQueue(thread_, GetEmptyTaggedQueue().GetTaggedValue()); + obj->SetScriptJobQueue(thread_, GetEmptyTaggedQueue().GetTaggedValue()); + return obj; +} + +JSHandle ObjectFactory::NewPendingJob(const JSHandle &func, + const JSHandle &argv) +{ + NewObjectHook(); + TaggedObject *header = heapHelper_.AllocateYoungGenerationOrHugeObject(pendingJobClass_); + JSHandle obj(thread_, header); + obj->SetJob(thread_, func.GetTaggedValue()); + obj->SetArguments(thread_, argv.GetTaggedValue()); + return obj; +} + +JSHandle ObjectFactory::NewFunctionExtraInfo(const JSHandle &callBack, + const JSHandle &vm, + const JSHandle &data) +{ + NewObjectHook(); + TaggedObject *header = heapHelper_.AllocateYoungGenerationOrHugeObject(functionExtraInfo_); + JSHandle obj(thread_, header); + obj->SetCallback(thread_, callBack.GetTaggedValue()); + obj->SetVm(thread_, vm.GetTaggedValue()); + obj->SetData(thread_, data.GetTaggedValue()); + return obj; +} + +JSHandle ObjectFactory::NewJSProxy(const JSHandle &target, + const JSHandle &handler) +{ + NewObjectHook(); + TaggedObject *header = nullptr; + if (target->IsCallable()) { + header = target->IsConstructor() ? heapHelper_.AllocateYoungGenerationOrHugeObject(jsProxyConstructClass_) + : heapHelper_.AllocateYoungGenerationOrHugeObject(jsProxyCallableClass_); + } else { + header = heapHelper_.AllocateYoungGenerationOrHugeObject(jsProxyOrdinaryClass_); + } + + JSHandle proxy(thread_, header); + JSMethod *method = nullptr; + if (target->IsCallable()) { + method = vm_->GetMethodForNativeFunction(reinterpret_cast(builtins::BuiltinsGlobal::CallJsProxy)); + proxy->SetCallTarget(thread_, method); + } + proxy->SetMethod(method); + + proxy->SetTarget(thread_, target.GetTaggedValue()); + proxy->SetHandler(thread_, handler.GetTaggedValue()); + return proxy; +} + +JSHandle ObjectFactory::NewJSRealm() +{ + JSHandle env = vm_->GetGlobalEnv(); + JSHandle realmEnvClass = NewEcmaDynClass(hclassClass_, GlobalEnv::SIZE, JSType::GLOBAL_ENV); + JSHandle realmEnvHandle = NewGlobalEnv(*realmEnvClass); + + ObtainRootClass(env); + realmEnvHandle->SetEmptyArray(thread_, NewEmptyArray()); + realmEnvHandle->SetEmptyTaggedQueue(thread_, NewTaggedQueue(0)); + auto result = TemplateMap::Create(thread_); + realmEnvHandle->SetTemplateMap(thread_, result); + + Builtins builtins; + builtins.Initialize(realmEnvHandle, thread_); + JSHandle protoValue = thread_->GlobalConstants()->GetHandledJSRealmClass(); + JSHandle dynHandle = NewEcmaDynClass(JSRealm::SIZE, JSType::JS_REALM, protoValue); + JSHandle realm(NewJSObject(dynHandle)); + realm->SetGlobalEnv(thread_, realmEnvHandle.GetTaggedValue()); + + JSHandle realmObj = realmEnvHandle->GetJSGlobalObject(); + JSHandle realmkey(thread_->GlobalConstants()->GetHandledGlobalString()); + PropertyDescriptor realmDesc(thread_, JSHandle::Cast(realmObj), true, false, true); + [[maybe_unused]] bool status = + JSObject::DefineOwnProperty(thread_, JSHandle::Cast(realm), realmkey, realmDesc); + ASSERT_PRINT(status, "Realm defineOwnProperty failed"); + + return realm; +} + +JSHandle ObjectFactory::NewEmptyArray() +{ + NewObjectHook(); + auto header = heapHelper_.AllocateNonMovableOrHugeObject(arrayClass_, TaggedArray::SIZE); + JSHandle array(thread_, header); + array->SetLength(0); + return array; +} + +JSHandle ObjectFactory::NewTaggedArray(uint32_t length, JSTaggedValue initVal, bool nonMovable) +{ + if (nonMovable) { + return NewTaggedArray(length, initVal, MemSpaceType::NON_MOVABLE); + } + return NewTaggedArray(length, initVal, MemSpaceType::SEMI_SPACE); +} + +JSHandle ObjectFactory::NewTaggedArray(uint32_t length, JSTaggedValue initVal, MemSpaceType spaceType) +{ + NewObjectHook(); + if (length == 0) { + return EmptyArray(); + } + + size_t size = TaggedArray::ComputeSize(JSTaggedValue::TaggedTypeSize(), length); + TaggedObject *header = nullptr; + switch (spaceType) { + case MemSpaceType::SEMI_SPACE: + header = heapHelper_.AllocateYoungGenerationOrHugeObject(arrayClass_, size); + break; + case MemSpaceType::OLD_SPACE: + header = heapHelper_.AllocateOldGenerationOrHugeObject(arrayClass_, size); + break; + case MemSpaceType::NON_MOVABLE: + header = heapHelper_.AllocateNonMovableOrHugeObject(arrayClass_, size); + break; + default: + UNREACHABLE(); + } + + JSHandle array(thread_, header); + array->InitializeWithSpecialValue(initVal, length); + return array; +} + +JSHandle ObjectFactory::NewTaggedArray(uint32_t length, JSTaggedValue initVal) +{ + NewObjectHook(); + if (length == 0) { + return EmptyArray(); + } + + size_t size = TaggedArray::ComputeSize(JSTaggedValue::TaggedTypeSize(), length); + auto header = heapHelper_.AllocateYoungGenerationOrHugeObject(arrayClass_, size); + JSHandle array(thread_, header); + array->InitializeWithSpecialValue(initVal, length); + return array; +} + +JSHandle ObjectFactory::NewDictionaryArray(uint32_t length) +{ + NewObjectHook(); + ASSERT(length > 0); + + size_t size = TaggedArray::ComputeSize(JSTaggedValue::TaggedTypeSize(), length); + auto header = heapHelper_.AllocateYoungGenerationOrHugeObject(dictionaryClass_, size); + JSHandle array(thread_, header); + array->InitializeWithSpecialValue(JSTaggedValue::Undefined(), length); + + return array; +} + +JSHandle ObjectFactory::NewLinkedHashTable(array_size_t length, JSType tableType, bool isWeak) +{ + NewObjectHook(); + ASSERT(length > 0); + size_t size = TaggedArray::ComputeSize(JSTaggedValue::TaggedTypeSize(), length); + JSHClass *tableClass = nullptr; + if (tableType == JSType::LINKED_HASH_MAP) { + tableClass = isWeak ? weakLinkedHashMapClass_ : linkedHashMapClass_; + } else if (tableType == JSType::LINKED_HASH_SET) { + tableClass = isWeak ? weakLinkedHashSetClass_ : linkedHashSetClass_; + } else { + tableClass = arrayClass_; + } + auto header = heapHelper_.AllocateYoungGenerationOrHugeObject(tableClass, size); + JSHandle array(thread_, header); + array->InitializeWithSpecialValue(JSTaggedValue::Hole(), length); + return array; +} + +JSHandle ObjectFactory::ExtendArray(const JSHandle &old, uint32_t length, + JSTaggedValue initVal) +{ + ASSERT(length > old->GetLength()); + NewObjectHook(); + size_t size = TaggedArray::ComputeSize(JSTaggedValue::TaggedTypeSize(), length); + auto header = heapHelper_.AllocateYoungGenerationOrHugeObject(arrayClass_, size); + JSHandle newArray(thread_, header); + newArray->SetLength(length); + + uint32_t oldLength = old->GetLength(); + for (uint32_t i = 0; i < oldLength; i++) { + JSTaggedValue value = old->Get(i); + newArray->Set(thread_, i, value); + } + + for (uint32_t i = oldLength; i < length; i++) { + newArray->Set(thread_, i, initVal); + } + + return newArray; +} + +JSHandle ObjectFactory::CopyPartArray(const JSHandle &old, uint32_t start, uint32_t end) +{ + ASSERT(start <= end); + ASSERT(end <= old->GetLength()); + + uint32_t newLength = end - start; + if (newLength == 0) { + return EmptyArray(); + } + + NewObjectHook(); + size_t size = TaggedArray::ComputeSize(JSTaggedValue::TaggedTypeSize(), newLength); + auto header = heapHelper_.AllocateYoungGenerationOrHugeObject(arrayClass_, size); + JSHandle newArray(thread_, header); + newArray->SetLength(newLength); + + for (uint32_t i = 0; i < newLength; i++) { + JSTaggedValue value = old->Get(i + start); + if (value.IsHole()) { + break; + } + newArray->Set(thread_, i, value); + } + return newArray; +} + +JSHandle ObjectFactory::CopyArray(const JSHandle &old, [[maybe_unused]] uint32_t oldLength, + uint32_t newLength, JSTaggedValue initVal) +{ + if (newLength == 0) { + return EmptyArray(); + } + if (newLength > oldLength) { + return ExtendArray(old, newLength, initVal); + } + + NewObjectHook(); + size_t size = TaggedArray::ComputeSize(JSTaggedValue::TaggedTypeSize(), newLength); + auto header = heapHelper_.AllocateYoungGenerationOrHugeObject(arrayClass_, size); + JSHandle newArray(thread_, header); + newArray->SetLength(newLength); + + for (uint32_t i = 0; i < newLength; i++) { + JSTaggedValue value = old->Get(i); + newArray->Set(thread_, i, value); + } + + return newArray; +} + +JSHandle ObjectFactory::CreateLayoutInfo(int properties, JSTaggedValue initVal) +{ + int arrayLength = LayoutInfo::ComputeArrayLength(LayoutInfo::ComputeGrowCapacity(properties)); + JSHandle layoutInfoHandle = JSHandle::Cast(NewTaggedArray(arrayLength, initVal)); + layoutInfoHandle->SetNumberOfElements(thread_, 0); + return layoutInfoHandle; +} + +JSHandle ObjectFactory::ExtendLayoutInfo(const JSHandle &old, int properties, + JSTaggedValue initVal) +{ + ASSERT(properties > old->NumberOfElements()); + int arrayLength = LayoutInfo::ComputeArrayLength(LayoutInfo::ComputeGrowCapacity(properties)); + return JSHandle(ExtendArray(JSHandle(old), arrayLength, initVal)); +} + +JSHandle ObjectFactory::CopyLayoutInfo(const JSHandle &old) +{ + int newLength = old->GetLength(); + return JSHandle(CopyArray(JSHandle::Cast(old), newLength, newLength)); +} + +JSHandle ObjectFactory::CopyAndReSort(const JSHandle &old, int end, int capacity) +{ + ASSERT(capacity >= end); + JSHandle newArr = CreateLayoutInfo(capacity); + Span sp(old->GetProperties(), end); + int i = 0; + for (; i < end; i++) { + newArr->AddKey(thread_, i, sp[i].key_, PropertyAttributes(sp[i].attr_)); + } + + return newArr; +} + +JSHandle ObjectFactory::NewConstantPool(uint32_t capacity) +{ + NewObjectHook(); + if (capacity == 0) { + return JSHandle::Cast(EmptyArray()); + } + size_t size = TaggedArray::ComputeSize(JSTaggedValue::TaggedTypeSize(), capacity); + auto header = heapHelper_.AllocateNonMovableOrHugeObject(arrayClass_, size); + JSHandle array(thread_, header); + array->InitializeWithSpecialValue(JSTaggedValue::Undefined(), capacity); + return array; +} + +JSHandle ObjectFactory::NewProgram() +{ + NewObjectHook(); + TaggedObject *header = heapHelper_.AllocateNonMovableOrHugeObject(programClass_); + JSHandle p(thread_, header); + p->SetLocation(thread_, JSTaggedValue::Undefined()); + p->SetConstantPool(thread_, JSTaggedValue::Undefined()); + p->SetMainFunction(thread_, JSTaggedValue::Undefined()); + p->SetMethodsData(nullptr); + return p; +} + +JSHandle ObjectFactory::NewEmptyEcmaModule() +{ + NewObjectHook(); + TaggedObject *header = heapHelper_.AllocateYoungGenerationOrHugeObject(ecmaModuleClass_); + JSHandle module(thread_, header); + module->SetNameDictionary(thread_, JSTaggedValue::Undefined()); + return module; +} + +JSHandle ObjectFactory::GetEmptyString() const +{ + return JSHandle(thread_->GlobalConstants()->GetHandledEmptyString()); +} + +JSHandle ObjectFactory::EmptyArray() const +{ + JSHandle env = vm_->GetGlobalEnv(); + return JSHandle(env->GetEmptyArray()); +} + +JSHandle ObjectFactory::NewObjectWrapper(const JSHandle &value) +{ + NewObjectHook(); + TaggedObject *header = heapHelper_.AllocateYoungGenerationOrHugeObject(objectWrapperClass_); + ObjectWrapper *obj = ObjectWrapper::Cast(header); + obj->SetValue(thread_, value); + return JSHandle(thread_, obj); +} + +JSHandle ObjectFactory::GetStringFromStringTable(const uint8_t *utf8Data, uint32_t utf8Len, + bool canBeCompress) const +{ + NewObjectHook(); + if (utf8Len == 0) { + return GetEmptyString(); + } + auto stringTable = vm_->GetEcmaStringTable(); + return JSHandle(thread_, stringTable->GetOrInternString(utf8Data, utf8Len, canBeCompress)); +} + +JSHandle ObjectFactory::GetStringFromStringTable(const uint16_t *utf16Data, uint32_t utf16Len, + bool canBeCompress) const +{ + NewObjectHook(); + if (utf16Len == 0) { + return GetEmptyString(); + } + auto stringTable = vm_->GetEcmaStringTable(); + return JSHandle(thread_, stringTable->GetOrInternString(utf16Data, utf16Len, canBeCompress)); +} + +JSHandle ObjectFactory::GetStringFromStringTable(EcmaString *string) const +{ + ASSERT(string != nullptr); + if (string->GetLength() == 0) { + return GetEmptyString(); + } + auto stringTable = vm_->GetEcmaStringTable(); + return JSHandle(thread_, stringTable->GetOrInternString(string)); +} + +// NB! don't do special case for C0 80, it means '\u0000', so don't convert to UTF-8 +EcmaString *ObjectFactory::GetRawStringFromStringTable(const uint8_t *mutf8Data, uint32_t utf16Len, + bool canBeCompressed) const +{ + NewObjectHook(); + if (UNLIKELY(utf16Len == 0)) { + return *GetEmptyString(); + } + + if (canBeCompressed) { + return EcmaString::Cast(vm_->GetEcmaStringTable()->GetOrInternString(mutf8Data, utf16Len, true)); + } + + CVector utf16Data(utf16Len); + auto len = utf::ConvertRegionMUtf8ToUtf16(mutf8Data, utf16Data.data(), utf::Mutf8Size(mutf8Data), utf16Len, 0); + return EcmaString::Cast(vm_->GetEcmaStringTable()->GetOrInternString(utf16Data.data(), len, false)); +} + +JSHandle ObjectFactory::NewPropertyBox(const JSHandle &value) +{ + NewObjectHook(); + TaggedObject *header = heapHelper_.AllocateYoungGenerationOrHugeObject(PropertyBoxClass_); + JSHandle box(thread_, header); + box->SetValue(thread_, value); + return box; +} + +JSHandle ObjectFactory::NewProtoChangeMarker() +{ + NewObjectHook(); + TaggedObject *header = heapHelper_.AllocateYoungGenerationOrHugeObject(protoChangeMarkerClass_); + JSHandle marker(thread_, header); + marker->SetHasChanged(false); + return marker; +} + +JSHandle ObjectFactory::NewProtoChangeDetails() +{ + NewObjectHook(); + TaggedObject *header = heapHelper_.AllocateYoungGenerationOrHugeObject(protoChangeDetailsClass_); + JSHandle protoInfo(thread_, header); + protoInfo->SetChangeListener(thread_, JSTaggedValue(0)); + protoInfo->SetRegisterIndex(thread_, JSTaggedValue(ProtoChangeDetails::UNREGISTERED)); + return protoInfo; +} + +JSHandle ObjectFactory::NewProfileTypeInfo(uint32_t length) +{ + NewObjectHook(); + ASSERT(length > 0); + + size_t size = TaggedArray::ComputeSize(JSTaggedValue::TaggedTypeSize(), length); + auto header = heapHelper_.AllocateYoungGenerationOrHugeObject(arrayClass_, size); + JSHandle array(thread_, header); + array->InitializeWithSpecialValue(JSTaggedValue::Undefined(), length); + + return array; +} + +// static +void ObjectFactory::NewObjectHook() const {} + +JSHandle ObjectFactory::NewTaggedQueue(uint32_t length) +{ + uint32_t queueLength = TaggedQueue::QueueToArrayIndex(length); + auto queue = JSHandle::Cast(NewTaggedArray(queueLength, JSTaggedValue::Hole())); + queue->SetStart(thread_, JSTaggedValue(0)); // equal to 0 when add 1. + queue->SetEnd(thread_, JSTaggedValue(0)); + queue->SetCapacity(thread_, JSTaggedValue(length)); + + return queue; +} + +JSHandle ObjectFactory::GetEmptyTaggedQueue() const +{ + JSHandle env = vm_->GetGlobalEnv(); + return JSHandle(env->GetEmptyTaggedQueue()); +} + +JSHandle ObjectFactory::NewJSSetIterator(const JSHandle &set, IterationKind kind) +{ + JSHandle env = vm_->GetGlobalEnv(); + JSHandle protoValue = env->GetSetIteratorPrototype(); + JSHandle dynHandle = NewEcmaDynClass(JSSetIterator::SIZE, JSType::JS_SET_ITERATOR, protoValue); + JSHandle iter(NewJSObject(dynHandle)); + iter->GetJSHClass()->SetExtensible(true); + iter->SetIteratedSet(thread_, set->GetLinkedSet()); + iter->SetNextIndex(thread_, JSTaggedValue(0)); + iter->SetIterationKind(thread_, JSTaggedValue(static_cast(kind))); + return iter; +} + +JSHandle ObjectFactory::NewJSMapIterator(const JSHandle &map, IterationKind kind) +{ + JSHandle env = vm_->GetGlobalEnv(); + JSHandle protoValue = env->GetMapIteratorPrototype(); + JSHandle dynHandle = NewEcmaDynClass(JSMapIterator::SIZE, JSType::JS_MAP_ITERATOR, protoValue); + JSHandle iter(NewJSObject(dynHandle)); + iter->GetJSHClass()->SetExtensible(true); + iter->SetIteratedMap(thread_, map->GetLinkedMap()); + iter->SetNextIndex(thread_, JSTaggedValue(0)); + iter->SetIterationKind(thread_, JSTaggedValue(static_cast(kind))); + return iter; +} + +JSHandle ObjectFactory::NewJSArrayIterator(const JSHandle &array, IterationKind kind) +{ + JSHandle env = vm_->GetGlobalEnv(); + JSHandle protoValue = env->GetArrayIteratorPrototype(); + JSHandle dynHandle = NewEcmaDynClass(JSArrayIterator::SIZE, JSType::JS_ARRAY_ITERATOR, protoValue); + JSHandle iter(NewJSObject(dynHandle)); + iter->GetJSHClass()->SetExtensible(true); + iter->SetIteratedArray(thread_, array); + iter->SetNextIndex(thread_, JSTaggedValue(0)); + iter->SetIterationKind(thread_, JSTaggedValue(static_cast(kind))); + return iter; +} + +JSHandle ObjectFactory::CreateJSPromiseReactionsFunction(const void *nativeFunc) +{ + JSHandle env = vm_->GetGlobalEnv(); + JSHandle dynclass = JSHandle::Cast(env->GetPromiseReactionFunctionClass()); + + JSHandle reactionsFunction = + JSHandle::Cast(NewJSObject(dynclass)); + reactionsFunction->SetPromise(thread_, JSTaggedValue::Hole()); + reactionsFunction->SetAlreadyResolved(thread_, JSTaggedValue::Hole()); + JSMethod *method = vm_->GetMethodForNativeFunction(nativeFunc); + reactionsFunction->SetCallTarget(thread_, method); + JSHandle function = JSHandle::Cast(reactionsFunction); + JSFunction::InitializeJSFunction(thread_, function); + JSFunction::SetFunctionLength(thread_, function, JSTaggedValue(1)); + return reactionsFunction; +} + +JSHandle ObjectFactory::CreateJSPromiseExecutorFunction(const void *nativeFunc) +{ + JSHandle env = vm_->GetGlobalEnv(); + JSHandle dynclass = JSHandle::Cast(env->GetPromiseExecutorFunctionClass()); + JSHandle executorFunction = + JSHandle::Cast(NewJSObject(dynclass)); + executorFunction->SetCapability(thread_, JSTaggedValue::Hole()); + JSMethod *method = vm_->GetMethodForNativeFunction(nativeFunc); + executorFunction->SetCallTarget(thread_, method); + executorFunction->SetCapability(thread_, JSTaggedValue::Undefined()); + JSHandle function = JSHandle::Cast(executorFunction); + JSFunction::InitializeJSFunction(thread_, function, FunctionKind::NORMAL_FUNCTION); + JSFunction::SetFunctionLength(thread_, function, JSTaggedValue(FunctionLength::TWO)); + return executorFunction; +} + +JSHandle ObjectFactory::CreateBuiltinFunction(EcmaEntrypoint steps, uint8_t length, + JSHandle name, JSTaggedValue proto, + JSHandle prefix) +{ + JSHandle function = NewJSFunction(vm_->GetGlobalEnv(), reinterpret_cast(steps)); + function->SetFunctionPrototype(thread_, proto); + function->GetJSHClass()->SetExtensible(true); + JSFunction::SetFunctionLength(thread_, function, JSTaggedValue(length)); + JSHandle baseFunction(function); + JSFunction::SetFunctionName(thread_, baseFunction, name, prefix); + + return function; +} + +JSHandle ObjectFactory::NewJSPromiseAllResolveElementFunction( + const void *nativeFunc) +{ + JSHandle env = vm_->GetGlobalEnv(); + JSHandle dynclass = JSHandle::Cast(env->GetPromiseAllResolveElementFunctionClass()); + JSHandle function = + JSHandle::Cast(NewJSObject(dynclass)); + JSFunction::InitializeJSFunction(thread_, JSHandle::Cast(function)); + JSMethod *method = vm_->GetMethodForNativeFunction(nativeFunc); + function->SetCallTarget(thread_, method); + JSFunction::SetFunctionLength(thread_, JSHandle::Cast(function), JSTaggedValue(1)); + return function; +} + +EcmaString *ObjectFactory::InternString(const JSHandle &key) +{ + EcmaString *str = EcmaString::Cast(key->GetTaggedObject()); + if (str->IsInternString()) { + return str; + } + + EcmaStringTable *stringTable = vm_->GetEcmaStringTable(); + return stringTable->GetOrInternString(str); +} + +JSHandle ObjectFactory::NewTransitionHandler() +{ + NewObjectHook(); + TransitionHandler *handler = + TransitionHandler::Cast(heapHelper_.AllocateYoungGenerationOrHugeObject(transitionHandlerClass_)); + return JSHandle(thread_, handler); +} + +JSHandle ObjectFactory::NewPrototypeHandler() +{ + NewObjectHook(); + PrototypeHandler *header = + PrototypeHandler::Cast(heapHelper_.AllocateYoungGenerationOrHugeObject(prototypeHandlerClass_)); + JSHandle handler(thread_, header); + handler->SetHandlerInfo(thread_, JSTaggedValue::Undefined()); + handler->SetProtoCell(thread_, JSTaggedValue::Undefined()); + handler->SetHolder(thread_, JSTaggedValue::Undefined()); + return handler; +} + +JSHandle ObjectFactory::NewPromiseRecord() +{ + NewObjectHook(); + TaggedObject *header = heapHelper_.AllocateYoungGenerationOrHugeObject(promiseRecordClass_); + JSHandle obj(thread_, header); + obj->SetValue(thread_, JSTaggedValue::Undefined()); + return obj; +} + +JSHandle ObjectFactory::NewResolvingFunctionsRecord() +{ + NewObjectHook(); + TaggedObject *header = heapHelper_.AllocateYoungGenerationOrHugeObject(promiseResolvingFunctionsRecord_); + JSHandle obj(thread_, header); + obj->SetResolveFunction(thread_, JSTaggedValue::Undefined()); + obj->SetRejectFunction(thread_, JSTaggedValue::Undefined()); + return obj; +} + +JSHandle ObjectFactory::CreateObjectClass(const JSHandle &properties, size_t length) +{ + JSHandle env = vm_->GetGlobalEnv(); + JSHandle proto = env->GetObjectFunctionPrototype(); + + uint32_t fieldOrder = 0; + JSMutableHandle key(thread_, JSTaggedValue::Undefined()); + JSHandle layoutInfoHandle = CreateLayoutInfo(length); + while (fieldOrder < length) { + key.Update(properties->Get(fieldOrder * 2)); // 2: Meaning to double + ASSERT_PRINT(JSTaggedValue::IsPropertyKey(key), "Key is not a property key"); + PropertyAttributes attributes = PropertyAttributes::Default(); + + if (properties->Get(fieldOrder * 2 + 1).IsAccessor()) { // 2: Meaning to double + attributes.SetIsAccessor(true); + } + + attributes.SetIsInlinedProps(true); + attributes.SetRepresentation(Representation::MIXED); + attributes.SetOffset(fieldOrder); + layoutInfoHandle->AddKey(thread_, fieldOrder, key.GetTaggedValue(), attributes); + fieldOrder++; + } + ASSERT(fieldOrder <= PropertyAttributes::MAX_CAPACITY_OF_PROPERTIES); + JSHandle objClass = NewEcmaDynClass(JSObject::SIZE, JSType::JS_OBJECT, fieldOrder); + objClass->SetPrototype(thread_, proto.GetTaggedValue()); + { + objClass->SetExtensible(true); + objClass->SetIsLiteral(true); + objClass->SetLayout(thread_, layoutInfoHandle); + objClass->SetNumberOfProps(fieldOrder); + } + return objClass; +} + +JSHandle ObjectFactory::NewJSObjectByClass(const JSHandle &properties, size_t length) +{ + JSHandle dynclass = CreateObjectClass(properties, length); + JSHandle obj = NewJSObject(dynclass); + return obj; +} + +JSHandle ObjectFactory::NewEmptyJSObject() +{ + JSHandle env = vm_->GetGlobalEnv(); + JSHandle builtinObj = env->GetObjectFunction(); + return NewJSObjectByConstructor(JSHandle(builtinObj), builtinObj); +} + +EcmaString *ObjectFactory::ResolveString(uint32_t stringId) +{ + auto *caller = panda::StackWalker::Create(thread_).GetMethod(); + auto *pf = caller->GetPandaFile(); + auto id = panda_file::File::EntityId(stringId); + auto foundStr = pf->GetStringData(id); + + return GetRawStringFromStringTable(foundStr.data, foundStr.utf16_length, foundStr.is_ascii); +} + +uintptr_t ObjectFactory::NewSpaceBySnapShotAllocator(size_t size) +{ + NewObjectHook(); + return heapHelper_.AllocateSnapShotSpace(size); +} + +JSHandle ObjectFactory::NewMachineCodeObject(size_t length, const uint8_t *data) +{ + NewObjectHook(); + TaggedObject *obj = heapHelper_.AllocateMachineCodeSpaceObject(machineCodeClass_, length + MachineCode::SIZE); + MachineCode *code = MachineCode::Cast(obj); + code->SetInstructionSizeInBytes(thread_, JSTaggedValue(static_cast(length))); + if (data != nullptr) { + code->SetData(data, length); + } + JSHandle codeObj(thread_, code); + return codeObj; +} + +JSHandle ObjectFactory::NewClassInfoExtractor(JSMethod *ctorMethod) +{ + NewObjectHook(); + TaggedObject *header = heapHelper_.AllocateYoungGenerationOrHugeObject(classInfoExtractorHClass_); + JSHandle obj(thread_, header); + obj->InitializeBitField(); + obj->SetConstructorMethod(ctorMethod); + JSHandle emptyArray = EmptyArray(); + obj->SetPrototypeHClass(thread_, JSTaggedValue::Undefined()); + obj->SetNonStaticKeys(thread_, emptyArray, SKIP_BARRIER); + obj->SetNonStaticProperties(thread_, emptyArray, SKIP_BARRIER); + obj->SetNonStaticElements(thread_, emptyArray, SKIP_BARRIER); + obj->SetConstructorHClass(thread_, JSTaggedValue::Undefined()); + obj->SetStaticKeys(thread_, emptyArray, SKIP_BARRIER); + obj->SetStaticProperties(thread_, emptyArray, SKIP_BARRIER); + obj->SetStaticElements(thread_, emptyArray, SKIP_BARRIER); + return obj; +} + +// ----------------------------------- new string ---------------------------------------- +JSHandle ObjectFactory::NewFromString(const CString &data) +{ + auto utf8Data = reinterpret_cast(data.c_str()); + bool canBeCompress = EcmaString::CanBeCompressed(utf8Data, data.length()); + return GetStringFromStringTable(utf8Data, data.length(), canBeCompress); +} + +JSHandle ObjectFactory::NewFromCanBeCompressString(const CString &data) +{ + auto utf8Data = reinterpret_cast(data.c_str()); + ASSERT(EcmaString::CanBeCompressed(utf8Data, data.length())); + return GetStringFromStringTable(utf8Data, data.length(), true); +} + +JSHandle ObjectFactory::NewFromStdString(const std::string &data) +{ + auto utf8Data = reinterpret_cast(data.c_str()); + bool canBeCompress = EcmaString::CanBeCompressed(utf8Data, data.length()); + return GetStringFromStringTable(utf8Data, data.size(), canBeCompress); +} + +JSHandle ObjectFactory::NewFromStdStringUnCheck(const std::string &data, bool canBeCompress) +{ + auto utf8Data = reinterpret_cast(data.c_str()); + return GetStringFromStringTable(utf8Data, data.size(), canBeCompress); +} + +JSHandle ObjectFactory::NewFromUtf8(const uint8_t *utf8Data, uint32_t utf8Len) +{ + bool canBeCompress = EcmaString::CanBeCompressed(utf8Data, utf8Len); + return GetStringFromStringTable(utf8Data, utf8Len, canBeCompress); +} + +JSHandle ObjectFactory::NewFromUtf8UnCheck(const uint8_t *utf8Data, uint32_t utf8Len, bool canBeCompress) +{ + return GetStringFromStringTable(utf8Data, utf8Len, canBeCompress); +} + +JSHandle ObjectFactory::NewFromUtf16(const uint16_t *utf16Data, uint32_t utf16Len) +{ + bool canBeCompress = EcmaString::CanBeCompressed(utf16Data, utf16Len); + return GetStringFromStringTable(utf16Data, utf16Len, canBeCompress); +} + +JSHandle ObjectFactory::NewFromUtf16UnCheck(const uint16_t *utf16Data, uint32_t utf16Len, + bool canBeCompress) +{ + return GetStringFromStringTable(utf16Data, utf16Len, canBeCompress); +} + +JSHandle ObjectFactory::NewFromUtf8Literal(const uint8_t *utf8Data, uint32_t utf8Len) +{ + NewObjectHook(); + bool canBeCompress = EcmaString::CanBeCompressed(utf8Data, utf8Len); + return JSHandle(thread_, EcmaString::CreateFromUtf8(utf8Data, utf8Len, vm_, canBeCompress)); +} + +JSHandle ObjectFactory::NewFromUtf8LiteralUnCheck(const uint8_t *utf8Data, uint32_t utf8Len, + bool canBeCompress) +{ + NewObjectHook(); + return JSHandle(thread_, EcmaString::CreateFromUtf8(utf8Data, utf8Len, vm_, canBeCompress)); +} + +JSHandle ObjectFactory::NewFromUtf16Literal(const uint16_t *utf16Data, uint32_t utf16Len) +{ + NewObjectHook(); + bool canBeCompress = EcmaString::CanBeCompressed(utf16Data, utf16Len); + return JSHandle(thread_, EcmaString::CreateFromUtf16(utf16Data, utf16Len, vm_, canBeCompress)); +} + +JSHandle ObjectFactory::NewFromUtf16LiteralUnCheck(const uint16_t *utf16Data, uint32_t utf16Len, + bool canBeCompress) +{ + NewObjectHook(); + return JSHandle(thread_, EcmaString::CreateFromUtf16(utf16Data, utf16Len, vm_, canBeCompress)); +} + +JSHandle ObjectFactory::NewFromString(EcmaString *str) +{ + return GetStringFromStringTable(str); +} + +JSHandle ObjectFactory::ConcatFromString(const JSHandle &prefix, + const JSHandle &suffix) +{ + EcmaString *concatString = EcmaString::Concat(prefix, suffix, vm_); + return GetStringFromStringTable(concatString); +} +} // namespace panda::ecmascript diff --git a/runtime/object_factory.h b/runtime/object_factory.h new file mode 100644 index 000000000..d766a832e --- /dev/null +++ b/runtime/object_factory.h @@ -0,0 +1,512 @@ +/* + * Copyright (c) 2021-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_OBJECT_FACTORY_H +#define ECMASCRIPT_OBJECT_FACTORY_H + +#include "plugins/ecmascript/runtime/base/error_type.h" +#include "plugins/ecmascript/runtime/ecma_runtime_call_info.h" +#include "plugins/ecmascript/runtime/ecma_string.h" +#include "plugins/ecmascript/runtime/js_function_kind.h" +#include "plugins/ecmascript/runtime/js_handle.h" +#include "plugins/ecmascript/runtime/js_hclass.h" +#include "plugins/ecmascript/runtime/js_native_pointer.h" +#include "plugins/ecmascript/runtime/js_tagged_value.h" +#include "plugins/ecmascript/runtime/mem/machine_code.h" +#include "plugins/ecmascript/runtime/mem/mem_manager.h" +#include "plugins/ecmascript/runtime/mem/region_factory.h" +#include "plugins/ecmascript/runtime/object_wrapper.h" +#include "plugins/ecmascript/runtime/tagged_array.h" + +namespace panda::ecmascript { +class JSObject; +class JSArray; +class JSSymbol; +class JSFunctionBase; +class JSFunction; +class JSBoundFunction; +class JSProxyRevocFunction; +class JSAsyncAwaitStatusFunction; +class JSAsyncGeneratorResolveNextFunction; +class JSAsyncFromSyncIteratorValueUnwrapFunction; +class JSPrimitiveRef; +class GlobalEnv; +class GlobalEnvConstants; +class AccessorData; +class JSGlobalObject; +class LexicalEnv; +class JSDate; +class JSProxy; +class JSRealm; +class JSArguments; +class TaggedQueue; +class JSForInIterator; +class JSSet; +class JSMap; +class JSRegExp; +class JSSetIterator; +class JSMapIterator; +class JSArrayIterator; +class JSStringIterator; +class JSGeneratorObject; +class CompletionRecord; +class GeneratorContext; +class JSArrayBuffer; +class JSDataView; +class JSPromise; +class JSPromiseReactionsFunction; +class JSPromiseExecutorFunction; +class JSPromiseAllResolveElementFunction; +class PromiseReaction; +class PromiseCapability; +class PromiseIteratorRecord; +class JSAsyncFuncObject; +class JSAsyncFunction; +class JSAsyncGeneratorObject; +class JSAsyncFromSyncIteratorObject; +class PromiseRecord; +class JSLocale; +class ResolvingFunctionsRecord; +class JSFunctionExtraInfo; +class EcmaVM; +class Heap; +class ConstantPool; +class Program; +class EcmaModule; +class LayoutInfo; +class JSIntlBoundFunction; +class FreeObject; +class JSNativePointer; +class DynamicObjectHelpersTest; + +namespace job { +class MicroJobQueue; +class PendingJob; +} // namespace job +class TransitionHandler; +class PrototypeHandler; +class PropertyBox; +class ProtoChangeMarker; +class ProtoChangeDetails; +class ProfileTypeInfo; +class MachineCode; +class ClassInfoExtractor; + +enum class PrimitiveType : uint8_t; +enum class IterationKind; + +using ErrorType = base::ErrorType; +using DeleteEntryPoint = void (*)(void *, void *); + +enum class RemoveSlots { YES, NO }; +class ObjectFactory { +public: + explicit ObjectFactory(JSThread *thread, Heap *heap); + + JSHandle NewProfileTypeInfo(uint32_t length); + JSHandle NewConstantPool(uint32_t capacity); + JSHandle NewProgram(); + JSHandle NewEmptyEcmaModule(); + + JSHandle GetJSError(const ErrorType &errorType, const char *data = nullptr); + + JSHandle NewJSError(const ErrorType &errorType, const JSHandle &message); + + JSHandle NewTransitionHandler(); + + JSHandle NewPrototypeHandler(); + + JSHandle NewEmptyJSObject(); + + // use for others create, prototype is Function.prototype + // use for native function + JSHandle NewJSFunction(const JSHandle &env, const void *nativeFunc = nullptr, + FunctionKind kind = FunctionKind::NORMAL_FUNCTION); + // use for method + JSHandle NewJSFunction(const JSHandle &env, JSMethod *method, + FunctionKind kind = FunctionKind::NORMAL_FUNCTION); + + JSHandle NewJSNativeErrorFunction(const JSHandle &env, const void *nativeFunc = nullptr); + + JSHandle NewSpecificTypedArrayFunction(const JSHandle &env, + const void *nativeFunc = nullptr); + + JSHandle OrdinaryNewJSObjectCreate(const JSHandle &proto); + + JSHandle NewJSBoundFunction(const JSHandle &target, + const JSHandle &boundThis, + const JSHandle &args); + + JSHandle NewJSIntlBoundFunction(const void *nativeFunc = nullptr, int functionLength = 1); + + JSHandle NewJSProxyRevocFunction(const JSHandle &proxy, + const void *nativeFunc = nullptr); + + JSHandle NewJSAsyncAwaitStatusFunction(const void *nativeFunc); + JSHandle NewJSAsyncGeneratorResolveNextFunction(const void *nativeFunc); + JSHandle NewJSAsyncFromSyncIteratorValueUnwrapFunction( + const void *nativeFunc); + + JSHandle NewJSGeneratorFunction(JSMethod *method); + JSHandle NewJSAsyncGeneratorFunction(JSMethod *method); + + JSHandle NewAsyncFunction(JSMethod *method); + + JSHandle NewJSGeneratorObject(JSHandle generatorFunction); + JSHandle NewJSAsyncGeneratorObject(JSHandle generatorFunction); + JSHandle NewJSAsyncFromSyncIteratorObject(); + + JSHandle NewJSAsyncFuncObject(); + + JSHandle NewJSPrimitiveRef(const JSHandle &function, + const JSHandle &object); + JSHandle NewJSPrimitiveRef(PrimitiveType type, const JSHandle &object); + + // get JSHClass for Ecma ClassLinker + JSHandle NewGlobalEnv(JSHClass *globalEnvClass); + + // get JSHClass for Ecma ClassLinker + JSHandle NewLexicalEnv(int numSlots); + + inline LexicalEnv *InlineNewLexicalEnv(int numSlots); + + JSHandle NewJSSymbol(); + + JSHandle NewPrivateSymbol(); + + JSHandle NewPrivateNameSymbol(const JSHandle &name); + + JSHandle NewWellKnownSymbol(const JSHandle &name); + + JSHandle NewPublicSymbol(const JSHandle &name); + + JSHandle NewSymbolWithTable(const JSHandle &name); + + JSHandle NewPrivateNameSymbolWithChar(const char *description); + + JSHandle NewWellKnownSymbolWithChar(const char *description); + + JSHandle NewPublicSymbolWithChar(const char *description); + + JSHandle NewSymbolWithTableWithChar(const char *description); + + JSHandle NewAccessorData(); + JSHandle NewInternalAccessor(void *setter, void *getter); + + JSHandle NewPromiseCapability(); + + JSHandle NewPromiseReaction(); + + JSHandle NewPromiseRecord(); + + JSHandle NewResolvingFunctionsRecord(); + + JSHandle NewPromiseIteratorRecord(const JSHandle &itor, + const JSHandle &done); + + JSHandle NewMicroJobQueue(); + + JSHandle NewPendingJob(const JSHandle &func, const JSHandle &argv); + + JSHandle NewFunctionExtraInfo(const JSHandle &callBack, + const JSHandle &vm, + const JSHandle &data); + + JSHandle NewJSArray(); + + JSHandle NewJSProxy(const JSHandle &target, const JSHandle &handler); + JSHandle NewJSRealm(); + + JSHandle NewJSArguments(); + + JSHandle NewJSString(const JSHandle &str); + + 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); + JSHandle NewDictionaryArray(uint32_t length); + JSHandle NewJSForinIterator(const JSHandle &obj); + + JSHandle NewLinkedHashTable(array_size_t length, JSType tableType, bool isWeak); + JSHandle NewPropertyBox(const JSHandle &value); + + JSHandle NewProtoChangeMarker(); + + JSHandle NewProtoChangeDetails(); + + // use for copy properties keys's array to another array + JSHandle ExtendArray(const JSHandle &old, uint32_t length, + JSTaggedValue initVal = JSTaggedValue::Hole()); + JSHandle CopyPartArray(const JSHandle &old, uint32_t start, uint32_t end); + JSHandle CopyArray(const JSHandle &old, uint32_t oldLength, uint32_t newLength, + JSTaggedValue initVal = JSTaggedValue::Hole()); + JSHandle CloneProperties(const JSHandle &old); + JSHandle CloneProperties(const JSHandle &old, const JSHandle &env, + const JSHandle &obj, const JSHandle &constpool); + + JSHandle CreateLayoutInfo(int properties, JSTaggedValue initVal = JSTaggedValue::Hole()); + + JSHandle ExtendLayoutInfo(const JSHandle &old, int properties, + JSTaggedValue initVal = JSTaggedValue::Hole()); + + JSHandle CopyLayoutInfo(const JSHandle &old); + + JSHandle CopyAndReSort(const JSHandle &old, int end, int capacity); + + JSHandle GetEmptyString() const; + + JSHandle EmptyArray() const; + + JSHandle NewObjectWrapper(const JSHandle &value); + + FreeObject *FillFreeObject(uintptr_t address, size_t size, RemoveSlots removeSlots = RemoveSlots::NO); + + TaggedObject *NewDynObject(const JSHandle &dynclass); + + TaggedObject *NewNonMovableDynObject(const JSHandle &dynclass, int inobjPropCount = 0); + + void InitializeExtraProperties(const JSHandle &dynclass, TaggedObject *obj, int inobjPropCount); + + JSHandle NewTaggedQueue(uint32_t length); + + JSHandle GetEmptyTaggedQueue() const; + + JSHandle NewJSSetIterator(const JSHandle &set, IterationKind kind); + + JSHandle NewJSMapIterator(const JSHandle &map, IterationKind kind); + + JSHandle NewJSArrayIterator(const JSHandle &array, IterationKind kind); + + JSHandle NewCompletionRecord(uint8_t type, const JSHandle &value); + + JSHandle NewGeneratorContext(); + + JSHandle CreateJSPromiseReactionsFunction(const void *nativeFunc); + + JSHandle CreateJSPromiseExecutorFunction(const void *nativeFunc); + + JSHandle CreateBuiltinFunction(EcmaEntrypoint steps, uint8_t length, JSHandle name, + JSTaggedValue proto, JSHandle prefix); + + JSHandle NewJSPromiseAllResolveElementFunction(const void *nativeFunc); + + JSHandle CloneObjectLiteral(JSHandle object, const JSHandle &env, + const JSHandle &constpool, bool canShareHClass = true); + JSHandle CloneObjectLiteral(JSHandle object); + JSHandle CloneArrayLiteral(JSHandle object); + JSHandle CloneJSFuction(JSHandle obj, FunctionKind kind); + JSHandle CloneClassCtor(JSHandle ctor, const JSHandle &lexenv, + bool canShareHClass); + + void NewJSArrayBufferData(const JSHandle &array, int32_t length); + + JSHandle NewJSArrayBuffer(int32_t length); + + JSHandle NewJSArrayBuffer(void *buffer, int32_t length, const DeleteEntryPoint &deleter, void *data, + bool share = false); + + JSHandle NewJSDataView(JSHandle buffer, int32_t offset, int32_t length); + + void NewJSRegExpByteCodeData(const JSHandle ®exp, void *buffer, size_t size); + + template + inline void NewJSIntlIcuData(const JSHandle &obj, const S &icu, const DeleteEntryPoint &callback); + + EcmaString *InternString(const JSHandle &key); + + inline JSHandle NewJSNativePointer(void *externalPointer, + const DeleteEntryPoint &callBack = nullptr, + void *data = nullptr, bool nonMovable = false); + + JSHandle NewJSObjectByClass(const JSHandle &properties, size_t length); + + // only use for creating Function.prototype and Function + JSHandle NewJSFunctionByDynClass(JSMethod *method, const JSHandle &clazz, + FunctionKind kind = FunctionKind::NORMAL_FUNCTION); + EcmaString *ResolveString(uint32_t stringId); + + void ObtainRootClass(const JSHandle &globalEnv); + + const MemManager &GetHeapManager() const + { + return heapHelper_; + } + + // used for creating jsobject by constructor + JSHandle NewJSObjectByConstructor(const JSHandle &constructor, + const JSHandle &newTarget); + + uintptr_t NewSpaceBySnapShotAllocator(size_t size); + JSHandle NewMachineCodeObject(size_t length, const uint8_t *data); + JSHandle NewClassInfoExtractor(JSMethod *ctorMethod); + + ~ObjectFactory() = default; + + // ----------------------------------- new string ---------------------------------------- + JSHandle NewFromString(const CString &data); + JSHandle NewFromCanBeCompressString(const CString &data); + + JSHandle NewFromStdString(const std::string &data); + JSHandle NewFromStdStringUnCheck(const std::string &data, bool canBeCompress); + + JSHandle NewFromUtf8(const uint8_t *utf8Data, uint32_t utf8Len); + JSHandle NewFromUtf8UnCheck(const uint8_t *utf8Data, uint32_t utf8Len, bool canBeCompress); + + JSHandle NewFromUtf16(const uint16_t *utf16Data, uint32_t utf16Len); + JSHandle NewFromUtf16UnCheck(const uint16_t *utf16Data, uint32_t utf16Len, bool canBeCompress); + + JSHandle NewFromUtf8Literal(const uint8_t *utf8Data, uint32_t utf8Len); + JSHandle NewFromUtf8LiteralUnCheck(const uint8_t *utf8Data, uint32_t utf8Len, bool canBeCompress); + + JSHandle NewFromUtf16Literal(const uint16_t *utf16Data, uint32_t utf16Len); + JSHandle NewFromUtf16LiteralUnCheck(const uint16_t *utf16Data, uint32_t utf16Len, bool canBeCompress); + + JSHandle NewFromString(EcmaString *str); + JSHandle ConcatFromString(const JSHandle &prefix, const JSHandle &suffix); + + // used for creating Function + JSHandle NewJSObject(const JSHandle &jshclass); + + // used for creating jshclass in GlobalEnv, EcmaVM + JSHandle NewEcmaDynClass(JSHClass *hclass, uint32_t size, JSType type, uint32_t flags = 0, + uint32_t inlinedProps = JSHClass::DEFAULT_CAPACITY_OF_IN_OBJECTS); + + // used for creating jshclass in Builtins, Function, Class_Linker + JSHandle NewEcmaDynClass(uint32_t size, JSType type, const JSHandle &prototype, + uint32_t flags = 0); + + void SetTriggerGc(bool status) + { + isTriggerGc_ = status; + } + +private: + friend class GlobalEnv; + friend class GlobalEnvConstants; + friend class EcmaString; + JSHandle NewJSFunctionImpl(JSMethod *method); + + void InitObjectFields(TaggedObject *object); + + JSThread *thread_ {nullptr}; + bool isTriggerGc_ {false}; + bool triggerSemiGC_ {false}; + MemManager heapHelper_; + + JSHClass *hclassClass_ {nullptr}; + JSHClass *stringClass_ {nullptr}; + JSHClass *arrayClass_ {nullptr}; + JSHClass *dictionaryClass_ {nullptr}; + JSHClass *freeObjectWithNoneFieldClass_ {nullptr}; + JSHClass *freeObjectWithOneFieldClass_ {nullptr}; + JSHClass *freeObjectWithTwoFieldClass_ {nullptr}; + + JSHClass *completionRecordClass_ {nullptr}; + JSHClass *generatorContextClass_ {nullptr}; + JSHClass *envClass_ {nullptr}; + JSHClass *symbolClass_ {nullptr}; + JSHClass *accessorDataClass_ {nullptr}; + JSHClass *internalAccessorClass_ {nullptr}; + JSHClass *capabilityRecordClass_ {nullptr}; + JSHClass *reactionsRecordClass_ {nullptr}; + JSHClass *promiseIteratorRecordClass_ {nullptr}; + JSHClass *microJobQueueClass_ {nullptr}; + JSHClass *pendingJobClass_ {nullptr}; + JSHClass *jsProxyOrdinaryClass_ {nullptr}; + JSHClass *jsProxyCallableClass_ {nullptr}; + JSHClass *jsProxyConstructClass_ {nullptr}; + JSHClass *objectWrapperClass_ {nullptr}; + JSHClass *PropertyBoxClass_ {nullptr}; + JSHClass *protoChangeDetailsClass_ {nullptr}; + JSHClass *protoChangeMarkerClass_ {nullptr}; + JSHClass *promiseRecordClass_ {nullptr}; + JSHClass *promiseResolvingFunctionsRecord_ {nullptr}; + JSHClass *jsNativePointerClass_ {nullptr}; + JSHClass *transitionHandlerClass_ {nullptr}; + JSHClass *prototypeHandlerClass_ {nullptr}; + JSHClass *functionExtraInfo_ {nullptr}; + JSHClass *jsRealmClass_ {nullptr}; + JSHClass *programClass_ {nullptr}; + JSHClass *machineCodeClass_ {nullptr}; + JSHClass *ecmaModuleClass_ {nullptr}; + JSHClass *classInfoExtractorHClass_ {nullptr}; + JSHClass *linkedHashMapClass_ {nullptr}; + JSHClass *linkedHashSetClass_ {nullptr}; + JSHClass *weakLinkedHashMapClass_ {nullptr}; + JSHClass *weakLinkedHashSetClass_ {nullptr}; + + EcmaVM *vm_ {nullptr}; + Heap *heap_ {nullptr}; + + NO_COPY_SEMANTIC(ObjectFactory); + NO_MOVE_SEMANTIC(ObjectFactory); + + void NewObjectHook() const; + + // used for creating jshclass in Builtins, Function, Class_Linker + JSHandle NewEcmaDynClass(uint32_t size, JSType type, + uint32_t inlinedProps = JSHClass::DEFAULT_CAPACITY_OF_IN_OBJECTS); + // used for creating jshclass in GlobalEnv, EcmaVM + JSHandle NewEcmaDynClassClass(JSHClass *hclass, uint32_t size, JSType type); + + // used to create nonmovable js_object + JSHandle NewNonMovableJSObject(const JSHandle &jshclass); + + // used for creating Function + JSHandle NewJSFunction(const JSHandle &env, const JSHandle &dynKlass); + JSHandle CreateObjectClass(const JSHandle &properties, size_t length); + JSHandle CreateFunctionClass(FunctionKind kind, uint32_t size, JSType type, + const JSHandle &prototype); + + // used for creating ref.prototype in buildins, such as Number.prototype + JSHandle NewJSPrimitiveRef(const JSHandle &dynKlass, + const JSHandle &object); + + JSHandle GetStringFromStringTable(const uint8_t *utf8Data, uint32_t utf8Len, bool canBeCompress) const; + // For MUtf-8 string data + EcmaString *GetRawStringFromStringTable(const uint8_t *mutf8Data, uint32_t utf16Len, bool canBeCompressed) const; + + JSHandle GetStringFromStringTable(const uint16_t *utf16Data, uint32_t utf16Len, + bool canBeCompress) const; + + JSHandle GetStringFromStringTable(EcmaString *string) const; + + inline EcmaString *AllocStringObject(size_t size); + inline EcmaString *AllocNonMovableStringObject(size_t size); + JSHandle NewEmptyArray(); // only used for EcmaVM. + + JSHandle CreateJSArguments(); + JSHandle CreateJSArrayInstanceClass(JSHandle proto); + JSHandle CreateJSRegExpInstanceClass(JSHandle proto); + + friend class Builtins; // create builtins object need dynclass + friend class JSFunction; // create prototype_or_dynclass need dynclass + friend class JSHClass; // HC transition need dynclass + friend class EcmaVM; // hold the factory instance + friend class JsVerificationTest; + friend class PandaFileTranslator; + friend class LiteralDataExtractor; + friend class RuntimeTrampolines; + friend class ClassInfoExtractor; + friend class DynamicObjectHelpersTest; +}; + +class ClassLinkerFactory { +private: + friend class GlobalEnv; // root class in class_linker need dynclass + friend class EcmaVM; // root class in class_linker need dynclass +}; +} // namespace panda::ecmascript +#endif // ECMASCRIPT_OBJECT_FACTORY_H diff --git a/runtime/object_operator.cpp b/runtime/object_operator.cpp new file mode 100644 index 000000000..7b7e171bc --- /dev/null +++ b/runtime/object_operator.cpp @@ -0,0 +1,703 @@ +/* + * Copyright (c) 2021-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 "plugins/ecmascript/runtime/object_operator.h" +#include "plugins/ecmascript/runtime/ecma_vm.h" +#include "plugins/ecmascript/runtime/accessor_data.h" +#include "plugins/ecmascript/runtime/global_dictionary-inl.h" +#include "plugins/ecmascript/runtime/global_env.h" +#include "plugins/ecmascript/runtime/ic/property_box.h" +#include "plugins/ecmascript/runtime/interpreter/fast_runtime_stub-inl.h" +#include "plugins/ecmascript/runtime/js_array.h" +#include "plugins/ecmascript/runtime/js_function.h" +#include "plugins/ecmascript/runtime/js_hclass-inl.h" +#include "plugins/ecmascript/runtime/js_object-inl.h" +#include "plugins/ecmascript/runtime/js_primitive_ref.h" +#include "plugins/ecmascript/runtime/mem/c_string.h" +#include "plugins/ecmascript/runtime/object_factory.h" +#include "plugins/ecmascript/runtime/tagged_dictionary.h" +#include "plugins/ecmascript/runtime/global_dictionary.h" + +namespace panda::ecmascript { +void ObjectOperator::HandleKey(const JSHandle &key) +{ + if (key->IsInt()) { + int32_t keyInt = key->GetInt(); + if (keyInt >= 0) { + elementIndex_ = static_cast(keyInt); + return; + } + key_ = JSHandle::Cast(base::NumberHelper::NumberToString(thread_, JSTaggedValue(keyInt))); + return; + } + + if (key->IsString()) { + uint32_t index = 0; + if (JSTaggedValue::ToElementIndex(key.GetTaggedValue(), &index)) { + ASSERT(index < JSObject::MAX_ELEMENT_INDEX); + elementIndex_ = index; + return; + } + if (EcmaString::Cast(key->GetTaggedObject())->IsInternString()) { + key_ = key; + return; + } + key_ = JSHandle(thread_, thread_->GetEcmaVM()->GetFactory()->InternString(key)); + return; + } + + if (key->IsDouble()) { + double number = key->GetDouble(); + if (number >= 0 && number < JSObject::MAX_ELEMENT_INDEX) { + auto integer = static_cast(number); + if (integer == number) { + elementIndex_ = static_cast(number); + return; + } + } + key_ = JSHandle::Cast(base::NumberHelper::NumberToString(thread_, key.GetTaggedValue())); + return; + } + + if (key->IsSymbol()) { + key_ = key; + return; + } + + JSHandle keyHandle(thread_, JSTaggedValue::ToPrimitive(thread_, key, PREFER_STRING)); + if (key->IsSymbol()) { + key_ = keyHandle; + return; + } + key_ = JSHandle(thread_, + thread_->GetEcmaVM()->GetFactory()->InternString( + JSHandle::Cast(JSTaggedValue::ToString(thread_, keyHandle)))); +} + +void ObjectOperator::UpdateHolder() +{ + if (holder_->IsString() && + (IsElement() && elementIndex_ < EcmaString::Cast(holder_->GetTaggedObject())->GetLength())) { + holder_.Update(JSPrimitiveRef::StringCreate(thread_, holder_).GetTaggedValue()); + } else { + holder_.Update(JSTaggedValue::ToPrototypeOrObj(thread_, holder_).GetTaggedValue()); + } +} + +void ObjectOperator::StartLookUp(OperatorType type) +{ + UpdateHolder(); + + if (type == OperatorType::OWN) { + LookupPropertyInHolder(); + } else { + LookupProperty(); + } +} + +void ObjectOperator::StartGlobalLookUp(OperatorType type) +{ + UpdateHolder(); + + if (type == OperatorType::OWN) { + GlobalLookupPropertyInHolder(); + } else { + GlobalLookupProperty(); + } +} + +ObjectOperator::ObjectOperator(JSThread *thread, const JSHandle &key, OperatorType type) + : thread_(thread), + holder_(thread, thread->GetEcmaVM()->GetGlobalEnv()->GetGlobalObject()), + receiver_(thread, thread->GetEcmaVM()->GetGlobalEnv()->GetGlobalObject()) +{ + HandleKey(key); + StartGlobalLookUp(type); +} + +ObjectOperator::ObjectOperator(JSThread *thread, const JSHandle &holder, const JSHandle &key, + OperatorType type) + : thread_(thread), holder_(thread, holder.GetTaggedValue()), receiver_(thread, holder.GetTaggedValue()) +{ + HandleKey(key); + StartLookUp(type); +} + +ObjectOperator::ObjectOperator(JSThread *thread, const JSHandle &holder, + const JSHandle &key, OperatorType type) + : thread_(thread), holder_(thread, holder.GetTaggedValue()), receiver_(thread, holder.GetTaggedValue()) +{ + HandleKey(key); + StartLookUp(type); +} + +ObjectOperator::ObjectOperator(JSThread *thread, const JSHandle &holder, uint32_t index, + OperatorType type) + : thread_(thread), + holder_(thread, holder.GetTaggedValue()), + receiver_(thread, holder.GetTaggedValue()), + elementIndex_(index) +{ + StartLookUp(type); +} + +ObjectOperator::ObjectOperator(JSThread *thread, const JSHandle &holder, + const JSHandle &receiver, const JSHandle &key, + OperatorType type) + : thread_(thread), holder_(thread, holder.GetTaggedValue()), receiver_(thread, receiver.GetTaggedValue()) +{ + SetHasReceiver(true); + HandleKey(key); + StartLookUp(type); +} + +// op for fast path +ObjectOperator::ObjectOperator(JSThread *thread, const JSTaggedValue &receiver, const JSTaggedValue &name, + OperatorType type) + : thread_(thread), holder_(thread, receiver), receiver_(thread, receiver), key_(thread, name) +{ + ASSERT(name.IsStringOrSymbol()); + StartLookUp(type); +} +JSHandle ObjectOperator::FastGetValue() +{ + ASSERT(IsFound() && !value_.IsEmpty()); + if (value_->IsPropertyBox()) { + value_.Update(PropertyBox::Cast(value_->GetTaggedObject())->GetValue()); + } + if (!IsAccessorDescriptor()) { + return value_; + } + AccessorData *accessor = AccessorData::Cast(value_->GetTaggedObject()); + ASSERT(!accessor->IsInternal()); + // 8. Return Call(getter, Receiver). + return JSHandle(thread_, JSObject::CallGetter(thread_, accessor, receiver_)); +} +ObjectOperator::ObjectOperator(JSThread *thread, const JSTaggedValue &receiver, const JSTaggedValue &name, + const PropertyAttributes &attr) + : thread_(thread), receiver_(thread, receiver), key_(thread, name) +{ + SetAttr(attr); +} +void ObjectOperator::FastAdd(JSThread *thread, const JSTaggedValue &receiver, const JSTaggedValue &name, + const JSHandle &value, const PropertyAttributes &attr) +{ + ObjectOperator op(thread, receiver, name, attr); + op.AddPropertyInternal(value); +} + +void ObjectOperator::ToPropertyDescriptor(PropertyDescriptor &desc) const +{ + DISALLOW_GARBAGE_COLLECTION; + if (!IsFound()) { + return; + } + + if (!IsAccessorDescriptor()) { + desc.SetWritable(IsWritable()); + JSTaggedValue val = GetValue(); + desc.SetValue(JSHandle(thread_, val)); + } else { + JSTaggedValue value = GetValue(); + if (value.IsPropertyBox()) { + value = PropertyBox::Cast(value.GetTaggedObject())->GetValue(); + } + + AccessorData *accessor = AccessorData::Cast(value.GetTaggedObject()); + + if (UNLIKELY(accessor->IsInternal())) { + desc.SetWritable(IsWritable()); + auto val = accessor->CallInternalGet(thread_, JSHandle::Cast(GetHolder())); + desc.SetValue(JSHandle(thread_, val)); + } else { + desc.SetGetter(JSHandle(thread_, accessor->GetGetter())); + desc.SetSetter(JSHandle(thread_, accessor->GetSetter())); + } + } + + desc.SetEnumerable(IsEnumerable()); + desc.SetConfigurable(IsConfigurable()); +} + +void ObjectOperator::GlobalLookupProperty() +{ + GlobalLookupPropertyInHolder(); + if (IsFound()) { + return; + } + JSTaggedValue proto = JSObject::Cast(holder_->GetTaggedObject())->GetPrototype(thread_); + if (!proto.IsHeapObject()) { + return; + } + holder_.Update(proto); + if (holder_->IsJSProxy()) { + return; + } + SetIsOnPrototype(true); + LookupProperty(); +} + +void ObjectOperator::LookupProperty() +{ + while (true) { + LookupPropertyInHolder(); + if (IsFound()) { + return; + } + + JSTaggedValue proto = holder_.GetObject()->GetPrototype(thread_); + if (!proto.IsHeapObject()) { + return; + } + + holder_.Update(proto); + if (holder_->IsJSProxy()) { + return; + } + + SetIsOnPrototype(true); + } +} + +void ObjectOperator::LookupGlobal(const JSHandle &obj) +{ + ASSERT(obj->IsJSGlobalObject()); + if (IsElement()) { + LookupElementInlinedProps(obj); + return; + } + TaggedArray *array = TaggedArray::Cast(obj->GetProperties().GetTaggedObject()); + if (array->GetLength() == 0) { + return; + } + GlobalDictionary *dict = GlobalDictionary::Cast(array); + int entry = dict->FindEntry(key_.GetTaggedValue()); + if (entry == -1) { + return; + } + JSTaggedValue value(dict->GetBox(entry)); + uint32_t attr = dict->GetAttributes(entry).GetValue(); + SetFound(entry, value, attr, true); +} + +void ObjectOperator::LookupPropertyInlinedProps(const JSHandle &obj) +{ + if (IsElement()) { + LookupElementInlinedProps(obj); + return; + } + + if (obj->IsJSGlobalObject()) { + TaggedArray *array = TaggedArray::Cast(obj->GetProperties().GetTaggedObject()); + if (array->GetLength() == 0) { + return; + } + + GlobalDictionary *dict = GlobalDictionary::Cast(array); + int entry = dict->FindEntry(key_.GetTaggedValue()); + if (entry == -1) { + return; + } + + JSTaggedValue value(dict->GetBox(entry)); + uint32_t attr = dict->GetAttributes(entry).GetValue(); + SetFound(entry, value, attr, true); + return; + } + + TaggedArray *array = TaggedArray::Cast(obj->GetProperties().GetTaggedObject()); + if (!array->IsDictionaryMode()) { + JSHClass *jshclass = obj->GetJSHClass(); + JSTaggedValue attrs = jshclass->GetLayout(); + LayoutInfo *layoutInfo = LayoutInfo::Cast(attrs.GetTaggedObject()); + int propsNumber = jshclass->NumberOfProps(); + int entry = layoutInfo->FindElementWithCache(thread_, jshclass, key_.GetTaggedValue(), propsNumber); + if (entry == -1) { + return; + } + PropertyAttributes attr(layoutInfo->GetAttr(entry)); + ASSERT(entry == static_cast(attr.GetOffset())); + JSTaggedValue value; + if (attr.IsInlinedProps()) { + value = obj->GetPropertyInlinedProps(entry); + } else { + entry -= jshclass->GetInlinedProperties(); + value = array->Get(entry); + } + + SetFound(entry, value, attr.GetValue(), true); + return; + } + + NameDictionary *dict = NameDictionary::Cast(array); + int entry = dict->FindEntry(key_.GetTaggedValue()); + if (entry == -1) { + return; + } + + JSTaggedValue value = dict->GetValue(entry); + uint32_t attr = dict->GetAttributes(entry).GetValue(); + SetFound(entry, value, attr, false); +} + +void ObjectOperator::TransitionForAttributeChanged(const JSHandle &receiver, PropertyAttributes attr) +{ + if (IsElement()) { + uint32_t index = GetIndex(); + if (!receiver->GetJSHClass()->IsDictionaryElement()) { + JSObject::ElementsToDictionary(thread_, receiver); + auto dict = NumberDictionary::Cast(receiver->GetElements().GetTaggedObject()); + index = dict->FindEntry(JSTaggedValue(index)); + PropertyAttributes origin = dict->GetAttributes(index); + attr.SetDictionaryOrder(origin.GetDictionaryOrder()); + dict->SetAttributes(thread_, index, attr); + } else { + auto dict = NumberDictionary::Cast(receiver->GetElements().GetTaggedObject()); + dict->SetAttributes(thread_, index, attr); + } + // update found result + UpdateFound(index, attr.GetValue(), false, true); + } else if (receiver->IsJSGlobalObject()) { + JSHandle dictHandle(thread_, receiver->GetProperties()); + GlobalDictionary::InvalidatePropertyBox(thread_, dictHandle, GetIndex(), attr); + } else { + uint32_t index = GetIndex(); + if (!receiver->GetJSHClass()->IsDictionaryMode()) { + JSHandle dict(JSObject::TransitionToDictionary(thread_, receiver)); + index = dict->FindEntry(key_.GetTaggedValue()); + PropertyAttributes origin = dict->GetAttributes(index); + attr.SetDictionaryOrder(origin.GetDictionaryOrder()); + dict->SetAttributes(thread_, index, attr); + } else { + auto dict = NameDictionary::Cast(receiver->GetProperties().GetTaggedObject()); + dict->SetAttributes(thread_, index, attr); + } + // update found result + UpdateFound(index, attr.GetValue(), false, true); + } +} + +bool ObjectOperator::UpdateValueAndDetails(const JSHandle &receiver, const JSHandle &value, + PropertyAttributes attr, bool attrChanged) +{ + bool isInternalAccessor = IsAccessorDescriptor() && AccessorData::Cast(GetValue().GetHeapObject())->IsInternal(); + if (attrChanged) { + TransitionForAttributeChanged(receiver, attr); + } + return UpdateDataValue(receiver, value, isInternalAccessor); +} + +bool ObjectOperator::UpdateDataValue(const JSHandle &receiver, const JSHandle &value, + bool isInternalAccessor, bool mayThrow) +{ + if (IsElement()) { + TaggedArray *elements = TaggedArray::Cast(receiver->GetElements().GetTaggedObject()); + if (!elements->IsDictionaryMode()) { + elements->Set(thread_, GetIndex(), value.GetTaggedValue()); + return true; + } + + NumberDictionary *dict = NumberDictionary::Cast(elements); + dict->UpdateValue(thread_, GetIndex(), value.GetTaggedValue()); + return true; + } + + if (receiver->IsJSGlobalObject()) { + // need update cell type ? + auto *dict = GlobalDictionary::Cast(receiver->GetProperties().GetTaggedObject()); + PropertyBox *cell = dict->GetBox(GetIndex()); + cell->SetValue(thread_, value.GetTaggedValue()); + return true; + } + + if (isInternalAccessor) { + auto accessor = AccessorData::Cast(GetValue().GetHeapObject()); + if (accessor->HasSetter()) { + return accessor->CallInternalSet(thread_, JSHandle(receiver), value, mayThrow); + } + } + + JSHandle properties(thread_, receiver->GetProperties().GetHeapObject()); + if (!properties->IsDictionaryMode()) { + PropertyAttributes attr = GetAttr(); + if (attr.IsInlinedProps()) { + receiver->SetPropertyInlinedProps(thread_, GetIndex(), value.GetTaggedValue()); + } else { + properties->Set(thread_, GetIndex(), value.GetTaggedValue()); + } + } else { + properties.GetObject()->UpdateValue(thread_, GetIndex(), value.GetTaggedValue()); + } + return true; +} + +bool ObjectOperator::WriteDataProperty(const JSHandle &receiver, const PropertyDescriptor &desc) +{ + PropertyAttributes attr = GetAttr(); + bool attrChanged = false; + + // composed new attribute from desc + if (desc.HasConfigurable() && attr.IsConfigurable() != desc.IsConfigurable()) { + attr.SetConfigurable(desc.IsConfigurable()); + attrChanged = true; + } + if (desc.HasEnumerable() && attr.IsEnumerable() != desc.IsEnumerable()) { + attr.SetEnumerable(desc.IsEnumerable()); + attrChanged = true; + } + + if (!desc.IsAccessorDescriptor()) { + if (desc.HasWritable() && attr.IsWritable() != desc.IsWritable()) { + attr.SetWritable(desc.IsWritable()); + attrChanged = true; + } + if (!desc.HasValue()) { + if (attrChanged) { + TransitionForAttributeChanged(receiver, attr); + } + return true; + } + + if (IsAccessorDescriptor()) { + auto accessor = AccessorData::Cast(GetValue().GetHeapObject()); + if (!accessor->IsInternal() || !accessor->HasSetter()) { + attr.SetIsAccessor(false); + attrChanged = true; + } + } + + return UpdateValueAndDetails(receiver, desc.GetValue(), attr, attrChanged); + } + + if (IsAccessorDescriptor() && !IsElement()) { + TaggedArray *properties = TaggedArray::Cast(receiver->GetProperties().GetTaggedObject()); + if (!properties->IsDictionaryMode()) { + // as some accessorData is in globalEnv, we need to new accessorData. + JSHandle accessor = thread_->GetEcmaVM()->GetFactory()->NewAccessorData(); + + if (desc.HasGetter()) { + accessor->SetGetter(thread_, desc.GetGetter().GetTaggedValue()); + } else { + accessor->SetGetter(thread_, JSHandle::Cast(value_)->GetGetter()); + } + if (desc.HasSetter()) { + accessor->SetSetter(thread_, desc.GetSetter().GetTaggedValue()); + } else { + accessor->SetSetter(thread_, JSHandle::Cast(value_)->GetSetter()); + } + + JSHandle dict(JSObject::TransitionToDictionary(thread_, receiver)); + int entry = dict->FindEntry(key_.GetTaggedValue()); + ASSERT(entry != -1); + attr.SetDictionaryOrder(dict->GetAttributes(entry).GetDictionaryOrder()); + dict->UpdateValueAndAttributes(thread_, entry, accessor.GetTaggedValue(), attr); + return true; + } + } + + JSHandle accessor = IsAccessorDescriptor() ? JSHandle::Cast(value_) + : thread_->GetEcmaVM()->GetFactory()->NewAccessorData(); + if (desc.HasGetter()) { + accessor->SetGetter(thread_, desc.GetGetter().GetTaggedValue()); + } + + if (desc.HasSetter()) { + accessor->SetSetter(thread_, desc.GetSetter().GetTaggedValue()); + } + + if (!IsAccessorDescriptor()) { + attr.SetIsAccessor(true); + attrChanged = true; + } + + JSHandle value = JSHandle::Cast(accessor); + return UpdateValueAndDetails(receiver, value, attr, attrChanged); +} + +void ObjectOperator::DeletePropertyInHolder() +{ + if (IsElement()) { + return DeleteElementInHolder(); + } + + JSObject::DeletePropertyInternal(thread_, JSHandle(holder_), key_, GetIndex()); +} + +bool ObjectOperator::AddProperty(const JSHandle &receiver, const JSHandle &value, + PropertyAttributes attr) +{ + if (IsElement()) { + return JSObject::AddElementInternal(thread_, receiver, elementIndex_, value, attr); + } + + ResetState(); + receiver_.Update(receiver.GetTaggedValue()); + SetAttr(attr.GetValue()); + AddPropertyInternal(value); + return true; +} + +void ObjectOperator::WriteElement(const JSHandle &receiver, JSTaggedValue value) const +{ + ASSERT(IsElement() && GetIndex() < JSObject::MAX_ELEMENT_INDEX); + + TaggedArray *elements = TaggedArray::Cast(receiver->GetElements().GetTaggedObject()); + if (!elements->IsDictionaryMode()) { + elements->Set(thread_, index_, value); + receiver->GetJSHClass()->UpdateRepresentation(value); + return; + } + + NumberDictionary *dictionary = NumberDictionary::Cast(elements); + dictionary->UpdateValue(thread_, GetIndex(), value); +} + +void ObjectOperator::DeleteElementInHolder() const +{ + JSHandle obj(holder_); + + TaggedArray *elements = TaggedArray::Cast(obj->GetElements().GetTaggedObject()); + if (!elements->IsDictionaryMode()) { + elements->Set(thread_, index_, JSTaggedValue::Hole()); + JSObject::ElementsToDictionary(thread_, JSHandle(holder_)); + } else { + JSHandle dictHandle(thread_, elements); + JSHandle newDict = NumberDictionary::Remove(thread_, dictHandle, GetIndex()); + obj->SetElements(thread_, newDict); + } +} + +void ObjectOperator::SetFound(uint32_t index, JSTaggedValue value, uint32_t attr, bool mode, bool transition) +{ + SetIndex(index); + SetValue(value); + SetFastMode(mode); + SetIsTransition(transition); + SetAttr(attr); +} + +void ObjectOperator::UpdateFound(uint32_t index, uint32_t attr, bool mode, bool transition) +{ + SetIndex(index); + SetFastMode(mode); + SetIsTransition(transition); + SetAttr(attr); +} + +void ObjectOperator::ResetState() +{ + // index may used by element + SetIndex(NOT_FOUND_INDEX); + SetValue(JSTaggedValue::Undefined()); + SetFastMode(false); + SetAttr(0); + SetIsOnPrototype(false); + SetHasReceiver(false); +} + +void ObjectOperator::LookupElementInlinedProps(const JSHandle &obj) +{ + // if is js string, do special. + if (obj->IsJSPrimitiveRef() && JSPrimitiveRef::Cast(obj.GetTaggedValue().GetTaggedObject())->IsString()) { + PropertyDescriptor desc(thread_); + bool status = JSPrimitiveRef::StringGetIndexProperty(thread_, obj, elementIndex_, &desc); + if (status) { + PropertyAttributes attr(desc); + SetFound(elementIndex_, desc.GetValue().GetTaggedValue(), attr.GetValue(), true); + return; + } + } + { + DISALLOW_GARBAGE_COLLECTION; + TaggedArray *elements = TaggedArray::Cast(obj->GetElements().GetTaggedObject()); + if (elements->GetLength() == 0) { + return; // Empty Array + } + + if (!elements->IsDictionaryMode()) { + if (elements->GetLength() <= elementIndex_) { + return; + } + + JSTaggedValue value = elements->Get(elementIndex_); + if (value.IsHole()) { + return; + } + SetFound(elementIndex_, value, PropertyAttributes::GetDefaultAttributes(), true); + } else { + NumberDictionary *dictionary = NumberDictionary::Cast(obj->GetElements().GetTaggedObject()); + JSTaggedValue key(static_cast(elementIndex_)); + int entry = dictionary->FindEntry(key); + if (entry == -1) { + return; + } + + uint32_t attr = dictionary->GetAttributes(entry).GetValue(); + SetFound(entry, dictionary->GetValue(entry), attr, false); + } + } +} + +void ObjectOperator::AddPropertyInternal(const JSHandle &value) +{ + ObjectFactory *factory = thread_->GetEcmaVM()->GetFactory(); + JSHandle obj(GetReceiver()); + PropertyAttributes attr = GetAttr(); + if (obj->IsJSGlobalObject()) { + JSMutableHandle dict(thread_, obj->GetProperties()); + if (dict->GetLength() == 0) { + dict.Update(GlobalDictionary::Create(thread_)); + } + + // Add PropertyBox to global dictionary + JSHandle cellHandle = factory->NewPropertyBox(key_); + cellHandle->SetValue(thread_, value.GetTaggedValue()); + PropertyBoxType cellType = value->IsUndefined() ? PropertyBoxType::UNDEFINED : PropertyBoxType::CONSTANT; + attr.SetBoxType(cellType); + + JSHandle properties = + GlobalDictionary::PutIfAbsent(thread_, dict, key_, JSHandle(cellHandle), attr); + obj->SetProperties(thread_, properties); + // index and fastMode is not essential for global obj; + SetFound(0, cellHandle.GetTaggedValue(), attr.GetValue(), true); + return; + } + + attr = FastRuntimeStub::AddPropertyByName(thread_, obj, key_, value, attr); + if (obj->GetJSHClass()->IsDictionaryMode()) { + SetFound(0, value.GetTaggedValue(), attr.GetValue(), false); + } else { + uint32_t index = + attr.IsInlinedProps() ? attr.GetOffset() : attr.GetOffset() - obj->GetJSHClass()->GetInlinedProperties(); + SetFound(index, value.GetTaggedValue(), attr.GetValue(), true, true); + } +} + +void ObjectOperator::DefineSetter(const JSHandle &value) +{ + ASSERT(IsAccessorDescriptor()); + JSHandle accessor = JSHandle::Cast(value_); + accessor->SetSetter(thread_, value.GetTaggedValue()); + UpdateDataValue(JSHandle::Cast(receiver_), JSHandle::Cast(accessor), false); +} + +void ObjectOperator::DefineGetter(const JSHandle &value) +{ + ASSERT(IsAccessorDescriptor()); + JSHandle accessor = JSHandle::Cast(value_); + accessor->SetGetter(thread_, value.GetTaggedValue()); + UpdateDataValue(JSHandle::Cast(receiver_), JSHandle::Cast(accessor), false); +} +} // namespace panda::ecmascript diff --git a/runtime/object_operator.h b/runtime/object_operator.h new file mode 100644 index 000000000..167ac85ca --- /dev/null +++ b/runtime/object_operator.h @@ -0,0 +1,307 @@ +/* + * Copyright (c) 2021-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_OBJECT_OPERATOR_H +#define ECMASCRIPT_OBJECT_OPERATOR_H + +#include "plugins/ecmascript/runtime/js_handle.h" +#include "plugins/ecmascript/runtime/js_object.h" +#include "plugins/ecmascript/runtime/property_attributes.h" +#include "libpandabase/utils/bit_field.h" + +namespace panda::ecmascript { +class PropertyDescriptor; + +enum class OperatorType : uint8_t { + PROTOTYPE_CHAIN, + OWN, +}; + +class ObjectOperator final { +public: + explicit ObjectOperator() = default; + + explicit ObjectOperator(JSThread *thread, const JSHandle &key, + OperatorType type = OperatorType::PROTOTYPE_CHAIN); + + explicit ObjectOperator(JSThread *thread, const JSHandle &holder, const JSHandle &key, + OperatorType type = OperatorType::PROTOTYPE_CHAIN); + + explicit ObjectOperator(JSThread *thread, const JSHandle &holder, const JSHandle &key, + OperatorType type = OperatorType::PROTOTYPE_CHAIN); + + explicit ObjectOperator(JSThread *thread, const JSHandle &holder, + const JSHandle &receiver, const JSHandle &key, + OperatorType type = OperatorType::PROTOTYPE_CHAIN); + + explicit ObjectOperator(JSThread *thread, const JSHandle &holder, uint32_t index, + OperatorType type = OperatorType::PROTOTYPE_CHAIN); + // op for fast path, name can only string and symbol, and can't be number. + explicit ObjectOperator(JSThread *thread, const JSTaggedValue &receiver, const JSTaggedValue &name, + OperatorType type = OperatorType::PROTOTYPE_CHAIN); + // op for fast add + explicit ObjectOperator(JSThread *thread, const JSTaggedValue &receiver, const JSTaggedValue &name, + const PropertyAttributes &attr); + + static void FastAdd(JSThread *thread, const JSTaggedValue &receiver, const JSTaggedValue &name, + const JSHandle &value, const PropertyAttributes &attr); + + NO_COPY_SEMANTIC(ObjectOperator); + DEFAULT_NOEXCEPT_MOVE_SEMANTIC(ObjectOperator); + ~ObjectOperator() = default; + + /** + * Create ObjectOperator instance by new operator is forbidden, for the member holder is a JSHandle type. it must + * be created and destroyed on stack + */ + void *operator new([[maybe_unused]] size_t t) = delete; + void operator delete([[maybe_unused]] void *ptr) = delete; + + inline bool IsFound() const + { + return index_ != NOT_FOUND_INDEX; + } + + inline bool IsFastMode() const + { + return IsFastModeField::Get(metaData_); + } + + inline void SetFastMode(bool flag) + { + IsFastModeField::Set(flag, &metaData_); + } + + inline bool IsElement() const + { + return key_.IsEmpty(); + } + + inline bool IsOnPrototype() const + { + return IsOnPrototypeField::Get(metaData_); + } + + inline void SetIsOnPrototype(bool flag) + { + IsOnPrototypeField::Set(flag, &metaData_); + } + + inline bool HasReceiver() const + { + return HasReceiverField::Get(metaData_); + } + + inline void SetHasReceiver(bool flag) + { + HasReceiverField::Set(flag, &metaData_); + } + + inline bool IsTransition() const + { + return IsTransitionField::Get(metaData_); + } + + inline void SetIsTransition(bool flag) + { + IsTransitionField::Set(flag, &metaData_); + } + + inline PropertyAttributes GetAttr() const + { + return attributes_; + } + + inline void SetAttr(uint32_t attr) + { + attributes_ = PropertyAttributes(attr); + } + + inline void SetAttr(const PropertyAttributes &attr) + { + attributes_ = PropertyAttributes(attr); + } + + inline bool IsPrimitiveAttr() const + { + return attributes_.GetValue() == 0U; + } + + inline bool IsWritable() const + { + return GetAttr().IsWritable(); + } + + inline bool IsEnumerable() const + { + return GetAttr().IsEnumerable(); + } + + inline bool IsConfigurable() const + { + return GetAttr().IsConfigurable(); + } + + inline bool IsAccessorDescriptor() const + { + return GetAttr().IsAccessor(); + } + + inline bool IsInlinedProps() const + { + return GetAttr().IsInlinedProps(); + } + + inline JSTaggedValue GetValue() const + { + if (value_.IsEmpty()) { + return JSTaggedValue::Undefined(); + } + return value_.GetTaggedValue(); + } + + JSHandle FastGetValue(); + inline void SetValue(JSTaggedValue value) + { + if (value_.IsEmpty()) { + value_ = JSMutableHandle(thread_, value); + } + value_.Update(value); + } + + inline void SetIndex(uint32_t index) + { + index_ = index; + } + + inline uint32_t GetIndex() const + { + return index_; + } + + inline bool HasHolder() const + { + return !holder_.IsEmpty(); + } + + inline JSHandle GetHolder() const + { + return holder_; + } + + inline JSHandle GetReceiver() const + { + return receiver_; + } + + inline JSHandle GetKey() const + { + if (key_.IsEmpty()) { + return JSHandle(thread_, JSTaggedValue::Undefined()); + } + return key_; + } + + inline uint32_t GetElementIndex() const + { + return elementIndex_; + } + + inline JSThread *GetThread() const + { + return thread_; + } + + void ToPropertyDescriptor(PropertyDescriptor &desc) const; + void LookupProperty(); + void GlobalLookupProperty(); + inline void ReLookupPropertyInReceiver() + { + ResetState(); + return LookupPropertyInlinedProps(JSHandle(receiver_)); + } + inline void SetAsDefaultAttr() + { + SetFound(NOT_FOUND_INDEX, JSTaggedValue::Undefined(), PropertyAttributes::GetDefaultAttributes(), false, false); + } + bool UpdateDataValue(const JSHandle &receiver, const JSHandle &value, + bool isInternalAccessor, bool mayThrow = false); + bool WriteDataPropertyInHolder(const PropertyDescriptor &desc) + { + JSHandle receiver(holder_); + return WriteDataProperty(receiver, desc); + } + bool WriteDataProperty(const JSHandle &receiver, const PropertyDescriptor &desc); + bool AddProperty(const JSHandle &receiver, const JSHandle &value, PropertyAttributes attr); + inline bool AddPropertyInHolder(const JSHandle &value, PropertyAttributes attr) + { + JSHandle obj(holder_); + return AddProperty(obj, value, attr); + } + void DeletePropertyInHolder(); + static constexpr uint32_t NOT_FOUND_INDEX = std::numeric_limits::max(); + static JSTaggedValue ToHolder(const JSHandle &holder); + void AddPropertyInternal(const JSHandle &value); + void DefineSetter(const JSHandle &value); + void DefineGetter(const JSHandle &value); + +private: + static constexpr uint64_t ATTR_LENGTH = 5; + static constexpr uint64_t INDEX_LENGTH = 32; + + using IsFastModeField = BitField; + using IsOnPrototypeField = IsFastModeField::NextFlag; // 1: on prototype + using HasReceiverField = IsOnPrototypeField::NextFlag; + using IsTransitionField = HasReceiverField::NextFlag; + + void UpdateHolder(); + void StartLookUp(OperatorType type); + void StartGlobalLookUp(OperatorType type); + void HandleKey(const JSHandle &key); + uint32_t ComputeElementCapacity(uint32_t oldCapacity); + void SetFound(uint32_t index, JSTaggedValue value, uint32_t attr, bool mode, bool transition = false); + void UpdateFound(uint32_t index, uint32_t attr, bool mode, bool transition); + void ResetState(); + inline void LookupPropertyInHolder() + { + JSHandle obj(holder_); + LookupPropertyInlinedProps(obj); + } + inline void GlobalLookupPropertyInHolder() + { + JSHandle obj(holder_); + LookupGlobal(obj); + } + void LookupGlobal(const JSHandle &obj); + void LookupPropertyInlinedProps(const JSHandle &obj); + void LookupElementInlinedProps(const JSHandle &obj); + void WriteElement(const JSHandle &receiver, const PropertyDescriptor &desc); + void WriteElement(const JSHandle &receiver, JSTaggedValue value) const; + void DeleteElementInHolder() const; + bool UpdateValueAndDetails(const JSHandle &receiver, const JSHandle &value, + PropertyAttributes attr, bool attrChanged); + void TransitionForAttributeChanged(const JSHandle &receiver, PropertyAttributes attr); + JSThread *thread_{nullptr}; + JSMutableHandle value_{}; + JSMutableHandle holder_{}; + JSMutableHandle receiver_{}; + JSHandle key_{}; + uint32_t elementIndex_{NOT_FOUND_INDEX}; + uint32_t index_{NOT_FOUND_INDEX}; + PropertyAttributes attributes_; + uint32_t metaData_{0}; +}; +} // namespace panda::ecmascript +#endif // ECMASCRIPT_OBJECT_OPERATOR_H diff --git a/runtime/object_wrapper.h b/runtime/object_wrapper.h new file mode 100644 index 000000000..6e95c78f9 --- /dev/null +++ b/runtime/object_wrapper.h @@ -0,0 +1,28 @@ + +/** + * Copyright (c) Huawei Technologies Co., Ltd. 2019-2021. All rights reserved. + * Description: Header file of object wrapper. + */ +#ifndef PANDA_RUNTIME_ECMASCRIPT_WRAPPER_OBJECT_H +#define PANDA_RUNTIME_ECMASCRIPT_WRAPPER_OBJECT_H + +#include "include/object_header.h" +#include "plugins/ecmascript/runtime/ecma_macros.h" +#include "plugins/ecmascript/runtime/js_tagged_value.h" +#include "plugins/ecmascript/runtime/js_hclass.h" + +namespace panda::ecmascript { +class ObjectWrapper : public TaggedObject { +public: + static ObjectWrapper *Cast(TaggedObject *object) + { + return static_cast(object); + } + + static constexpr size_t VALUE_OFFSET = TaggedObject::TaggedObjectSize(); + ACCESSORS(Value, VALUE_OFFSET, SIZE) + + DECL_VISIT_OBJECT(VALUE_OFFSET, SIZE) +}; +} // namespace panda::ecmascript +#endif // PANDA_RUNTIME_ECMASCRIPT_WRAPPER_OBJECT_H diff --git a/runtime/platform/platform.cpp b/runtime/platform/platform.cpp new file mode 100644 index 000000000..e226a1f56 --- /dev/null +++ b/runtime/platform/platform.cpp @@ -0,0 +1,51 @@ +/* + * Copyright (c) 2021-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 "plugins/ecmascript/runtime/platform/platform.h" + +#include "sys/sysinfo.h" + +namespace panda::ecmascript { +void Platform::Initialize(int threadNum) +{ + os::memory::LockHolder lock(mutex_); + if (isInitialized_++ <= 0) { + runner_ = std::make_unique(TheMostSuitableThreadNum(threadNum)); + } +} + +void Platform::Destroy() +{ + os::memory::LockHolder lock(mutex_); + if (isInitialized_ <= 0) { + return; + } + isInitialized_--; + if (isInitialized_ == 0) { + runner_->TerminateThread(); + } else { + runner_->TerminateTask(); + } +} + +uint32_t Platform::TheMostSuitableThreadNum(uint32_t threadNum) const +{ + if (threadNum > 0) { + return std::min(threadNum, MAX_PLATFORM_THREAD_NUM); + } + uint32_t numOfCpuCore = get_nprocs() - 1; + return std::min(numOfCpuCore, MAX_PLATFORM_THREAD_NUM); +} +} // namespace panda::ecmascript diff --git a/runtime/platform/platform.h b/runtime/platform/platform.h new file mode 100644 index 000000000..2a4f2b54a --- /dev/null +++ b/runtime/platform/platform.h @@ -0,0 +1,71 @@ +/* + * Copyright (c) 2021-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_PALTFORM_PLATFORM_H +#define ECMASCRIPT_PALTFORM_PLATFORM_H + +#include + +#include "plugins/ecmascript/runtime/platform/runner.h" +#include "os/mutex.h" + +namespace panda::ecmascript { +class Platform { +public: + static Platform *GetCurrentPlatform() + { + static Platform platform; + return &platform; + } + + Platform() = default; + ~Platform() + { + os::memory::LockHolder lock(mutex_); + runner_->TerminateThread(); + isInitialized_ = 0; + } + + NO_COPY_SEMANTIC(Platform); + NO_MOVE_SEMANTIC(Platform); + + void Initialize(int threadNum = DEFAULT_PLATFORM_THREAD_NUM); + void Destroy(); + + void PostTask(std::unique_ptr task) const + { + ASSERT(isInitialized_ > 0); + runner_->PostTask(std::move(task)); + } + + uint32_t GetTotalThreadNum() const + { + return runner_->GetTotalThreadNum(); + } + + bool IsInThreadPool(std::thread::id id) const + { + return runner_->IsInThreadPool(id); + } + +private: + uint32_t TheMostSuitableThreadNum(uint32_t threadNum) const; + + std::unique_ptr runner_; + int isInitialized_ = 0; + os::memory::Mutex mutex_; +}; +} // namespace panda::ecmascript +#endif // ECMASCRIPT_PALTFORM_PLATFORM_H diff --git a/runtime/platform/runner.cpp b/runtime/platform/runner.cpp new file mode 100644 index 000000000..a70f59a9f --- /dev/null +++ b/runtime/platform/runner.cpp @@ -0,0 +1,65 @@ +/* + * Copyright (c) 2021-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 "plugins/ecmascript/runtime/platform/runner.h" + +#include "os/thread.h" + +namespace panda::ecmascript { +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)); + } + + for (auto ¤t : runningTask_) { + current = nullptr; + } +} + +void Runner::TerminateTask() +{ + for (auto ¤t : runningTask_) { + auto val = current.load(); + if (val != nullptr) { + val->Terminated(); + } + } +} + +void Runner::TerminateThread() +{ + taskQueue_.Terminate(); + TerminateTask(); + + int threadNum = threadPool_.size(); + for (int i = 0; i < threadNum; i++) { + threadPool_.at(i)->join(); + } + threadPool_.clear(); +} + +void Runner::Run(uint32_t threadId) +{ + while (std::unique_ptr task = taskQueue_.PopTask()) { + runningTask_[threadId].store(task.get()); + task->Run(threadId); + runningTask_[threadId].store(nullptr); + } +} +} // namespace panda::ecmascript diff --git a/runtime/platform/runner.h b/runtime/platform/runner.h new file mode 100644 index 000000000..f5d5cca57 --- /dev/null +++ b/runtime/platform/runner.h @@ -0,0 +1,72 @@ +/* + * Copyright (c) 2021-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_PLATFORM_RUNNER_H +#define ECMASCRIPT_PLATFORM_RUNNER_H + +#include +#include +#include + +#include "plugins/ecmascript/runtime/platform/task_queue.h" +#include "libpandabase/os/thread.h" + +namespace panda::ecmascript { +static constexpr uint32_t MAX_PLATFORM_THREAD_NUM = 7; +static constexpr uint32_t DEFAULT_PLATFORM_THREAD_NUM = 0; + +class Runner { +public: + explicit Runner(uint32_t threadNum); + ~Runner() = default; + + NO_COPY_SEMANTIC(Runner); + NO_MOVE_SEMANTIC(Runner); + + void PostTask(std::unique_ptr task) + { + taskQueue_.PostTask(std::move(task)); + } + + void TerminateThread(); + void TerminateTask(); + + uint32_t GetTotalThreadNum() const + { + return totalThreadNum_; + } + + bool IsInThreadPool(std::thread::id id) const + { + for (auto &thread : threadPool_) { + if (thread->get_id() == id) { + return true; + } + } + return false; + } + +private: + void Run(uint32_t threadId); + + std::vector> threadPool_ {}; + TaskQueue taskQueue_ {}; + std::array, MAX_PLATFORM_THREAD_NUM + 1> runningTask_ {}; + uint32_t totalThreadNum_ {0}; + std::vector threadIdToIndexList_; +}; +} // namespace panda::ecmascript + +#endif // ECMASCRIPT_PLATFORM_RUNNER_H diff --git a/runtime/platform/task.h b/runtime/platform/task.h new file mode 100644 index 000000000..254eace71 --- /dev/null +++ b/runtime/platform/task.h @@ -0,0 +1,47 @@ +/* + * Copyright (c) 2021-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_PLATFORM_TASK_H +#define ECMASCRIPT_PLATFORM_TASK_H + +#include + +#include "macros.h" + +namespace panda::ecmascript { +class Task { +public: + Task() = default; + virtual ~Task() = default; + virtual bool Run(uint32_t threadIndex) = 0; + + NO_COPY_SEMANTIC(Task); + NO_MOVE_SEMANTIC(Task); + + void Terminated() + { + terminate_.store(true); + } + + bool IsTerminate() + { + return terminate_.load(); + } + +private: + std::atomic terminate_ {false}; +}; +} // namespace panda::ecmascript +#endif // ECMASCRIPT_PLATFORM_TASK_H diff --git a/runtime/platform/task_queue.cpp b/runtime/platform/task_queue.cpp new file mode 100644 index 000000000..38b736283 --- /dev/null +++ b/runtime/platform/task_queue.cpp @@ -0,0 +1,50 @@ +/* + * Copyright (c) 2021-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 "plugins/ecmascript/runtime/platform/task_queue.h" + +namespace panda::ecmascript { +void TaskQueue::PostTask(std::unique_ptr task) +{ + os::memory::LockHolder holder(mtx_); + ASSERT(!terminate_); + tasks_.push(std::move(task)); + cv_.Signal(); +} + +std::unique_ptr TaskQueue::PopTask() +{ + os::memory::LockHolder holder(mtx_); + while (true) { + if (!tasks_.empty()) { + std::unique_ptr task = std::move(tasks_.front()); + tasks_.pop(); + return task; + } + if (terminate_) { + cv_.SignalAll(); + return nullptr; + } + cv_.Wait(&mtx_); + } +} + +void TaskQueue::Terminate() +{ + os::memory::LockHolder holder(mtx_); + terminate_ = true; + cv_.SignalAll(); +} +} // namespace panda::ecmascript diff --git a/runtime/platform/task_queue.h b/runtime/platform/task_queue.h new file mode 100644 index 000000000..2285246a8 --- /dev/null +++ b/runtime/platform/task_queue.h @@ -0,0 +1,49 @@ +/* + * Copyright (c) 2021-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_PLATFORM_TASK_QUEUE_H +#define ECMASCRIPT_PLATFORM_TASK_QUEUE_H + +#include +#include +#include +#include + +#include "plugins/ecmascript/runtime/platform/task.h" +#include "os/mutex.h" + +namespace panda::ecmascript { +class TaskQueue { +public: + TaskQueue() = default; + ~TaskQueue() = default; + + NO_COPY_SEMANTIC(TaskQueue); + NO_MOVE_SEMANTIC(TaskQueue); + + void PostTask(std::unique_ptr task); + std::unique_ptr PopTask(); + + void Terminate(); + +private: + std::queue> tasks_; + + std::atomic_bool terminate_ = false; + os::memory::Mutex mtx_; + os::memory::ConditionVariable cv_; +}; +} // namespace panda::ecmascript +#endif // ECMASCRIPT_PLATFORM_TASK_QUEUE_H diff --git a/runtime/property_attributes.h b/runtime/property_attributes.h new file mode 100644 index 000000000..7fb1f0fe2 --- /dev/null +++ b/runtime/property_attributes.h @@ -0,0 +1,312 @@ +/* + * Copyright (c) 2021-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_PROPERTY_ATTRIBUTES_H +#define ECMASCRIPT_PROPERTY_ATTRIBUTES_H + +#include "plugins/ecmascript/runtime/js_tagged_value.h" +#include "utils/bit_field.h" + +namespace panda::ecmascript { +class PropertyDescriptor; + +enum class Representation { + NONE, + INT, + DOUBLE, + NUMBER, + OBJECT, + MIXED, +}; + +enum class PropertyBoxType { + // Meaningful when a property cell does not contain the hole. + UNDEFINED, // The PREMONOMORPHIC of property cells. + CONSTANT, // Cell has been assigned only once. + CONSTANTTYPE, // Cell has been assigned only one type. + MUTABLE, // Cell will no longer be tracked as constant. + + // Meaningful when a property cell contains the hole. + UNINITIALIZED = UNDEFINED, // Cell has never been initialized. + INVALIDATED = CONSTANT, // Cell has been deleted, invalidated or never existed. +}; + +class PropertyAttributes { +public: + explicit PropertyAttributes() = default; + ~PropertyAttributes() = default; + + DEFAULT_NOEXCEPT_MOVE_SEMANTIC(PropertyAttributes); + DEFAULT_COPY_SEMANTIC(PropertyAttributes); + + explicit PropertyAttributes(uint32_t v) : value_(v) {} + explicit PropertyAttributes(int32_t v) : value_(static_cast(v)) {} + explicit PropertyAttributes(JSTaggedValue v) : value_(v.GetInt()) {} + explicit PropertyAttributes(const PropertyDescriptor &desc); + + static constexpr uint32_t DICTIONARY_ORDER_NUM = 20; + static constexpr uint32_t OFFSET_BITFIELD_NUM = 10; + static constexpr uint32_t MAX_CAPACITY_OF_PROPERTIES = (1U << OFFSET_BITFIELD_NUM) - 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 + using EnumerableField = WritableField::NextFlag; + using ConfigurableField = EnumerableField::NextFlag; + using IsAccessorField = ConfigurableField::NextFlag; // 4 + + // fast mode + using IsInlinedPropsField = PropertyMetaDataField::NextFlag; // 5 + using RepresentationField = IsInlinedPropsField::NextField; // 3: 3 bits, 6-8 + using OffsetField = RepresentationField::NextField; // 18 + + static constexpr uint32_t NORMAL_ATTR_BITS = 18; + using NormalAttrField = BitField; + using SortedIndexField = OffsetField::NextField; // 28 + using IsConstPropsField = SortedIndexField::NextFlag; // 29 + // dictionary mode, include global + using PropertyBoxTypeField = PropertyMetaDataField::NextField; // 2: 2 bits, 5-6 + using DictionaryOrderField = PropertyBoxTypeField::NextField; // 26 + + static constexpr uint32_t BIT_SIZE = 28; + static constexpr int INTIAL_PROPERTY_INDEX = 0; + + inline int GetPropertyMetaData() const + { + return PropertyMetaDataField::Get(value_); + } + + static PropertyAttributes Default() + { + return PropertyAttributes(GetDefaultAttributes()); + } + + static PropertyAttributes Default(bool w, bool e, bool c, bool isAccessor = false) + { + uint32_t value = WritableField::Encode(w) | EnumerableField::Encode(e) | ConfigurableField::Encode(c) | + IsAccessorField::Encode(isAccessor); + return PropertyAttributes(value); + } + + static PropertyAttributes DefaultAccessor(bool w, bool e, bool c) + { + uint32_t value = WritableField::Encode(w) | EnumerableField::Encode(e) | ConfigurableField::Encode(c) | + IsAccessorField::Encode(true); + return PropertyAttributes(value); + } + + inline void SetDefaultAttributes() + { + AttributesField::Set(DefaultAttributesField::Mask(), &value_); + } + + static inline int GetDefaultAttributes() + { + return DefaultAttributesField::Mask(); + } + + static inline Representation TaggedToRepresentation(JSTaggedValue value) + { + if (value.IsInt()) { + return Representation::INT; + } + if (value.IsDouble()) { + return Representation::DOUBLE; + } + + return Representation::OBJECT; + } + + static Representation UpdateRepresentation(Representation oldRep, JSTaggedValue value) + { + if (oldRep == Representation::MIXED) { + return oldRep; + } + + Representation newRep = TaggedToRepresentation(value); + if (oldRep == Representation::NONE) { + return newRep; + } + if (oldRep == newRep) { + return oldRep; + } + + switch (oldRep) { + case Representation::INT: + case Representation::DOUBLE: + case Representation::NUMBER: + if (newRep != Representation::OBJECT) { + return Representation::NUMBER; + } + return Representation::MIXED; + case Representation::OBJECT: + return Representation::MIXED; + default: + UNREACHABLE(); + } + } + + inline bool IsDefaultAttributes() const + { + return AttributesField::Get(value_) == static_cast(DefaultAttributesField::Mask()); + } + + inline void SetNoneAttributes() + { + AttributesField::Set(0U, &value_); + } + + inline bool IsNoneAttributes() const + { + return AttributesField::Get(value_) == 0; + } + + inline void SetWritable(bool flag) + { + WritableField::Set(flag, &value_); + } + inline bool IsWritable() const + { + return WritableField::Get(value_); + } + inline void SetEnumerable(bool flag) + { + EnumerableField::Set(flag, &value_); + } + inline bool IsEnumerable() const + { + return EnumerableField::Get(value_); + } + inline void SetConfigurable(bool flag) + { + ConfigurableField::Set(flag, &value_); + } + inline bool IsConfigurable() const + { + return ConfigurableField::Get(value_); + } + + inline void SetIsAccessor(bool flag) + { + IsAccessorField::Set(flag, &value_); + } + + inline bool IsAccessor() const + { + return IsAccessorField::Get(value_); + } + + inline void SetIsInlinedProps(bool flag) + { + IsInlinedPropsField::Set(flag, &value_); + } + + inline bool IsInlinedProps() const + { + return IsInlinedPropsField::Get(value_); + } + + inline void SetIsConstProps(bool flag) + { + IsConstPropsField::Set(flag, &value_); + } + + inline bool IsConstProps() const + { + return IsConstPropsField::Get(value_); + } + + inline void SetRepresentation(Representation representation) + { + RepresentationField::Set(representation, &value_); + } + inline Representation GetRepresentation() const + { + return RepresentationField::Get(value_); + } + + inline void SetDictionaryOrder(uint32_t order) + { + DictionaryOrderField::Set(order, &value_); + } + inline uint32_t GetDictionaryOrder() const + { + return DictionaryOrderField::Get(value_); + } + + inline void SetOffset(uint32_t offset) + { + OffsetField::Set(offset, &value_); + } + inline uint32_t GetOffset() const + { + return OffsetField::Get(value_); + } + + inline void SetSortedIndex(uint32_t sortedIndex) + { + SortedIndexField::Set(sortedIndex, &value_); + } + inline uint32_t GetSortedIndex() const + { + return SortedIndexField::Get(value_); + } + + inline void SetNormalAttr(uint32_t normalAttr) + { + NormalAttrField::Set(normalAttr, &value_); + } + + inline uint32_t GetNormalAttr() const + { + return NormalAttrField::Get(value_); + } + + inline JSTaggedValue GetNormalTagged() const + { + return JSTaggedValue(static_cast(GetNormalAttr())); + } + + inline uint32_t GetValue() const + { + return value_; + } + + inline void SetBoxType(PropertyBoxType cellType) + { + PropertyBoxTypeField::Set(cellType, &value_); + } + + inline PropertyBoxType GetBoxType() const + { + return PropertyBoxTypeField::Get(value_); + } + + inline static bool IsValidIndex(int index) + { + return DictionaryOrderField::IsValid(index); + } + + inline JSTaggedValue GetTaggedValue() const + { + return JSTaggedValue(static_cast(value_)); + } + +private: + uint32_t value_{0}; +}; +} // namespace panda::ecmascript +#endif // ECMASCRIPT_PROPERTY_ATTRIBUTES_H diff --git a/runtime/record.h b/runtime/record.h new file mode 100644 index 000000000..9bfaee1ee --- /dev/null +++ b/runtime/record.h @@ -0,0 +1,27 @@ +/* + * Copyright (c) 2021-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_RECORD_H +#define ECMASCRIPT_RECORD_H + +#include "plugins/ecmascript/runtime/mem/tagged_object.h" + +namespace panda::ecmascript { +class Record : public TaggedObject { +public: + static constexpr size_t SIZE = TaggedObjectSize(); +}; +} // namespace panda::ecmascript +#endif // ECMASCRIPT_RECORD_H diff --git a/runtime/regexp/dyn_chunk.cpp b/runtime/regexp/dyn_chunk.cpp new file mode 100644 index 000000000..3b415b9bf --- /dev/null +++ b/runtime/regexp/dyn_chunk.cpp @@ -0,0 +1,113 @@ +/* + * Copyright (c) 2021-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 "plugins/ecmascript/runtime/regexp/dyn_chunk.h" +#include "securec.h" + +namespace panda::ecmascript { +int DynChunk::Expand(size_t newSize) +{ + if (newSize > allocatedSize_) { + if (error_) { + return FAILURE; + } + ASSERT(allocatedSize_ <= std::numeric_limits::max() / ALLOCATE_MULTIPLIER); + size_t size = allocatedSize_ * ALLOCATE_MULTIPLIER; + if (size > newSize) { + newSize = size; + } + newSize = std::max(newSize, ALLOCATE_MIN_SIZE); + auto *newBuf = chunk_->NewArray(newSize); + if (newBuf == nullptr) { + error_ = true; + return FAILURE; + } + if (memset_s(newBuf, newSize, 0, newSize) != EOK) { + error_ = true; + return FAILURE; + } + if (buf_ != nullptr) { + if (memcpy_s(newBuf, size_, buf_, size_) != EOK) { + error_ = true; + return FAILURE; + } + } + buf_ = newBuf; + allocatedSize_ = newSize; + } + return SUCCESS; +} + +int DynChunk::Insert(uint32_t position, size_t len) +{ + if (size_ < position) { + return FAILURE; + } + if (Expand(size_ + len) != 0) { + return FAILURE; + } + size_t moveSize = size_ - position; + // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-pointer-arithmetic) + if (memmove_s(buf_ + position + len, moveSize, buf_ + position, moveSize) != EOK) { + return FAILURE; + } + size_ += len; + return SUCCESS; +} + +int DynChunk::Emit(const uint8_t *data, size_t length) +{ + if (UNLIKELY((size_ + length) > allocatedSize_)) { + if (Expand(size_ + length) != 0) { + return FAILURE; + } + } + + if (memcpy_s(buf_ + size_, // NOLINT(cppcoreguidelines-pro-bounds-pointer-arithmetic) + length, data, length) != EOK) { + return FAILURE; + } + size_ += length; + return SUCCESS; +} + +int DynChunk::EmitChar(uint8_t c) +{ + return Emit(&c, 1); +} + +int DynChunk::EmitSelf(size_t offset, size_t length) +{ + if (UNLIKELY((size_ + length) > allocatedSize_)) { + if (Expand(size_ + length) != 0) { + return FAILURE; + } + } + + if (memcpy_s(buf_ + size_, // NOLINT(cppcoreguidelines-pro-bounds-pointer-arithmetic) + length, + buf_ + offset, // NOLINT(cppcoreguidelines-pro-bounds-pointer-arithmetic) + length) != EOK) { + return FAILURE; + } + size_ += length; + return SUCCESS; +} + +int DynChunk::EmitStr(const char *str) +{ + return Emit(reinterpret_cast(str), strlen(str) + 1); +} +} // namespace panda::ecmascript diff --git a/runtime/regexp/dyn_chunk.h b/runtime/regexp/dyn_chunk.h new file mode 100644 index 000000000..61646def7 --- /dev/null +++ b/runtime/regexp/dyn_chunk.h @@ -0,0 +1,150 @@ +/* + * Copyright (c) 2021-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_REGEXP_DYN_BUFFER_H +#define ECMASCRIPT_REGEXP_DYN_BUFFER_H + +#include +#include "plugins/ecmascript/runtime/ecma_macros.h" +#include "plugins/ecmascript/runtime/ecma_vm.h" +#include "plugins/ecmascript/runtime/js_thread.h" +#include "plugins/ecmascript/runtime/mem/chunk.h" + +namespace panda::ecmascript { +class DynChunk { +public: + static constexpr size_t ALLOCATE_MIN_SIZE = 64; + static constexpr int FAILURE = -1; + static constexpr int SUCCESS = 0; + explicit DynChunk(Chunk *chunk) : chunk_(chunk) + { + ASSERT(chunk_ != nullptr); + }; + + ~DynChunk() = default; + + NO_COPY_SEMANTIC(DynChunk); + NO_MOVE_SEMANTIC(DynChunk); + + int Expand(size_t newSize); + + int Insert(uint32_t position, size_t len); + + int Emit(const uint8_t *data, size_t len); + + int EmitSelf(size_t offset, size_t len); + + int EmitChar(uint8_t c); + + int EmitStr(const char *str); + + inline int EmitU16(uint16_t data) + { + return Emit(reinterpret_cast(&data), U16_SIZE); + } + + inline int EmitU32(uint32_t data) + { + return Emit(reinterpret_cast(&data), U32_SIZE); + } + + inline int EmitU64(uint64_t data) + { + return Emit(reinterpret_cast(&data), U64_SIZE); + } + + inline void SetError() + { + error_ = true; + } + + inline size_t GetSize() const + { + return size_; + } + + inline size_t GetAllocatedSize() const + { + return allocatedSize_; + } + + inline bool GetError() const + { + return error_; + } + + inline uint32_t GetU32(size_t offset) const + { + // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-pointer-arithmetic) + return UnalignedLoad(reinterpret_cast(buf_ + offset)); + } + + inline void PutU32(size_t offset, uint32_t data) const + { + // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-pointer-arithmetic) + return UnalignedStore(reinterpret_cast(buf_ + offset), data); + } + + inline uint32_t GetU16(size_t offset) const + { + // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-pointer-arithmetic) + return UnalignedLoad(reinterpret_cast(buf_ + offset)); + } + + inline void PutU16(size_t offset, uint16_t data) const + { + // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-pointer-arithmetic) + return UnalignedStore(reinterpret_cast(buf_ + offset), data); + } + + inline uint32_t GetU8(size_t offset) const + { + // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-pointer-arithmetic) + return *(buf_ + offset); + } + + inline void PutU8(size_t offset, uint8_t data) const + { + // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-pointer-arithmetic) + *(buf_ + offset) = data; + } + + ALWAYS_INLINE static inline constexpr uint32_t GetBufferOffset() + { + return MEMBER_OFFSET(DynChunk, buf_); + } + +private: + static constexpr size_t ALLOCATE_MULTIPLIER = 2; + static constexpr size_t U16_SIZE = 2; + static constexpr size_t U32_SIZE = 4; + static constexpr size_t U64_SIZE = 8; + friend class RegExpParser; + friend class RegExpOpCode; + friend class RegExpExecutor; + + DynChunk(uint8_t *buf, Chunk *chunk) : buf_(buf), chunk_(chunk) + { + ASSERT(chunk_ != nullptr); + }; + + uint8_t *buf_ {nullptr}; + size_t size_ {0}; + size_t allocatedSize_ {0}; + bool error_ {false}; + Chunk *chunk_ {nullptr}; +}; +} // namespace panda::ecmascript +#endif // ECMASCRIPT_REGEXP_DYN_BUFFER_H diff --git a/runtime/regexp/regexp_executor.cpp b/runtime/regexp/regexp_executor.cpp new file mode 100644 index 000000000..7c06e5a2f --- /dev/null +++ b/runtime/regexp/regexp_executor.cpp @@ -0,0 +1,732 @@ +/* + * Copyright (c) 2021-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 "plugins/ecmascript/runtime/regexp/regexp_executor.h" + +#include "plugins/ecmascript/runtime/base/string_helper.h" +#include "plugins/ecmascript/runtime/regexp/dyn_chunk.h" +#include "plugins/ecmascript/runtime/regexp/regexp_opcode.h" +#include "securec.h" + +namespace panda::ecmascript { +using RegExpState = RegExpExecutor::RegExpState; +using MatchResult = RegExpExecutor::MatchResult; +bool RegExpExecutor::Execute(const uint8_t *input, uint32_t lastIndex, uint32_t length, uint8_t *buf, bool isWideChar) +{ + DynChunk buffer(buf, chunk_); + input_ = const_cast(input); + // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-pointer-arithmetic) + inputEnd_ = const_cast(input + length * (isWideChar ? WIDE_CHAR_SIZE : CHAR_SIZE)); + uint32_t size = buffer.GetU32(0); + nCapture_ = buffer.GetU32(RegExpParser::NUM_CAPTURE__OFFSET); + nStack_ = buffer.GetU32(RegExpParser::NUM_STACK_OFFSET); + flags_ = buffer.GetU32(RegExpParser::FLAGS_OFFSET); + isWideChar_ = isWideChar; + + uint32_t captureResultSize = sizeof(CaptureState) * nCapture_; + uint32_t stackSize = sizeof(uintptr_t) * nStack_; + stateSize_ = sizeof(RegExpState) + captureResultSize + stackSize; + stateStackLen_ = 0; + + if (captureResultSize != 0) { + captureResultList_ = chunk_->NewArray(nCapture_); + if (memset_s(captureResultList_, captureResultSize, 0, captureResultSize) != EOK) { + LOG_ECMA(FATAL) << "memset_s failed"; + UNREACHABLE(); + } + } + if (stackSize != 0) { + stack_ = chunk_->NewArray(nStack_); + if (memset_s(stack_, stackSize, 0, stackSize) != EOK) { + LOG_ECMA(FATAL) << "memset_s failed"; + UNREACHABLE(); + } + } + // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-pointer-arithmetic) + SetCurrentPtr(input + lastIndex * (isWideChar ? WIDE_CHAR_SIZE : CHAR_SIZE)); + SetCurrentPC(RegExpParser::OP_START_OFFSET); + + // first split + if ((flags_ & RegExpParser::FLAG_STICKY) == 0) { + PushRegExpState(STATE_SPLIT, RegExpParser::OP_START_OFFSET); + } + return ExecuteInternal(buffer, size); +} + +bool RegExpExecutor::MatchFailed(bool isMatched) +{ + while (true) { + if (stateStackLen_ == 0) { + return true; + } + RegExpState *state = PeekRegExpState(); + if (state->type_ == StateType::STATE_SPLIT) { + if (!isMatched) { + PopRegExpState(); + return false; + } + } else { + isMatched = (state->type_ == StateType::STATE_MATCH_AHEAD && isMatched) || + (state->type_ == StateType::STATE_NEGATIVE_MATCH_AHEAD && !isMatched); + if (isMatched) { + if (state->type_ == StateType::STATE_MATCH_AHEAD) { + PopRegExpState(false); + return false; + } + if (state->type_ == StateType::STATE_NEGATIVE_MATCH_AHEAD) { + PopRegExpState(); + return false; + } + } + } + DropRegExpState(); + } + + return true; +} + +bool RegExpExecutor::HandleFirstSplit() +{ + if (GetCurrentPC() == RegExpParser::OP_START_OFFSET && stateStackLen_ == 0 && + (flags_ & RegExpParser::FLAG_STICKY) == 0) { + if (IsEOF()) { + if (MatchFailed()) { + return false; + } + } else { + AdvanceCurrentPtr(); + PushRegExpState(STATE_SPLIT, RegExpParser::OP_START_OFFSET); + } + } + return true; +} + +bool RegExpExecutor::HandleOpAll(uint8_t opCode) +{ + if (IsEOF()) { + return !MatchFailed(); + } + uint32_t currentChar = GetCurrentChar(); + if ((opCode == RegExpOpCode::OP_DOTS) && IsTerminator(currentChar)) { + return !MatchFailed(); + } + Advance(opCode); + return true; +} + +bool RegExpExecutor::HandleOpChar(const DynChunk &byteCode, uint8_t opCode) +{ + uint32_t expectedChar; + if (opCode == RegExpOpCode::OP_CHAR32) { + expectedChar = byteCode.GetU32(GetCurrentPC() + 1); + } else { + expectedChar = byteCode.GetU16(GetCurrentPC() + 1); + } + if (IsEOF()) { + return !MatchFailed(); + } + uint32_t currentChar = GetCurrentChar(); + if (IsIgnoreCase()) { + currentChar = RegExpParser::Canonicalize(currentChar, IsUtf16()); + } + if (currentChar == expectedChar) { + Advance(opCode); + } else { + if (MatchFailed()) { + return false; + } + } + return true; +} + +bool RegExpExecutor::HandleOpWordBoundary(uint8_t opCode) +{ + if (IsEOF()) { + if (opCode == RegExpOpCode::OP_WORD_BOUNDARY) { + Advance(opCode); + } else { + if (MatchFailed()) { + return false; + } + } + return true; + } + bool preIsWord = false; + if (GetCurrentPtr() != input_) { + // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-pointer-arithmetic) + preIsWord = IsWordChar(PeekPrevChar(currentPtr_, input_)); + } + bool currentIsWord = IsWordChar(PeekChar(currentPtr_, inputEnd_)); + if (((opCode == RegExpOpCode::OP_WORD_BOUNDARY) && + ((!preIsWord && currentIsWord) || (preIsWord && !currentIsWord))) || + ((opCode == RegExpOpCode::OP_NOT_WORD_BOUNDARY) && + ((preIsWord && currentIsWord) || (!preIsWord && !currentIsWord)))) { + Advance(opCode); + } else { + if (MatchFailed()) { + return false; + } + } + return true; +} + +bool RegExpExecutor::HandleOpLineStart(uint8_t opCode) +{ + if (IsEOF()) { + return !MatchFailed(); + } + if ((GetCurrentPtr() == input_) || + // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-pointer-arithmetic) + ((flags_ & RegExpParser::FLAG_MULTILINE) != 0 && PeekPrevChar(currentPtr_, input_) == '\n')) { + Advance(opCode); + } else { + if (MatchFailed()) { + return false; + } + } + return true; +} + +bool RegExpExecutor::HandleOpLineEnd(uint8_t opCode) +{ + if (IsEOF() || + // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-pointer-arithmetic) + ((flags_ & RegExpParser::FLAG_MULTILINE) != 0 && PeekChar(currentPtr_, inputEnd_) == '\n')) { + Advance(opCode); + } else { + if (MatchFailed()) { + return false; + } + } + return true; +} + +void RegExpExecutor::HandleOpSaveStart(const DynChunk &byteCode, uint8_t opCode) +{ + uint32_t captureIndex = byteCode.GetU8(GetCurrentPC() + 1); + ASSERT(captureIndex < nCapture_); + // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-pointer-arithmetic) + CaptureState *captureState = &captureResultList_[captureIndex]; + captureState->captureStart = GetCurrentPtr(); + Advance(opCode); +} + +void RegExpExecutor::HandleOpSaveEnd(const DynChunk &byteCode, uint8_t opCode) +{ + uint32_t captureIndex = byteCode.GetU8(GetCurrentPC() + 1); + ASSERT(captureIndex < nCapture_); + // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-pointer-arithmetic) + CaptureState *captureState = &captureResultList_[captureIndex]; + captureState->captureEnd = GetCurrentPtr(); + Advance(opCode); +} + +void RegExpExecutor::HandleOpSaveReset(const DynChunk &byteCode, uint8_t opCode) +{ + uint32_t catpureStartIndex = byteCode.GetU8(GetCurrentPC() + SAVE_RESET_START); + uint32_t catpureEndIndex = byteCode.GetU8(GetCurrentPC() + SAVE_RESET_END); + for (uint32_t i = catpureStartIndex; i <= catpureEndIndex; i++) { + CaptureState *captureState = + &captureResultList_[i]; // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-pointer-arithmetic) + captureState->captureStart = nullptr; + captureState->captureEnd = nullptr; + } + Advance(opCode); +} + +void RegExpExecutor::HandleOpMatch(const DynChunk &byteCode, uint8_t opCode) +{ + auto type = static_cast(opCode - RegExpOpCode::OP_SPLIT_NEXT); + ASSERT(type == STATE_SPLIT || type == STATE_MATCH_AHEAD || type == STATE_NEGATIVE_MATCH_AHEAD); + uint32_t offset = byteCode.GetU32(GetCurrentPC() + 1); + Advance(opCode); + uint32_t splitPc = GetCurrentPC() + offset; + PushRegExpState(type, splitPc); +} + +void RegExpExecutor::HandleOpSplitFirst(const DynChunk &byteCode, uint8_t opCode) +{ + uint32_t offset = byteCode.GetU32(GetCurrentPC() + 1); + Advance(opCode); + PushRegExpState(STATE_SPLIT, GetCurrentPC()); + AdvanceOffset(offset); +} + +bool RegExpExecutor::HandleOpPrev(uint8_t opCode) +{ + if (GetCurrentPtr() == input_) { + if (MatchFailed()) { + return false; + } + } else { + PrevPtr(¤tPtr_, input_); + Advance(opCode); + } + return true; +} + +void RegExpExecutor::HandleOpLoop(const DynChunk &byteCode, uint8_t opCode) +{ + uint32_t quantifyMin = byteCode.GetU32(GetCurrentPC() + LOOP_MIN_OFFSET); + uint32_t quantifyMax = byteCode.GetU32(GetCurrentPC() + LOOP_MAX_OFFSET); + uint32_t pcOffset = byteCode.GetU32(GetCurrentPC() + LOOP_PC_OFFSET); + Advance(opCode); + uint32_t loopPcEnd = GetCurrentPC(); + uint32_t loopPcStart = GetCurrentPC() + pcOffset; + bool isGreedy = opCode == RegExpOpCode::OP_LOOP_GREEDY; + uint32_t loopMax = isGreedy ? quantifyMax : quantifyMin; + + uint32_t loopCount = PeekStack(); + SetStackValue(++loopCount); + if (loopCount < loopMax) { + // greedy failed, goto next + if (loopCount >= quantifyMin) { + PushRegExpState(STATE_SPLIT, loopPcEnd); + } + // Goto loop start + SetCurrentPC(loopPcStart); + } else { + if (!isGreedy && (loopCount < quantifyMax)) { + PushRegExpState(STATE_SPLIT, loopPcStart); + } + } +} + +bool RegExpExecutor::HandleOpRange32(const DynChunk &byteCode) +{ + if (IsEOF()) { + return !MatchFailed(); + } + uint32_t currentChar = GetCurrentChar(); + if (IsIgnoreCase()) { + currentChar = RegExpParser::Canonicalize(currentChar, IsUtf16()); + } + uint16_t rangeCount = byteCode.GetU16(GetCurrentPC() + 1); + bool isFound = false; + int32_t idxMin = 0; + int32_t idxMax = rangeCount - 1; + int32_t idx = 0; + uint32_t low = 0; + uint32_t high = + byteCode.GetU32(GetCurrentPC() + RANGE32_HEAD_OFFSET + idxMax * RANGE32_MAX_OFFSET + RANGE32_MAX_HALF_OFFSET); + if (currentChar <= high) { + while (idxMin <= idxMax) { + idx = (idxMin + idxMax) / RANGE32_OFFSET; + low = byteCode.GetU32(GetCurrentPC() + RANGE32_HEAD_OFFSET + idx * RANGE32_MAX_OFFSET); + high = byteCode.GetU32(GetCurrentPC() + RANGE32_HEAD_OFFSET + idx * RANGE32_MAX_OFFSET + + RANGE32_MAX_HALF_OFFSET); + if (currentChar < low) { + idxMax = idx - 1; + } else if (currentChar > high) { + idxMin = idx + 1; + } else { + isFound = true; + break; + } + } + } + if (isFound) { + AdvanceOffset(rangeCount * RANGE32_MAX_OFFSET + RANGE32_HEAD_OFFSET); + } else { + if (MatchFailed()) { + return false; + } + } + return true; +} + +bool RegExpExecutor::HandleOpRange(const DynChunk &byteCode) +{ + if (IsEOF()) { + return !MatchFailed(); + } + uint32_t currentChar = GetCurrentChar(); + if (IsIgnoreCase()) { + currentChar = RegExpParser::Canonicalize(currentChar, IsUtf16()); + } + uint16_t rangeCount = byteCode.GetU16(GetCurrentPC() + 1); + bool isFound = false; + int32_t idxMin = 0; + int32_t idxMax = rangeCount - 1; + int32_t idx = 0; + uint32_t low = 0; + uint32_t high = + byteCode.GetU16(GetCurrentPC() + RANGE32_HEAD_OFFSET + idxMax * RANGE32_MAX_HALF_OFFSET + RANGE32_OFFSET); + if (currentChar <= high) { + while (idxMin <= idxMax) { + idx = (idxMin + idxMax) / RANGE32_OFFSET; + low = byteCode.GetU16(GetCurrentPC() + RANGE32_HEAD_OFFSET + idx * RANGE32_MAX_HALF_OFFSET); + high = + byteCode.GetU16(GetCurrentPC() + RANGE32_HEAD_OFFSET + idx * RANGE32_MAX_HALF_OFFSET + RANGE32_OFFSET); + if (currentChar < low) { + idxMax = idx - 1; + } else if (currentChar > high) { + idxMin = idx + 1; + } else { + isFound = true; + break; + } + } + } + if (isFound) { + AdvanceOffset(rangeCount * RANGE32_MAX_HALF_OFFSET + RANGE32_HEAD_OFFSET); + } else { + if (MatchFailed()) { + return false; + } + } + return true; +} + +bool RegExpExecutor::HandleOpBackReference(const DynChunk &byteCode, uint8_t opCode) +{ + uint32_t captureIndex = byteCode.GetU8(GetCurrentPC() + 1); + if (captureIndex >= nCapture_) { + return !MatchFailed(); + } + // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-pointer-arithmetic) + const uint8_t *captureStart = captureResultList_[captureIndex].captureStart; + // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-pointer-arithmetic) + const uint8_t *captureEnd = captureResultList_[captureIndex].captureEnd; + if (captureStart == nullptr || captureEnd == nullptr) { + Advance(opCode); + return true; + } + bool isMatched = true; + if (opCode == RegExpOpCode::OP_BACKREFERENCE) { + const uint8_t *refCptr = captureStart; + while (refCptr < captureEnd) { + if (IsEOF()) { + isMatched = false; + break; + } + // NOLINTNEXTLINE(readability-identifier-naming) + uint32_t c1 = GetChar(&refCptr, captureEnd); + // NOLINTNEXTLINE(readability-identifier-naming) + uint32_t c2 = GetChar(¤tPtr_, inputEnd_); + if (IsIgnoreCase()) { + c1 = RegExpParser::Canonicalize(c1, IsUtf16()); + c2 = RegExpParser::Canonicalize(c2, IsUtf16()); + } + if (c1 != c2) { + isMatched = false; + break; + } + } + if (!isMatched) { + if (MatchFailed()) { + return false; + } + } else { + Advance(opCode); + } + } else { + const uint8_t *refCptr = captureEnd; + while (refCptr > captureStart) { + if (GetCurrentPtr() == input_) { + isMatched = false; + break; + } + // NOLINTNEXTLINE(readability-identifier-naming) + uint32_t c1 = GetPrevChar(&refCptr, captureStart); + // NOLINTNEXTLINE(readability-identifier-naming) + uint32_t c2 = GetPrevChar(¤tPtr_, input_); + if (IsIgnoreCase()) { + c1 = RegExpParser::Canonicalize(c1, IsUtf16()); + c2 = RegExpParser::Canonicalize(c2, IsUtf16()); + } + if (c1 != c2) { + isMatched = false; + break; + } + } + if (!isMatched) { + if (MatchFailed()) { + return false; + } + } else { + Advance(opCode); + } + } + return true; +} + +// NOLINTNEXTLINE(readability-function-size) +bool RegExpExecutor::ExecuteInternal(const DynChunk &byteCode, uint32_t pcEnd) +{ + while (GetCurrentPC() < pcEnd) { + // first split + if (!HandleFirstSplit()) { + return false; + } + uint8_t opCode = byteCode.GetU8(GetCurrentPC()); + switch (opCode) { + case RegExpOpCode::OP_DOTS: + case RegExpOpCode::OP_ALL: + if (!HandleOpAll(opCode)) { + return false; + } + break; + case RegExpOpCode::OP_CHAR32: + case RegExpOpCode::OP_CHAR: + if (!HandleOpChar(byteCode, opCode)) { + return false; + } + break; + case RegExpOpCode::OP_NOT_WORD_BOUNDARY: + case RegExpOpCode::OP_WORD_BOUNDARY: + if (!HandleOpWordBoundary(opCode)) { + return false; + } + break; + case RegExpOpCode::OP_LINE_START: + if (!HandleOpLineStart(opCode)) { + return false; + } + break; + case RegExpOpCode::OP_LINE_END: + if (!HandleOpLineEnd(opCode)) { + return false; + } + break; + case RegExpOpCode::OP_SAVE_START: + HandleOpSaveStart(byteCode, opCode); + break; + case RegExpOpCode::OP_SAVE_END: + HandleOpSaveEnd(byteCode, opCode); + break; + case RegExpOpCode::OP_GOTO: { + uint32_t offset = byteCode.GetU32(GetCurrentPC() + 1); + Advance(opCode, offset); + } break; + case RegExpOpCode::OP_MATCH: { + // jump to match ahead + if (MatchFailed(true)) { + return false; + } + } break; + case RegExpOpCode::OP_MATCH_END: { + return true; + } break; + case RegExpOpCode::OP_SAVE_RESET: + HandleOpSaveReset(byteCode, opCode); + break; + case RegExpOpCode::OP_SPLIT_NEXT: + case RegExpOpCode::OP_MATCH_AHEAD: + case RegExpOpCode::OP_NEGATIVE_MATCH_AHEAD: + HandleOpMatch(byteCode, opCode); + break; + case RegExpOpCode::OP_SPLIT_FIRST: + HandleOpSplitFirst(byteCode, opCode); + break; + case RegExpOpCode::OP_PREV: + if (!HandleOpPrev(opCode)) { + return false; + } + break; + case RegExpOpCode::OP_LOOP_GREEDY: + case RegExpOpCode::OP_LOOP: + HandleOpLoop(byteCode, opCode); + break; + case RegExpOpCode::OP_PUSH_CHAR: { + PushStack(reinterpret_cast(GetCurrentPtr())); + Advance(opCode); + } break; + case RegExpOpCode::OP_CHECK_CHAR: { + if (PopStack() != reinterpret_cast(GetCurrentPtr())) { + Advance(opCode); + } else { + uint32_t offset = byteCode.GetU32(GetCurrentPC() + 1); + Advance(opCode, offset); + } + } break; + case RegExpOpCode::OP_PUSH: { + PushStack(0); + Advance(opCode); + } break; + case RegExpOpCode::OP_POP: { + PopStack(); + Advance(opCode); + } break; + case RegExpOpCode::OP_RANGE32: + if (!HandleOpRange32(byteCode)) { + return false; + } + break; + case RegExpOpCode::OP_RANGE: + if (!HandleOpRange(byteCode)) { + return false; + } + break; + case RegExpOpCode::OP_BACKREFERENCE: + case RegExpOpCode::OP_BACKWARD_BACKREFERENCE: + if (!HandleOpBackReference(byteCode, opCode)) { + return false; + } + break; + default: + UNREACHABLE(); + } + } + // for loop match + return true; +} + +void RegExpExecutor::DumpResult(std::ostream &out) const +{ + out << "captures:" << std::endl; + for (uint32_t i = 0; i < nCapture_; i++) { + // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-pointer-arithmetic) + CaptureState *captureState = &captureResultList_[i]; + int32_t len = captureState->captureEnd - captureState->captureStart; + if ((captureState->captureStart != nullptr && captureState->captureEnd != nullptr) && (len >= 0)) { + out << i << ":\t" << CString(reinterpret_cast(captureState->captureStart), len) << std::endl; + } else { + out << i << ":\t" + << "undefined" << std::endl; + } + } +} + +MatchResult RegExpExecutor::GetResult(const JSThread *thread, bool isSuccess) const +{ + ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); + MatchResult result; + std::vector>> captures; + result.isSuccess_ = isSuccess; + if (isSuccess) { + for (uint32_t i = 0; i < nCapture_; i++) { + // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-pointer-arithmetic) + CaptureState *captureState = &captureResultList_[i]; + if (i == 0) { + result.index_ = captureState->captureStart - input_; + if (isWideChar_) { + result.index_ /= WIDE_CHAR_SIZE; + } + } + int32_t len = captureState->captureEnd - captureState->captureStart; + std::pair> pair; + if ((captureState->captureStart != nullptr && captureState->captureEnd != nullptr) && (len >= 0)) { + pair.first = false; + if (isWideChar_) { + // create utf-16 string + pair.second = factory->NewFromUtf16UnCheck( + reinterpret_cast(captureState->captureStart), len / 2, false); + } else { + // create utf-8 string + CVector buffer(len + 1); + uint8_t *dest = buffer.data(); + if (memcpy_s(dest, len + 1, reinterpret_cast(captureState->captureStart), len) != + EOK) { + LOG_ECMA(FATAL) << "memcpy_s failed"; + UNREACHABLE(); + } + dest[len] = '\0'; // NOLINT(cppcoreguidelines-pro-bounds-pointer-arithmetic) + pair.second = + factory->NewFromUtf8UnCheck(reinterpret_cast(buffer.data()), len, true); + } + } else { + // undefined + pair.first = true; + } + captures.emplace_back(pair); + } + result.captures_ = captures; + result.endIndex_ = currentPtr_ - input_; + if (isWideChar_) { + result.endIndex_ /= WIDE_CHAR_SIZE; + } + } + return result; +} + +void RegExpExecutor::PushRegExpState(StateType type, uint32_t pc) +{ + ReAllocStack(stateStackLen_ + 1); + auto state = reinterpret_cast( + stateStack_ + // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-pointer-arithmetic) + stateStackLen_ * stateSize_); + state->type_ = type; + state->currentPc_ = pc; + state->currentStack_ = currentStack_; + state->currentPtr_ = GetCurrentPtr(); + size_t listSize = sizeof(CaptureState) * nCapture_; + if (memcpy_s(state->captureResultList_, listSize, GetCaptureResultList(), listSize) != EOK) { + LOG_ECMA(FATAL) << "memcpy_s failed"; + UNREACHABLE(); + } + uint8_t *stackStart = + // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-pointer-arithmetic) + reinterpret_cast(state->captureResultList_) + sizeof(CaptureState) * nCapture_; + if (stack_ != nullptr) { + size_t stackSize = sizeof(uintptr_t) * nStack_; + if (memcpy_s(stackStart, stackSize, stack_, stackSize) != EOK) { + LOG_ECMA(FATAL) << "memcpy_s failed"; + UNREACHABLE(); + } + } + stateStackLen_++; +} + +RegExpState *RegExpExecutor::PopRegExpState(bool copyCaptrue) +{ + if (stateStackLen_ != 0) { + auto state = PeekRegExpState(); + size_t listSize = sizeof(CaptureState) * nCapture_; + if (copyCaptrue) { + if (memcpy_s(GetCaptureResultList(), listSize, state->captureResultList_, listSize) != EOK) { + LOG_ECMA(FATAL) << "memcpy_s failed"; + UNREACHABLE(); + } + } + SetCurrentPtr(state->currentPtr_); + SetCurrentPC(state->currentPc_); + currentStack_ = state->currentStack_; + // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-pointer-arithmetic) + uint8_t *stackStart = reinterpret_cast(state->captureResultList_) + listSize; + if (stack_ != nullptr) { + size_t stackSize = sizeof(uintptr_t) * nStack_; + if (memcpy_s(stack_, stackSize, stackStart, stackSize) != EOK) { + LOG_ECMA(FATAL) << "memcpy_s failed"; + UNREACHABLE(); + } + } + stateStackLen_--; + return state; + } + return nullptr; +} + +void RegExpExecutor::ReAllocStack(uint32_t stackLen) +{ + if (stackLen > stateStackSize_) { + uint32_t newStackSize = std::max(stateStackSize_ * 2, MIN_STACK_SIZE); // 2: double the size + uint32_t stackByteSize = newStackSize * stateSize_; + auto newStack = chunk_->NewArray(stackByteSize); + if (memset_s(newStack, stackByteSize, 0, stackByteSize) != EOK) { + LOG_ECMA(FATAL) << "memset_s failed"; + UNREACHABLE(); + } + if (stateStack_ != nullptr) { + size_t stackSize = stateStackSize_ * stateSize_; + if (memcpy_s(newStack, stackSize, stateStack_, stackSize) != EOK) { + return; + } + } + stateStack_ = newStack; + stateStackSize_ = newStackSize; + } +} +} // namespace panda::ecmascript diff --git a/runtime/regexp/regexp_executor.h b/runtime/regexp/regexp_executor.h new file mode 100644 index 000000000..b21a20e94 --- /dev/null +++ b/runtime/regexp/regexp_executor.h @@ -0,0 +1,367 @@ +/* + * Copyright (c) 2021-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_REGEXP_REGEXP_EXECUTOR_H +#define ECMASCRIPT_REGEXP_REGEXP_EXECUTOR_H + +#include "plugins/ecmascript/runtime/regexp/regexp_parser.h" +#include "plugins/ecmascript/runtime/mem/chunk.h" + +namespace panda::ecmascript { +class RegExpExecutor { +public: + struct CaptureState { + const uint8_t *captureStart; + const uint8_t *captureEnd; + }; + + enum StateType : uint8_t { + STATE_SPLIT = 0, + STATE_MATCH_AHEAD, + STATE_NEGATIVE_MATCH_AHEAD, + }; + + struct RegExpState { + StateType type_ = STATE_SPLIT; + uint32_t currentPc_ = 0; + uint32_t currentStack_ = 0; + const uint8_t *currentPtr_ = nullptr; + __extension__ CaptureState *captureResultList_[0]; // NOLINT(modernize-avoid-c-arrays) + }; + + struct MatchResult { + uint32_t endIndex_ = 0; + uint32_t index_ = 0; + // first value is true if result is undefined + std::vector>> captures_; + bool isSuccess_ = false; + }; + + explicit RegExpExecutor(Chunk *chunk) : chunk_(chunk) + { + ASSERT(chunk_ != nullptr); + }; + + ~RegExpExecutor() = default; + + NO_COPY_SEMANTIC(RegExpExecutor); + NO_MOVE_SEMANTIC(RegExpExecutor); + + bool Execute(const uint8_t *input, uint32_t lastIndex, uint32_t length, uint8_t *buf, bool isWideChar = false); + + bool ExecuteInternal(const DynChunk &byteCode, uint32_t pcEnd); + bool HandleFirstSplit(); + bool HandleOpAll(uint8_t opCode); + bool HandleOpChar(const DynChunk &byteCode, uint8_t opCode); + bool HandleOpWordBoundary(uint8_t opCode); + bool HandleOpLineStart(uint8_t opCode); + bool HandleOpLineEnd(uint8_t opCode); + void HandleOpSaveStart(const DynChunk &byteCode, uint8_t opCode); + void HandleOpSaveEnd(const DynChunk &byteCode, uint8_t opCode); + void HandleOpSaveReset(const DynChunk &byteCode, uint8_t opCode); + void HandleOpMatch(const DynChunk &byteCode, uint8_t opCode); + void HandleOpSplitFirst(const DynChunk &byteCode, uint8_t opCode); + bool HandleOpPrev(uint8_t opCode); + void HandleOpLoop(const DynChunk &byteCode, uint8_t opCode); + bool HandleOpRange32(const DynChunk &byteCode); + bool HandleOpRange(const DynChunk &byteCode); + bool HandleOpBackReference(const DynChunk &byteCode, uint8_t opCode); + + inline void Advance(uint8_t opCode, uint32_t offset = 0) + { + currentPc_ += offset + RegExpOpCode::GetRegExpOpCode(opCode)->GetSize(); + } + + inline void AdvanceOffset(uint32_t offset) + { + // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-pointer-arithmetic) + currentPc_ += offset; + } + + inline uint32_t GetCurrentChar() + { + return GetChar(¤tPtr_, inputEnd_); + } + + inline void AdvanceCurrentPtr() + { + AdvancePtr(¤tPtr_, inputEnd_); + } + + uint32_t GetChar(const uint8_t **pp, const uint8_t *end) const + { + uint32_t c; + const uint8_t *cptr = *pp; + if (!isWideChar_) { + c = *cptr; + *pp += 1; // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-pointer-arithmetic) + } else { + uint16_t c1 = *(uint16_t *)cptr; // NOLINTNEXTLINE(cppcoreguidelines-pro-type-cstyle-cast) + c = c1; + cptr += WIDE_CHAR_SIZE; // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-pointer-arithmetic) + if (U16_IS_LEAD(c) && IsUtf16() && cptr < end) { + c1 = *(uint16_t *)cptr; // NOLINTNEXTLINE(cppcoreguidelines-pro-type-cstyle-cast) + if (U16_IS_TRAIL(c1)) { + c = U16_GET_SUPPLEMENTARY(c, c1); // NOLINTNEXTLINE(hicpp-signed-bitwise) + cptr += WIDE_CHAR_SIZE; // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-pointer-arithmetic) + } + } + *pp = cptr; + } + return c; + } + + uint32_t PeekChar(const uint8_t *p, const uint8_t *end) const + { + uint32_t c; + const uint8_t *cptr = p; + if (!isWideChar_) { + c = *cptr; + } else { + uint16_t c1 = *(uint16_t *)cptr; // NOLINTNEXTLINE(cppcoreguidelines-pro-type-cstyle-cast) + c = c1; + cptr += WIDE_CHAR_SIZE; // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-pointer-arithmetic) + if (U16_IS_LEAD(c) && IsUtf16() && cptr < end) { + c1 = *(uint16_t *)cptr; // NOLINTNEXTLINE(cppcoreguidelines-pro-type-cstyle-cast) + if (U16_IS_TRAIL(c1)) { + c = U16_GET_SUPPLEMENTARY(c, c1); // NOLINTNEXTLINE(hicpp-signed-bitwise) + } + } + } + return c; + } + + void AdvancePtr(const uint8_t **pp, const uint8_t *end) const + { + const uint8_t *cptr = *pp; + if (!isWideChar_) { + *pp += 1; // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-pointer-arithmetic) + } else { + uint16_t c1 = *(uint16_t *)cptr; // NOLINTNEXTLINE(cppcoreguidelines-pro-type-cstyle-cast) + cptr += WIDE_CHAR_SIZE; // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-pointer-arithmetic) + if (U16_IS_LEAD(c1) && IsUtf16() && cptr < end) { + c1 = *(uint16_t *)cptr; // NOLINTNEXTLINE(cppcoreguidelines-pro-type-cstyle-cast) + if (U16_IS_TRAIL(c1)) { + cptr += WIDE_CHAR_SIZE; // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-pointer-arithmetic) + } + } + *pp = cptr; + } + } + + uint32_t PeekPrevChar(const uint8_t *p, const uint8_t *start) const + { + uint32_t c; + const uint8_t *cptr = p; + if (!isWideChar_) { + c = *(cptr - 1); // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-pointer-arithmetic) + } else { + cptr -= WIDE_CHAR_SIZE; // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-pointer-arithmetic) + uint16_t c1 = *(uint16_t *)cptr; // NOLINTNEXTLINE(cppcoreguidelines-pro-type-cstyle-cast) + c = c1; + if (U16_IS_TRAIL(c) && IsUtf16() && cptr > start) { + c1 = ((uint16_t *)cptr)[-1]; // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-pointer-arithmetic) + if (U16_IS_LEAD(c1)) { + c = U16_GET_SUPPLEMENTARY(c1, c); // NOLINTNEXTLINE(hicpp-signed-bitwise) + } + } + } + return c; + } + + uint32_t GetPrevChar(const uint8_t **pp, const uint8_t *start) const + { + uint32_t c; + const uint8_t *cptr = *pp; + if (!isWideChar_) { + c = *(cptr - 1); // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-pointer-arithmetic) + cptr -= 1; // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-pointer-arithmetic) + *pp = cptr; + } else { + cptr -= WIDE_CHAR_SIZE; // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-pointer-arithmetic) + uint16_t c1 = *(uint16_t *)cptr; // NOLINTNEXTLINE(cppcoreguidelines-pro-type-cstyle-cast) + c = c1; + if (U16_IS_TRAIL(c) && IsUtf16() && cptr > start) { + c1 = ((uint16_t *)cptr)[-1]; // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-pointer-arithmetic) + if (U16_IS_LEAD(c1)) { + c = U16_GET_SUPPLEMENTARY(c1, c); // NOLINTNEXTLINE(hicpp-signed-bitwise) + cptr -= WIDE_CHAR_SIZE; // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-pointer-arithmetic) + } + } + *pp = cptr; + } + return c; + } + + void PrevPtr(const uint8_t **pp, const uint8_t *start) const + { + const uint8_t *cptr = *pp; + if (!isWideChar_) { + cptr -= 1; // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-pointer-arithmetic) + *pp = cptr; + } else { + cptr -= WIDE_CHAR_SIZE; // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-pointer-arithmetic) + uint16_t c1 = *(uint16_t *)cptr; // NOLINTNEXTLINE(cppcoreguidelines-pro-type-cstyle-cast) + if (U16_IS_TRAIL(c1) && IsUtf16() && cptr > start) { + c1 = ((uint16_t *)cptr)[-1]; // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-pointer-arithmetic) + if (U16_IS_LEAD(c1)) { + cptr -= WIDE_CHAR_SIZE; // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-pointer-arithmetic) + } + } + *pp = cptr; + } + } + + bool MatchFailed(bool isMatched = false); + + void SetCurrentPC(uint32_t pc) + { + currentPc_ = pc; + } + + void SetCurrentPtr(const uint8_t *ptr) + { + currentPtr_ = ptr; + } + + bool IsEOF() const + { + return currentPtr_ >= inputEnd_; + } + + uint32_t GetCurrentPC() const + { + return currentPc_; + } + + void PushStack(uintptr_t val) + { + ASSERT(currentStack_ < nStack_); + // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-pointer-arithmetic) + stack_[currentStack_++] = val; + } + + void SetStackValue(uintptr_t val) const + { + ASSERT(currentStack_ >= 1); + // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-pointer-arithmetic) + stack_[currentStack_ - 1] = val; + } + + uintptr_t PopStack() + { + ASSERT(currentStack_ >= 1); + // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-pointer-arithmetic) + return stack_[--currentStack_]; + } + + uintptr_t PeekStack() const + { + ASSERT(currentStack_ >= 1); + // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-pointer-arithmetic) + return stack_[currentStack_ - 1]; + } + + const uint8_t *GetCurrentPtr() const + { + return currentPtr_; + } + + CaptureState *GetCaptureResultList() const + { + return captureResultList_; + } + + void DumpResult(std::ostream &out) const; + + MatchResult GetResult(const JSThread *thread, bool isSuccess) const; + + void PushRegExpState(StateType type, uint32_t pc); + + RegExpState *PopRegExpState(bool copyCaptrue = true); + + void DropRegExpState() + { + stateStackLen_--; + } + + RegExpState *PeekRegExpState() const + { + ASSERT(stateStackLen_ >= 1); + return reinterpret_cast( + stateStack_ + // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-pointer-arithmetic) + (stateStackLen_ - 1) * stateSize_); + } + + void ReAllocStack(uint32_t stackLen); + + inline bool IsWordChar(uint8_t value) const + { + return ((value >= '0' && value <= '9') || (value >= 'a' && value <= 'z') || (value >= 'A' && value <= 'Z') || + (value == '_')); + } + + inline bool IsTerminator(uint32_t value) const + { + // NOLINTNEXTLINE(readability-magic-numbers) + return (value == '\n' || value == '\r' || value == 0x2028 || value == 0x2029); + } + + inline bool IsIgnoreCase() const + { + return (flags_ & RegExpParser::FLAG_IGNORECASE) != 0; + } + + inline bool IsUtf16() const + { + return (flags_ & RegExpParser::FLAG_UTF16) != 0; + } + +private: + static constexpr size_t CHAR_SIZE = 1; + static constexpr size_t WIDE_CHAR_SIZE = 2; + static constexpr size_t SAVE_RESET_START = 1; + static constexpr size_t SAVE_RESET_END = 2; + static constexpr size_t LOOP_MIN_OFFSET = 5; + static constexpr size_t LOOP_MAX_OFFSET = 9; + static constexpr size_t LOOP_PC_OFFSET = 1; + static constexpr size_t RANGE32_HEAD_OFFSET = 3; + static constexpr size_t RANGE32_MAX_HALF_OFFSET = 4; + static constexpr size_t RANGE32_MAX_OFFSET = 8; + static constexpr size_t RANGE32_OFFSET = 2; + static constexpr uint32_t STACK_MULTIPLIER = 2; + static constexpr uint32_t MIN_STACK_SIZE = 8; + uint8_t *input_ = nullptr; + uint8_t *inputEnd_ = nullptr; + bool isWideChar_ = false; + + uint32_t currentPc_ = 0; + const uint8_t *currentPtr_ = nullptr; + CaptureState *captureResultList_ = nullptr; + uintptr_t *stack_ = nullptr; + uint32_t currentStack_ = 0; + + uint32_t nCapture_ = 0; + uint32_t nStack_ = 0; + + uint32_t flags_ = 0; + uint32_t stateStackLen_ = 0; + uint32_t stateStackSize_ = 0; + uint32_t stateSize_ = 0; + uint8_t *stateStack_ = nullptr; + Chunk *chunk_ = nullptr; +}; +} // namespace panda::ecmascript +#endif // ECMASCRIPT_REGEXP_REGEXP_EXECUTOR_H diff --git a/runtime/regexp/regexp_opcode.cpp b/runtime/regexp/regexp_opcode.cpp new file mode 100644 index 000000000..f1c56839f --- /dev/null +++ b/runtime/regexp/regexp_opcode.cpp @@ -0,0 +1,666 @@ +/* + * Copyright (c) 2021-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 "plugins/ecmascript/runtime/regexp/regexp_opcode.h" + +#include "plugins/ecmascript/runtime/regexp/regexp_executor.h" + +namespace panda::ecmascript { +using CaptureState = RegExpExecutor::CaptureState; + +static SaveStartOpCode g_saveStartOpcode = SaveStartOpCode(); // NOLINTNEXTLINE(fuchsia-statically-constructed-objects) +static SaveEndOpCode g_saveEndOpcode = SaveEndOpCode(); // NOLINTNEXTLINE(fuchsia-statically-constructed-objects) +static CharOpCode g_charOpcode = CharOpCode(); // NOLINTNEXTLINE(fuchsia-statically-constructed-objects) +static GotoOpCode g_gotoOpcode = GotoOpCode(); // NOLINTNEXTLINE(fuchsia-statically-constructed-objects) +static SplitNextOpCode g_splitNextOpcode = SplitNextOpCode(); // NOLINTNEXTLINE(fuchsia-statically-constructed-objects) +static SplitFirstOpCode g_splitFirstOpcode = + SplitFirstOpCode(); // NOLINTNEXTLINE(fuchsia-statically-constructed-objects) +static MatchOpCode g_matchOpcode = MatchOpCode(); // NOLINTNEXTLINE(fuchsia-statically-constructed-objects) +static LoopOpCode g_loopOpcode = LoopOpCode(); // NOLINTNEXTLINE(fuchsia-statically-constructed-objects) +static LoopGreedyOpCode g_loopGreedyOpcode = + LoopGreedyOpCode(); // NOLINTNEXTLINE(fuchsia-statically-constructed-objects) +static PushCharOpCode g_pushCharOpcode = PushCharOpCode(); // NOLINTNEXTLINE(fuchsia-statically-constructed-objects) +static CheckCharOpCode g_checkCharOpcode = CheckCharOpCode(); // NOLINTNEXTLINE(fuchsia-statically-constructed-objects) +static PushOpCode g_pushOpcode = PushOpCode(); // NOLINTNEXTLINE(fuchsia-statically-constructed-objects) +static PopOpCode g_popOpcode = PopOpCode(); // NOLINTNEXTLINE(fuchsia-statically-constructed-objects) +static SaveResetOpCode g_saveResetOpcode = SaveResetOpCode(); // NOLINTNEXTLINE(fuchsia-statically-constructed-objects) +static LineStartOpCode g_lineStartOpcode = LineStartOpCode(); // NOLINTNEXTLINE(fuchsia-statically-constructed-objects) +static LineEndOpCode g_lineEndOpcode = LineEndOpCode(); // NOLINTNEXTLINE(fuchsia-statically-constructed-objects) +static WordBoundaryOpCode g_wordBoundaryOpcode = + WordBoundaryOpCode(); // NOLINTNEXTLINE(fuchsia-statically-constructed-objects) +static NotWordBoundaryOpCode g_notWordBoundaryOpcode = + NotWordBoundaryOpCode(); // NOLINTNEXTLINE(fuchsia-statically-constructed-objects) +static AllOpCode g_allOpcode = AllOpCode(); // NOLINTNEXTLINE(fuchsia-statically-constructed-objects) +static DotsOpCode g_dotsOpcode = DotsOpCode(); // NOLINTNEXTLINE(fuchsia-statically-constructed-objects) +static MatchAheadOpCode g_matchAheadOpcode = + MatchAheadOpCode(); // NOLINTNEXTLINE(fuchsia-statically-constructed-objects) +static NegativeMatchAheadOpCode g_negativeMatchAheadOpcode = + NegativeMatchAheadOpCode(); // NOLINTNEXTLINE(fuchsia-statically-constructed-objects) +static MatchEndOpCode g_matchEndOpcode = MatchEndOpCode(); // NOLINTNEXTLINE(fuchsia-statically-constructed-objects) +static PrevOpCode g_prevOpcode = PrevOpCode(); // NOLINTNEXTLINE(fuchsia-statically-constructed-objects) +static RangeOpCode g_rangeOpcode = RangeOpCode(); // NOLINTNEXTLINE(fuchsia-statically-constructed-objects) +static BackReferenceOpCode g_backreferenceOpcode = + BackReferenceOpCode(); // NOLINTNEXTLINE(fuchsia-statically-constructed-objects) +static BackwardBackReferenceOpCode g_backwardBackreferenceOpcode = + BackwardBackReferenceOpCode(); // NOLINTNEXTLINE(fuchsia-statically-constructed-objects) +static Char32OpCode g_char32Opcode = Char32OpCode(); // NOLINTNEXTLINE(fuchsia-statically-constructed-objects) +static Range32OpCode g_range32Opcode = Range32OpCode(); // NOLINTNEXTLINE(fuchsia-statically-constructed-objects) +// NOLINTNEXTLINE(fuchsia-statically-constructed-objects) +static std::vector g_intrinsicSet = { + &g_saveStartOpcode, + &g_saveEndOpcode, + &g_charOpcode, + &g_gotoOpcode, + &g_splitFirstOpcode, + &g_splitNextOpcode, + &g_matchAheadOpcode, + &g_negativeMatchAheadOpcode, + &g_matchOpcode, + &g_loopOpcode, + &g_loopGreedyOpcode, + &g_pushCharOpcode, + &g_checkCharOpcode, + &g_pushOpcode, + &g_popOpcode, + &g_saveResetOpcode, + &g_lineStartOpcode, + &g_lineEndOpcode, + &g_wordBoundaryOpcode, + &g_notWordBoundaryOpcode, + &g_allOpcode, + &g_dotsOpcode, + &g_matchEndOpcode, + &g_prevOpcode, + &g_rangeOpcode, + &g_backreferenceOpcode, + &g_backwardBackreferenceOpcode, + &g_char32Opcode, + &g_range32Opcode, +}; + +RegExpOpCode::RegExpOpCode(uint8_t opCode, int size) : opCode_(opCode), size_(size) {} + +/* static */ +RegExpOpCode *RegExpOpCode::GetRegExpOpCode(const DynChunk &buf, int pc) +{ + uint8_t opCode = buf.GetU8(pc); + ASSERT_PRINT(opCode <= g_intrinsicSet.size(), "invalid op code"); + return g_intrinsicSet.at(opCode); +} + +/* static */ +RegExpOpCode *RegExpOpCode::GetRegExpOpCode(uint8_t opCode) +{ + ASSERT_PRINT(opCode <= g_intrinsicSet.size(), "invalid op code"); + return g_intrinsicSet.at(opCode); +} + +/* static */ +void RegExpOpCode::DumpRegExpOpCode(std::ostream &out, const DynChunk &buf) +{ + out << "OpCode:\t" << std::endl; + uint32_t pc = RegExpParser::OP_START_OFFSET; + do { + RegExpOpCode *byteCode = GetRegExpOpCode(buf, pc); + pc = byteCode->DumpOpCode(out, buf, pc); + } while (pc < buf.size_); +} + +uint32_t SaveStartOpCode::EmitOpCode(DynChunk *buf, uint32_t para) const +{ + auto capture = static_cast(para & 0xffU); // NOLINTNEXTLINE(readability-magic-numbers) + buf->EmitChar(GetOpCode()); + buf->EmitChar(capture); + return GetDynChunkfSize(*buf); +} + +uint32_t SaveStartOpCode::DumpOpCode(std::ostream &out, const DynChunk &buf, uint32_t offset) const +{ + out << offset << ":\t" + << "save_start\t" << buf.GetU8(offset + 1) << std::endl; + return offset + GetSize(); +} + +uint32_t SaveEndOpCode::EmitOpCode(DynChunk *buf, uint32_t para) const +{ + auto capture = static_cast(para & 0xffU); // NOLINTNEXTLINE(readability-magic-numbers) + buf->EmitChar(GetOpCode()); + buf->EmitChar(capture); + return GetDynChunkfSize(*buf); +} + +uint32_t SaveEndOpCode::DumpOpCode(std::ostream &out, const DynChunk &buf, uint32_t offset) const +{ + out << offset << ":\t" + << "save_end\t" << buf.GetU8(offset + 1) << std::endl; + return offset + GetSize(); +} + +uint32_t CharOpCode::EmitOpCode(DynChunk *buf, uint32_t para) const +{ + auto paraChar = static_cast(para & 0xffffU); // NOLINTNEXTLINE(readability-magic-numbers) + buf->EmitChar(GetOpCode()); + buf->EmitU16(paraChar); + return GetDynChunkfSize(*buf); +} + +uint32_t CharOpCode::DumpOpCode(std::ostream &out, const DynChunk &buf, uint32_t offset) const +{ + out << offset << ":\t" + << "char\t" << static_cast(buf.GetU16(offset + 1)) << std::endl; + return offset + GetSize(); +} + +uint32_t Char32OpCode::EmitOpCode(DynChunk *buf, uint32_t para) const +{ + buf->EmitChar(GetOpCode()); + buf->EmitU32(para); + return GetDynChunkfSize(*buf); +} + +uint32_t Char32OpCode::DumpOpCode(std::ostream &out, const DynChunk &buf, uint32_t offset) const +{ + out << offset << ":\t" + << "char32\t" << static_cast(buf.GetU32(offset + 1)) << std::endl; + return offset + GetSize(); +} + +uint32_t GotoOpCode::EmitOpCode(DynChunk *buf, uint32_t para) const +{ + buf->EmitChar(GetOpCode()); + buf->EmitU32(para); + return GetDynChunkfSize(*buf); +} + +void GotoOpCode::UpdateOpPara(DynChunk *buf, uint32_t offset, uint32_t para) const +{ + buf->PutU32(offset + 1, para); +} + +uint32_t GotoOpCode::DumpOpCode(std::ostream &out, const DynChunk &buf, uint32_t offset) const +{ + out << offset << ":\t" + << "goto\t" << buf.GetU32(offset + 1) + offset + GetSize() << std::endl; + return offset + GetSize(); +} + +uint32_t SplitNextOpCode::InsertOpCode(DynChunk *buf, uint32_t offset, uint32_t para) const +{ + buf->Insert(offset, GetSize()); + buf->PutU8(offset, GetOpCode()); + buf->PutU32(offset + 1, para); + return GetDynChunkfSize(*buf); +} + +uint32_t SplitNextOpCode::DumpOpCode(std::ostream &out, const DynChunk &buf, uint32_t offset) const +{ + out << offset << ":\t" + << "split_next\t" << buf.GetU32(offset + 1) + offset + GetSize() << std::endl; + return offset + GetSize(); +} + +uint32_t SplitFirstOpCode::InsertOpCode(DynChunk *buf, uint32_t offset, uint32_t para) const +{ + buf->Insert(offset, GetSize()); + buf->PutU8(offset, GetOpCode()); + buf->PutU32(offset + 1, para); + return GetDynChunkfSize(*buf); +} + +uint32_t SplitFirstOpCode::DumpOpCode(std::ostream &out, const DynChunk &buf, uint32_t offset) const +{ + out << offset << ":\t" + << "split_first\t" << buf.GetU32(offset + 1) + offset + GetSize() << std::endl; + return offset + GetSize(); +} + +uint32_t LoopOpCode::EmitOpCode(DynChunk *buf, uint32_t start, uint32_t min, uint32_t max) const +{ + buf->EmitChar(GetOpCode()); + buf->EmitU32(start); + buf->EmitU32(min); + buf->EmitU32(max); + return GetDynChunkfSize(*buf); +} + +uint32_t LoopOpCode::DumpOpCode(std::ostream &out, const DynChunk &buf, uint32_t offset) const +{ + out << offset << ":\t" + << "loop\t" << buf.GetU32(offset + 1) + offset + GetSize() << "\t" + << buf.GetU32(offset + RegExpOpCode::OP_SIZE_FIVE) << "\t" << buf.GetU32(offset + RegExpOpCode::OP_SIZE_NINE) + << std::endl; + return offset + GetSize(); +} + +uint32_t LoopGreedyOpCode::EmitOpCode(DynChunk *buf, uint32_t start, uint32_t min, uint32_t max) const +{ + buf->EmitChar(GetOpCode()); + buf->EmitU32(start); + buf->EmitU32(min); + buf->EmitU32(max); + return GetDynChunkfSize(*buf); +} + +uint32_t LoopGreedyOpCode::DumpOpCode(std::ostream &out, const DynChunk &buf, uint32_t offset) const +{ + out << offset << ":\t" + << "greedy_loop\t" << buf.GetU32(offset + 1) + offset + GetSize() << "\t" + << buf.GetU32(offset + RegExpOpCode::OP_SIZE_FIVE) << "\t" << buf.GetU32(offset + RegExpOpCode::OP_SIZE_NINE) + << std::endl; + return offset + GetSize(); +} + +uint32_t PushCharOpCode::InsertOpCode(DynChunk *buf, uint32_t offset) const +{ + buf->Insert(offset, GetSize()); + buf->PutU8(offset, GetOpCode()); + return GetDynChunkfSize(*buf); +} + +uint32_t PushCharOpCode::DumpOpCode(std::ostream &out, [[maybe_unused]] const DynChunk &buf, uint32_t offset) const +{ + out << offset << ":\t" + << "push_char" << std::endl; + return offset + GetSize(); +} + +uint32_t PushOpCode::InsertOpCode(DynChunk *buf, uint32_t offset) const +{ + buf->Insert(offset, GetSize()); + buf->PutU8(offset, GetOpCode()); + return GetDynChunkfSize(*buf); +} + +uint32_t PushOpCode::DumpOpCode(std::ostream &out, [[maybe_unused]] const DynChunk &buf, uint32_t offset) const +{ + out << offset << ":\t" + << "push" << std::endl; + return offset + GetSize(); +} + +uint32_t PopOpCode::EmitOpCode(DynChunk *buf) const +{ + buf->EmitChar(GetOpCode()); + return GetDynChunkfSize(*buf); +} + +uint32_t PopOpCode::DumpOpCode(std::ostream &out, [[maybe_unused]] const DynChunk &buf, uint32_t offset) const +{ + out << offset << ":\t" + << "pop" << std::endl; + return offset + GetSize(); +} + +uint32_t CheckCharOpCode::EmitOpCode(DynChunk *buf, uint32_t offset) const +{ + buf->EmitChar(GetOpCode()); + buf->EmitU32(offset); + return GetDynChunkfSize(*buf); +} + +uint32_t CheckCharOpCode::DumpOpCode(std::ostream &out, const DynChunk &buf, uint32_t offset) const +{ + out << offset << ":\t" + << "check_char\t" << buf.GetU32(offset + 1) + offset + GetSize() << std::endl; + return offset + GetSize(); +} + +uint32_t SaveResetOpCode::InsertOpCode(DynChunk *buf, uint32_t offset, uint32_t start, uint32_t end) const +{ + auto captureStart = static_cast(start & 0xffU); // NOLINTNEXTLINE(readability-magic-numbers) + auto captureEnd = static_cast(end & 0xffU); // NOLINTNEXTLINE(readability-magic-numbers) + buf->Insert(offset, GetSize()); + buf->PutU8(offset, GetOpCode()); + buf->PutU8(offset + RegExpOpCode::OP_SIZE_ONE, captureStart); + buf->PutU8(offset + RegExpOpCode::OP_SIZE_TWO, captureEnd); + return GetDynChunkfSize(*buf); +} + +uint32_t SaveResetOpCode::DumpOpCode(std::ostream &out, const DynChunk &buf, uint32_t offset) const +{ + out << offset << ":\t" + << "save_reset\t" << buf.GetU8(offset + RegExpOpCode::OP_SIZE_ONE) << "\t" + << buf.GetU8(offset + RegExpOpCode::OP_SIZE_TWO) << std::endl; + return offset + GetSize(); +} + +uint32_t MatchOpCode::EmitOpCode(DynChunk *buf, [[maybe_unused]] uint32_t para) const +{ + buf->EmitChar(GetOpCode()); + return GetDynChunkfSize(*buf); +} + +uint32_t MatchOpCode::DumpOpCode(std::ostream &out, [[maybe_unused]] const DynChunk &buf, uint32_t offset) const +{ + out << offset << ":\t" + << "match" << std::endl; + return offset + GetSize(); +} + +uint32_t MatchEndOpCode::EmitOpCode(DynChunk *buf, [[maybe_unused]] uint32_t para) const +{ + buf->EmitChar(GetOpCode()); + return GetDynChunkfSize(*buf); +} + +uint32_t MatchEndOpCode::DumpOpCode(std::ostream &out, [[maybe_unused]] const DynChunk &buf, uint32_t offset) const +{ + out << offset << ":\t" + << "match_end" << std::endl; + return offset + GetSize(); +} + +uint32_t LineStartOpCode::EmitOpCode(DynChunk *buf, [[maybe_unused]] uint32_t para) const +{ + buf->EmitChar(GetOpCode()); + return GetDynChunkfSize(*buf); +} + +uint32_t LineStartOpCode::DumpOpCode(std::ostream &out, [[maybe_unused]] const DynChunk &buf, uint32_t offset) const +{ + out << offset << ":\t" + << "line_start" << std::endl; + return offset + GetSize(); +} + +uint32_t LineEndOpCode::EmitOpCode(DynChunk *buf, [[maybe_unused]] uint32_t para) const +{ + buf->EmitChar(GetOpCode()); + return GetDynChunkfSize(*buf); +} + +uint32_t LineEndOpCode::DumpOpCode(std::ostream &out, [[maybe_unused]] const DynChunk &buf, uint32_t offset) const +{ + out << offset << ":\t" + << "line_end" << std::endl; + return offset + GetSize(); +} + +uint32_t WordBoundaryOpCode::EmitOpCode(DynChunk *buf, [[maybe_unused]] uint32_t para) const +{ + buf->EmitChar(GetOpCode()); + return GetDynChunkfSize(*buf); +} + +uint32_t WordBoundaryOpCode::DumpOpCode(std::ostream &out, [[maybe_unused]] const DynChunk &buf, uint32_t offset) const +{ + out << offset << ":\t" + << "word_boundary" << std::endl; + return offset + GetSize(); +} + +uint32_t NotWordBoundaryOpCode::EmitOpCode(DynChunk *buf, [[maybe_unused]] uint32_t para) const +{ + buf->EmitChar(GetOpCode()); + return GetDynChunkfSize(*buf); +} + +uint32_t NotWordBoundaryOpCode::DumpOpCode(std::ostream &out, [[maybe_unused]] const DynChunk &buf, + uint32_t offset) const +{ + out << offset << ":\t" + << "not_word_boundary" << std::endl; + return offset + GetSize(); +} + +uint32_t AllOpCode::EmitOpCode(DynChunk *buf, [[maybe_unused]] uint32_t para) const +{ + buf->EmitChar(GetOpCode()); + return GetDynChunkfSize(*buf); +} + +uint32_t AllOpCode::DumpOpCode(std::ostream &out, [[maybe_unused]] const DynChunk &buf, uint32_t offset) const +{ + out << offset << ":\t" + << "all" << std::endl; + return offset + GetSize(); +} + +uint32_t DotsOpCode::EmitOpCode(DynChunk *buf, [[maybe_unused]] uint32_t para) const +{ + buf->EmitChar(GetOpCode()); + return GetDynChunkfSize(*buf); +} + +uint32_t DotsOpCode::DumpOpCode(std::ostream &out, [[maybe_unused]] const DynChunk &buf, uint32_t offset) const +{ + out << offset << ":\t" + << "dots" << std::endl; + return offset + GetSize(); +} + +uint32_t MatchAheadOpCode::DumpOpCode(std::ostream &out, const DynChunk &buf, uint32_t offset) const +{ + out << offset << ":\t" + << "match_ahead\t" << buf.GetU32(offset + 1) + offset + GetSize() << std::endl; + return offset + GetSize(); +} + +uint32_t RangeOpCode::DumpOpCode(std::ostream &out, const DynChunk &buf, uint32_t offset) const +{ + out << offset << ":\t" + << "range\t"; + int size = buf.GetU16(offset + 1); + for (int i = 0; i < size; i++) { + out << buf.GetU16(offset + RegExpOpCode::OP_SIZE_THREE + (i * RegExpOpCode::OP_SIZE_FOUR)) << "\t" + << buf.GetU16(offset + RegExpOpCode::OP_SIZE_THREE + + (i * RegExpOpCode::OP_SIZE_FOUR + RegExpOpCode::OP_SIZE_TWO)) + << "\t"; + } + out << std::endl; + return offset + size * RegExpOpCode::OP_SIZE_FOUR + RegExpOpCode::OP_SIZE_THREE; +} + +uint32_t RangeOpCode::InsertOpCode(DynChunk *buf, const RangeSet &rangeSet) const +{ + buf->EmitChar(GetOpCode()); + size_t size = rangeSet.rangeSet_.size(); + buf->EmitU16(size); + for (auto range : rangeSet.rangeSet_) { + buf->EmitU16(range.first); + buf->EmitU16(range.second); + } + return GetDynChunkfSize(*buf); +} + +uint32_t Range32OpCode::DumpOpCode(std::ostream &out, const DynChunk &buf, uint32_t offset) const +{ + out << offset << ":\t" + << "range32\t"; + int size = buf.GetU16(offset + 1); + for (int i = 0; i < size; i++) { + out << buf.GetU32(offset + RegExpOpCode::OP_SIZE_THREE + (i * RegExpOpCode::OP_SIZE_EIGHT)) << "\t" + << buf.GetU32(offset + RegExpOpCode::OP_SIZE_THREE + + (i * RegExpOpCode::OP_SIZE_EIGHT + RegExpOpCode::OP_SIZE_FOUR)) + << "\t"; + } + out << std::endl; + return offset + size * +RegExpOpCode::OP_SIZE_EIGHT + RegExpOpCode::OP_SIZE_THREE; +} + +uint32_t Range32OpCode::InsertOpCode(DynChunk *buf, const RangeSet &rangeSet) const +{ + buf->EmitChar(GetOpCode()); + size_t size = rangeSet.rangeSet_.size(); + buf->EmitU16(size); + for (auto range : rangeSet.rangeSet_) { + buf->EmitU32(range.first); + buf->EmitU32(range.second); + } + return GetDynChunkfSize(*buf); +} + +uint32_t MatchAheadOpCode::InsertOpCode(DynChunk *buf, uint32_t offset, uint32_t para) const +{ + buf->Insert(offset, GetSize()); + buf->PutU8(offset, GetOpCode()); + buf->PutU32(offset + 1, para); + return GetDynChunkfSize(*buf); +} + +uint32_t NegativeMatchAheadOpCode::DumpOpCode(std::ostream &out, const DynChunk &buf, uint32_t offset) const +{ + out << offset << ":\t" + << "negative_match_ahead\t" << buf.GetU32(offset + 1) + offset + GetSize() << std::endl; + return offset + GetSize(); +} + +uint32_t NegativeMatchAheadOpCode::InsertOpCode(DynChunk *buf, uint32_t offset, uint32_t para) const +{ + buf->Insert(offset, GetSize()); + buf->PutU8(offset, GetOpCode()); + buf->PutU32(offset + 1, para); + return GetDynChunkfSize(*buf); +} + +uint32_t PrevOpCode::EmitOpCode(DynChunk *buf, [[maybe_unused]] uint32_t para) const +{ + buf->EmitChar(GetOpCode()); + return GetDynChunkfSize(*buf); +} + +uint32_t PrevOpCode::DumpOpCode(std::ostream &out, [[maybe_unused]] const DynChunk &buf, uint32_t offset) const +{ + out << offset << ":\t" + << "prev" << std::endl; + return offset + GetSize(); +} + +uint32_t BackReferenceOpCode::EmitOpCode(DynChunk *buf, uint32_t para) const +{ + auto capture = static_cast(para & 0xffU); // NOLINTNEXTLINE(readability-magic-numbers) + buf->EmitChar(GetOpCode()); + buf->EmitChar(capture); + return GetDynChunkfSize(*buf); +} + +uint32_t BackReferenceOpCode::DumpOpCode(std::ostream &out, const DynChunk &buf, uint32_t offset) const +{ + out << offset << ":\t" + << "backreference\t" << buf.GetU8(offset + 1) << std::endl; + return offset + GetSize(); +} + +uint32_t BackwardBackReferenceOpCode::EmitOpCode(DynChunk *buf, uint32_t para) const +{ + auto capture = static_cast(para & 0xffU); // NOLINTNEXTLINE(readability-magic-numbers) + buf->EmitChar(GetOpCode()); + buf->EmitChar(capture); + return GetDynChunkfSize(*buf); +} + +uint32_t BackwardBackReferenceOpCode::DumpOpCode(std::ostream &out, const DynChunk &buf, uint32_t offset) const +{ + out << offset << ":\t" + << "backward_backreference\t" << buf.GetU8(offset + 1) << std::endl; + return offset + GetSize(); +} + +void RangeSet::Insert(uint32_t start, uint32_t end) +{ + if (start > end) { + return; + } + std::pair pairElement = std::make_pair(start, end); + if (rangeSet_.empty()) { + rangeSet_.emplace_back(pairElement); + } else { + for (auto iter = rangeSet_.begin(); iter != rangeSet_.end(); iter++) { + if (IsIntersect(start, end, iter->first, iter->second) || + IsAdjacent(start, end, iter->first, iter->second)) { + iter->first = std::min(iter->first, start); + iter->second = std::max(iter->second, end); + return; + } + if (iter->first > end) { + rangeSet_.insert(iter, pairElement); + return; + } + } + rangeSet_.emplace_back(pairElement); + } +} + +void RangeSet::Insert(const RangeSet &s1) +{ + if (s1.rangeSet_.empty()) { + return; + } + if (rangeSet_.empty()) { + rangeSet_ = s1.rangeSet_; + } else { + for (auto range : s1.rangeSet_) { + Insert(range.first, range.second); + } + Compress(); + } +} + +void RangeSet::Invert(bool isUtf16) +{ + uint32_t maxValue = isUtf16 ? UINT32_MAX : UINT16_MAX; + if (rangeSet_.empty()) { + rangeSet_.emplace_back(std::make_pair(0, maxValue)); + return; + } + + auto iter = rangeSet_.begin(); + auto iter2 = rangeSet_.begin(); + if (iter->first == 0 && iter->second == maxValue) { + rangeSet_.clear(); + return; + } + iter2++; + + uint32_t first = iter->first; + + for (iter = rangeSet_.begin(); iter != rangeSet_.end(); iter++) { + if (iter->second == maxValue) { + rangeSet_.erase(iter); + break; + } + iter->first = iter->second + 1; + if (iter2 != rangeSet_.end()) { + iter->second = iter2->first - 1; + iter2++; + } else { + iter->second = maxValue; + } + } + if (first > 0) { + std::pair pair1 = std::make_pair(0, first - 1); + rangeSet_.push_front(pair1); + } + Compress(); +} + +void RangeSet::Compress() +{ + auto iter = rangeSet_.begin(); + auto iter2 = rangeSet_.begin(); + iter2++; + while (iter2 != rangeSet_.end()) { + if (IsIntersect(iter->first, iter->second, iter2->first, iter2->second) || + IsAdjacent(iter->first, iter->second, iter2->first, iter2->second)) { + iter->first = std::min(iter->first, iter2->first); + iter->second = std::max(iter->second, iter2->second); + iter2 = rangeSet_.erase(iter2); + } else { + iter++; + iter2++; + } + } +} +} // namespace panda::ecmascript diff --git a/runtime/regexp/regexp_opcode.h b/runtime/regexp/regexp_opcode.h new file mode 100644 index 000000000..b3f651010 --- /dev/null +++ b/runtime/regexp/regexp_opcode.h @@ -0,0 +1,453 @@ +/* + * Copyright (c) 2021-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_REGEXP_OPCODE_H +#define ECMASCRIPT_REGEXP_OPCODE_H + +#include + +#include "plugins/ecmascript/runtime/regexp/dyn_chunk.h" + +namespace panda { +namespace ecmascript { +class RegExpOpCode { +public: + enum : uint8_t { + OP_SAVE_START = 0U, + OP_SAVE_END, + OP_CHAR, + OP_GOTO, + OP_SPLIT_FIRST, + OP_SPLIT_NEXT, + OP_MATCH_AHEAD, + OP_NEGATIVE_MATCH_AHEAD, + OP_MATCH, + OP_LOOP, + OP_LOOP_GREEDY, + OP_PUSH_CHAR, + OP_CHECK_CHAR, + OP_PUSH, + OP_POP, + OP_SAVE_RESET, + OP_LINE_START, + OP_LINE_END, + OP_WORD_BOUNDARY, + OP_NOT_WORD_BOUNDARY, + OP_ALL, + OP_DOTS, + OP_MATCH_END, + OP_PREV, + OP_RANGE, + OP_BACKREFERENCE, + OP_BACKWARD_BACKREFERENCE, + OP_CHAR32, + OP_RANGE32, + OP_INVALID, + }; + + static constexpr size_t OP_SIZE_ONE = 1; + static constexpr size_t OP_SIZE_TWO = 2; + static constexpr size_t OP_SIZE_THREE = 3; + static constexpr size_t OP_SIZE_FOUR = 4; + static constexpr size_t OP_SIZE_FIVE = 5; + static constexpr size_t OP_SIZE_EIGHT = 8; + static constexpr size_t OP_SIZE_NINE = 9; + static constexpr size_t OP_SIZE_THIRTEEN = 13; + + RegExpOpCode(uint8_t opCode, int size); + NO_COPY_SEMANTIC(RegExpOpCode); + NO_MOVE_SEMANTIC(RegExpOpCode); + + virtual ~RegExpOpCode() = default; + static RegExpOpCode *GetRegExpOpCode(const DynChunk &buf, int pcOffset); + static RegExpOpCode *GetRegExpOpCode(uint8_t opCode); + static void DumpRegExpOpCode(std::ostream &out, const DynChunk &buf); + inline int GetSize() const + { + return size_; + } + inline uint8_t GetOpCode() const + { + return opCode_; + } + inline int GetDynChunkfSize(const DynChunk &buf) const + { + return buf.size_; + } + virtual uint32_t DumpOpCode(std::ostream &out, const DynChunk &buf, uint32_t offset) const = 0; + +private: + uint8_t opCode_{0}; + uint8_t size_{0}; +}; + +class SaveStartOpCode : public RegExpOpCode { +public: + SaveStartOpCode() : RegExpOpCode(OP_SAVE_START, RegExpOpCode::OP_SIZE_TWO) {} + uint32_t EmitOpCode(DynChunk *buf, uint32_t para) const; + ~SaveStartOpCode() override = default; + NO_COPY_SEMANTIC(SaveStartOpCode); + NO_MOVE_SEMANTIC(SaveStartOpCode); + uint32_t DumpOpCode(std::ostream &out, const DynChunk &buf, uint32_t offset) const override; +}; + +class SaveEndOpCode : public RegExpOpCode { +public: + SaveEndOpCode() : RegExpOpCode(OP_SAVE_END, RegExpOpCode::OP_SIZE_TWO) {} + uint32_t EmitOpCode(DynChunk *buf, uint32_t para) const; + ~SaveEndOpCode() override = default; + NO_COPY_SEMANTIC(SaveEndOpCode); + NO_MOVE_SEMANTIC(SaveEndOpCode); + uint32_t DumpOpCode(std::ostream &out, const DynChunk &buf, uint32_t offset) const override; +}; + +class CharOpCode : public RegExpOpCode { +public: + CharOpCode() : RegExpOpCode(OP_CHAR, RegExpOpCode::OP_SIZE_THREE) {} + uint32_t EmitOpCode(DynChunk *buf, uint32_t para) const; + ~CharOpCode() override = default; + NO_COPY_SEMANTIC(CharOpCode); + NO_MOVE_SEMANTIC(CharOpCode); + uint32_t DumpOpCode(std::ostream &out, const DynChunk &buf, uint32_t offset) const override; +}; + +class GotoOpCode : public RegExpOpCode { +public: + GotoOpCode() : RegExpOpCode(OP_GOTO, RegExpOpCode::OP_SIZE_FIVE) {} + uint32_t EmitOpCode(DynChunk *buf, uint32_t para) const; + void UpdateOpPara(DynChunk *buf, uint32_t offset, uint32_t para) const; + ~GotoOpCode() override = default; + NO_COPY_SEMANTIC(GotoOpCode); + NO_MOVE_SEMANTIC(GotoOpCode); + uint32_t DumpOpCode(std::ostream &out, const DynChunk &buf, uint32_t offset) const override; +}; + +class SplitNextOpCode : public RegExpOpCode { +public: + SplitNextOpCode() : RegExpOpCode(OP_SPLIT_NEXT, RegExpOpCode::OP_SIZE_FIVE) {} + uint32_t InsertOpCode(DynChunk *buf, uint32_t offset, uint32_t para) const; + ~SplitNextOpCode() override = default; + NO_COPY_SEMANTIC(SplitNextOpCode); + NO_MOVE_SEMANTIC(SplitNextOpCode); + uint32_t DumpOpCode(std::ostream &out, const DynChunk &buf, uint32_t offset) const override; +}; + +class SplitFirstOpCode : public RegExpOpCode { +public: + SplitFirstOpCode() : RegExpOpCode(OP_SPLIT_FIRST, RegExpOpCode::OP_SIZE_FIVE) {} + uint32_t InsertOpCode(DynChunk *buf, uint32_t offset, uint32_t para) const; + ~SplitFirstOpCode() override = default; + NO_COPY_SEMANTIC(SplitFirstOpCode); + NO_MOVE_SEMANTIC(SplitFirstOpCode); + uint32_t DumpOpCode(std::ostream &out, const DynChunk &buf, uint32_t offset) const override; +}; + +class PushOpCode : public RegExpOpCode { +public: + PushOpCode() : RegExpOpCode(OP_PUSH, RegExpOpCode::OP_SIZE_ONE) {} + uint32_t InsertOpCode(DynChunk *buf, uint32_t offset) const; + ~PushOpCode() override = default; + NO_COPY_SEMANTIC(PushOpCode); + NO_MOVE_SEMANTIC(PushOpCode); + uint32_t DumpOpCode(std::ostream &out, const DynChunk &buf, uint32_t offset) const override; +}; + +class PopOpCode : public RegExpOpCode { +public: + PopOpCode() : RegExpOpCode(OP_POP, RegExpOpCode::OP_SIZE_ONE) {} + uint32_t EmitOpCode(DynChunk *buf) const; + ~PopOpCode() override = default; + NO_COPY_SEMANTIC(PopOpCode); + NO_MOVE_SEMANTIC(PopOpCode); + uint32_t DumpOpCode(std::ostream &out, const DynChunk &buf, uint32_t offset) const override; +}; + +class PushCharOpCode : public RegExpOpCode { +public: + PushCharOpCode() : RegExpOpCode(OP_PUSH_CHAR, RegExpOpCode::OP_SIZE_ONE) {} + uint32_t InsertOpCode(DynChunk *buf, uint32_t offset) const; + ~PushCharOpCode() override = default; + NO_COPY_SEMANTIC(PushCharOpCode); + NO_MOVE_SEMANTIC(PushCharOpCode); + uint32_t DumpOpCode(std::ostream &out, const DynChunk &buf, uint32_t offset) const override; +}; + +class CheckCharOpCode : public RegExpOpCode { +public: + CheckCharOpCode() : RegExpOpCode(OP_CHECK_CHAR, RegExpOpCode::OP_SIZE_FIVE) {} + uint32_t EmitOpCode(DynChunk *buf, uint32_t offset) const; + ~CheckCharOpCode() override = default; + NO_COPY_SEMANTIC(CheckCharOpCode); + NO_MOVE_SEMANTIC(CheckCharOpCode); + uint32_t DumpOpCode(std::ostream &out, const DynChunk &buf, uint32_t offset) const override; +}; + +class LoopOpCode : public RegExpOpCode { +public: + LoopOpCode() : RegExpOpCode(OP_LOOP, RegExpOpCode::OP_SIZE_THIRTEEN) {} + uint32_t EmitOpCode(DynChunk *buf, uint32_t start, uint32_t min, uint32_t max) const; + ~LoopOpCode() override = default; + NO_COPY_SEMANTIC(LoopOpCode); + NO_MOVE_SEMANTIC(LoopOpCode); + uint32_t DumpOpCode(std::ostream &out, const DynChunk &buf, uint32_t offset) const override; +}; + +class LoopGreedyOpCode : public RegExpOpCode { +public: + LoopGreedyOpCode() : RegExpOpCode(OP_LOOP_GREEDY, RegExpOpCode::OP_SIZE_THIRTEEN) {} + uint32_t EmitOpCode(DynChunk *buf, uint32_t start, uint32_t min, uint32_t max) const; + ~LoopGreedyOpCode() override = default; + NO_COPY_SEMANTIC(LoopGreedyOpCode); + NO_MOVE_SEMANTIC(LoopGreedyOpCode); + uint32_t DumpOpCode(std::ostream &out, const DynChunk &buf, uint32_t offset) const override; +}; + +class SaveResetOpCode : public RegExpOpCode { +public: + SaveResetOpCode() : RegExpOpCode(OP_SAVE_RESET, RegExpOpCode::OP_SIZE_THREE) {} + uint32_t InsertOpCode(DynChunk *buf, uint32_t offset, uint32_t start, uint32_t end) const; + ~SaveResetOpCode() override = default; + NO_COPY_SEMANTIC(SaveResetOpCode); + NO_MOVE_SEMANTIC(SaveResetOpCode); + uint32_t DumpOpCode(std::ostream &out, const DynChunk &buf, uint32_t offset) const override; +}; + +class MatchOpCode : public RegExpOpCode { +public: + MatchOpCode() : RegExpOpCode(OP_MATCH, RegExpOpCode::OP_SIZE_ONE) {} + uint32_t EmitOpCode(DynChunk *buf, uint32_t para) const; + ~MatchOpCode() override = default; + NO_COPY_SEMANTIC(MatchOpCode); + NO_MOVE_SEMANTIC(MatchOpCode); + uint32_t DumpOpCode(std::ostream &out, const DynChunk &buf, uint32_t offset) const override; +}; + +class MatchEndOpCode : public RegExpOpCode { +public: + MatchEndOpCode() : RegExpOpCode(OP_MATCH_END, RegExpOpCode::OP_SIZE_ONE) {} + uint32_t EmitOpCode(DynChunk *buf, uint32_t para) const; + ~MatchEndOpCode() override = default; + NO_COPY_SEMANTIC(MatchEndOpCode); + NO_MOVE_SEMANTIC(MatchEndOpCode); + uint32_t DumpOpCode(std::ostream &out, const DynChunk &buf, uint32_t offset) const override; +}; + +class LineStartOpCode : public RegExpOpCode { +public: + LineStartOpCode() : RegExpOpCode(OP_LINE_START, RegExpOpCode::OP_SIZE_ONE) {} + uint32_t EmitOpCode(DynChunk *buf, uint32_t para) const; + ~LineStartOpCode() override = default; + NO_COPY_SEMANTIC(LineStartOpCode); + NO_MOVE_SEMANTIC(LineStartOpCode); + uint32_t DumpOpCode(std::ostream &out, const DynChunk &buf, uint32_t offset) const override; +}; + +class LineEndOpCode : public RegExpOpCode { +public: + LineEndOpCode() : RegExpOpCode(OP_LINE_END, RegExpOpCode::OP_SIZE_ONE) {} + uint32_t EmitOpCode(DynChunk *buf, uint32_t para) const; + ~LineEndOpCode() override = default; + NO_COPY_SEMANTIC(LineEndOpCode); + NO_MOVE_SEMANTIC(LineEndOpCode); + uint32_t DumpOpCode(std::ostream &out, const DynChunk &buf, uint32_t offset) const override; +}; + +class WordBoundaryOpCode : public RegExpOpCode { +public: + WordBoundaryOpCode() : RegExpOpCode(OP_WORD_BOUNDARY, RegExpOpCode::OP_SIZE_ONE) {} + uint32_t EmitOpCode(DynChunk *buf, uint32_t para) const; + ~WordBoundaryOpCode() override = default; + NO_COPY_SEMANTIC(WordBoundaryOpCode); + NO_MOVE_SEMANTIC(WordBoundaryOpCode); + uint32_t DumpOpCode(std::ostream &out, const DynChunk &buf, uint32_t offset) const override; +}; + +class NotWordBoundaryOpCode : public RegExpOpCode { +public: + NotWordBoundaryOpCode() : RegExpOpCode(OP_NOT_WORD_BOUNDARY, RegExpOpCode::OP_SIZE_ONE) {} + uint32_t EmitOpCode(DynChunk *buf, uint32_t para) const; + ~NotWordBoundaryOpCode() override = default; + NO_COPY_SEMANTIC(NotWordBoundaryOpCode); + NO_MOVE_SEMANTIC(NotWordBoundaryOpCode); + uint32_t DumpOpCode(std::ostream &out, const DynChunk &buf, uint32_t offset) const override; +}; + +class AllOpCode : public RegExpOpCode { +public: + AllOpCode() : RegExpOpCode(OP_ALL, RegExpOpCode::OP_SIZE_ONE) {} + uint32_t EmitOpCode(DynChunk *buf, uint32_t para) const; + ~AllOpCode() override = default; + NO_COPY_SEMANTIC(AllOpCode); + NO_MOVE_SEMANTIC(AllOpCode); + uint32_t DumpOpCode(std::ostream &out, const DynChunk &buf, uint32_t offset) const override; +}; + +class DotsOpCode : public RegExpOpCode { +public: + DotsOpCode() : RegExpOpCode(OP_DOTS, RegExpOpCode::OP_SIZE_ONE) {} + uint32_t EmitOpCode(DynChunk *buf, uint32_t para) const; + ~DotsOpCode() override = default; + NO_COPY_SEMANTIC(DotsOpCode); + NO_MOVE_SEMANTIC(DotsOpCode); + uint32_t DumpOpCode(std::ostream &out, const DynChunk &buf, uint32_t offset) const override; +}; + +class RangeSet { +public: + RangeSet() = default; + explicit RangeSet(uint32_t value) + { + Insert(value, value); + } + explicit RangeSet(uint32_t start, uint32_t end) + { + Insert(start, end); + } + explicit RangeSet(const std::list> &rangeSet) + { + rangeSet_ = rangeSet; + } + ~RangeSet() = default; + + inline bool IsIntersect(uint64_t start, uint64_t end, uint64_t start1, uint64_t end1) const + { + return ((start1 > start) && (start1 < end)) || ((start > start1) && (start < end1)); + } + inline bool IsAdjacent(uint64_t start, uint64_t end, uint64_t start1, uint64_t end1) const + { + return ((end == start1 || (end + 1) == start1)) || ((end1 == start) || (end1 + 1 == start)); + } + + inline bool operator==(const RangeSet &other) const + { + return rangeSet_ == other.rangeSet_; + } + + inline bool IsContain(uint32_t value) const + { + for (auto range : rangeSet_) { + if (value >= range.first && value <= range.second) { + return true; + } + } + return false; + } + + inline uint32_t HighestValue() const + { + if (!rangeSet_.empty()) { + return rangeSet_.back().second; + } + return 0; + } + + RangeSet(RangeSet const &) = default; + RangeSet &operator=(RangeSet const &) = default; + RangeSet(RangeSet &&) = default; + RangeSet &operator=(RangeSet &&) = default; + + void Insert(uint32_t start, uint32_t end); + void Insert(const RangeSet &s1); + void Invert(bool isUtf16); + void Compress(); + +private: + friend class RangeOpCode; + friend class Range32OpCode; + std::list> rangeSet_{}; +}; + +class RangeOpCode : public RegExpOpCode { +public: + RangeOpCode() : RegExpOpCode(OP_RANGE, RegExpOpCode::OP_SIZE_ONE) {} + ~RangeOpCode() override = default; + NO_COPY_SEMANTIC(RangeOpCode); + NO_MOVE_SEMANTIC(RangeOpCode); + uint32_t DumpOpCode(std::ostream &out, const DynChunk &buf, uint32_t offset) const override; + uint32_t InsertOpCode(DynChunk *buf, const RangeSet &rangeSet) const; +}; + +class MatchAheadOpCode : public RegExpOpCode { +public: + MatchAheadOpCode() : RegExpOpCode(OP_MATCH_AHEAD, RegExpOpCode::OP_SIZE_FIVE) {} + ~MatchAheadOpCode() override = default; + NO_COPY_SEMANTIC(MatchAheadOpCode); + NO_MOVE_SEMANTIC(MatchAheadOpCode); + uint32_t DumpOpCode(std::ostream &out, const DynChunk &buf, uint32_t offset) const override; + uint32_t InsertOpCode(DynChunk *buf, uint32_t offset, uint32_t para) const; +}; + +class NegativeMatchAheadOpCode : public RegExpOpCode { +public: + NegativeMatchAheadOpCode() : RegExpOpCode(OP_NEGATIVE_MATCH_AHEAD, RegExpOpCode::OP_SIZE_FIVE) {} + uint32_t InsertOpCode(DynChunk *buf, uint32_t offset, uint32_t para) const; + ~NegativeMatchAheadOpCode() override = default; + NO_COPY_SEMANTIC(NegativeMatchAheadOpCode); + NO_MOVE_SEMANTIC(NegativeMatchAheadOpCode); + uint32_t DumpOpCode(std::ostream &out, const DynChunk &buf, uint32_t offset) const override; +}; + +class PrevOpCode : public RegExpOpCode { +public: + PrevOpCode() : RegExpOpCode(OP_PREV, RegExpOpCode::OP_SIZE_ONE) {} + uint32_t EmitOpCode(DynChunk *buf, uint32_t para) const; + ~PrevOpCode() override = default; + NO_COPY_SEMANTIC(PrevOpCode); + NO_MOVE_SEMANTIC(PrevOpCode); + uint32_t DumpOpCode(std::ostream &out, const DynChunk &buf, uint32_t offset) const override; +}; + +class BackReferenceOpCode : public RegExpOpCode { +public: + BackReferenceOpCode() : RegExpOpCode(OP_BACKREFERENCE, RegExpOpCode::OP_SIZE_TWO) {} + uint32_t EmitOpCode(DynChunk *buf, uint32_t para) const; + ~BackReferenceOpCode() override = default; + NO_COPY_SEMANTIC(BackReferenceOpCode); + NO_MOVE_SEMANTIC(BackReferenceOpCode); + uint32_t DumpOpCode(std::ostream &out, const DynChunk &buf, uint32_t offset) const override; +}; + +class BackwardBackReferenceOpCode : public RegExpOpCode { +public: + BackwardBackReferenceOpCode() : RegExpOpCode(OP_BACKWARD_BACKREFERENCE, RegExpOpCode::OP_SIZE_TWO) {} + uint32_t EmitOpCode(DynChunk *buf, uint32_t para) const; + ~BackwardBackReferenceOpCode() override = default; + NO_COPY_SEMANTIC(BackwardBackReferenceOpCode); + NO_MOVE_SEMANTIC(BackwardBackReferenceOpCode); + uint32_t DumpOpCode(std::ostream &out, const DynChunk &buf, uint32_t offset) const override; +}; + +class Char32OpCode : public RegExpOpCode { +public: + Char32OpCode() : RegExpOpCode(OP_CHAR32, RegExpOpCode::OP_SIZE_FIVE) {} + uint32_t EmitOpCode(DynChunk *buf, uint32_t para) const; + ~Char32OpCode() override = default; + NO_COPY_SEMANTIC(Char32OpCode); + NO_MOVE_SEMANTIC(Char32OpCode); + uint32_t DumpOpCode(std::ostream &out, const DynChunk &buf, uint32_t offset) const override; +}; + +class Range32OpCode : public RegExpOpCode { +public: + Range32OpCode() : RegExpOpCode(OP_RANGE32, RegExpOpCode::OP_SIZE_ONE) {} + ~Range32OpCode() override = default; + NO_COPY_SEMANTIC(Range32OpCode); + NO_MOVE_SEMANTIC(Range32OpCode); + uint32_t DumpOpCode(std::ostream &out, const DynChunk &buf, uint32_t offset) const override; + uint32_t InsertOpCode(DynChunk *buf, const RangeSet &rangeSet) const; +}; +} // namespace ecmascript +} // namespace panda +#endif diff --git a/runtime/regexp/regexp_parser.cpp b/runtime/regexp/regexp_parser.cpp new file mode 100644 index 000000000..f09378b9f --- /dev/null +++ b/runtime/regexp/regexp_parser.cpp @@ -0,0 +1,1294 @@ +/* + * Copyright (c) 2021-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 "plugins/ecmascript/runtime/regexp/regexp_parser.h" + +#include "plugins/ecmascript/runtime/base/string_helper.h" +#include "plugins/ecmascript/runtime/ecma_macros.h" +#include "plugins/ecmascript/runtime/regexp/regexp_opcode.h" +#include "libpandabase/utils/utils.h" +#include "securec.h" +#include "unicode/uniset.h" + +#define _NO_DEBUG_ + +namespace panda::ecmascript { +static RangeSet g_rangeD(0x30, 0x39); // NOLINTNEXTLINE(fuchsia-statically-constructed-objects) +// NOLINTNEXTLINE(fuchsia-statically-constructed-objects) +static RangeSet g_rangeS({ + std::pair(0x0009, 0x000D), // NOLINTNEXTLINE(readability-magic-numbers) + std::pair(0x0020, 0x0020), // NOLINTNEXTLINE(readability-magic-numbers) + std::pair(0x00A0, 0x00A0), // NOLINTNEXTLINE(readability-magic-numbers) + std::pair(0x1680, 0x1680), // NOLINTNEXTLINE(readability-magic-numbers) + std::pair(0x2000, 0x200A), // NOLINTNEXTLINE(readability-magic-numbers) + /* 2028;LINE SEPARATOR;Zl;0;WS;;;;;N;;;;; */ + /* 2029;PARAGRAPH SEPARATOR;Zp;0;B;;;;;N;;;;; */ + std::pair(0x2028, 0x2029), // NOLINTNEXTLINE(readability-magic-numbers) + std::pair(0x202F, 0x202F), // NOLINTNEXTLINE(readability-magic-numbers) + std::pair(0x205F, 0x205F), // NOLINTNEXTLINE(readability-magic-numbers) + std::pair(0x3000, 0x3000), // NOLINTNEXTLINE(readability-magic-numbers) + /* FEFF;ZERO WIDTH NO-BREAK SPACE;Cf;0;BN;;;;;N;BYTE ORDER MARK;;;; */ + std::pair(0xFEFF, 0xFEFF), // NOLINTNEXTLINE(readability-magic-numbers) +}); + +// NOLINTNEXTLINE(fuchsia-statically-constructed-objects) +static RangeSet g_rangeW({ + std::pair(0x0030, 0x0039), // NOLINTNEXTLINE(readability-magic-numbers) + std::pair(0x0041, 0x005A), // NOLINTNEXTLINE(readability-magic-numbers) + std::pair(0x005F, 0x005F), // NOLINTNEXTLINE(readability-magic-numbers) + std::pair(0x0061, 0x007A), // NOLINTNEXTLINE(readability-magic-numbers) +}); + +// NOLINTNEXTLINE(fuchsia-statically-constructed-objects) +static RangeSet g_regexpIdentifyStart({ + std::pair(0x0024, 0x0024), // NOLINTNEXTLINE(readability-magic-numbers) + std::pair(0x0041, 0x005A), // NOLINTNEXTLINE(readability-magic-numbers) + std::pair(0x0061, 0x007A), // NOLINTNEXTLINE(readability-magic-numbers) +}); + +// NOLINTNEXTLINE(fuchsia-statically-constructed-objects) +static RangeSet g_regexpIdentifyContinue({ + std::pair(0x0024, 0x0024), // NOLINTNEXTLINE(readability-magic-numbers) + std::pair(0x0030, 0x0039), // NOLINTNEXTLINE(readability-magic-numbers) + std::pair(0x0041, 0x005A), // NOLINTNEXTLINE(readability-magic-numbers) + std::pair(0x0061, 0x007A), // NOLINTNEXTLINE(readability-magic-numbers) +}); + +void RegExpParser::Parse() +{ + // dynbuffer head init [size,capture_count,statck_count,flags] + buffer_.EmitU32(0); + buffer_.EmitU32(0); + buffer_.EmitU32(0); + buffer_.EmitU32(0); + // NOLINTNEXTLINE(cppcoreguidelines-pro-type-vararg) + PrintF("Parse Pattern------\n"); + // Pattern[U, N]:: + // Disjunction[?U, ?N] + // NOLINTNEXTLINE(cppcoreguidelines-pro-type-vararg) + Advance(); + SaveStartOpCode saveStartOp; + int captureIndex = captureCount_++; + saveStartOp.EmitOpCode(&buffer_, captureIndex); + ParseDisjunction(false); + if (c0_ != KEY_EOF) { + ParseError("extraneous characters at the end"); + return; + } + SaveEndOpCode saveEndOp; + saveEndOp.EmitOpCode(&buffer_, captureIndex); + MatchEndOpCode matchEndOp; + matchEndOp.EmitOpCode(&buffer_, 0); + // dynbuffer head assignments + buffer_.PutU32(0, buffer_.size_); + buffer_.PutU32(NUM_CAPTURE__OFFSET, captureCount_); + buffer_.PutU32(NUM_STACK_OFFSET, stackCount_); + buffer_.PutU32(FLAGS_OFFSET, flags_); +#ifndef _NO_DEBUG_ + RegExpOpCode::DumpRegExpOpCode(std::cout, buffer_); +#endif +} + +void RegExpParser::ParseDisjunction(bool isBackward) +{ + // NOLINTNEXTLINE(cppcoreguidelines-pro-type-vararg) + PrintF("Parse Disjunction------\n"); + size_t start = buffer_.size_; + ParseAlternative(isBackward); + if (isError_) { + return; + } + do { + if (c0_ == '|') { + SplitNextOpCode splitOp; + uint32_t len = buffer_.size_ - start; + GotoOpCode gotoOp; + splitOp.InsertOpCode(&buffer_, start, len + gotoOp.GetSize()); + uint32_t pos = gotoOp.EmitOpCode(&buffer_, 0) - gotoOp.GetSize(); + Advance(); + ParseAlternative(isBackward); + gotoOp.UpdateOpPara(&buffer_, pos, buffer_.size_ - pos - gotoOp.GetSize()); + } + } while (c0_ != KEY_EOF && c0_ != ')'); +} + +uint32_t RegExpParser::ParseOctalLiteral() +{ + // For compatibility with some other browsers (not all), we parse + // up to three octal digits with a value below 256. + // ES#prod-annexB-LegacyOctalEscapeSequence + uint32_t value = c0_ - '0'; + Advance(); + if (c0_ >= '0' && c0_ <= '7') { + value = value * OCTAL_VALUE + c0_ - '0'; + Advance(); + if (value < OCTAL_VALUE_RANGE && c0_ >= '0' && c0_ <= '7') { + value = value * OCTAL_VALUE + c0_ - '0'; + Advance(); + } + } + return value; +} + +bool RegExpParser::ParseUnlimitedLengthHexNumber(uint32_t maxValue, uint32_t *value) +{ + uint32_t x = 0; + int d = HexValue(c0_); + if (d < 0) { + return false; + } + while (d >= 0) { + if (UNLIKELY(x > (std::numeric_limits::max() - d) / HEX_VALUE)) { + LOG_ECMA(FATAL) << "value overflow"; + return false; + } + x = x * HEX_VALUE + d; + if (x > maxValue) { + return false; + } + Advance(); + d = HexValue(c0_); + } + *value = x; + return true; +} + +// This parses RegExpUnicodeEscapeSequence as described in ECMA262. +bool RegExpParser::ParseUnicodeEscape(uint32_t *value) +{ + // Accept both \uxxxx and \u{xxxxxx} (if allowed). + // In the latter case, the number of hex digits between { } is arbitrary. + // \ and u have already been read. + if (c0_ == '{' && IsUtf16()) { + uint8_t *start = pc_ - 1; // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-pointer-arithmetic) + Advance(); + if (ParseUnlimitedLengthHexNumber(0x10FFFF, value)) { // NOLINTNEXTLINE(readability-magic-numbers) + if (c0_ == '}') { + Advance(); + return true; + } + } + pc_ = start; + Advance(); + return false; + } + // \u but no {, or \u{...} escapes not allowed. + bool result = ParseHexEscape(UNICODE_HEX_VALUE, value); + if (result && IsUtf16() && U16_IS_LEAD(*value) && c0_ == '\\') { + // Attempt to read trail surrogate. + uint8_t *start = pc_ - 1; // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-pointer-arithmetic) + if (*pc_ == 'u') { + Advance(UNICODE_HEX_ADVANCE); + uint32_t trail; + if (ParseHexEscape(UNICODE_HEX_VALUE, &trail) && U16_IS_TRAIL(trail)) { + *value = U16_GET_SUPPLEMENTARY((*value), (trail)); // NOLINTNEXTLINE(hicpp-signed-bitwise) + return true; + } + } + pc_ = start; + Advance(); + } + return result; +} + +bool RegExpParser::ParseHexEscape(int length, uint32_t *value) +{ + uint8_t *start = pc_ - 1; // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-pointer-arithmetic) + uint32_t val = 0; + for (int i = 0; i < length; ++i) { + uint32_t c = c0_; + int d = HexValue(c); + if (d < 0) { + pc_ = start; + Advance(); + return false; + } + val = val * HEX_VALUE + d; + Advance(); + } + *value = val; + return true; +} + +// NOLINTNEXTLINE(readability-function-size) +void RegExpParser::ParseAlternative(bool isBackward) +{ + size_t start = buffer_.size_; + while (c0_ != '|' && c0_ != KEY_EOF && c0_ != ')') { + if (isError_) { + return; + } + size_t atomBcStart = buffer_.GetSize(); + int captureIndex = 0; + bool isAtom = false; + switch (c0_) { + case '^': { + // NOLINTNEXTLINE(cppcoreguidelines-pro-type-vararg) + PrintF("Assertion %c line start \n", c0_); + LineStartOpCode lineStartOp; + lineStartOp.EmitOpCode(&buffer_, 0); + Advance(); + } break; + case '$': { + // NOLINTNEXTLINE(cppcoreguidelines-pro-type-vararg) + PrintF("Assertion %c line end \n", c0_); + LineEndOpCode lineEndOp; + lineEndOp.EmitOpCode(&buffer_, 0); + Advance(); + } break; + case '\\': + // NOLINTNEXTLINE(cppcoreguidelines-pro-type-vararg) + PrintF("Escape %c \n", c0_); + Advance(); + switch (c0_) { + case 'b': { + // NOLINTNEXTLINE(cppcoreguidelines-pro-type-vararg) + PrintF("Assertion %c \n", c0_); + WordBoundaryOpCode wordBoundaryOp; + wordBoundaryOp.EmitOpCode(&buffer_, 0); + Advance(); + } break; + case 'B': { + // NOLINTNEXTLINE(cppcoreguidelines-pro-type-vararg) + PrintF("Assertion %c \n", c0_); + NotWordBoundaryOpCode notWordBoundaryOp; + notWordBoundaryOp.EmitOpCode(&buffer_, 0); + Advance(); + } break; + default: + isAtom = true; + int atomValue = ParseAtomEscape(isBackward); + if (atomValue != -1) { + if (IsIgnoreCase()) { + if (!IsUtf16()) { + atomValue = Canonicalize(atomValue, false); + } else { + icu::UnicodeSet set(atomValue, atomValue); + set.closeOver(USET_CASE_INSENSITIVE); + set.removeAllStrings(); + int32_t size = set.size(); + RangeOpCode rangeOp; + RangeSet rangeResult; + for (int32_t idx = 0; idx < size; idx++) { + int32_t uc = set.charAt(idx); + RangeSet curRange(uc); + rangeResult.Insert(curRange); + } + rangeOp.InsertOpCode(&buffer_, rangeResult); + break; + } + } + if (atomValue <= UINT16_MAX) { + CharOpCode charOp; + charOp.EmitOpCode(&buffer_, atomValue); + } else { + Char32OpCode charOp; + charOp.EmitOpCode(&buffer_, atomValue); + } + } + break; + } + break; + case '(': { + Advance(); + isAtom = ParseAssertionCapture(&captureIndex, isBackward); + // NOLINTNEXTLINE(cppcoreguidelines-pro-type-vararg) + Advance(); + } break; + case '.': { + PrevOpCode prevOp; + if (isBackward) { + prevOp.EmitOpCode(&buffer_, 0); + } + if (IsDotAll()) { + AllOpCode allOp; + allOp.EmitOpCode(&buffer_, 0); + } else { + DotsOpCode dotsOp; + dotsOp.EmitOpCode(&buffer_, 0); + } + if (isBackward) { + prevOp.EmitOpCode(&buffer_, 0); + } + // NOLINTNEXTLINE(cppcoreguidelines-pro-type-vararg) + PrintF("Atom %c match any \n", c0_); + isAtom = true; + Advance(); + } break; + case '[': { + // NOLINTNEXTLINE(cppcoreguidelines-pro-type-vararg) + PrintF("Atom %c match range \n", c0_); + isAtom = true; + PrevOpCode prevOp; + Advance(); + if (isBackward) { + prevOp.EmitOpCode(&buffer_, 0); + } + bool isInvert = false; + if (c0_ == '^') { + isInvert = true; + Advance(); + } + RangeSet rangeResult; + if (!ParseClassRanges(&rangeResult)) { + break; + } + if (isInvert) { + rangeResult.Invert(IsUtf16()); + } + uint32_t highValue = rangeResult.HighestValue(); + if (highValue <= UINT16_MAX) { + RangeOpCode rangeOp; + rangeOp.InsertOpCode(&buffer_, rangeResult); + } else { + Range32OpCode rangeOp; + rangeOp.InsertOpCode(&buffer_, rangeResult); + } + + if (isBackward) { + prevOp.EmitOpCode(&buffer_, 0); + } + } break; + case '*': + case '+': + case '?': + // NOLINTNEXTLINE(cppcoreguidelines-pro-type-vararg) + ParseError("nothing to repeat"); + return; + case '{': { + uint8_t *begin = pc_ - 1; // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-pointer-arithmetic) + int dummy; + if (ParserIntervalQuantifier(&dummy, &dummy)) { + ParseError("nothing to repeat"); + return; + } + pc_ = begin; + Advance(); + } + [[fallthrough]]; + case '}': + case ']': + if (IsUtf16()) { + ParseError("syntax error"); + return; + } + [[fallthrough]]; + default: + // PatternCharacter + // NOLINTNEXTLINE(cppcoreguidelines-pro-type-vararg) + PrintF("PatternCharacter %c\n", c0_); + isAtom = true; + { + PrevOpCode prevOp; + if (isBackward) { + prevOp.EmitOpCode(&buffer_, 0); + } + uint32_t matchedChar = c0_; + if (c0_ > (INT8_MAX + 1)) { + Prev(); + int i = 0; + UChar32 c; + int32_t length = end_ - pc_ + 1; + // NOLINTNEXTLINE(hicpp-signed-bitwise) + U8_NEXT(pc_, i, length, c); // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-pointer-arithmetic) + matchedChar = c; + pc_ += i; // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-pointer-arithmetic) + } + if (IsIgnoreCase()) { + matchedChar = Canonicalize(matchedChar, IsUtf16()); + } + if (matchedChar > UINT16_MAX) { + Char32OpCode charOp; + charOp.EmitOpCode(&buffer_, matchedChar); + } else { + CharOpCode charOp; + charOp.EmitOpCode(&buffer_, matchedChar); + } + if (isBackward) { + prevOp.EmitOpCode(&buffer_, 0); + } + } + Advance(); + break; + } + if (isAtom && !isError_) { + ParseQuantifier(atomBcStart, captureIndex, captureCount_ - 1); + } + if (isBackward) { + size_t end = buffer_.GetSize(); + size_t termSize = end - atomBcStart; + size_t moveSize = end - start; + buffer_.Expand(end + termSize); + if (memmove_s(buffer_.buf_ + start + // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-pointer-arithmetic) + termSize, // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-pointer-arithmetic) + moveSize, + buffer_.buf_ + start, // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-pointer-arithmetic) + moveSize) != EOK) { + LOG_ECMA(FATAL) << "memmove_s failed"; + UNREACHABLE(); + } + // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-pointer-arithmetic) + if (memcpy_s(buffer_.buf_ + start, termSize, buffer_.buf_ + end, termSize) != EOK) { + LOG_ECMA(FATAL) << "memcpy_s failed"; + UNREACHABLE(); + } + } + } +} + +int RegExpParser::FindGroupName(const CString &name) +{ + size_t len = 0; + size_t nameLen = name.size(); + const char *p = reinterpret_cast(groupNames_.buf_); + // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-pointer-arithmetic) + const char *bufEnd = reinterpret_cast(groupNames_.buf_) + groupNames_.size_; + int captureIndex = 1; + while (p < bufEnd) { + len = strlen(p); + if (len == nameLen && memcmp(name.c_str(), p, nameLen) == 0) { + return captureIndex; + } + p += len + 1; // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-pointer-arithmetic) + captureIndex++; + } + return -1; +} + +bool RegExpParser::ParseAssertionCapture(int *captureIndex, bool isBackward) +{ + bool isAtom = false; + do { + if (c0_ == '?') { + Advance(); + switch (c0_) { + // (?=Disjunction[?U, ?N]) + case '=': { + // NOLINTNEXTLINE(cppcoreguidelines-pro-type-vararg) + PrintF("Assertion(?= Disjunction)\n"); + Advance(); + uint32_t start = buffer_.size_; + ParseDisjunction(isBackward); + MatchOpCode matchOp; + matchOp.EmitOpCode(&buffer_, 0); + MatchAheadOpCode matchAheadOp; + uint32_t len = buffer_.size_ - start; + matchAheadOp.InsertOpCode(&buffer_, start, len); + } break; + // (?!Disjunction[?U, ?N]) + case '!': { + // NOLINTNEXTLINE(cppcoreguidelines-pro-type-vararg) + PrintF("Assertion(?! Disjunction)\n"); + uint32_t start = buffer_.size_; + Advance(); + ParseDisjunction(isBackward); + MatchOpCode matchOp; + matchOp.EmitOpCode(&buffer_, 0); + NegativeMatchAheadOpCode matchAheadOp; + uint32_t len = buffer_.size_ - start; + matchAheadOp.InsertOpCode(&buffer_, start, len); + } break; + case '<': + Advance(); + // (?<=Disjunction[?U, ?N]) + if (c0_ == '=') { + // NOLINTNEXTLINE(cppcoreguidelines-pro-type-vararg) + PrintF("Assertion(?<= Disjunction)\n"); + Advance(); + uint32_t start = buffer_.size_; + ParseDisjunction(true); + MatchOpCode matchOp; + matchOp.EmitOpCode(&buffer_, 0); + MatchAheadOpCode matchAheadOp; + uint32_t len = buffer_.size_ - start; + matchAheadOp.InsertOpCode(&buffer_, start, len); + // (?(&pc_); + if (!ParseGroupSpecifier(pp, name)) { + ParseError("GroupName Syntax error."); + return false; + } + if (FindGroupName(name) > 0) { + ParseError("Duplicate GroupName error."); + return false; + } + groupNames_.EmitStr(name.c_str()); + // NOLINTNEXTLINE(cppcoreguidelines-pro-type-vararg) + PrintF("group name %s", name.c_str()); + Advance(); + goto parseCapture; // NOLINTNEXTLINE(cppcoreguidelines-avoid-goto) + } + break; + // (?:Disjunction[?U, ?N]) + case ':': + // NOLINTNEXTLINE(cppcoreguidelines-pro-type-vararg) + PrintF("Atom(?<: Disjunction)\n"); + isAtom = true; + Advance(); + ParseDisjunction(isBackward); + break; + default: + Advance(); + ParseError("? Syntax error."); + return false; + } + } else { + groupNames_.EmitChar(0); + parseCapture: + isAtom = true; + *captureIndex = captureCount_++; + SaveEndOpCode saveEndOp; + SaveStartOpCode saveStartOp; + if (isBackward) { + saveEndOp.EmitOpCode(&buffer_, *captureIndex); + } else { + saveStartOp.EmitOpCode(&buffer_, *captureIndex); + } + // NOLINTNEXTLINE(cppcoreguidelines-pro-type-vararg) + PrintF("capture start %d \n", *captureIndex); + ParseDisjunction(isBackward); + // NOLINTNEXTLINE(cppcoreguidelines-pro-type-vararg) + PrintF("capture end %d \n", *captureIndex); + if (isBackward) { + saveStartOp.EmitOpCode(&buffer_, *captureIndex); + } else { + saveEndOp.EmitOpCode(&buffer_, *captureIndex); + } + } + } while (c0_ != ')' && c0_ != KEY_EOF); + if (c0_ != ')') { + ParseError("capture syntax error"); + return false; + } + return isAtom; +} + +int RegExpParser::ParseDecimalDigits() +{ + // NOLINTNEXTLINE(cppcoreguidelines-pro-type-vararg) + PrintF("Parse DecimalDigits------\n"); + int result = 0; + bool overflow = false; + while (true) { + if (c0_ < '0' || c0_ > '9') { + break; + } + if (!overflow) { + if (UNLIKELY(result > (INT32_MAX - c0_ + '0') / DECIMAL_DIGITS_ADVANCE)) { + overflow = true; + } else { + result = result * DECIMAL_DIGITS_ADVANCE + c0_ - '0'; + } + } + Advance(); + } + if (overflow) { + return INT32_MAX; + } + return result; +} + +bool RegExpParser::ParserIntervalQuantifier(int *pmin, int *pmax) +{ + // Quantifier:: + // QuantifierPrefix + // QuantifierPrefix? + // QuantifierPrefix:: + // * + // + + // ? + // {DecimalDigits} + // {DecimalDigits,} + // {DecimalDigits,DecimalDigits} + Advance(); + *pmin = ParseDecimalDigits(); + *pmax = *pmin; + switch (c0_) { + case ',': + Advance(); + if (c0_ == '}') { + // NOLINTNEXTLINE(cppcoreguidelines-pro-type-vararg) + PrintF("QuantifierPrefix{DecimalDigits,}\n"); + *pmax = INT32_MAX; + Advance(); + } else { + *pmax = ParseDecimalDigits(); + if (c0_ == '}') { + // NOLINTNEXTLINE(cppcoreguidelines-pro-type-vararg) + PrintF("QuantifierPrefix{DecimalDigits,DecimalDigits}\n"); + Advance(); + } else { + return false; + } + } + break; + case '}': + // NOLINTNEXTLINE(cppcoreguidelines-pro-type-vararg) + PrintF("QuantifierPrefix{DecimalDigits}\n"); + Advance(); + break; + default: + Advance(); + return false; + } + return true; +} + +void RegExpParser::ParseQuantifier(size_t atomBcStart, int captureStart, int captureEnd) +{ + int min = -1; + int max = -1; + bool isGreedy = true; + switch (c0_) { + case '*': + // NOLINTNEXTLINE(cppcoreguidelines-pro-type-vararg) + PrintF("QuantifierPrefix %c\n", c0_); + min = 0; + max = INT32_MAX; + Advance(); + break; + case '+': + // NOLINTNEXTLINE(cppcoreguidelines-pro-type-vararg) + PrintF("QuantifierPrefix %c\n", c0_); + min = 1; + max = INT32_MAX; + Advance(); + break; + case '?': + // NOLINTNEXTLINE(cppcoreguidelines-pro-type-vararg) + PrintF("QuantifierPrefix %c\n", c0_); + Advance(); + min = 0; + max = 1; + break; + case '{': { + uint8_t *start = pc_ - 1; // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-pointer-arithmetic) + if (!ParserIntervalQuantifier(&min, &max)) { + pc_ = start; + Advance(); // back to '{' + return; + } + if (min > max) { + ParseError("Invalid repetition count"); + return; + } + } break; + default: + break; + } + if (c0_ == '?') { + isGreedy = false; + // NOLINTNEXTLINE(cppcoreguidelines-pro-type-vararg) + PrintF("Quantifier::QuantifierPrefix?\n"); + Advance(); + } else if (c0_ == '?' || c0_ == '+' || c0_ == '*' || c0_ == '{') { + ParseError("nothing to repeat"); + return; + } + if (min != -1 && max != -1) { + stackCount_++; + PushOpCode pushOp; + pushOp.InsertOpCode(&buffer_, atomBcStart); + atomBcStart += pushOp.GetSize(); + + if (captureStart != 0) { + SaveResetOpCode saveResetOp; + saveResetOp.InsertOpCode(&buffer_, atomBcStart, captureStart, captureEnd); + } + + // zero advance check + if (max == INT32_MAX) { + stackCount_++; + PushCharOpCode pushCharOp; + pushCharOp.InsertOpCode(&buffer_, atomBcStart); + CheckCharOpCode checkCharOp; + // NOLINTNEXTLINE(readability-magic-numbers) + checkCharOp.EmitOpCode(&buffer_, RegExpOpCode::GetRegExpOpCode(RegExpOpCode::OP_LOOP)->GetSize()); + } + + if (isGreedy) { + LoopGreedyOpCode loopOp; + loopOp.EmitOpCode(&buffer_, atomBcStart - buffer_.GetSize() - loopOp.GetSize(), min, max); + } else { + LoopOpCode loopOp; + loopOp.EmitOpCode(&buffer_, atomBcStart - buffer_.GetSize() - loopOp.GetSize(), min, max); + } + + if (min == 0) { + if (isGreedy) { + SplitNextOpCode splitNextOp; + splitNextOp.InsertOpCode(&buffer_, atomBcStart, buffer_.GetSize() - atomBcStart); + } else { + SplitFirstOpCode splitFirstOp; + splitFirstOp.InsertOpCode(&buffer_, atomBcStart, buffer_.GetSize() - atomBcStart); + } + } + + PopOpCode popOp; + popOp.EmitOpCode(&buffer_); + } +} + +bool RegExpParser::ParseGroupSpecifier(const uint8_t **pp, CString &name) +{ + const uint8_t *p = *pp; + int c = *p; + while (c != '>') { + if (c < (INT8_MAX + 1)) { + if (name.empty()) { + if (!g_regexpIdentifyStart.IsContain(c)) { + return false; + } + } else { + if (!g_regexpIdentifyContinue.IsContain(c)) { + return false; + } + } + name += static_cast(c); + } + c = *++p; // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-pointer-arithmetic) + } + p++; // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-pointer-arithmetic) + *pp = p; + return true; +} + +int RegExpParser::ParseCaptureCount(const char *groupName) +{ + const uint8_t *p; + int captureIndex = 1; + CString name; + for (p = base_; p < end_; p++) { // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-pointer-arithmetic) + switch (*p) { + case '(': { + if (p[1] == '?') { // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-pointer-arithmetic) + // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-pointer-arithmetic) + if (p[CAPTURE_CONUT_ADVANCE - 1] == '<' && p[CAPTURE_CONUT_ADVANCE] != '!' && + // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-pointer-arithmetic) + p[CAPTURE_CONUT_ADVANCE] != '=') { + // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-pointer-arithmetic) + p += CAPTURE_CONUT_ADVANCE; + if (groupName != nullptr) { + if (ParseGroupSpecifier(&p, name)) { + if (strcmp(name.c_str(), groupName) == 0) { + return captureIndex; + } + } + } + captureIndex++; + } + } else { + captureIndex++; + } + } break; + case '\\': + p++; // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-pointer-arithmetic) + break; + case '[': + while (p < end_ && *p != ']') { + if (*p == '\\') { + p++; // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-pointer-arithmetic) + } + p++; // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-pointer-arithmetic) + } + break; + default: + break; + } + } + return captureIndex; +} + +// NOLINTNEXTLINE(readability-function-size) +int RegExpParser::ParseAtomEscape(bool isBackward) +{ + // AtomEscape[U, N]:: + // DecimalEscape + // CharacterClassEscape[?U] + // CharacterEscape[?U] + // [+N]kGroupName[?U] + int result = -1; + // NOLINTNEXTLINE(cppcoreguidelines-pro-type-vararg) + PrintF("Parse AtomEscape------\n"); + switch (c0_) { + case KEY_EOF: + // NOLINTNEXTLINE(cppcoreguidelines-pro-type-vararg) + ParseError("unexpected end"); + break; + // DecimalEscape + case '1': + case '2': + case '3': + case '4': + case '5': + case '6': + case '7': + case '8': + case '9': { + // NOLINTNEXTLINE(cppcoreguidelines-pro-type-vararg) + PrintF("NonZeroDigit %c\n", c0_); + int capture = ParseDecimalDigits(); + if (capture > captureCount_ - 1 && capture > ParseCaptureCount(nullptr) - 1) { + ParseError("invalid backreference count"); + break; + } + if (isBackward) { + BackwardBackReferenceOpCode backReferenceOp; + backReferenceOp.EmitOpCode(&buffer_, capture); + } else { + BackReferenceOpCode backReferenceOp; + backReferenceOp.EmitOpCode(&buffer_, capture); + } + } break; + // CharacterClassEscape + case 'd': { + // [0-9] + RangeOpCode rangeOp; + rangeOp.InsertOpCode(&buffer_, g_rangeD); + Advance(); + } break; + case 'D': { + // [^0-9] + RangeSet atomRange(g_rangeD); + atomRange.Invert(IsUtf16()); + Range32OpCode rangeOp; + rangeOp.InsertOpCode(&buffer_, atomRange); + Advance(); + } break; + case 's': { + // [\f\n\r\t\v] + RangeOpCode rangeOp; + rangeOp.InsertOpCode(&buffer_, g_rangeS); + Advance(); + } break; + case 'S': { + RangeSet atomRange(g_rangeS); + atomRange.Invert(IsUtf16()); + Range32OpCode rangeOp; + rangeOp.InsertOpCode(&buffer_, atomRange); + Advance(); + } break; + case 'w': { + // [A-Za-z0-9] + RangeOpCode rangeOp; + rangeOp.InsertOpCode(&buffer_, g_rangeW); + Advance(); + } break; + case 'W': { + // [^A-Za-z0-9] + RangeSet atomRange(g_rangeW); + atomRange.Invert(IsUtf16()); + Range32OpCode rangeOp; + rangeOp.InsertOpCode(&buffer_, atomRange); + Advance(); + } break; + // P{UnicodePropertyValueExpression} + // p{UnicodePropertyValueExpression} + case 'P': + case 'p': + // [+N]kGroupName[?U] + case 'k': + default: + result = ParseCharacterEscape(); + break; + } + return result; +} + +int RegExpParser::ParseCharacterEscape() +{ + // CharacterEscape[U]:: + // ControlEscape + // c ControlLetter + // 0 [lookahead ∉ DecimalDigit] + // HexEscapeSequence + // RegExpUnicodeEscapeSequence[?U] + // IdentityEscape[?U] + uint32_t result = 0; + // NOLINTNEXTLINE(cppcoreguidelines-pro-type-vararg) + switch (c0_) { + // ControlEscape + case 'f': + result = '\f'; + // NOLINTNEXTLINE(cppcoreguidelines-pro-type-vararg) + PrintF("ControlEscape %c\n", c0_); + Advance(); + break; + case 'n': + result = '\n'; + // NOLINTNEXTLINE(cppcoreguidelines-pro-type-vararg) + PrintF("ControlEscape %c\n", c0_); + Advance(); + break; + case 'r': + result = '\r'; + // NOLINTNEXTLINE(cppcoreguidelines-pro-type-vararg) + PrintF("ControlEscape %c\n", c0_); + Advance(); + break; + case 't': + result = '\t'; + // NOLINTNEXTLINE(cppcoreguidelines-pro-type-vararg) + PrintF("ControlEscape %c\n", c0_); + Advance(); + break; + case 'v': + result = '\v'; + // NOLINTNEXTLINE(cppcoreguidelines-pro-type-vararg) + PrintF("ControlEscape %c\n", c0_); + Advance(); + break; + // c ControlLetter + case 'c': + Advance(); + if ((c0_ >= 'A' && c0_ <= 'Z') || (c0_ >= 'a' && c0_ <= 'z')) { + // NOLINTNEXTLINE(cppcoreguidelines-pro-type-vararg) + PrintF("ControlLetter %c\n", c0_); + result = static_cast(c0_) & 0x1f; // NOLINTNEXTLINE(readability-magic-numbers) + Advance(); + } else { + if (!IsUtf16()) { + pc_--; // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-pointer-arithmetic) + result = '\\'; + } else { + ParseError("Invalid control letter"); + return -1; + } + } + break; + case '0': + // NOLINTNEXTLINE(cppcoreguidelines-pro-type-vararg) + PrintF("CharacterEscape 0 [lookahead ∉ DecimalDigit]\n"); + if (IsUtf16() && !(*pc_ >= '0' && *pc_ <= '9')) { // NOLINTNEXTLINE(readability-magic-numbers) + Advance(); + result = 0; + break; + } + [[fallthrough]]; + case '1': + case '2': + case '3': + case '4': + case '5': + case '6': + case '7': + if (IsUtf16()) { + // With /u, decimal escape is not interpreted as octal character code. + ParseError("Invalid class escape"); + return 0; + } + result = ParseOctalLiteral(); + break; + // ParseHexEscapeSequence + // ParseRegExpUnicodeEscapeSequence + case 'x': { + Advance(); + if (ParseHexEscape(UNICODE_HEX_ADVANCE, &result)) { + return result; + } + if (IsUtf16()) { + ParseError("Invalid class escape"); + return -1; + } + result = 'x'; + break; + } + case 'u': { + Advance(); + if (ParseUnicodeEscape(&result)) { + return result; + } + if (IsUtf16()) { + // With /u, invalid escapes are not treated as identity escapes. + ParseError("Invalid unicode escape"); + return 0; + } + // If \u is not followed by a two-digit hexadecimal, treat it + // as an identity escape. + result = 'u'; + } break; + // IdentityEscape[?U] + case '$': + case '(': + case ')': + case '*': + case '+': + case '.': + case '/': + case '?': + case '[': + case '\\': + case ']': + case '^': + case '{': + case '|': + case '}': + // NOLINTNEXTLINE(cppcoreguidelines-pro-type-vararg) + PrintF("IdentityEscape %c\n", c0_); + result = c0_; + Advance(); + break; + default: + if (IsUtf16()) { + ParseError("Invalid unicode escape"); + return 0; + } + // NOLINTNEXTLINE(cppcoreguidelines-pro-type-vararg) + PrintF("SourceCharacter %c\n", c0_); + result = c0_; + Advance(); + break; + } + return result; +} + +bool RegExpParser::ParseClassRanges(RangeSet *result) +{ + // NOLINTNEXTLINE(cppcoreguidelines-pro-type-vararg) + PrintF("Parse ClassRanges------\n"); + while (c0_ != ']') { + RangeSet s1; + uint32_t c1 = ParseClassAtom(&s1); + if (c1 == UINT32_MAX) { + ParseError("invalid class range"); + return false; + } + + int next_c0 = *pc_; + if (c0_ == '-' && next_c0 != ']') { + if (c1 == CLASS_RANGE_BASE) { + if (IsUtf16()) { + ParseError("invalid class range"); + return false; + } + result->Insert(s1); + continue; + } + Advance(); + RangeSet s2; + uint32_t c2 = ParseClassAtom(&s2); + if (c2 == UINT32_MAX) { + ParseError("invalid class range"); + return false; + } + if (c2 == CLASS_RANGE_BASE) { + if (IsUtf16()) { + ParseError("invalid class range"); + return false; + } + result->Insert(s2); + continue; + } + + if (c1 > c2) { + ParseError("invalid class range"); + return false; + } + if (IsIgnoreCase()) { + c1 = Canonicalize(c1, IsUtf16()); + c2 = Canonicalize(c2, IsUtf16()); + } + + result->Insert(c1, c2); + } else { + result->Insert(s1); + } + } + Advance(); + return true; +} + +uint32_t RegExpParser::ParseClassAtom(RangeSet *atom) +{ + uint32_t ret = UINT32_MAX; + switch (c0_) { + case '\\': { + Advance(); + ret = ParseClassEscape(atom); + } break; + case KEY_EOF: + break; + case 0: + if (pc_ >= end_) { + return UINT32_MAX; + } + [[fallthrough]]; + default: + uint32_t value = c0_; + int u16_size = 0; + if (c0_ > INT8_MAX) { // NOLINTNEXTLINE(readability-magic-numbers) + pc_ -= 1; // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-pointer-arithmetic) + auto u16_result = base::utf_helper::ConvertUtf8ToUtf16Pair(pc_, true); + value = u16_result.first; + u16_size = u16_result.second; + Advance(u16_size + 1); + } else { + Advance(); + } + if (IsIgnoreCase()) { + value = Canonicalize(value, IsUtf16()); + } + atom->Insert(RangeSet(value)); + ret = value; + break; + } + return ret; +} + +int RegExpParser::ParseClassEscape(RangeSet *atom) +{ + // NOLINTNEXTLINE(cppcoreguidelines-pro-type-vararg) + PrintF("Parse ClassEscape------\n"); + int result = -1; + switch (c0_) { + case 'b': + Advance(); + // NOLINTNEXTLINE(cppcoreguidelines-pro-type-vararg) + PrintF("ClassEscape %c", 'b'); + result = '\b'; + atom->Insert(RangeSet(static_cast('\b'))); + break; + case '-': + Advance(); + result = '-'; + // NOLINTNEXTLINE(cppcoreguidelines-pro-type-vararg) + PrintF("ClassEscape %c", '-'); + atom->Insert(RangeSet(static_cast('-'))); + break; + // CharacterClassEscape + case 'd': + case 'D': + result = CLASS_RANGE_BASE; + atom->Insert(g_rangeD); + if (c0_ == 'D') { + atom->Invert(IsUtf16()); + } + Advance(); + break; + case 's': + case 'S': + result = CLASS_RANGE_BASE; + atom->Insert(g_rangeS); + if (c0_ == 'S') { + atom->Invert(IsUtf16()); + } + Advance(); + break; + case 'w': + case 'W': + // NOLINTNEXTLINE(cppcoreguidelines-pro-type-vararg) + PrintF("ClassEscape::CharacterClassEscape %c\n", c0_); + result = CLASS_RANGE_BASE; + atom->Insert(g_rangeW); + if (c0_ == 'W') { + atom->Invert(IsUtf16()); + } + Advance(); + break; + // P{UnicodePropertyValueExpression} + // p{UnicodePropertyValueExpression} + case 'P': + case 'p': + Advance(); + if (c0_ == '{') { + Advance(); + bool isValue = false; + ParseUnicodePropertyValueCharacters(&isValue); + if (!isValue) { + ParseUnicodePropertyValueCharacters(&isValue); + } + } + break; + default: + result = ParseCharacterEscape(); + int value = result; + if (IsIgnoreCase()) { + value = Canonicalize(value, IsUtf16()); + } + atom->Insert(RangeSet(static_cast(value))); + break; + } + return result; +} + +void RegExpParser::ParseUnicodePropertyValueCharacters(bool *isValue) +{ + if ((c0_ >= 'A' && c0_ <= 'Z') || (c0_ >= 'a' && c0_ <= 'z')) { + // NOLINTNEXTLINE(cppcoreguidelines-pro-type-vararg) + PrintF("UnicodePropertyCharacter::ControlLetter %c\n", c0_); + Advance(); + } else if (c0_ == '-') { + // NOLINTNEXTLINE(cppcoreguidelines-pro-type-vararg) + PrintF("UnicodePropertyCharacter:: - \n"); + Advance(); + } else if (c0_ >= '0' && c0_ <= '9') { + *isValue = true; + // NOLINTNEXTLINE(cppcoreguidelines-pro-type-vararg) + PrintF("UnicodePropertyValueCharacter::DecimalDigit %c\n", c0_); + Advance(); + } else if (*isValue && c0_ == '}') { + Advance(); + return; + } else if (!*isValue && c0_ == '=') { + Advance(); + return; + } + ParseUnicodePropertyValueCharacters(isValue); +} + +// NOLINTNEXTLINE(cert-dcl50-cpp) +void RegExpParser::PrintF(const char *fmt, ...) +{ +#ifndef _NO_DEBUG_ + va_list args; + // NOLINTNEXTLINE(cppcoreguidelines-pro-type-vararg,) + va_start(args, fmt); + vprintf(fmt, args); + va_end(args); +#else + (void)fmt; +#endif +} + +void RegExpParser::ParseError(const char *errorMessage) +{ + // NOLINTNEXTLINE(cppcoreguidelines-pro-type-vararg) + PrintF("error: "); + // NOLINTNEXTLINE(cppcoreguidelines-pro-type-vararg) + PrintF(errorMessage); + // NOLINTNEXTLINE(cppcoreguidelines-pro-type-vararg) + PrintF("\n"); + SetIsError(); + size_t length = strlen(errorMessage) + 1; + if (memcpy_s(errorMsg_, length, errorMessage, length) != EOK) { + LOG_ECMA(FATAL) << "memcpy_s failed"; + UNREACHABLE(); + } +} +} // namespace panda::ecmascript diff --git a/runtime/regexp/regexp_parser.h b/runtime/regexp/regexp_parser.h new file mode 100644 index 000000000..e83172f6c --- /dev/null +++ b/runtime/regexp/regexp_parser.h @@ -0,0 +1,233 @@ +/* + * Copyright (c) 2021-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_REGEXP_PARSER_H +#define ECMASCRIPT_REGEXP_PARSER_H + +#include +#include +#include +#include "plugins/ecmascript/runtime/mem/chunk.h" +#include "plugins/ecmascript/runtime/regexp/dyn_chunk.h" +#include "plugins/ecmascript/runtime/regexp/regexp_opcode.h" +#include "unicode/stringpiece.h" +#include "unicode/uchar.h" +#include "unicode/utf16.h" +#include "unicode/utf8.h" +#include "unicode/utypes.h" + +namespace panda::ecmascript { +class RegExpParser { +public: + static constexpr auto FLAG_GLOBAL = (1U << 0U); + static constexpr auto FLAG_IGNORECASE = (1U << 1U); + static constexpr auto FLAG_MULTILINE = (1U << 2U); + static constexpr auto FLAG_DOTALL = (1U << 3U); + static constexpr auto FLAG_UTF16 = (1U << 4U); + static constexpr auto FLAG_STICKY = (1U << 5U); + static const int KEY_EOF = -1; + static constexpr int CLASS_RANGE_BASE = 0x40000000; + static constexpr uint32_t NUM_CAPTURE__OFFSET = 4; + static constexpr uint32_t NUM_STACK_OFFSET = 8; + static constexpr uint32_t OCTAL_VALUE = 8; + static constexpr uint32_t OCTAL_VALUE_RANGE = 32; + static constexpr uint32_t HEX_VALUE = 16; + static constexpr int32_t DECIMAL_DIGITS_ADVANCE = 10; + static constexpr uint32_t FLAGS_OFFSET = 12; + static constexpr uint32_t OP_START_OFFSET = 16; + static constexpr uint32_t UNICODE_HEX_VALUE = 4; + static constexpr uint32_t UNICODE_HEX_ADVANCE = 2; + static constexpr uint32_t CAPTURE_CONUT_ADVANCE = 3; + + explicit RegExpParser(Chunk *chunk) + : base_(nullptr), + pc_(nullptr), + end_(nullptr), + flags_(0), + c0_(KEY_EOF), + captureCount_(0), + stackCount_(0), + isError_(false), + buffer_(chunk), + groupNames_(chunk) + { + } + + ~RegExpParser() + { + Clear(); + } + + NO_COPY_SEMANTIC(RegExpParser); + NO_MOVE_SEMANTIC(RegExpParser); + + inline void Init(char *source, size_t length, uint32_t flags) + { + pc_ = reinterpret_cast(source); + base_ = pc_; + // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-pointer-arithmetic) + end_ = reinterpret_cast(source) + length - 1; + flags_ = flags; + } + + void Parse(); + void ParseDisjunction(bool isBackward); + void ParseAlternative(bool isBackward); + bool ParseAssertionCapture(int *captureIndex, bool isBackward); + void ParseQuantifier(size_t atomBcStart, int captureStart, int captureEnd); + int ParseDecimalDigits(); + int ParseAtomEscape(bool isBackward); + int ParseCharacterEscape(); + bool ParseGroupSpecifier(const uint8_t **pp, CString &name); + int ParseCaptureCount(const char *groupName); + bool ParseClassRanges(RangeSet *result); + void ParseNonemptyClassRangesNoDash(DynChunk *buffer); + uint32_t ParseClassAtom(RangeSet *atom); + int ParseClassEscape(RangeSet *atom); + void ParseError(const char *errorMessage); + void ParseUnicodePropertyValueCharacters(bool *isValue); + int FindGroupName(const CString &name); + uint32_t ParseOctalLiteral(); + bool ParseHexEscape(int length, uint32_t *value); + bool ParseUnlimitedLengthHexNumber(uint32_t maxValue, uint32_t *value); + bool ParseUnicodeEscape(uint32_t *value); + bool ParserIntervalQuantifier(int *pmin, int *pmax); + + inline bool IsError() const + { + return isError_; + } + + inline uint8_t *GetOriginBuffer() const + { + return buffer_.buf_; + } + + inline size_t GetOriginBufferSize() const + { + return buffer_.size_; + } + + inline CString GetErrorMsg() const + { + if (isError_) { + return CString(errorMsg_); + } + return CString(""); + } + + inline bool IsGlobal() const + { + return (flags_ & FLAG_GLOBAL) != 0; + } + + inline bool IsIgnoreCase() const + { + return (flags_ & FLAG_IGNORECASE) != 0; + } + + inline bool IsMultiline() const + { + return (flags_ & FLAG_MULTILINE) != 0; + } + + inline bool IsDotAll() const + { + return (flags_ & FLAG_DOTALL) != 0; + } + + inline bool IsUtf16() const + { + return (flags_ & FLAG_UTF16) != 0; + } + + inline bool IsStick() const + { + return (flags_ & FLAG_STICKY) != 0; + } + + inline static int Canonicalize(int c, bool isUnicode) + { + if (c < TMP_BUF_SIZE) { // NOLINTNEXTLINE(readability-magic-numbers) + if (c >= 'a' && c <= 'z') { + c = c - 'a' + 'A'; + } + } else { + if (isUnicode) { + c = u_toupper(static_cast(c)); + } + } + return c; + } + +private: + friend class RegExpExecutor; + static constexpr int TMP_BUF_SIZE = 128; + void Clear() + { + base_ = nullptr; + pc_ = nullptr; + end_ = nullptr; + c0_ = KEY_EOF; + isError_ = false; + } + + void Advance() + { + if (pc_ <= end_) { + // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-pointer-arithmetic) + c0_ = *pc_++; + } else { + c0_ = KEY_EOF; + } + } + + void Advance(int offset) + { + // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-pointer-arithmetic) + pc_ += offset - 1; + Advance(); + } + + void Prev() + { + if (pc_ >= base_) { + // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-pointer-arithmetic) + c0_ = *pc_--; + } else { + c0_ = KEY_EOF; + } + } + + void SetIsError() + { + isError_ = true; + } + + void PrintF(const char *fmt, ...); + uint8_t *base_; + uint8_t *pc_; + uint8_t *end_; + uint32_t flags_; + int c0_; + int captureCount_; + int stackCount_; + bool isError_; + char errorMsg_[TMP_BUF_SIZE] = {0}; // NOLINTNEXTLINE(modernize-avoid-c-arrays) + DynChunk buffer_; + DynChunk groupNames_; +}; +} // namespace panda::ecmascript +#endif // ECMASCRIPT_REGEXP_PARSER_H diff --git a/runtime/regexp/regexp_parser_cache.cpp b/runtime/regexp/regexp_parser_cache.cpp new file mode 100644 index 000000000..65eb81731 --- /dev/null +++ b/runtime/regexp/regexp_parser_cache.cpp @@ -0,0 +1,64 @@ +/* + * Copyright (c) 2021-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 "plugins/ecmascript/runtime/regexp/regexp_parser_cache.h" + +namespace panda::ecmascript { +RegExpParserCache::RegExpParserCache() +{ + Clear(); +} + +RegExpParserCache::~RegExpParserCache() +{ + Clear(); +} + +void RegExpParserCache::Clear() +{ + for (ParserKey &info : info_) { + info.pattern_ = nullptr; + info.flags_ = UINT32_MAX; // flags cannot be UINT32_MAX, so it means invalid. + info.codeBuffer_ = JSTaggedValue::Hole(); + info.bufferSize_ = 0; + } +} + +size_t RegExpParserCache::GetHash(EcmaString *pattern, const uint32_t flags) +{ + return (pattern->GetHashcode() ^ flags) % CACHE_SIZE; +} + +std::pair RegExpParserCache::GetCache(EcmaString *pattern, const uint32_t flags) +{ + size_t hash = GetHash(pattern, flags); + ParserKey &info = info_[hash]; + if (info.flags_ != flags || !EcmaString::StringsAreEqual(info.pattern_, pattern)) { + return std::pair(JSTaggedValue::Hole(), 0); + } + return std::pair(info.codeBuffer_, info.bufferSize_); +} + +void RegExpParserCache::SetCache(EcmaString *pattern, const uint32_t flags, + const JSTaggedValue codeBuffer, const size_t bufferSize) +{ + size_t hash = GetHash(pattern, flags); + ParserKey &info = info_[hash]; + info.pattern_ = pattern; + info.flags_ = flags; + info.codeBuffer_ = codeBuffer; + info.bufferSize_ = bufferSize; +} +} // namespace panda::ecmascript diff --git a/runtime/regexp/regexp_parser_cache.h b/runtime/regexp/regexp_parser_cache.h new file mode 100644 index 000000000..397d3e5a3 --- /dev/null +++ b/runtime/regexp/regexp_parser_cache.h @@ -0,0 +1,52 @@ +/* + * Copyright (c) 2021-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_REGEXP_PARSER_CACHE_H +#define ECMASCRIPT_REGEXP_PARSER_CACHE_H + +#include + +#include "plugins/ecmascript/runtime/ecma_string.h" +#include "plugins/ecmascript/runtime/js_tagged_value.h" + +namespace panda::ecmascript { +class RegExpParserCache { +public: + RegExpParserCache(); + ~RegExpParserCache(); + static constexpr size_t CACHE_SIZE = 128; + + bool IsInCache(EcmaString *pattern, uint32_t flags); + std::pair GetCache(EcmaString *pattern, uint32_t flags); + void SetCache(EcmaString *pattern, uint32_t flags, JSTaggedValue codeBuffer, size_t bufferSize); + void Clear(); + + DEFAULT_MOVE_SEMANTIC(RegExpParserCache); + DEFAULT_COPY_SEMANTIC(RegExpParserCache); + +private: + size_t GetHash(EcmaString *pattern, uint32_t flags); + + struct ParserKey { + EcmaString *pattern_{nullptr}; + uint32_t flags_{UINT32_MAX}; + JSTaggedValue codeBuffer_{JSTaggedValue::Hole()}; + size_t bufferSize_{0}; + }; + + std::array info_{}; +}; +} // namespace panda::ecmascript +#endif // ECMASCRIPT_REGEXP_PARSER_CACHE_H diff --git a/runtime/runtime_api.h b/runtime/runtime_api.h new file mode 100644 index 000000000..488618ffe --- /dev/null +++ b/runtime/runtime_api.h @@ -0,0 +1,35 @@ +/* + * Copyright (c) 2021-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_INTERFACE_RUNTIME_API_H +#define ECMASCRIPT_INTERFACE_RUNTIME_API_H + +#include "plugins/ecmascript/runtime/common.h" +#include "plugins/ecmascript/runtime/mem/parallel_work_helper.h" +#include "plugins/ecmascript/runtime/mem/region.h" +#include "plugins/ecmascript/runtime/mem/remembered_set.h" + +namespace panda::ecmascript { +class WorkerHelper; + +class PUBLIC_API RuntimeApi { +public: + static bool AtomicTestAndSet(RangeBitmap *bitmap, TaggedObject *object); + static void PushWorkList(WorkerHelper *worklist, uint32_t threadId, TaggedObject *object, Region *region); + static void AtomicInsertCrossRegionRememberedSet(RememberedSet *rset, uintptr_t addr); +}; +} // namespace panda::ecmascript + +#endif // ECMASCRIPT_INTERFACE_RUNTIME_API_H diff --git a/runtime/runtime_call_id.h b/runtime/runtime_call_id.h new file mode 100644 index 000000000..766deb375 --- /dev/null +++ b/runtime/runtime_call_id.h @@ -0,0 +1,603 @@ +/* + * Copyright (c) 2021-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_RUNTIME_CALL_ID_H +#define ECMASCRIPT_RUNTIME_CALL_ID_H + +#include "plugins/ecmascript/runtime/base/config.h" + +namespace panda::ecmascript { +// NOLINTNEXTLINE(cppcoreguidelines-macro-usage) +#define INTERPRETER_CALLER_LIST(V) \ + V(RunInternal) \ + V(Ldnan) \ + V(Ldinfinity) \ + V(Ldglobalthis) \ + V(Ldundefined) \ + V(Ldboolean) \ + V(Ldnumber) \ + V(Ldstring) \ + V(Ldbigint) \ + V(Ldnull) \ + V(Ldsymbol) \ + V(Ldfunction) \ + V(Ldglobal) \ + V(Ldtrue) \ + V(Ldfalse) \ + V(Tonumber) \ + V(Toboolean) \ + V(Add2Dyn) \ + V(Sub2Dyn) \ + V(Mul2Dyn) \ + V(Div2Dyn) \ + V(Mod2Dyn) \ + V(EqDyn) \ + V(NotEqDyn) \ + V(LessDyn) \ + V(LessEqDyn) \ + V(GreaterDyn) \ + V(GreaterEqDyn) \ + V(StrictNotEqDyn) \ + V(StrictEqDyn) \ + V(Shl2Dyn) \ + V(Shr2Dyn) \ + V(Ashr2Dyn) \ + V(And2Dyn) \ + V(Or2Dyn) \ + V(Xor2Dyn) \ + V(NegDyn) \ + V(NotDyn) \ + V(IncDyn) \ + V(DecDyn) \ + V(ExpDyn) \ + V(ThrowDyn) \ + V(LdObjByIndexDyn) \ + V(StObjByIndexDyn) \ + V(LdObjByNameDyn) \ + V(StObjByNameDyn) \ + V(LdObjByValueDyn) \ + V(StObjByValueDyn) \ + V(StOwnByNameDyn) \ + V(StOwnByIdDyn) \ + V(StOwnByValueDyn) \ + V(Trygetobjprop) \ + V(Delobjprop) \ + V(Defineglobalvar) \ + V(Definelocalvar) \ + V(Definefuncexpr) \ + V(DefinefuncDyn) \ + V(DefineNCFuncDyn) \ + V(NewobjDynrange) \ + V(RefeqDyn) \ + V(TypeofDyn) \ + V(LdnewobjrangeDyn) \ + V(IsInDyn) \ + V(InstanceofDyn) \ + V(NewobjspreadDyn) \ + V(CallArg0Dyn) \ + V(CallArg1Dyn) \ + V(CallArg2Dyn) \ + V(CallArg3Dyn) \ + V(CallThisRangeDyn) \ + V(CallRangeDyn) \ + V(CallSpreadDyn) \ + V(NewlexenvDyn) \ + V(CopylexenvDyn) \ + V(StlexvarDyn) \ + V(LdlexvarDyn) \ + V(LdlexenvDyn) \ + V(GetPropIterator) \ + V(CreateIterResultObj) \ + V(DefineGeneratorFunc) \ + V(SuspendGenerator) \ + V(SuspendAsyncGenerator) \ + V(ResumeGenerator) \ + V(GetResumeMode) \ + V(CreateGeneratorObj) \ + V(SetGeneratorState) \ + V(CreateAsyncGeneratorObj) \ + V(DefineAsyncFunc) \ + V(DefineGetterSetterByValue) \ + V(DefineAsyncGeneratorFunc) \ + V(AsyncFunctionEnter) \ + V(AsyncFunctionAwait) \ + V(AsyncFunctionResolve) \ + V(AsyncFunctionReject) \ + V(AsyncGeneratorResolve) \ + V(AsyncGeneratorReject) \ + V(ThrowUndefined) \ + V(ThrowConstAssignment) \ + V(ThrowUndefinedIfHole) \ + V(Copyrestargs) \ + V(Trystobjprop) \ + V(GetMethod) \ + V(GetTemplateObject) \ + V(GetIterator) \ + V(GetAsyncIterator) \ + V(ThrowIfNotObject) \ + V(ThrowThrowNotExists) \ + V(CreateObjectWithExcludedKeys) \ + V(ThrowPatternNonCoercible) \ + V(IterNext) \ + V(CloseIterator) \ + V(StArraySpread) \ + V(GetCallSpreadArgs) \ + V(TryLoadICByName) \ + V(LoadICByName) \ + V(GetPropertyByName) \ + V(TryLoadICByValue) \ + V(LoadICByValue) \ + V(TryStoreICByName) \ + V(StoreICByName) \ + V(TryStoreICByValue) \ + V(StoreICByValue) \ + V(NotifyInlineCache) \ + V(CompressCollector_RunPhases) \ + V(MixSpaceCollector_RunPhases) \ + V(SemiSpaceCollector_RunPhases) \ + V(LoadGlobalICByName) \ + V(StoreGlobalICByName) \ + V(StoreICWithHandler) \ + V(StorePrototype) \ + V(StoreWithTransition) \ + V(StoreField) \ + V(StoreGlobal) \ + V(LoadPrototype) \ + V(LoadICWithHandler) \ + V(StoreElement) \ + V(CallGetter) \ + V(CallSetter) \ + V(AddPropertyByName) \ + V(AddPropertyByIndex) \ + V(GetPropertyByIndex) \ + V(GetPropertyByValue) \ + V(SetPropertyByIndex) \ + V(SetPropertyByValue) \ + V(FastTypeOf) \ + V(FastSetPropertyByIndex) \ + V(FastSetPropertyByValue) \ + V(FastGetPropertyByName) \ + V(FastGetPropertyByValue) \ + V(FastGetPropertyByIndex) \ + V(NewLexicalEnvDyn) \ + V(SetElement) \ + V(SetGlobalOwnProperty) \ + V(SetOwnPropertyByName) \ + V(SetOwnElement) \ + V(FastSetProperty) \ + V(FastGetProperty) \ + V(FindOwnProperty) \ + V(HasOwnProperty) \ + V(ExecuteNative) \ + V(Execute) \ + V(ToJSTaggedValueWithInt32) \ + V(ToJSTaggedValueWithUint32) \ + V(ThrowIfSuperNotCorrectCall) \ + V(CreateEmptyArray) \ + V(CreateEmptyObject) \ + V(CreateObjectWithBuffer) \ + V(CreateObjectHavingMethod) \ + V(SetObjectWithProto) \ + V(ImportModule) \ + V(StModuleVar) \ + V(CopyModule) \ + V(LdModvarByName) \ + V(CreateRegExpWithLiteral) \ + V(CreateArrayWithBuffer) \ + V(GetNextPropName) \ + V(CopyDataProperties) \ + V(GetUnmapedArgs) \ + V(TryStGlobalByName) \ + V(LdGlobalVar) \ + V(StGlobalVar) \ + V(TryUpdateGlobalRecord) \ + V(LdGlobalRecord) \ + V(StGlobalRecord) \ + V(ThrowReferenceError) \ + V(ThrowTypeError) \ + V(ThrowSyntaxError) \ + V(NewClassFunc) \ + V(DefineClass) \ + V(SuperCall) \ + V(SuperCallSpread) \ + V(DefineMethod) \ + V(LdSuperByValue) \ + V(StSuperByValue) \ + V(ThrowDeleteSuperProperty) \ + V(GetIteratorNext) \ + V(ModWithTSType) \ + V(MulWithTSType) \ + V(SubWithTSType) \ + V(DivWithTSType) \ + V(AddWithTSType) \ + V(GetBitOPDate) \ + V(ShlWithTSType) \ + V(ShrWithTSType) \ + V(AshrWithTSType) \ + V(AndWithTSType) \ + V(OrWithTSType) \ + V(XorWithTSType) \ + V(EqualWithIC) \ + V(NotEqualWithIC) \ + V(Compare) \ + V(LessDynWithIC) \ + V(LessEqDynWithIC) \ + V(GreaterDynWithIC) \ + V(GreaterEqDynWithIC) \ + V(SetPropertyByName) +// NOLINTNEXTLINE(cppcoreguidelines-macro-usage) +#define BUITINS_API_LIST(V) \ + V(Array, Constructor) \ + V(Array, From) \ + V(Array, Of) \ + V(Array, IsArray) \ + V(Array, Entries) \ + V(Array, Species) \ + V(Array, Concat) \ + V(Array, CopyWithin) \ + V(Array, FlatMap) \ + V(Array, Flat) \ + V(Array, Fill) \ + V(Array, Filter) \ + V(Array, Find) \ + V(Array, FindIndex) \ + V(Array, Includes) \ + V(Array, IndexOf) \ + V(Array, Join) \ + V(Array, Keys) \ + V(Array, LastIndexOf) \ + V(Array, Map) \ + V(Array, Pop) \ + V(Array, Push) \ + V(Array, Reduce) \ + V(Array, ReduceRight) \ + V(Array, Reverse) \ + V(Array, Shift) \ + V(Array, Slice) \ + V(Array, Some) \ + V(Array, Sort) \ + V(Array, Splice) \ + V(Array, ToLocaleString) \ + V(Array, ToString) \ + V(Array, Unshift) \ + V(Array, Values) \ + V(ArrayBuffer, Constructor) \ + V(ArrayBuffer, Slice) \ + V(ArrayBuffer, GetValueFromBuffer) \ + V(ArrayBuffer, SetValueInBuffer) \ + V(ArrayBuffer, CloneArrayBuffer) \ + V(ArrayBuffer, AllocateArrayBuffer) \ + V(AsyncFunction, Constructor) \ + V(Boolean, Constructor) \ + V(Boolean, ThisBooleanValue) \ + V(DataView, Constructor) \ + V(DataView, GetBuffer) \ + V(DataView, GetByteLength) \ + V(DataView, GetOffset) \ + V(DataView, GetViewValue) \ + V(DataView, SetViewValue) \ + V(Date, Constructor) \ + V(Date, Now) \ + V(Date, UTC) \ + V(Date, Parse) \ + V(Date, GetDateField) \ + V(Date, GetTime) \ + V(Date, SetTime) \ + V(Date, ToJSON) \ + V(Date, ValueOf) \ + V(Date, ToPrimitive) \ + V(Function, Constructor) \ + V(Function, PrototypeApply) \ + V(Function, PrototypeBind) \ + V(Function, PrototypeCall) \ + V(Function, PrototypeToString) \ + V(Function, PrototypeHasInstance) \ + V(Generator, Constructor) \ + V(GeneratorPrototype, Next) \ + V(GeneratorPrototype, Return) \ + V(GeneratorPrototype, Throw) \ + V(AsyncGenerator, Constructor) \ + V(AsyncGeneratorPrototype, Next) \ + V(AsyncGeneratorPrototype, Return) \ + V(AsyncGeneratorPrototype, Throw) \ + V(AsyncFromSyncIteratorPrototype, Next) \ + V(AsyncFromSyncIteratorPrototype, Return) \ + V(AsyncFromSyncIteratorPrototype, Throw) \ + V(Global, IsFinite) \ + V(Global, IsNaN) \ + V(Global, PrintEntryPoint) \ + V(Global, NewobjDynrange) \ + V(Global, CallJsBoundFunction) \ + V(Global, CallJsProxy) \ + V(Global, DecodeURI) \ + V(Global, EncodeURI) \ + V(Global, DecodeURIComponent) \ + V(Global, EncodeURIComponent) \ + V(Iterator, Constructor) \ + V(Iterator, Next) \ + V(Iterator, Throw) \ + V(Iterator, Return) \ + V(Iterator, GetObj) \ + V(Json, Parse) \ + V(Json, Stringify) \ + V(Map, Constructor) \ + V(Map, Species) \ + V(Map, Clear) \ + V(Map, Delete) \ + V(Map, Entries) \ + V(Map, Get) \ + V(Map, Has) \ + V(Map, Keys) \ + V(Map, Set) \ + V(Map, GetSize) \ + V(Map, Values) \ + V(Math, Abs) \ + V(Math, Acos) \ + V(Math, Acosh) \ + V(Math, Asin) \ + V(Math, Asinh) \ + V(Math, Atan) \ + V(Math, Atanh) \ + V(Math, Atan2) \ + V(Math, Cbrt) \ + V(Math, Ceil) \ + V(Math, Clz32) \ + V(Math, Cos) \ + V(Math, Cosh) \ + V(Math, Exp) \ + V(Math, Expm1) \ + V(Math, Floor) \ + V(Math, Fround) \ + V(Math, Hypot) \ + V(Math, Imul) \ + V(Math, Log) \ + V(Math, Log1p) \ + V(Math, Log10) \ + V(Math, Log2) \ + V(Math, Max) \ + V(Math, Min) \ + V(Math, Pow) \ + V(Math, Random) \ + V(Math, Round) \ + V(Math, Sign) \ + V(Math, Sin) \ + V(Math, Sinh) \ + V(Math, Sqrt) \ + V(Math, Tan) \ + V(Math, Tanh) \ + V(Math, Trunc) \ + V(Number, Constructor) \ + V(Number, IsFinite) \ + V(Number, IsInteger) \ + V(Number, IsNaN) \ + V(Number, IsSafeInteger) \ + V(Number, ParseFloat) \ + V(Number, ParseInt) \ + V(Number, ToExponential) \ + V(Number, ToFixed) \ + V(Number, ToLocaleString) \ + V(Number, ToPrecision) \ + V(Number, ToString) \ + V(Number, ValueOf) \ + V(Number, ThisNumberValue) \ + V(Object, Constructor) \ + V(Object, Assign) \ + V(Object, Create) \ + V(Object, DefineProperties) \ + V(Object, DefineProperty) \ + V(Object, Freeze) \ + V(Object, FromEntries) \ + V(Object, GetOwnPropertyDesciptor) \ + V(Object, GetOwnPropertyDesciptors) \ + V(Object, GetOwnPropertyKeys) \ + V(Object, GetOwnPropertyNames) \ + V(Object, GetOwnPropertySymbols) \ + V(Object, GetPrototypeOf) \ + V(Object, Is) \ + V(Object, Keys) \ + V(Object, PreventExtensions) \ + V(Object, Seal) \ + V(Object, SetPrototypeOf) \ + V(Object, HasOwnProperty) \ + V(Object, IsPrototypeOf) \ + V(Object, ToLocaleString) \ + V(Object, GetBuiltinTag) \ + V(Object, ToString) \ + V(Object, ValueOf) \ + V(Object, ProtoGetter) \ + V(Object, ProtoSetter) \ + V(Object, Values) \ + V(Object, DefineGetter) \ + V(Object, DefineSetter) \ + V(Object, LookupGetter) \ + V(Object, LookupSetter) \ + V(PromiseHandler, Resolve) \ + V(PromiseHandler, Reject) \ + V(PromiseHandler, Executor) \ + V(PromiseHandler, ResolveElementFunction) \ + V(PromiseHandler, AsyncAwaitFulfilled) \ + V(PromiseHandler, AsyncAwaitRejected) \ + V(PromiseJob, Reaction) \ + V(PromiseJob, ResolveThenableJob) \ + V(Promise, Constructor) \ + V(Promise, All) \ + V(Promise, Race) \ + V(Promise, Reject) \ + V(Promise, Resolve) \ + V(Promise, GetSpecies) \ + V(Promise, Catch) \ + V(Promise, Then) \ + V(Promise, PerformPromiseThen) \ + V(Proxy, Constructor) \ + V(Proxy, Revocable) \ + V(Proxy, InvalidateProxyFunction) \ + V(Reflect, Apply) \ + V(Reflect, Constructor) \ + V(Reflect, DefineProperty) \ + V(Reflect, DeleteProperty) \ + V(Reflect, Get) \ + V(Reflect, GetOwnPropertyDescriptor) \ + V(Reflect, GetPrototypeOf) \ + V(Reflect, Has) \ + V(Reflect, OwnKeys) \ + V(Reflect, PreventExtensions) \ + V(Reflect, Set) \ + V(Reflect, SetPrototypeOf) \ + V(RegExp, Constructor) \ + V(RegExp, Exec) \ + V(RegExp, Test) \ + V(RegExp, ToString) \ + V(RegExp, GetFlags) \ + V(RegExp, GetSpecies) \ + V(RegExp, Match) \ + V(RegExp, Replace) \ + V(RegExp, Search) \ + V(RegExp, Split) \ + V(RegExp, Create) \ + V(Set, Constructor) \ + V(Set, Species) \ + V(Set, Add) \ + V(Set, Clear) \ + V(Set, Delete) \ + V(Set, Entries) \ + V(Set, Has) \ + V(Set, GetSize) \ + V(Set, Values) \ + V(StringIterator, Next) \ + V(String, Constructor) \ + V(String, FromCharCode) \ + V(String, FromCodePoint) \ + V(String, Raw) \ + V(String, GetSubstitution) \ + V(String, CharAt) \ + V(String, CharCodeAt) \ + V(String, CodePointAt) \ + V(String, Concat) \ + V(String, EndsWith) \ + V(String, Includes) \ + V(String, IndexOf) \ + V(String, LastIndexOf) \ + V(String, LocaleCompare) \ + V(String, Match) \ + V(String, Normalize) \ + V(String, PadEnd) \ + V(String, PadStart) \ + V(String, Repeat) \ + V(String, Replace) \ + V(String, ReplaceAll) \ + V(String, Search) \ + V(String, Slice) \ + V(String, Split) \ + V(String, StartsWith) \ + V(String, Substring) \ + V(String, ToLocaleLowerCase) \ + V(String, ToLocaleUpperCase) \ + V(String, ToLowerCase) \ + V(String, ToString) \ + V(String, ToUpperCase) \ + V(String, Trim) \ + V(String, TrimEnd) \ + V(String, TrimStart) \ + V(String, GetStringIterator) \ + V(String, SubStr) \ + V(Symbol, Constructor) \ + V(Symbol, ToString) \ + V(Symbol, ValueOf) \ + V(Symbol, For) \ + V(Symbol, KeyFor) \ + V(Symbol, DescriptionGetter) \ + V(Symbol, ThisSymbolValue) \ + V(Symbol, ToPrimitive) \ + V(Symbol, SymbolDescriptiveString) \ + V(TypedArray, BaseConstructor) \ + V(TypedArray, From) \ + V(TypedArray, Of) \ + V(TypedArray, Species) \ + V(TypedArray, GetBuffer) \ + V(TypedArray, GetByteLength) \ + V(TypedArray, GetByteOffset) \ + V(TypedArray, CopyWithin) \ + V(TypedArray, Entries) \ + V(TypedArray, Every) \ + V(TypedArray, Filter) \ + V(TypedArray, ForEach) \ + V(TypedArray, Includes) \ + V(TypedArray, Keys) \ + V(TypedArray, GetLength) \ + V(TypedArray, Map) \ + V(TypedArray, Set) \ + V(TypedArray, Slice) \ + V(TypedArray, Sort) \ + V(TypedArray, Subarray) \ + V(TypedArray, Values) \ + V(TypedArray, ToStringTag) \ + V(WeakMap, Constructor) \ + V(WeakMap, Delete) \ + V(WeakMap, Get) \ + V(WeakMap, Has) \ + V(WeakMap, Set) \ + V(WeakSet, Constructor) \ + V(WeakSet, Delete) \ + V(WeakSet, Add) \ + V(WeakSet, Has) \ + V(ArrayList, Constructor) \ + V(ArrayList, Add) \ + V(ArrayList, Iterator) + +// NOLINTNEXTLINE(cppcoreguidelines-macro-usage) +#define ABSTRACT_OPERATION_LIST(V) \ + V(JSTaggedValue, ToString) \ + +// NOLINTNEXTLINE(cppcoreguidelines-macro-usage) +#define INTERPRETER_CALLER_ID(name) INTERPRETER_ID_##name, +// NOLINTNEXTLINE(cppcoreguidelines-macro-usage) +#define BUILTINS_API_ID(class, name) BUILTINS_ID_##class##_##name, +// NOLINTNEXTLINE(cppcoreguidelines-macro-usage) +#define GC_RUNPHASE_ID(name) name##_GC_TRACE_RUNPHASE, +// NOLINTNEXTLINE(cppcoreguidelines-macro-usage) +#define ABSTRACT_OPERATION_ID(class, name) ABSTRACT_ID_##class##_##name, + +enum EcmaRuntimeCallerId { + INTERPRETER_CALLER_LIST(INTERPRETER_CALLER_ID) BUITINS_API_LIST(BUILTINS_API_ID) + ABSTRACT_OPERATION_LIST(ABSTRACT_OPERATION_ID) + RUNTIME_CALLER_NUMBER, +}; + +#if ECMASCRIPT_ENABLE_RUNTIME_STAT +// NOLINTNEXTLINE(cppcoreguidelines-macro-usage) +#define INTERPRETER_TRACE(thread, name) \ + [[maybe_unused]] JSThread *_js_thread_ = thread; \ + [[maybe_unused]] EcmaRuntimeStat *_run_stat_ = _js_thread_->GetEcmaVM()->GetRuntimeStat(); \ + RuntimeTimerScope interpret_##name##_scope_(thread, INTERPRETER_CALLER_ID(name) _run_stat_) + +// NOLINTNEXTLINE(cppcoreguidelines-macro-usage) +#define BUILTINS_API_TRACE(thread, class, name) \ + [[maybe_unused]] JSThread *_js_thread_ = thread; \ + [[maybe_unused]] EcmaRuntimeStat *_run_stat_ = _js_thread_->GetEcmaVM()->GetRuntimeStat(); \ + RuntimeTimerScope builtins_##class##name##_scope_(thread, BUILTINS_API_ID(class, name) _run_stat_) + +// NOLINTNEXTLINE(cppcoreguidelines-macro-usage) +#define ABSTRACT_OPERATION_TRACE(thread, class, name) \ + [[maybe_unused]] JSThread *_js_thread_ = thread; \ + [[maybe_unused]] EcmaRuntimeStat *_run_stat_ = _js_thread_->GetEcmaVM()->GetRuntimeStat(); \ + RuntimeTimerScope abstract_##class##name##_scope_(thread, ABSTRACT_OPERATION_ID(class, name) _run_stat_) + +#else +#define INTERPRETER_TRACE(thread, name) static_cast(0) // NOLINT(cppcoreguidelines-macro-usage) +#define BUILTINS_API_TRACE(thread, class, name) static_cast(0) // NOLINT(cppcoreguidelines-macro-usage) +#define ABSTRACT_OPERATION_TRACE(thread, class, name) static_cast(0) // NOLINT(cppcoreguidelines-macro-usage) +#endif // ECMASCRIPT_ENABLE_RUNTIME_STAT +} // namespace panda::ecmascript +#endif diff --git a/runtime/runtime_sources.gn b/runtime/runtime_sources.gn new file mode 100644 index 000000000..725dd229d --- /dev/null +++ b/runtime/runtime_sources.gn @@ -0,0 +1,178 @@ +srcs = [ + "builtins/builtins_collator.cpp", + "builtins/builtins_date_time_format.cpp", + "builtins/builtins_intl.cpp", + "builtins/builtins_locale.cpp", + "builtins/builtins_number_format.cpp", + "builtins/builtins_plural_rules.cpp", + "builtins/builtins_relative_time_format.cpp", + "js_collator.cpp", + "js_date_time_format.cpp", + "js_locale.cpp", + "js_number_format.cpp", + "js_plural_rules.cpp", + "js_relative_time_format.cpp", + "base/array_helper.cpp", + "base/builtins_base.cpp", + "base/error_helper.cpp", + "base/json_parser.cpp", + "base/json_stringifier.cpp", + "base/number_helper.cpp", + "base/object_helper.cpp", + "base/string_helper.cpp", + "base/typed_array_helper.cpp", + "base/utf_helper.cpp", + "bridge/ecma_bridge_helpers.cpp", + "builtins.cpp", + "builtins/builtins_ark_tools.cpp", + "builtins/builtins_array.cpp", + "builtins/builtins_arraybuffer.cpp", + "builtins/builtins_async_from_sync_iterator.cpp", + "builtins/builtins_async_function.cpp", + "builtins/builtins_async_generator.cpp", + "builtins/builtins_async_iterator.cpp", + "builtins/builtins_boolean.cpp", + "builtins/builtins_dataview.cpp", + "builtins/builtins_date.cpp", + "builtins/builtins_errors.cpp", + "builtins/builtins_function.cpp", + "builtins/builtins_generator.cpp", + "builtins/builtins_global.cpp", + "builtins/builtins_iterator.cpp", + "builtins/builtins_json.cpp", + "builtins/builtins_map.cpp", + "builtins/builtins_math.cpp", + "builtins/builtins_number.cpp", + "builtins/builtins_object.cpp", + "builtins/builtins_promise.cpp", + "builtins/builtins_promise_handler.cpp", + "builtins/builtins_promise_job.cpp", + "builtins/builtins_proxy.cpp", + "builtins/builtins_reflect.cpp", + "builtins/builtins_regexp.cpp", + "builtins/builtins_set.cpp", + "builtins/builtins_string.cpp", + "builtins/builtins_string_iterator.cpp", + "builtins/builtins_symbol.cpp", + "builtins/builtins_typedarray.cpp", + "builtins/builtins_weak_map.cpp", + "builtins/builtins_weak_set.cpp", + "class_linker/panda_file_translator.cpp", + "containers/containers_arraylist.cpp", + "containers/containers_private.cpp", + "dump.cpp", + "ecma_class_linker_extension.cpp", + "ecma_exceptions.cpp", + "ecma_language_context.cpp", + "ecma_module.cpp", + "ecma_string.cpp", + "ecma_string_table.cpp", + "ecma_vm.cpp", + "free_object.cpp", + "generator_helper.cpp", + "global_env.cpp", + "global_env_constants.cpp", + "hprof/heap_profiler.cpp", + "hprof/heap_profiler_interface.cpp", + "hprof/heap_root_visitor.cpp", + "hprof/heap_snapshot.cpp", + "hprof/heap_snapshot_json_serializer.cpp", + "hprof/heap_tracker.cpp", + "hprof/string_hashmap.cpp", + "ic/profile_type_info.cpp", + "ic/ic_runtime.cpp", + "ic/ic_runtime_stub.cpp", + "ic/property_box.cpp", + "ic/proto_change_details.cpp", + "internal_call_params.cpp", + "interpreter/slow_runtime_helper.cpp", + "interpreter/slow_runtime_stub.cpp", + "intrinsics.cpp", + "jobs/micro_job_queue.cpp", + "js_arguments.cpp", + "js_array.cpp", + "js_array_iterator.cpp", + "js_arraybuffer.cpp", + "js_arraylist.cpp", + "js_async_from_sync_iterator_object.cpp", + "js_async_function.cpp", + "js_async_generator_object.cpp", + "js_dataview.cpp", + "js_date.cpp", + "js_for_in_iterator.cpp", + "js_function.cpp", + "js_generator_object.cpp", + "js_hclass.cpp", + "js_invoker.cpp", + "js_iterator.cpp", + "js_map.cpp", + "js_map_iterator.cpp", + "js_method.cpp", + "js_object.cpp", + "js_primitive_ref.cpp", + "js_promise.cpp", + "js_proxy.cpp", + "js_serializer.cpp", + "js_set.cpp", + "js_set_iterator.cpp", + "js_stable_array.cpp", + "js_string_iterator.cpp", + "js_tagged_value.cpp", + "js_thread.cpp", + "js_typed_array.cpp", + "js_weak_container.cpp", + "linked_hash_table.cpp", + "literal_data_extractor.cpp", + "message_string.cpp", + "mem/ecma_reference_processor.cpp", + "mem/c_string.cpp", + "mem/chunk.cpp", + "mem/compress_collector.cpp", + "mem/concurrent_marker.cpp", + "mem/concurrent_sweeper.cpp", + "mem/mem_manager.cpp", + "mem/evacuation_allocator.cpp", + "mem/free_object_kind.cpp", + "mem/free_object_list.cpp", + "mem/gc_stats.cpp", + "mem/heap.cpp", + "mem/mem_controller.cpp", + "mem/mix_space_collector.cpp", + "mem/parallel_work_helper.cpp", + "mem/parallel_evacuation.cpp", + "mem/parallel_marker.cpp", + "mem/region_factory.cpp", + "mem/semi_space_collector.cpp", + "mem/space.cpp", + "mem/verification.cpp", + "napi/jsnapi.cpp", + "object_factory.cpp", + "object_operator.cpp", + "platform/platform.cpp", + "platform/runner.cpp", + "platform/task_queue.cpp", + "layout_info.cpp", + "regexp/dyn_chunk.cpp", + "regexp/regexp_executor.cpp", + "regexp/regexp_opcode.cpp", + "regexp/regexp_parser.cpp", + "regexp/regexp_parser_cache.cpp", + "snapshot/mem/slot_bit.cpp", + "snapshot/mem/snapshot.cpp", + "snapshot/mem/snapshot_serialize.cpp", + "tagged_dictionary.cpp", + "template_string.cpp", + "vmstat/caller_stat.cpp", + "vmstat/runtime_stat.cpp", + "weak_vector.cpp", + "class_info_extractor.cpp", + "compiler/ecmascript_runtime_interface.cpp", + "tooling/pt_ecmascript_extension.cpp", +] + +# Should be files deleted??? +#srcs_arm = [ "arch/arm/builtin_bridge_arm.S" ] +#srcs_arm64 = [ "arch/aarch64/builtin_bridge_aarch64.S" ] +#srcs_x86 = [ "arch/amd64/builtin_bridge_amd64.S" ] + +runtime_yamls = [ "ecma_runtime.yaml" ] diff --git a/runtime/snapshot/mem/CMakeLists.txt b/runtime/snapshot/mem/CMakeLists.txt new file mode 100644 index 000000000..041b4d6b9 --- /dev/null +++ b/runtime/snapshot/mem/CMakeLists.txt @@ -0,0 +1,15 @@ +# Huawei Technologies Co.,Ltd. + +cmake_minimum_required(VERSION 3.10) + +set(SNAPSHOT_OUTPUT ${PANDA_BINARY_ROOT}/snapshot) +set(SNAPSHOT_INPUT ${PANDA_ROOT}/runtime/ecmascript/snapshot/strip.native.min.abc) +set(SERIALIZE_ARGUMENTS --load-runtimes=ecmascript --compiler-enable-jit=false --gc-type=epsilon --snapshot-serialize-enabled=true --snapshot-file=${SNAPSHOT_OUTPUT} ${SNAPSHOT_INPUT} _GLOBAL::func_main_0) +set(DESERIALIZE_ARGUMENTS --load-runtimes=ecmascript --compiler-enable-jit=false --gc-type=epsilon --snapshot-file=${SNAPSHOT_OUTPUT} ${SNAPSHOT_INPUT} _GLOBAL::func_main_0) +if(PANDA_TARGET_64 AND NOT PANDA_TARGET_MOBILE) + #add_custom_target(clearSnapshot ALL COMMAND rm -f ${SNAPSHOT_OUTPUT}) + #add_custom_target(generalSnapshot ALL COMMAND ${PANDA_RUN_PREFIX} $ ${SERIALIZE_ARGUMENTS}) + #add_dependencies(generalSnapshot ark clearSnapshot arkruntime) + #add_custom_target(deserializeSnapshot ALL COMMAND ${PANDA_RUN_PREFIX} $ ${DESERIALIZE_ARGUMENTS}) + #add_dependencies(deserializeSnapshot generalSnapshot) +endif() diff --git a/runtime/snapshot/mem/constants.h b/runtime/snapshot/mem/constants.h new file mode 100644 index 000000000..9a59371eb --- /dev/null +++ b/runtime/snapshot/mem/constants.h @@ -0,0 +1,50 @@ +/* + * Copyright (c) 2021-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_SNAPSHOT_MEM_CONSTANTS_H +#define ECMASCRIPT_SNAPSHOT_MEM_CONSTANTS_H + +// slot bit +static constexpr int OBJECT_INDEX_BIT_NUMBER = 13; // object index +static constexpr int OBJECT_TO_STRING_FLAG_NUMBER = 1; // 1 : reference to string +static constexpr int OBJECT_IN_CONSTANTS_INDEX_NUMBER = 8; // index +static constexpr int OBJECT_TYPE_BIT_NUMBER = 7; // js_type +static constexpr int OBJECT_SIZE_BIT_NUMBER = 18; // 4M +static constexpr int OBJECT_SPECIAL = 1; // special +static constexpr int IS_REFERENCE_SLOT_BIT_NUMBER = 16; // [0x0000] is reference + +static constexpr int MAX_C_POINTER_INDEX = 1024 * 8 - 1; +static constexpr int MAX_OBJECT_INDEX = 8192; +static constexpr int MAX_OBJECT_SIZE_INDEX = 1024 * 256 - 1; + +// object or space align up +static constexpr size_t PAGE_SIZE_ALIGN_UP = 4096; +static constexpr size_t MAX_UINT_32 = 0xFFFFFFFF; + +// builtins native method encode +// builtins deserialize: nativeMehtods_ + getter/setter; program deserialize: getter/setter + programFunctionMethods +static constexpr size_t PROGRAM_NATIVE_METHOD_BEGIN = 6; + +// address slot size +static constexpr int ADDRESS_SIZE = sizeof(uintptr_t); + +// serialize use constants +static constexpr uint8_t MASK_METHOD_SPACE_BEGIN = 0x7F; +static constexpr int OBJECT_SIZE_EXTEND_PAGE = 512; +static constexpr int NATIVE_METHOD_SIZE = 427; + +static constexpr size_t MANAGED_OBJECT_OFFSET = 16; + +#endif // ECMASCRIPT_SNAPSHOT_MEM_CONSTANTS_H diff --git a/runtime/snapshot/mem/slot_bit.cpp b/runtime/snapshot/mem/slot_bit.cpp new file mode 100644 index 000000000..f1e2661a4 --- /dev/null +++ b/runtime/snapshot/mem/slot_bit.cpp @@ -0,0 +1,56 @@ +/* + * Copyright (c) 2021-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 "plugins/ecmascript/runtime/snapshot/mem/slot_bit.h" + +#include "plugins/ecmascript/runtime/js_hclass-inl.h" +#include "plugins/ecmascript/runtime/js_tagged_value.h" + +namespace panda::ecmascript { +/* static */ +uint8_t SerializeHelper::GetObjectType(TaggedObject *objectHeader) +{ + auto hclass = objectHeader->GetClass(); + return static_cast(JSHClass::Cast(hclass)->GetObjectType()); +} + +/* static */ +SlotBit SerializeHelper::AddObjectHeaderToData(TaggedObject *objectHeader, CQueue *queue, + std::unordered_map *data, size_t index) +{ + queue->emplace(objectHeader); + SlotBit slotBits(data->size()); + slotBits.SetObjectInConstantsIndex(index); + data->emplace(ToUintPtr(objectHeader), slotBits); + return slotBits; +} + +void SerializeHelper::AddTaggedObjectRangeToData(ObjectSlot start, ObjectSlot end, CQueue *queue, + std::unordered_map *data) +{ + size_t index = 0; + while (start < end) { + JSTaggedValue object(start.GetTaggedType()); + index++; + start++; + if (object.IsHeapObject()) { + SlotBit slotBit(0); + if (data->find(object.GetRawData()) == data->end()) { + slotBit = AddObjectHeaderToData(object.GetTaggedObject(), queue, data, index); + } + } + } +} +} // namespace panda::ecmascript diff --git a/runtime/snapshot/mem/slot_bit.h b/runtime/snapshot/mem/slot_bit.h new file mode 100644 index 000000000..d7afb9011 --- /dev/null +++ b/runtime/snapshot/mem/slot_bit.h @@ -0,0 +1,134 @@ +/* + * Copyright (c) 2021-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_SNAPSHOT_MEM_SLOT_BIT_H +#define ECMASCRIPT_SNAPSHOT_MEM_SLOT_BIT_H + +#include "plugins/ecmascript/runtime/mem/c_containers.h" +#include "plugins/ecmascript/runtime/mem/slots.h" +#include "plugins/ecmascript/runtime/snapshot/mem/constants.h" + +#include "utils/bit_field.h" + +namespace panda::ecmascript { +class SlotBit final { +public: + ~SlotBit() = default; + explicit SlotBit() = delete; + + DEFAULT_COPY_SEMANTIC(SlotBit); + DEFAULT_MOVE_SEMANTIC(SlotBit); + + explicit SlotBit(uint64_t value) : value_(value) {} + + using ObjectIndexBits = BitField; + using ObjectToStringBits = ObjectIndexBits::NextField; + using ObjectInConstantsIndexBits = ObjectToStringBits::NextField; + using ObjectTypeBits = ObjectInConstantsIndexBits::NextField; + using ObjectSizeBits = ObjectTypeBits::NextField; + using ObjectSpecial = ObjectSizeBits::NextField; + using IsReferenceSlotBits = ObjectSpecial::NextField; + + void SetReferenceToString(bool flag) + { + ObjectToStringBits::Set(flag, &value_); + } + + void SetObjectInConstantsIndex(size_t index) + { + ObjectInConstantsIndexBits::Set(index, &value_); + } + + size_t GetObjectInConstantsIndex() const + { + return ObjectInConstantsIndexBits::Decode(value_); + } + + bool IsReferenceToString() const + { + return ObjectToStringBits::Decode(value_); + } + + void SetObjectType(uint8_t object_type) + { + ObjectTypeBits::Set(object_type, &value_); + } + + void SetObjectSize(size_t object_size) + { + ObjectSizeBits::Set(object_size, &value_); + } + + void SetObjectIndex(uint32_t object_index) + { + ObjectIndexBits::Set(object_index, &value_); + } + + uint64_t GetValue() const + { + return value_; + } + + uint32_t GetObjectIndex() const + { + return ObjectIndexBits::Decode(value_); + } + + uint8_t GetObjectType() const + { + return ObjectTypeBits::Decode(value_); + } + + size_t GetObjectSize() const + { + return ObjectSizeBits::Decode(value_); + } + + bool IsReferenceSlot() const + { + return IsReferenceSlotBits::Decode(value_) == 0; + } + + bool IsSpecial() const + { + return ObjectSpecial::Decode(value_); + } + + void SetObjectSpecial() + { + ObjectSpecial::Set(true, &value_); + } + + void ClearObjectSpecialFlag() + { + ObjectSpecial::Set(false, &value_); + } + +private: + uint64_t value_; +}; + +class SerializeHelper final { +public: + static uint8_t GetObjectType(TaggedObject *objectHeader); + + static SlotBit AddObjectHeaderToData(TaggedObject *objectHeader, CQueue *queue, + std::unordered_map *data, size_t index = 0); + static void AddTaggedObjectRangeToData(ObjectSlot start, ObjectSlot end, CQueue *queue, + std::unordered_map *data); +}; +} // namespace panda::ecmascript + +#endif // ECMASCRIPT_SNAPSHOT_MEM_SLOT_BIT_H diff --git a/runtime/snapshot/mem/snapshot.cpp b/runtime/snapshot/mem/snapshot.cpp new file mode 100644 index 000000000..be2a667f0 --- /dev/null +++ b/runtime/snapshot/mem/snapshot.cpp @@ -0,0 +1,216 @@ +/* + * Copyright (c) 2021-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 "plugins/ecmascript/runtime/snapshot/mem/snapshot.h" + +#include +#include +#include +#include + +#include "plugins/ecmascript/runtime/class_linker/program_object.h" +#include "plugins/ecmascript/runtime/ecma_vm.h" +#include "plugins/ecmascript/runtime/global_env.h" +#include "plugins/ecmascript/runtime/jobs/micro_job_queue.h" +#include "plugins/ecmascript/runtime/js_hclass.h" +#include "plugins/ecmascript/runtime/js_thread.h" +#include "plugins/ecmascript/runtime/mem/c_containers.h" +#include "plugins/ecmascript/runtime/mem/mem_manager.h" +#include "plugins/ecmascript/runtime/mem/heap.h" +#include "plugins/ecmascript/runtime/object_factory.h" +#include "plugins/ecmascript/runtime/snapshot/mem/snapshot_serialize.h" +#include "libpandabase/mem/mem.h" + +namespace panda::ecmascript { +constexpr uint32_t PANDA_FILE_ALIGNMENT = 4096; + +void SnapShot::MakeSnapShotProgramObject(Program *program, const panda_file::File *pf, const CString &fileName) +{ + std::fstream write; + std::pair filePath = VerifyFilePath(fileName); + if (!filePath.first) { + LOG(ERROR, RUNTIME) << "snapshot file path error"; + return; + } + write.open(filePath.second.c_str(), std::ios::out | std::ios::binary | std::ios::trunc); + if (!write.good()) { + LOG(DEBUG, RUNTIME) << "snapshot open file failed"; + return; + } + + SnapShotSerialize serialize(vm_, true); + + std::unordered_map data; + CQueue objectQueue; + + serialize.RegisterNativeMethod(); + + // handle GlobalEnvConstants + auto constant = const_cast(vm_->GetJSThread()->GlobalConstants()); + constant->VisitRangeSlot([&objectQueue, &data]([[maybe_unused]] Root type, ObjectSlot start, ObjectSlot end) { + SerializeHelper::AddTaggedObjectRangeToData(start, end, &objectQueue, &data); + }); + + vm_->Iterate([&objectQueue, &data]([[maybe_unused]] Root type, ObjectSlot object) { + SerializeHelper::AddObjectHeaderToData(object.GetTaggedObjectHeader(), &objectQueue, &data); + }); + + while (!objectQueue.empty()) { + auto taggedObject = objectQueue.front(); + if (taggedObject == nullptr) { + break; + } + objectQueue.pop(); + + serialize.Serialize(taggedObject, &objectQueue, &data); + } + + serialize.SetProgramSerializeStart(); + + // handle program + if (program != nullptr) { + SerializeHelper::AddObjectHeaderToData(program, &objectQueue, &data); + } + + while (!objectQueue.empty()) { + auto taggedObject = objectQueue.front(); + if (taggedObject == nullptr) { + break; + } + objectQueue.pop(); + serialize.Serialize(taggedObject, &objectQueue, &data); + } + + serialize.SerializePandaFileMethod(); + + auto region = vm_->GetHeap()->GetSnapShotSpace()->GetCurrentRegion(); + region->SetHighWaterMark(vm_->GetFactory()->GetHeapManager().GetSnapShotSpaceAllocator().GetTop()); + + // write to file + SnapShotSpace *space = vm_->GetHeap()->GetSnapShotSpace(); + uint32_t snapshot_size = space->GetRegionCount() * DEFAULT_SNAPSHOT_SPACE_SIZE; + uint32_t panda_file_begin = RoundUp(snapshot_size + sizeof(Header), PANDA_FILE_ALIGNMENT); + Header hdr {snapshot_size, panda_file_begin}; + write.write(reinterpret_cast(&hdr), sizeof(hdr)); + + space->EnumerateRegions([&write](Region *current) { + write.write(reinterpret_cast(current), DEFAULT_SNAPSHOT_SPACE_SIZE); + write.flush(); + }); + space->ReclaimRegions(); + ASSERT(static_cast(write.tellp()) == snapshot_size + sizeof(Header)); + + write.seekp(panda_file_begin); + write.write(reinterpret_cast(pf->GetBase()), pf->GetHeader()->file_size); + write.close(); +} + +std::unique_ptr SnapShot::DeserializeGlobalEnvAndProgram(const CString &fileName) +{ + SnapShotSerialize serialize(vm_, false); + + serialize.GeneratedNativeMethod(); + + std::pair filePath = VerifyFilePath(fileName); + if (!filePath.first) { + LOG(ERROR, RUNTIME) << "snapshot file path error"; + return std::unique_ptr(); + } + + int fd = open(filePath.second.c_str(), O_CLOEXEC); // NOLINT(cppcoreguidelines-pro-type-vararg) + if (UNLIKELY(fd == -1)) { + LOG_ECMA(FATAL) << "open file failed"; + UNREACHABLE(); + } + size_t file_size = lseek(fd, 0, SEEK_END); + auto readFile = ToUintPtr(mmap(nullptr, file_size, PROT_READ, MAP_PRIVATE, fd, 0)); + auto hdr = *ToNativePtr(readFile); + if (hdr.snapshot_size % DEFAULT_SNAPSHOT_SPACE_SIZE != 0) { + LOG_ECMA(FATAL) << "Invalid snapshot file"; + UNREACHABLE(); + } + SnapShotSpace *space = vm_->GetHeap()->GetSnapShotSpace(); + + uintptr_t snapshot_begin = readFile + sizeof(Header); + for (size_t i = 0; i < hdr.snapshot_size / DEFAULT_SNAPSHOT_SPACE_SIZE; i++) { + Region *region = const_cast(vm_->GetHeap()->GetRegionFactory()) + ->AllocateAlignedRegion(space, DEFAULT_SNAPSHOT_SPACE_SIZE); + auto fileRegion = ToNativePtr(snapshot_begin + i * DEFAULT_SNAPSHOT_SPACE_SIZE); + + uint64_t base = region->allocateBase_; + uint64_t begin = (fileRegion->begin_) % DEFAULT_SNAPSHOT_SPACE_SIZE; + uint64_t waterMark = (fileRegion->highWaterMark_) % DEFAULT_SNAPSHOT_SPACE_SIZE; + + if (memcpy_s(region, DEFAULT_SNAPSHOT_SPACE_SIZE, fileRegion, DEFAULT_SNAPSHOT_SPACE_SIZE) != EOK) { + LOG_ECMA(FATAL) << "memcpy_s failed"; + UNREACHABLE(); + } + + // space + region->space_ = space; + // allocate_base_ + region->allocateBase_ = base; + // begin_ + region->begin_ = ToUintPtr(region) + begin; + // end_ + region->end_ = ToUintPtr(region) + DEFAULT_SNAPSHOT_SPACE_SIZE; + // high_water_mark_ + region->highWaterMark_ = ToUintPtr(region) + waterMark; + // prev_ + region->prev_ = nullptr; + // next_ + region->next_ = nullptr; + // mark_bitmap_ + region->markBitmap_ = nullptr; + // cross_region_set_ + region->crossRegionSet_ = nullptr; + // old_to_new_set_ + region->oldToNewSet_ = nullptr; + + space->AddRegion(region); + } + munmap(ToNativePtr(readFile), hdr.panda_file_begin); + uintptr_t panda_file_mem = readFile + hdr.panda_file_begin; + auto pf = + panda_file::File::OpenFromMemory(os::mem::ConstBytePtr(ToNativePtr(panda_file_mem), + file_size - hdr.panda_file_begin, os::mem::MmapDeleter), + fileName); + close(fd); + // redirect object field + serialize.RedirectSlot(pf.get()); + return pf; +} + +size_t SnapShot::AlignUpPageSize(size_t spaceSize) +{ + if (spaceSize % PAGE_SIZE_ALIGN_UP == 0) { + return spaceSize; + } + return PAGE_SIZE_ALIGN_UP * (spaceSize / PAGE_SIZE_ALIGN_UP + 1); +} + +std::pair SnapShot::VerifyFilePath(const CString &filePath) +{ + if (filePath.size() > PATH_MAX) { + return std::make_pair(false, ""); + } + CVector resolvedPath(PATH_MAX); + auto result = realpath(filePath.c_str(), resolvedPath.data()); + if (result == resolvedPath.data() || errno == ENOENT) { + return std::make_pair(true, CString(resolvedPath.data())); + } + return std::make_pair(false, ""); +} +} // namespace panda::ecmascript diff --git a/runtime/snapshot/mem/snapshot.h b/runtime/snapshot/mem/snapshot.h new file mode 100644 index 000000000..9e97403f8 --- /dev/null +++ b/runtime/snapshot/mem/snapshot.h @@ -0,0 +1,55 @@ +/* + * Copyright (c) 2021-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_SNAPSHOT_MEM_SNAPSHOT_H +#define ECMASCRIPT_SNAPSHOT_MEM_SNAPSHOT_H + +#include "libpandabase/macros.h" +#include "libpandafile/file.h" + +#include "plugins/ecmascript/runtime/snapshot/mem/slot_bit.h" +#include "plugins/ecmascript/runtime/mem/c_string.h" + +namespace panda::ecmascript { +class Program; +class EcmaVM; + +class SnapShot final { +public: + explicit SnapShot(EcmaVM *vm) : vm_(vm) {} + ~SnapShot() = default; + + void MakeSnapShotProgramObject(Program *program, const panda_file::File *pf, + const CString &fileName = "./snapshot"); + std::unique_ptr DeserializeGlobalEnvAndProgram( + const CString &fileName = "./snapshot"); + +private: + struct Header { + uint32_t snapshot_size; + uint32_t panda_file_begin; + }; + +private: + size_t AlignUpPageSize(size_t spaceSize); + std::pair VerifyFilePath(const CString &filePath); + + NO_MOVE_SEMANTIC(SnapShot); + NO_COPY_SEMANTIC(SnapShot); + + EcmaVM *vm_; +}; +} // namespace panda::ecmascript +#endif // ECMASCRIPT_SNAPSHOT_MEM_SNAPSHOT_H diff --git a/runtime/snapshot/mem/snapshot_serialize.cpp b/runtime/snapshot/mem/snapshot_serialize.cpp new file mode 100644 index 000000000..30a071b7b --- /dev/null +++ b/runtime/snapshot/mem/snapshot_serialize.cpp @@ -0,0 +1,1268 @@ +/* + * Copyright (c) 2021-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 "plugins/ecmascript/runtime/snapshot/mem/snapshot_serialize.h" + +#include "plugins/ecmascript/runtime/base/error_type.h" +#include "plugins/ecmascript/runtime/builtins/builtins_array.h" +#include "plugins/ecmascript/runtime/builtins/builtins_arraybuffer.h" +#include "plugins/ecmascript/runtime/builtins/builtins_async_from_sync_iterator.h" +#include "plugins/ecmascript/runtime/builtins/builtins_async_function.h" +#include "plugins/ecmascript/runtime/builtins/builtins_async_generator.h" +#include "plugins/ecmascript/runtime/builtins/builtins_async_iterator.h" +#include "plugins/ecmascript/runtime/builtins/builtins_boolean.h" +#include "plugins/ecmascript/runtime/builtins/builtins_collator.h" +#include "plugins/ecmascript/runtime/builtins/builtins_dataview.h" +#include "plugins/ecmascript/runtime/builtins/builtins_date.h" +#include "plugins/ecmascript/runtime/builtins/builtins_date_time_format.h" +#include "plugins/ecmascript/runtime/builtins/builtins_errors.h" +#include "plugins/ecmascript/runtime/builtins/builtins_function.h" +#include "plugins/ecmascript/runtime/builtins/builtins_generator.h" +#include "plugins/ecmascript/runtime/builtins/builtins_global.h" +#include "plugins/ecmascript/runtime/builtins/builtins_intl.h" +#include "plugins/ecmascript/runtime/builtins/builtins_iterator.h" +#include "plugins/ecmascript/runtime/builtins/builtins_json.h" +#include "plugins/ecmascript/runtime/builtins/builtins_locale.h" +#include "plugins/ecmascript/runtime/builtins/builtins_map.h" +#include "plugins/ecmascript/runtime/builtins/builtins_math.h" +#include "plugins/ecmascript/runtime/builtins/builtins_number.h" +#include "plugins/ecmascript/runtime/builtins/builtins_number_format.h" +#include "plugins/ecmascript/runtime/builtins/builtins_object.h" +#include "plugins/ecmascript/runtime/builtins/builtins_plural_rules.h" +#include "plugins/ecmascript/runtime/builtins/builtins_promise.h" +#include "plugins/ecmascript/runtime/builtins/builtins_promise_handler.h" +#include "plugins/ecmascript/runtime/builtins/builtins_promise_job.h" +#include "plugins/ecmascript/runtime/builtins/builtins_proxy.h" +#include "plugins/ecmascript/runtime/builtins/builtins_reflect.h" +#include "plugins/ecmascript/runtime/builtins/builtins_regexp.h" +#include "plugins/ecmascript/runtime/builtins/builtins_relative_time_format.h" +#include "plugins/ecmascript/runtime/builtins/builtins_set.h" +#include "plugins/ecmascript/runtime/builtins/builtins_string.h" +#include "plugins/ecmascript/runtime/builtins/builtins_string_iterator.h" +#include "plugins/ecmascript/runtime/builtins/builtins_symbol.h" +#include "plugins/ecmascript/runtime/builtins/builtins_typedarray.h" +#include "plugins/ecmascript/runtime/builtins/builtins_weak_map.h" +#include "plugins/ecmascript/runtime/builtins/builtins_weak_set.h" +#include "plugins/ecmascript/runtime/class_linker/program_object.h" +#include "plugins/ecmascript/runtime/containers/containers_arraylist.h" +#include "plugins/ecmascript/runtime/global_env.h" +#include "plugins/ecmascript/runtime/js_array_iterator.h" +#include "plugins/ecmascript/runtime/js_for_in_iterator.h" +#include "plugins/ecmascript/runtime/js_hclass.h" +#include "plugins/ecmascript/runtime/js_map_iterator.h" +#include "plugins/ecmascript/runtime/js_set_iterator.h" +#include "plugins/ecmascript/runtime/js_tagged_value-inl.h" +#include "plugins/ecmascript/runtime/mem/heap.h" +#include "plugins/ecmascript/runtime/mem/region_factory.h" +#include "plugins/ecmascript/runtime/mem/space-inl.h" +#include "plugins/ecmascript/runtime/object_factory.h" + +namespace panda::ecmascript { +using Number = builtins::BuiltinsNumber; +using Object = builtins::BuiltinsObject; +using Date = builtins::BuiltinsDate; +using Symbol = builtins::BuiltinsSymbol; +using Boolean = builtins::BuiltinsBoolean; +using BuiltinsMap = builtins::BuiltinsMap; +using BuiltinsSet = builtins::BuiltinsSet; +using BuiltinsWeakMap = builtins::BuiltinsWeakMap; +using BuiltinsWeakSet = builtins::BuiltinsWeakSet; +using BuiltinsArray = builtins::BuiltinsArray; +using BuiltinsTypedArray = builtins::BuiltinsTypedArray; +using BuiltinsIterator = builtins::BuiltinsIterator; +using BuiltinsAsyncIterator = builtins::BuiltinsAsyncIterator; +using Error = builtins::BuiltinsError; +using RangeError = builtins::BuiltinsRangeError; +using ReferenceError = builtins::BuiltinsReferenceError; +using TypeError = builtins::BuiltinsTypeError; +using URIError = builtins::BuiltinsURIError; +using SyntaxError = builtins::BuiltinsSyntaxError; +using EvalError = builtins::BuiltinsEvalError; +using ErrorType = base::ErrorType; +using Global = builtins::BuiltinsGlobal; +using BuiltinsString = builtins::BuiltinsString; +using StringIterator = builtins::BuiltinsStringIterator; +using RegExp = builtins::BuiltinsRegExp; +using Function = builtins::BuiltinsFunction; +using Math = builtins::BuiltinsMath; +using ArrayBuffer = builtins::BuiltinsArrayBuffer; +using Json = builtins::BuiltinsJson; +using Proxy = builtins::BuiltinsProxy; +using Reflect = builtins::BuiltinsReflect; +using AsyncFunction = builtins::BuiltinsAsyncFunction; +using AsyncGeneratorObject = builtins::BuiltinsAsyncGenerator; +using AsyncFromSyncIterator = builtins::BuiltinsAsyncFromSyncIterator; +using GeneratorObject = builtins::BuiltinsGenerator; +using Promise = builtins::BuiltinsPromise; +using BuiltinsPromiseHandler = builtins::BuiltinsPromiseHandler; +using BuiltinsPromiseJob = builtins::BuiltinsPromiseJob; +using ErrorType = base::ErrorType; +using DataView = builtins::BuiltinsDataView; +using Intl = builtins::BuiltinsIntl; +using Locale = builtins::BuiltinsLocale; +using DateTimeFormat = builtins::BuiltinsDateTimeFormat; +using NumberFormat = builtins::BuiltinsNumberFormat; +using RelativeTimeFormat = builtins::BuiltinsRelativeTimeFormat; +using Collator = builtins::BuiltinsCollator; +using PluralRules = builtins::BuiltinsPluralRules; +using ArrayList = containers::ContainersArrayList; + +constexpr int TAGGED_SIZE = JSTaggedValue::TaggedTypeSize(); +constexpr int OBJECT_HEADER_SIZE = TaggedObject::TaggedObjectSize(); +constexpr int METHOD_SIZE = sizeof(JSMethod); + +// NOLINTNEXTLINE(modernize-avoid-c-arrays) +static uintptr_t g_nativeTable[] = { + reinterpret_cast(nullptr), + reinterpret_cast(BuiltinsMap::Species), + reinterpret_cast(StringIterator::Next), + reinterpret_cast(Function::FunctionPrototypeInvokeSelf), + reinterpret_cast(Function::FunctionConstructor), + reinterpret_cast(JSFunction::AccessCallerArgumentsThrowTypeError), + reinterpret_cast(Function::FunctionPrototypeApply), + reinterpret_cast(Function::FunctionPrototypeBind), + reinterpret_cast(Function::FunctionPrototypeCall), + reinterpret_cast(Function::FunctionPrototypeToString), + reinterpret_cast(Object::ObjectConstructor), + reinterpret_cast(Error::ErrorConstructor), + reinterpret_cast(Error::ToString), + reinterpret_cast(RangeError::RangeErrorConstructor), + reinterpret_cast(RangeError::ToString), + reinterpret_cast(ReferenceError::ReferenceErrorConstructor), + reinterpret_cast(ReferenceError::ToString), + reinterpret_cast(TypeError::TypeErrorConstructor), + reinterpret_cast(TypeError::ToString), + reinterpret_cast(TypeError::ThrowTypeError), + reinterpret_cast(URIError::URIErrorConstructor), + reinterpret_cast(URIError::ToString), + reinterpret_cast(SyntaxError::SyntaxErrorConstructor), + reinterpret_cast(SyntaxError::ToString), + reinterpret_cast(EvalError::EvalErrorConstructor), + reinterpret_cast(EvalError::ToString), + reinterpret_cast(Number::NumberConstructor), + reinterpret_cast(Number::ToExponential), + reinterpret_cast(Number::ToFixed), + reinterpret_cast(Number::ToLocaleString), + reinterpret_cast(Number::ToPrecision), + reinterpret_cast(Number::ToString), + reinterpret_cast(Number::ValueOf), + reinterpret_cast(Number::IsFinite), + reinterpret_cast(Number::IsInteger), + reinterpret_cast(Number::IsNaN), + reinterpret_cast(Number::IsSafeInteger), + reinterpret_cast(Number::ParseFloat), + reinterpret_cast(Number::ParseInt), + reinterpret_cast(Symbol::SymbolConstructor), + reinterpret_cast(Symbol::For), + reinterpret_cast(Symbol::KeyFor), + reinterpret_cast(Symbol::DescriptionGetter), + reinterpret_cast(Symbol::ToPrimitive), + reinterpret_cast(Symbol::ToString), + reinterpret_cast(Symbol::ValueOf), + reinterpret_cast(Function::FunctionPrototypeHasInstance), + reinterpret_cast(Date::DateConstructor), + reinterpret_cast(Date::GetDate), + reinterpret_cast(Date::GetDay), + reinterpret_cast(Date::GetFullYear), + reinterpret_cast(Date::GetHours), + reinterpret_cast(Date::GetMilliseconds), + reinterpret_cast(Date::GetMinutes), + reinterpret_cast(Date::GetMonth), + reinterpret_cast(Date::GetSeconds), + reinterpret_cast(Date::GetTime), + reinterpret_cast(Date::GetTimezoneOffset), + reinterpret_cast(Date::GetUTCDate), + reinterpret_cast(Date::GetUTCDay), + reinterpret_cast(Date::GetUTCFullYear), + reinterpret_cast(Date::GetUTCHours), + reinterpret_cast(Date::GetUTCMilliseconds), + reinterpret_cast(Date::GetUTCMinutes), + reinterpret_cast(Date::GetUTCMonth), + reinterpret_cast(Date::GetUTCSeconds), + reinterpret_cast(Date::SetDate), + reinterpret_cast(Date::SetFullYear), + reinterpret_cast(Date::SetHours), + reinterpret_cast(Date::SetMilliseconds), + reinterpret_cast(Date::SetMinutes), + reinterpret_cast(Date::SetMonth), + reinterpret_cast(Date::SetSeconds), + reinterpret_cast(Date::SetTime), + reinterpret_cast(Date::SetUTCDate), + reinterpret_cast(Date::SetUTCFullYear), + reinterpret_cast(Date::SetUTCHours), + reinterpret_cast(Date::SetUTCMilliseconds), + reinterpret_cast(Date::SetUTCMinutes), + reinterpret_cast(Date::SetUTCMonth), + reinterpret_cast(Date::SetUTCSeconds), + reinterpret_cast(Date::ToDateString), + reinterpret_cast(Date::ToISOString), + reinterpret_cast(Date::ToJSON), + reinterpret_cast(Date::ToLocaleDateString), + reinterpret_cast(Date::ToLocaleString), + reinterpret_cast(Date::ToLocaleTimeString), + reinterpret_cast(Date::ToString), + reinterpret_cast(Date::ToTimeString), + reinterpret_cast(Date::ToUTCString), + reinterpret_cast(Date::ValueOf), + reinterpret_cast(Date::ToPrimitive), + reinterpret_cast(Date::Now), + reinterpret_cast(Date::Parse), + reinterpret_cast(Date::UTC), + reinterpret_cast(Object::Assign), + reinterpret_cast(Object::Create), + reinterpret_cast(Object::DefineProperties), + reinterpret_cast(Object::DefineProperty), + reinterpret_cast(Object::Freeze), + reinterpret_cast(Object::GetOwnPropertyDesciptor), + reinterpret_cast(Object::GetOwnPropertyNames), + reinterpret_cast(Object::GetOwnPropertySymbols), + reinterpret_cast(Object::GetPrototypeOf), + reinterpret_cast(Object::Is), + reinterpret_cast(Object::IsExtensible), + reinterpret_cast(Object::IsFrozen), + reinterpret_cast(Object::IsSealed), + reinterpret_cast(Object::Keys), + reinterpret_cast(Object::PreventExtensions), + reinterpret_cast(Object::Seal), + reinterpret_cast(Object::SetPrototypeOf), + reinterpret_cast(Object::HasOwnProperty), + reinterpret_cast(Object::IsPrototypeOf), + reinterpret_cast(Object::PropertyIsEnumerable), + reinterpret_cast(Object::ToLocaleString), + reinterpret_cast(Object::ToString), + reinterpret_cast(Object::ValueOf), + reinterpret_cast(Object::ProtoGetter), + reinterpret_cast(Object::ProtoSetter), + reinterpret_cast(Object::DefineGetter), + reinterpret_cast(Object::DefineSetter), + reinterpret_cast(Object::LookupGetter), + reinterpret_cast(Object::LookupSetter), + reinterpret_cast(Object::CreateRealm), + reinterpret_cast(Object::Entries), + reinterpret_cast(Boolean::BooleanConstructor), + reinterpret_cast(Boolean::BooleanPrototypeToString), + reinterpret_cast(Boolean::BooleanPrototypeValueOf), + reinterpret_cast(RegExp::RegExpConstructor), + reinterpret_cast(RegExp::Exec), + reinterpret_cast(RegExp::Test), + reinterpret_cast(RegExp::ToString), + reinterpret_cast(RegExp::GetFlags), + reinterpret_cast(RegExp::GetSource), + reinterpret_cast(RegExp::GetGlobal), + reinterpret_cast(RegExp::GetIgnoreCase), + reinterpret_cast(RegExp::GetMultiline), + reinterpret_cast(RegExp::GetDotAll), + reinterpret_cast(RegExp::GetSticky), + reinterpret_cast(RegExp::GetUnicode), + reinterpret_cast(RegExp::Split), + reinterpret_cast(RegExp::Search), + reinterpret_cast(RegExp::Match), + reinterpret_cast(RegExp::Replace), + reinterpret_cast(BuiltinsSet::SetConstructor), + reinterpret_cast(BuiltinsSet::Add), + reinterpret_cast(BuiltinsSet::Clear), + reinterpret_cast(BuiltinsSet::Delete), + reinterpret_cast(BuiltinsSet::Has), + reinterpret_cast(BuiltinsSet::ForEach), + reinterpret_cast(BuiltinsSet::Entries), + reinterpret_cast(BuiltinsSet::Values), + reinterpret_cast(BuiltinsSet::GetSize), + reinterpret_cast(BuiltinsSet::Species), + reinterpret_cast(BuiltinsMap::MapConstructor), + reinterpret_cast(BuiltinsMap::Set), + reinterpret_cast(BuiltinsMap::Clear), + reinterpret_cast(BuiltinsMap::Delete), + reinterpret_cast(BuiltinsMap::Has), + reinterpret_cast(BuiltinsMap::Get), + reinterpret_cast(BuiltinsMap::ForEach), + reinterpret_cast(BuiltinsMap::Keys), + reinterpret_cast(BuiltinsMap::Values), + reinterpret_cast(BuiltinsMap::Entries), + reinterpret_cast(BuiltinsMap::GetSize), + reinterpret_cast(BuiltinsWeakMap::WeakMapConstructor), + reinterpret_cast(BuiltinsWeakMap::Set), + reinterpret_cast(BuiltinsWeakMap::Delete), + reinterpret_cast(BuiltinsWeakMap::Has), + reinterpret_cast(BuiltinsWeakMap::Get), + reinterpret_cast(BuiltinsWeakSet::WeakSetConstructor), + reinterpret_cast(BuiltinsWeakSet::Add), + reinterpret_cast(BuiltinsWeakSet::Delete), + reinterpret_cast(BuiltinsWeakSet::Has), + reinterpret_cast(BuiltinsArray::ArrayConstructor), + reinterpret_cast(BuiltinsArray::Concat), + reinterpret_cast(BuiltinsArray::CopyWithin), + reinterpret_cast(BuiltinsArray::Entries), + reinterpret_cast(BuiltinsArray::Every), + reinterpret_cast(BuiltinsArray::Fill), + reinterpret_cast(BuiltinsArray::Filter), + reinterpret_cast(BuiltinsArray::Find), + reinterpret_cast(BuiltinsArray::FindIndex), + reinterpret_cast(BuiltinsArray::ForEach), + reinterpret_cast(BuiltinsArray::IndexOf), + reinterpret_cast(BuiltinsArray::Join), + reinterpret_cast(BuiltinsArray::Keys), + reinterpret_cast(BuiltinsArray::LastIndexOf), + reinterpret_cast(BuiltinsArray::Map), + reinterpret_cast(BuiltinsArray::Pop), + reinterpret_cast(BuiltinsArray::Push), + reinterpret_cast(BuiltinsArray::Reduce), + reinterpret_cast(BuiltinsArray::ReduceRight), + reinterpret_cast(BuiltinsArray::Reverse), + reinterpret_cast(BuiltinsArray::Shift), + reinterpret_cast(BuiltinsArray::Slice), + reinterpret_cast(BuiltinsArray::Some), + reinterpret_cast(BuiltinsArray::Sort), + reinterpret_cast(BuiltinsArray::Splice), + reinterpret_cast(BuiltinsArray::ToLocaleString), + reinterpret_cast(BuiltinsArray::ToString), + reinterpret_cast(BuiltinsArray::Unshift), + reinterpret_cast(BuiltinsArray::Values), + reinterpret_cast(BuiltinsArray::From), + reinterpret_cast(BuiltinsArray::IsArray), + reinterpret_cast(BuiltinsArray::Of), + reinterpret_cast(BuiltinsArray::Species), + reinterpret_cast(BuiltinsArray::Unscopables), + reinterpret_cast(BuiltinsTypedArray::TypedArrayBaseConstructor), + reinterpret_cast(BuiltinsTypedArray::CopyWithin), + reinterpret_cast(BuiltinsTypedArray::Entries), + reinterpret_cast(BuiltinsTypedArray::Every), + reinterpret_cast(BuiltinsTypedArray::Fill), + reinterpret_cast(BuiltinsTypedArray::Filter), + reinterpret_cast(BuiltinsTypedArray::Find), + reinterpret_cast(BuiltinsTypedArray::FindIndex), + reinterpret_cast(BuiltinsTypedArray::ForEach), + reinterpret_cast(BuiltinsTypedArray::IndexOf), + reinterpret_cast(BuiltinsTypedArray::Join), + reinterpret_cast(BuiltinsTypedArray::Keys), + reinterpret_cast(BuiltinsTypedArray::LastIndexOf), + reinterpret_cast(BuiltinsTypedArray::Map), + reinterpret_cast(BuiltinsTypedArray::Reduce), + reinterpret_cast(BuiltinsTypedArray::ReduceRight), + reinterpret_cast(BuiltinsTypedArray::Reverse), + reinterpret_cast(BuiltinsTypedArray::Set), + reinterpret_cast(BuiltinsTypedArray::Slice), + reinterpret_cast(BuiltinsTypedArray::Some), + reinterpret_cast(BuiltinsTypedArray::Sort), + reinterpret_cast(BuiltinsTypedArray::Subarray), + reinterpret_cast(BuiltinsTypedArray::ToLocaleString), + reinterpret_cast(BuiltinsTypedArray::Values), + reinterpret_cast(BuiltinsTypedArray::GetBuffer), + reinterpret_cast(BuiltinsTypedArray::GetByteLength), + reinterpret_cast(BuiltinsTypedArray::GetByteOffset), + reinterpret_cast(BuiltinsTypedArray::GetLength), + reinterpret_cast(BuiltinsTypedArray::ToStringTag), + reinterpret_cast(BuiltinsTypedArray::From), + reinterpret_cast(BuiltinsTypedArray::Of), + reinterpret_cast(BuiltinsTypedArray::Species), + reinterpret_cast(BuiltinsTypedArray::Int8ArrayConstructor), + reinterpret_cast(BuiltinsTypedArray::Uint8ArrayConstructor), + reinterpret_cast(BuiltinsTypedArray::Uint8ClampedArrayConstructor), + reinterpret_cast(BuiltinsTypedArray::Int16ArrayConstructor), + reinterpret_cast(BuiltinsTypedArray::Uint16ArrayConstructor), + reinterpret_cast(BuiltinsTypedArray::Int32ArrayConstructor), + reinterpret_cast(BuiltinsTypedArray::Uint32ArrayConstructor), + reinterpret_cast(BuiltinsTypedArray::Float32ArrayConstructor), + reinterpret_cast(BuiltinsTypedArray::Float64ArrayConstructor), + reinterpret_cast(BuiltinsString::StringConstructor), + reinterpret_cast(BuiltinsString::CharAt), + reinterpret_cast(BuiltinsString::CharCodeAt), + reinterpret_cast(BuiltinsString::CodePointAt), + reinterpret_cast(BuiltinsString::Concat), + reinterpret_cast(BuiltinsString::EndsWith), + reinterpret_cast(BuiltinsString::Includes), + reinterpret_cast(BuiltinsString::IndexOf), + reinterpret_cast(BuiltinsString::LastIndexOf), + reinterpret_cast(BuiltinsString::LocaleCompare), + reinterpret_cast(BuiltinsString::Match), + reinterpret_cast(BuiltinsString::Normalize), + reinterpret_cast(BuiltinsString::Repeat), + reinterpret_cast(BuiltinsString::Replace), + reinterpret_cast(BuiltinsString::Search), + reinterpret_cast(BuiltinsString::Slice), + reinterpret_cast(BuiltinsString::Split), + reinterpret_cast(BuiltinsString::StartsWith), + reinterpret_cast(BuiltinsString::Substring), + reinterpret_cast(BuiltinsString::SubStr), + reinterpret_cast(BuiltinsString::ToLocaleLowerCase), + reinterpret_cast(BuiltinsString::ToLocaleUpperCase), + reinterpret_cast(BuiltinsString::ToLowerCase), + reinterpret_cast(BuiltinsString::ToString), + reinterpret_cast(BuiltinsString::ToUpperCase), + reinterpret_cast(BuiltinsString::Trim), + reinterpret_cast(BuiltinsString::ValueOf), + reinterpret_cast(BuiltinsString::GetStringIterator), + reinterpret_cast(BuiltinsString::FromCharCode), + reinterpret_cast(BuiltinsString::FromCodePoint), + reinterpret_cast(BuiltinsString::Raw), + reinterpret_cast(BuiltinsString::GetLength), + reinterpret_cast(ArrayBuffer::ArrayBufferConstructor), + reinterpret_cast(ArrayBuffer::Slice), + reinterpret_cast(ArrayBuffer::IsView), + reinterpret_cast(ArrayBuffer::Species), + reinterpret_cast(ArrayBuffer::GetByteLength), + reinterpret_cast(DataView::DataViewConstructor), + reinterpret_cast(DataView::GetFloat32), + reinterpret_cast(DataView::GetFloat64), + reinterpret_cast(DataView::GetInt8), + reinterpret_cast(DataView::GetInt16), + reinterpret_cast(DataView::GetInt32), + reinterpret_cast(DataView::GetUint8), + reinterpret_cast(DataView::GetUint16), + reinterpret_cast(DataView::GetUint32), + reinterpret_cast(DataView::SetFloat32), + reinterpret_cast(DataView::SetFloat64), + reinterpret_cast(DataView::SetInt8), + reinterpret_cast(DataView::SetInt16), + reinterpret_cast(DataView::SetInt32), + reinterpret_cast(DataView::SetUint8), + reinterpret_cast(DataView::SetUint16), + reinterpret_cast(DataView::SetUint32), + reinterpret_cast(DataView::GetBuffer), + reinterpret_cast(DataView::GetByteLength), + reinterpret_cast(DataView::GetOffset), + reinterpret_cast(Global::PrintEntrypoint), + reinterpret_cast(Global::NotSupportEval), + reinterpret_cast(Global::IsFinite), + reinterpret_cast(Global::IsNaN), + reinterpret_cast(Global::DecodeURI), + reinterpret_cast(Global::DecodeURIComponent), + reinterpret_cast(Global::EncodeURI), + reinterpret_cast(Global::EncodeURIComponent), + reinterpret_cast(Math::Abs), + reinterpret_cast(Math::Acos), + reinterpret_cast(Math::Acosh), + reinterpret_cast(Math::Asin), + reinterpret_cast(Math::Asinh), + reinterpret_cast(Math::Atan), + reinterpret_cast(Math::Atanh), + reinterpret_cast(Math::Atan2), + reinterpret_cast(Math::Cbrt), + reinterpret_cast(Math::Ceil), + reinterpret_cast(Math::Clz32), + reinterpret_cast(Math::Cos), + reinterpret_cast(Math::Cosh), + reinterpret_cast(Math::Exp), + reinterpret_cast(Math::Expm1), + reinterpret_cast(Math::Floor), + reinterpret_cast(Math::Fround), + reinterpret_cast(Math::Hypot), + reinterpret_cast(Math::Imul), + reinterpret_cast(Math::Log), + reinterpret_cast(Math::Log1p), + reinterpret_cast(Math::Log10), + reinterpret_cast(Math::Log2), + reinterpret_cast(Math::Max), + reinterpret_cast(Math::Min), + reinterpret_cast(Math::Pow), + reinterpret_cast(Math::Random), + reinterpret_cast(Math::Round), + reinterpret_cast(Math::Sign), + reinterpret_cast(Math::Sin), + reinterpret_cast(Math::Sinh), + reinterpret_cast(Math::Sqrt), + reinterpret_cast(Math::Tan), + reinterpret_cast(Math::Tanh), + reinterpret_cast(Math::Trunc), + reinterpret_cast(Json::Parse), + reinterpret_cast(Json::Stringify), + reinterpret_cast(BuiltinsIterator::Next), + reinterpret_cast(BuiltinsIterator::Return), + reinterpret_cast(BuiltinsIterator::Throw), + reinterpret_cast(BuiltinsIterator::GetIteratorObj), + reinterpret_cast(BuiltinsAsyncIterator::GetAsyncIteratorObj), + reinterpret_cast(JSForInIterator::Next), + reinterpret_cast(JSSetIterator::Next), + reinterpret_cast(JSMapIterator::Next), + reinterpret_cast(JSArrayIterator::Next), + reinterpret_cast(Proxy::ProxyConstructor), + reinterpret_cast(Proxy::Revocable), + reinterpret_cast(Reflect::ReflectApply), + reinterpret_cast(Reflect::ReflectConstruct), + reinterpret_cast(Reflect::ReflectDefineProperty), + reinterpret_cast(Reflect::ReflectDeleteProperty), + reinterpret_cast(Reflect::ReflectGet), + reinterpret_cast(Reflect::ReflectGetOwnPropertyDescriptor), + reinterpret_cast(Reflect::ReflectGetPrototypeOf), + reinterpret_cast(Reflect::ReflectHas), + reinterpret_cast(Reflect::ReflectIsExtensible), + reinterpret_cast(Reflect::ReflectOwnKeys), + reinterpret_cast(Reflect::ReflectPreventExtensions), + reinterpret_cast(Reflect::ReflectSet), + reinterpret_cast(Reflect::ReflectSetPrototypeOf), + reinterpret_cast(AsyncFunction::AsyncFunctionConstructor), + reinterpret_cast(AsyncGeneratorObject::AsyncGeneratorPrototypeNext), + reinterpret_cast(AsyncGeneratorObject::AsyncGeneratorPrototypeReturn), + reinterpret_cast(AsyncGeneratorObject::AsyncGeneratorPrototypeThrow), + reinterpret_cast(AsyncGeneratorObject::AsyncGeneratorFunctionConstructor), + reinterpret_cast(AsyncFromSyncIterator::AsyncFromSyncIteratorPrototypeNext), + reinterpret_cast(AsyncFromSyncIterator::AsyncFromSyncIteratorPrototypeReturn), + reinterpret_cast(AsyncFromSyncIterator::AsyncFromSyncIteratorPrototypeThrow), + reinterpret_cast(GeneratorObject::GeneratorPrototypeNext), + reinterpret_cast(GeneratorObject::GeneratorPrototypeReturn), + reinterpret_cast(GeneratorObject::GeneratorPrototypeThrow), + reinterpret_cast(GeneratorObject::GeneratorFunctionConstructor), + reinterpret_cast(Promise::PromiseConstructor), + reinterpret_cast(Promise::All), + reinterpret_cast(Promise::Race), + reinterpret_cast(Promise::Resolve), + reinterpret_cast(Promise::Reject), + reinterpret_cast(Promise::Catch), + reinterpret_cast(Promise::Then), + reinterpret_cast(Promise::GetSpecies), + reinterpret_cast(BuiltinsPromiseJob::PromiseReactionJob), + reinterpret_cast(BuiltinsPromiseJob::PromiseResolveThenableJob), + reinterpret_cast(Intl::GetCanonicalLocales), + reinterpret_cast(Locale::LocaleConstructor), + reinterpret_cast(Locale::Maximize), + reinterpret_cast(Locale::Minimize), + reinterpret_cast(Locale::ToString), + reinterpret_cast(Locale::GetBaseName), + reinterpret_cast(Locale::GetCalendar), + reinterpret_cast(Locale::GetCaseFirst), + reinterpret_cast(Locale::GetCollation), + reinterpret_cast(Locale::GetHourCycle), + reinterpret_cast(Locale::GetNumeric), + reinterpret_cast(Locale::GetNumberingSystem), + reinterpret_cast(Locale::GetLanguage), + reinterpret_cast(Locale::GetScript), + reinterpret_cast(Locale::GetRegion), + reinterpret_cast(DateTimeFormat::DateTimeFormatConstructor), + reinterpret_cast(DateTimeFormat::SupportedLocalesOf), + reinterpret_cast(DateTimeFormat::Format), + reinterpret_cast(DateTimeFormat::FormatToParts), + reinterpret_cast(DateTimeFormat::ResolvedOptions), + reinterpret_cast(DateTimeFormat::FormatRange), + reinterpret_cast(DateTimeFormat::FormatRangeToParts), + reinterpret_cast(NumberFormat::NumberFormatConstructor), + reinterpret_cast(NumberFormat::SupportedLocalesOf), + reinterpret_cast(NumberFormat::Format), + reinterpret_cast(NumberFormat::FormatToParts), + reinterpret_cast(NumberFormat::ResolvedOptions), + reinterpret_cast(NumberFormat::NumberFormatInternalFormatNumber), + reinterpret_cast(RelativeTimeFormat::RelativeTimeFormatConstructor), + reinterpret_cast(RelativeTimeFormat::SupportedLocalesOf), + reinterpret_cast(RelativeTimeFormat::Format), + reinterpret_cast(RelativeTimeFormat::FormatToParts), + reinterpret_cast(RelativeTimeFormat::ResolvedOptions), + reinterpret_cast(Collator::CollatorConstructor), + reinterpret_cast(Collator::SupportedLocalesOf), + reinterpret_cast(Collator::Compare), + reinterpret_cast(Collator::ResolvedOptions), + reinterpret_cast(PluralRules::PluralRulesConstructor), + reinterpret_cast(PluralRules::SupportedLocalesOf), + reinterpret_cast(PluralRules::Select), + reinterpret_cast(PluralRules::ResolvedOptions), + reinterpret_cast(ArrayList::ArrayListConstructor), + reinterpret_cast(ArrayList::Add), + reinterpret_cast(ArrayList::Iterator), + + // not builtins method + reinterpret_cast(JSFunction::PrototypeSetter), + reinterpret_cast(JSFunction::PrototypeGetter), + reinterpret_cast(JSFunction::NameGetter), + reinterpret_cast(JSArray::LengthSetter), + reinterpret_cast(JSArray::LengthGetter), +}; + +SnapShotSerialize::SnapShotSerialize(EcmaVM *vm, bool serialize) : vm_(vm), serialize_(serialize) +{ + objectArraySize_ = OBJECT_SIZE_EXTEND_PAGE; + if (serialize_) { + addressSlot_ = ToUintPtr(vm_->GetRegionFactory()->Allocate(sizeof(uintptr_t) * OBJECT_SIZE_EXTEND_PAGE)); + } else { + addressSlot_ = ToUintPtr(vm_->GetRegionFactory()->Allocate(sizeof(uintptr_t) * objectArraySize_)); + } +} + +SnapShotSerialize::~SnapShotSerialize() +{ + if (serialize_) { + vm_->GetRegionFactory()->Free(ToVoidPtr(addressSlot_), sizeof(uintptr_t) * OBJECT_SIZE_EXTEND_PAGE); + } else { + vm_->GetRegionFactory()->Free(ToVoidPtr(addressSlot_), sizeof(uintptr_t) * objectArraySize_); + } +} + +void SnapShotSerialize::SetObjectSlotField(uintptr_t obj, size_t offset, uint64_t value) +{ + *reinterpret_cast(obj + offset) = value; +} + +void SnapShotSerialize::SetObjectSlotFieldUint32(uintptr_t obj, size_t offset, uint32_t value) +{ + *reinterpret_cast(obj + offset) = value; +} + +void SnapShotSerialize::Serialize(TaggedObject *objectHeader, CQueue *queue, + std::unordered_map *data) +{ + uint8_t objectType = SerializeHelper::GetObjectType(objectHeader); + size_t objectSize = objectHeader->GetClass()->SizeFromJSHClass(objectHeader); + if (objectSize > MAX_REGULAR_HEAP_OBJECT_SIZE) { + LOG_ECMA_MEM(FATAL) << "It is a huge object. Not Support."; + } + + if (objectSize == 0) { + LOG_ECMA_MEM(FATAL) << "It is a zero object. Not Support."; + } + + uintptr_t snapshotObj = vm_->GetFactory()->NewSpaceBySnapShotAllocator(objectSize); + if (snapshotObj == 0) { + LOG_ECMA(DEBUG) << "SnapShotAllocator OOM"; + return; + } + if (memcpy_s(ToVoidPtr(snapshotObj), objectSize, objectHeader, objectSize) != EOK) { + LOG_ECMA(FATAL) << "memcpy_s failed"; + UNREACHABLE(); + } + + // header + SlotBit headSlot = HandleObjectHeader(objectHeader, objectType, objectSize, queue, data); + SetObjectSlotField(snapshotObj, 0, headSlot.GetValue()); + + count_++; + LOG_IF(count_ > MAX_OBJECT_INDEX, FATAL, RUNTIME) << "objectCount: " + ToCString(count_); + LOG_IF(objectSize > MAX_OBJECT_SIZE_INDEX, FATAL, RUNTIME) << "objectSize: " + ToCString(objectSize); + switch (JSType(objectType)) { + case JSType::HCLASS: + DynClassSerialize(objectHeader, snapshotObj, objectSize, queue, data); + break; + case JSType::STRING: + DynStringSerialize(objectHeader, snapshotObj); + break; + case JSType::TAGGED_ARRAY: + case JSType::LINKED_HASH_SET: + case JSType::LINKED_HASH_MAP: + case JSType::TAGGED_DICTIONARY: + DynArraySerialize(objectHeader, snapshotObj, queue, data); + break; + case JSType::JS_NATIVE_POINTER: + NativePointerSerialize(objectHeader, snapshotObj); + break; + case JSType::PROGRAM: + DynProgramSerialize(objectHeader, snapshotObj, queue, data); + break; + case JSType::JS_FUNCTION_BASE: + case JSType::JS_FUNCTION: + case JSType::JS_PROXY_REVOC_FUNCTION: + case JSType::JS_PROMISE_REACTIONS_FUNCTION: + case JSType::JS_PROMISE_EXECUTOR_FUNCTION: + case JSType::JS_PROMISE_ALL_RESOLVE_ELEMENT_FUNCTION: + case JSType::JS_GENERATOR_FUNCTION: + case JSType::JS_ASYNC_FUNCTION: + case JSType::JS_ASYNC_GENERATOR_FUNCTION: + case JSType::JS_ASYNC_AWAIT_STATUS_FUNCTION: + case JSType::JS_ASYNC_GENERATOR_RESOLVE_NEXT_FUNCTION: + case JSType::JS_ASYNC_FROM_SYNC_ITERATOR_VALUE_UNWRAP_FUNCTION: + case JSType::JS_BOUND_FUNCTION: + JSFunctionBaseSerialize(objectHeader, snapshotObj, objectSize, queue, data); + break; + case JSType::JS_PROXY: + JSProxySerialize(objectHeader, snapshotObj, objectSize, queue, data); + break; + default: + JSObjectSerialize(objectHeader, snapshotObj, objectSize, queue, data); + break; + } +} + +void SnapShotSerialize::ExtendObjectArray() +{ + int countNow = objectArraySize_; + objectArraySize_ = objectArraySize_ + OBJECT_SIZE_EXTEND_PAGE; + + auto addr = vm_->GetRegionFactory()->Allocate(sizeof(uintptr_t) * objectArraySize_); + int size = countNow * ADDRESS_SIZE; + if (memcpy_s(addr, size, ToVoidPtr(addressSlot_), size) != EOK) { + LOG_ECMA(FATAL) << "memcpy_s failed"; + UNREACHABLE(); + } + + vm_->GetRegionFactory()->Free(ToVoidPtr(addressSlot_), sizeof(uintptr_t) * objectArraySize_); + addressSlot_ = ToUintPtr(addr); +} + +void SnapShotSerialize::RedirectSlot(const panda_file::File *pf) +{ + SnapShotSpace *space = vm_->GetHeap()->GetSnapShotSpace(); + EcmaStringTable *stringTable = vm_->GetEcmaStringTable(); + + size_t others = 0; + space->EnumerateRegions([stringTable, &others, this, pf](Region *current) { + size_t allocated = current->GetAllocatedBytes(); + uintptr_t begin = current->GetBegin(); + uintptr_t end = begin + allocated; + while (begin < end) { + if (others != 0) { + for (size_t i = 0; i < others; i++) { + pandaMethod_.emplace_back(begin); + auto method = reinterpret_cast(begin); + method->SetPandaFile(pf); + method->SetBytecodeArray(method->GetInstructions()); + vm_->frameworkProgramMethods_.emplace_back(method); + begin += METHOD_SIZE; + if (begin >= end) { + others = others - i - 1; + } + } + break; + } + + if (count_ == objectArraySize_) { + ExtendObjectArray(); + } + SlotBit slot(*reinterpret_cast(begin)); + if (slot.GetObjectType() == MASK_METHOD_SPACE_BEGIN) { + begin += sizeof(uint64_t); + for (size_t i = 0; i < slot.GetObjectSize(); i++) { + pandaMethod_.emplace_back(begin); + auto method = reinterpret_cast(begin); + method->SetPandaFile(pf); + method->SetBytecodeArray(method->GetInstructions()); + vm_->frameworkProgramMethods_.emplace_back(method); + begin += METHOD_SIZE; + if (begin >= end) { + others = slot.GetObjectSize() - i - 1; + break; + } + } + break; + } + + if (JSType(slot.GetObjectType()) == JSType::STRING) { + stringTable->InsertStringIfNotExist(reinterpret_cast(begin)); + } + + SetAddressToSlot(count_, begin); + begin = begin + AlignUp(slot.GetObjectSize(), static_cast(MemAlignment::MEM_ALIGN_OBJECT)); + count_++; + } + }); + + auto constants = const_cast(vm_->GetJSThread()->GlobalConstants()); + for (int i = 0; i < count_; i++) { + SlotBit slot(*GetAddress(i)); + size_t objectSize = slot.GetObjectSize(); + uint8_t objectType = slot.GetObjectType(); + size_t index = slot.GetObjectInConstantsIndex(); + + switch (JSType(objectType)) { + case JSType::HCLASS: + DynClassDeserialize(GetAddress(i)); + break; + case JSType::STRING: + DynStringDeserialize(GetAddress(i)); + break; + case JSType::TAGGED_ARRAY: + case JSType::TAGGED_DICTIONARY: + case JSType::LINKED_HASH_SET: + case JSType::LINKED_HASH_MAP: + DynArrayDeserialize(GetAddress(i)); + break; + case JSType::JS_NATIVE_POINTER: + NativePointerDeserialize(GetAddress(i)); + break; + case JSType::GLOBAL_ENV: + JSObjectDeserialize(GetAddress(i), objectSize); + vm_->SetGlobalEnv(GetAddress(i)); + break; + case JSType::MICRO_JOB_QUEUE: + JSObjectDeserialize(GetAddress(i), objectSize); + vm_->SetMicroJobQueue(GetAddress(i)); + break; + case JSType::PROGRAM: + DynProgramDeserialize(GetAddress(i), objectSize); + vm_->frameworkProgram_ = JSTaggedValue(GetAddress(i)); + break; + case JSType::JS_FUNCTION_BASE: + case JSType::JS_FUNCTION: + case JSType::JS_PROXY_REVOC_FUNCTION: + case JSType::JS_PROMISE_REACTIONS_FUNCTION: + case JSType::JS_PROMISE_EXECUTOR_FUNCTION: + case JSType::JS_PROMISE_ALL_RESOLVE_ELEMENT_FUNCTION: + case JSType::JS_GENERATOR_FUNCTION: + case JSType::JS_ASYNC_FUNCTION: + case JSType::JS_ASYNC_GENERATOR_FUNCTION: + case JSType::JS_ASYNC_AWAIT_STATUS_FUNCTION: + case JSType::JS_ASYNC_GENERATOR_RESOLVE_NEXT_FUNCTION: + case JSType::JS_ASYNC_FROM_SYNC_ITERATOR_VALUE_UNWRAP_FUNCTION: + case JSType::JS_BOUND_FUNCTION: + JSFunctionBaseDeserialize(GetAddress(i), objectSize); + break; + case JSType::JS_PROXY: + JSProxyDeserialize(GetAddress(i), objectSize); + break; + default: + JSObjectDeserialize(GetAddress(i), objectSize); + break; + } + if (index != 0) { + JSTaggedValue result(GetAddress(i)); + constants->SetConstant(ConstantIndex(index - 1), result); + } + } +} + +SlotBit SnapShotSerialize::HandleObjectHeader(TaggedObject *objectHeader, uint8_t objectType, size_t objectSize, + CQueue *queue, + std::unordered_map *data) +{ + auto *hclassClass = objectHeader->GetClass(); + SlotBit slot(0); + + ASSERT(hclassClass != nullptr); + if (data->find(ToUintPtr(hclassClass)) == data->end()) { + slot = SerializeHelper::AddObjectHeaderToData(hclassClass, queue, data); + } else { + slot = data->find(ToUintPtr(hclassClass))->second; + } + + SlotBit objectSlotBit = data->find(ToUintPtr(objectHeader))->second; + slot.SetObjectInConstantsIndex(objectSlotBit.GetObjectInConstantsIndex()); + slot.SetObjectSize(objectSize); + slot.SetObjectType(objectType); + return slot; +} + +uint64_t SnapShotSerialize::HandleTaggedField(JSTaggedType *tagged, CQueue *queue, + std::unordered_map *data) +{ + JSTaggedValue taggedValue(*tagged); + if (taggedValue.IsWeak()) { + return JSTaggedValue::Undefined().GetRawData(); // Undefind + } + + if (taggedValue.IsSpecial()) { + SlotBit special(taggedValue.GetRawData()); + special.SetObjectSpecial(); + return special.GetValue(); // special slot + } + + if (!taggedValue.IsHeapObject()) { + return taggedValue.GetRawData(); // not object + } + + SlotBit slotBit(0); + if (data->find(*tagged) == data->end()) { + slotBit = SerializeHelper::AddObjectHeaderToData(taggedValue.GetTaggedObject(), queue, data); + } else { + slotBit = data->find(taggedValue.GetRawData())->second; + } + + if (taggedValue.IsString()) { + slotBit.SetReferenceToString(true); + } + return slotBit.GetValue(); // object +} + +void SnapShotSerialize::DeserializeHandleTaggedField(uint64_t *value) +{ + SlotBit slot(*value); + if (slot.IsReferenceSlot() && !slot.IsSpecial()) { + uint32_t index = slot.GetObjectIndex(); + + if (slot.IsReferenceToString()) { + auto str = vm_->GetEcmaStringTable()->GetString(GetAddress(index)); + ASSERT(str != nullptr); + *value = ToUintPtr(str); + } else { + *value = GetAddress(index); + } + return; + } + + if (slot.IsSpecial()) { + slot.ClearObjectSpecialFlag(); + *value = slot.GetValue(); + } +} + +void SnapShotSerialize::DeserializeHandleClassWord(TaggedObject *object) +{ + SlotBit slot(*reinterpret_cast(object)); + ASSERT(slot.IsReferenceSlot()); + uint32_t index = slot.GetObjectIndex(); + *reinterpret_cast(object) = 0; + + object->SetClass(GetAddress(index)); +} + +void SnapShotSerialize::DynClassSerialize(TaggedObject *objectHeader, uintptr_t snapshotObj, size_t objectSize, + CQueue *queue, std::unordered_map *data) +{ + size_t beginOffset = JSHClass::PROTOTYPE_OFFSET; + int numOfFields = static_cast((objectSize - beginOffset) / TAGGED_SIZE); + uintptr_t startAddr = ToUintPtr(objectHeader) + beginOffset; + for (int i = 0; i < numOfFields; i++) { + auto fieldAddr = reinterpret_cast(startAddr + i * TAGGED_SIZE); + SetObjectSlotField(snapshotObj, beginOffset + i * TAGGED_SIZE, HandleTaggedField(fieldAddr, queue, data)); + } +} + +void SnapShotSerialize::DynClassDeserialize(uint64_t *objectHeader) +{ + auto dynClass = reinterpret_cast(objectHeader); + // handle object_header + DeserializeHandleClassWord(dynClass); + + uintptr_t startAddr = ToUintPtr(dynClass) + JSHClass::PROTOTYPE_OFFSET; + int numOfFields = static_cast((JSHClass::SIZE - JSHClass::PROTOTYPE_OFFSET) / TAGGED_SIZE); + for (int i = 0; i < numOfFields; i++) { + auto fieldAddr = reinterpret_cast(startAddr + i * TAGGED_SIZE); + DeserializeHandleTaggedField(fieldAddr); + } +} + +void SnapShotSerialize::DynStringSerialize(TaggedObject *objectHeader, uintptr_t snapshotObj) +{ + auto *str = EcmaString::Cast(objectHeader); + SetObjectSlotFieldUint32(snapshotObj, OBJECT_HEADER_SIZE, str->GetLength() << 2U); +} + +void SnapShotSerialize::DynStringDeserialize(uint64_t *objectHeader) +{ + auto object = reinterpret_cast(objectHeader); + // handle object_header + DeserializeHandleClassWord(object); +} + +void SnapShotSerialize::DynArraySerialize(TaggedObject *objectHeader, uintptr_t snapshotObj, + CQueue *queue, std::unordered_map *data) +{ + auto arrayObject = reinterpret_cast(objectHeader); + size_t beginOffset = TaggedArray::DATA_OFFSET; + auto arrayLength = arrayObject->GetLength(); + uintptr_t startAddr = ToUintPtr(objectHeader) + beginOffset; + for (uint32_t i = 0; i < arrayLength; i++) { + auto fieldAddr = reinterpret_cast(startAddr + i * TAGGED_SIZE); + SetObjectSlotField(snapshotObj, beginOffset + i * TAGGED_SIZE, HandleTaggedField(fieldAddr, queue, data)); + } +} + +void SnapShotSerialize::DynArrayDeserialize(uint64_t *objectHeader) +{ + auto object = reinterpret_cast(objectHeader); + // handle object_header + DeserializeHandleClassWord(object); + + auto arrayLength = object->GetLength(); + size_t dataOffset = TaggedArray::DATA_OFFSET; + uintptr_t startAddr = ToUintPtr(objectHeader) + dataOffset; + for (uint32_t i = 0; i < arrayLength; i++) { + auto fieldAddr = reinterpret_cast(startAddr + i * TAGGED_SIZE); + DeserializeHandleTaggedField(fieldAddr); + } +} + +SlotBit SnapShotSerialize::NativePointerToSlotBit(void *nativePointer) +{ + SlotBit native(0); + if (nativePointer != nullptr) { // nativePointer + uint32_t index = MAX_UINT_32; + + if (programSerialize_) { + pandaMethod_.emplace_back(ToUintPtr(nativePointer)); + // NOLINTNEXTLINE(bugprone-narrowing-conversions, cppcoreguidelines-narrowing-conversions) + index = pandaMethod_.size() + PROGRAM_NATIVE_METHOD_BEGIN + NATIVE_METHOD_SIZE - 1; + } else { + for (size_t i = 0; i < PROGRAM_NATIVE_METHOD_BEGIN; i++) { + if (nativePointer == reinterpret_cast(g_nativeTable[i + NATIVE_METHOD_SIZE])) { + index = i + NATIVE_METHOD_SIZE; + break; + } + } + + // not found + if (index == MAX_UINT_32) { + auto nativeMethod = reinterpret_cast(nativePointer)->GetNativePointer(); + for (size_t i = 0; i < NATIVE_METHOD_SIZE; i++) { + if (nativeMethod == GetAddress(i)) { + index = i; + break; + } + } + } + } + + ASSERT(index != MAX_UINT_32); + LOG_IF(index > MAX_C_POINTER_INDEX, FATAL, RUNTIME) << "MAX_C_POINTER_INDEX: " + ToCString(index); + native.SetObjectIndex(index); + } + return native; +} + +void *SnapShotSerialize::NativePointerSlotBitToAddr(SlotBit native) +{ + uint32_t index = native.GetObjectIndex(); + void *addr = nullptr; + + if (index < NATIVE_METHOD_SIZE) { + addr = reinterpret_cast(vm_->nativeMethods_.at(index)); + } else if (index < NATIVE_METHOD_SIZE + PROGRAM_NATIVE_METHOD_BEGIN) { + addr = reinterpret_cast(g_nativeTable[index]); + } else { + addr = ToVoidPtr(pandaMethod_.at(index - PROGRAM_NATIVE_METHOD_BEGIN - NATIVE_METHOD_SIZE)); + } + return addr; +} + +void SnapShotSerialize::NativePointerSerialize(TaggedObject *objectHeader, uintptr_t snapshotObj) +{ + void *nativePointer = JSNativePointer::Cast(objectHeader)->GetExternalPointer(); + SetObjectSlotField(snapshotObj, OBJECT_HEADER_SIZE, NativePointerToSlotBit(nativePointer).GetValue()); +} + +void SnapShotSerialize::NativePointerDeserialize(uint64_t *objectHeader) +{ + auto object = reinterpret_cast(objectHeader); + // handle object_header + DeserializeHandleClassWord(object); + + size_t nativeAddr = ToUintPtr(object) + OBJECT_HEADER_SIZE; + SlotBit native(*reinterpret_cast(nativeAddr)); + if (native.GetObjectIndex() == MAX_OBJECT_INDEX) { + return; + } + JSNativePointer::Cast(object)->ResetExternalPointer(NativePointerSlotBitToAddr(native)); +} + +void SnapShotSerialize::JSObjectSerialize(TaggedObject *objectHeader, uintptr_t snapshotObj, size_t objectSize, + CQueue *queue, std::unordered_map *data) +{ + int numOfFields = static_cast((objectSize - OBJECT_HEADER_SIZE) / TAGGED_SIZE); + uintptr_t startAddr = ToUintPtr(objectHeader) + OBJECT_HEADER_SIZE; + for (int i = 0; i < numOfFields; i++) { + auto fieldAddr = reinterpret_cast(startAddr + i * TAGGED_SIZE); + SetObjectSlotField(snapshotObj, OBJECT_HEADER_SIZE + i * TAGGED_SIZE, + HandleTaggedField(fieldAddr, queue, data)); + } +} + +void SnapShotSerialize::JSFunctionBaseSerialize(TaggedObject *objectHeader, uintptr_t snapshotObj, size_t objectSize, + CQueue *queue, + std::unordered_map *data) +{ + // befour + int befourFields = static_cast((JSFunctionBase::METHOD_OFFSET - OBJECT_HEADER_SIZE) / TAGGED_SIZE); + uintptr_t befourStartAddr = ToUintPtr(objectHeader) + OBJECT_HEADER_SIZE; + for (int i = 0; i < befourFields; i++) { + auto fieldAddr = reinterpret_cast(befourStartAddr + i * TAGGED_SIZE); + SetObjectSlotField(snapshotObj, OBJECT_HEADER_SIZE + i * TAGGED_SIZE, + HandleTaggedField(fieldAddr, queue, data)); + } + + // method + auto functionBase = static_cast(objectHeader); + size_t methodOffset = functionBase->OffsetMethod(); + auto nativePointer = reinterpret_cast(functionBase->GetMethod()); + SetObjectSlotField(snapshotObj, methodOffset, NativePointerToSlotBit(nativePointer).GetValue()); + + // after + size_t afterOffset = JSFunctionBase::METHOD_OFFSET + TAGGED_SIZE; + int afterFields = static_cast((objectSize - afterOffset) / TAGGED_SIZE); + uintptr_t afterStartAddr = ToUintPtr(objectHeader) + JSFunctionBase::METHOD_OFFSET + TAGGED_SIZE; + for (int i = 0; i < afterFields; i++) { + auto fieldAddr = reinterpret_cast(afterStartAddr + i * TAGGED_SIZE); + SetObjectSlotField(snapshotObj, afterOffset + i * TAGGED_SIZE, HandleTaggedField(fieldAddr, queue, data)); + } +} + +void SnapShotSerialize::JSProxySerialize(TaggedObject *objectHeader, uintptr_t snapshotObj, size_t objectSize, + CQueue *queue, std::unordered_map *data) +{ + // befour + int befourFields = static_cast((JSProxy::METHOD_OFFSET - OBJECT_HEADER_SIZE) / TAGGED_SIZE); + uintptr_t befourStartAddr = ToUintPtr(objectHeader) + OBJECT_HEADER_SIZE; + for (int i = 0; i < befourFields; i++) { + auto fieldAddr = reinterpret_cast(befourStartAddr + i * TAGGED_SIZE); + SetObjectSlotField(snapshotObj, OBJECT_HEADER_SIZE + i * TAGGED_SIZE, + HandleTaggedField(fieldAddr, queue, data)); + } + + // method + auto jsproxy = static_cast(objectHeader); + size_t methodOffset = jsproxy->OffsetMethod(); + auto nativePointer = reinterpret_cast(jsproxy->GetMethod()); + SetObjectSlotField(snapshotObj, methodOffset, NativePointerToSlotBit(nativePointer).GetValue()); + + // after + size_t afterOffset = JSProxy::METHOD_OFFSET + TAGGED_SIZE; + int afterFields = static_cast((objectSize - afterOffset) / TAGGED_SIZE); + uintptr_t afterStartAddr = ToUintPtr(objectHeader) + JSProxy::METHOD_OFFSET + TAGGED_SIZE; + for (int i = 0; i < afterFields; i++) { + auto fieldAddr = reinterpret_cast(afterStartAddr + i * TAGGED_SIZE); + SetObjectSlotField(snapshotObj, afterOffset + i * TAGGED_SIZE, HandleTaggedField(fieldAddr, queue, data)); + } +} + +void SnapShotSerialize::DeserializeRangeTaggedField(size_t beginAddr, int numOfFields) +{ + for (int i = 0; i < numOfFields; i++) { + auto fieldAddr = reinterpret_cast(beginAddr + i * TAGGED_SIZE); + DeserializeHandleTaggedField(fieldAddr); + } +} + +void SnapShotSerialize::JSObjectDeserialize(uint64_t *objectHeader, size_t objectSize) +{ + auto object = reinterpret_cast(objectHeader); + // handle object_header + DeserializeHandleClassWord(object); + + auto objBodySize = objectSize - OBJECT_HEADER_SIZE; + ASSERT(objBodySize % TAGGED_SIZE == 0); + int numOfFields = static_cast(objBodySize / TAGGED_SIZE); + size_t addr = ToUintPtr(objectHeader) + OBJECT_HEADER_SIZE; + DeserializeRangeTaggedField(addr, numOfFields); +} + +void SnapShotSerialize::JSFunctionBaseDeserialize(uint64_t *objectHeader, size_t objectSize) +{ + auto object = reinterpret_cast(objectHeader); + DeserializeHandleClassWord(object); + + // befour + auto befourMethod = JSFunctionBase::METHOD_OFFSET - OBJECT_HEADER_SIZE; + ASSERT(befourMethod % TAGGED_SIZE == 0); + int befourMethodFields = static_cast(befourMethod / TAGGED_SIZE); + size_t befourAddr = ToUintPtr(objectHeader) + OBJECT_HEADER_SIZE; + DeserializeRangeTaggedField(befourAddr, befourMethodFields); + + // method + size_t nativeAddr = ToUintPtr(object) + JSFunctionBase::METHOD_OFFSET; + SlotBit native(*reinterpret_cast(nativeAddr)); + if (native.GetObjectIndex() != MAX_OBJECT_INDEX) { + object->SetMethod(reinterpret_cast(NativePointerSlotBitToAddr(native))); + } + + // after + auto afterMethod = objectSize - JSFunctionBase::METHOD_OFFSET - TAGGED_SIZE; + ASSERT(afterMethod % TAGGED_SIZE == 0); + int afterMethodFields = static_cast(afterMethod / TAGGED_SIZE); + size_t afterAddr = ToUintPtr(objectHeader) + JSFunctionBase::METHOD_OFFSET + TAGGED_SIZE; + DeserializeRangeTaggedField(afterAddr, afterMethodFields); +} + +void SnapShotSerialize::JSProxyDeserialize(uint64_t *objectHeader, size_t objectSize) +{ + auto object = reinterpret_cast(objectHeader); + DeserializeHandleClassWord(object); + + // befour + auto befourMethod = JSProxy::METHOD_OFFSET - OBJECT_HEADER_SIZE; + ASSERT(befourMethod % TAGGED_SIZE == 0); + int befourMethodFields = static_cast(befourMethod / TAGGED_SIZE); + size_t befourAddr = ToUintPtr(objectHeader) + OBJECT_HEADER_SIZE; + DeserializeRangeTaggedField(befourAddr, befourMethodFields); + + // method + size_t nativeAddr = ToUintPtr(object) + JSProxy::METHOD_OFFSET; + SlotBit native(*reinterpret_cast(nativeAddr)); + if (native.GetObjectIndex() != MAX_OBJECT_INDEX) { + object->SetMethod(reinterpret_cast(NativePointerSlotBitToAddr(native))); + } + + // after + auto afterMethod = objectSize - JSProxy::METHOD_OFFSET - TAGGED_SIZE; + ASSERT(afterMethod % TAGGED_SIZE == 0); + int afterMethodFields = static_cast(afterMethod / TAGGED_SIZE); + size_t afterAddr = ToUintPtr(objectHeader) + JSProxy::METHOD_OFFSET + TAGGED_SIZE; + DeserializeRangeTaggedField(afterAddr, afterMethodFields); +} + +void SnapShotSerialize::DynProgramSerialize(TaggedObject *objectHeader, uintptr_t snapshotObj, + CQueue *queue, + std::unordered_map *data) +{ + size_t beginOffset = OBJECT_HEADER_SIZE; + auto objBodySize = Program::METHODS_DATA_OFFSET - OBJECT_HEADER_SIZE; + int numOfFields = static_cast((objBodySize) / TAGGED_SIZE); + uintptr_t startAddr = ToUintPtr(objectHeader) + beginOffset; + for (int i = 0; i < numOfFields; i++) { + auto fieldAddr = reinterpret_cast(startAddr + i * TAGGED_SIZE); + SetObjectSlotField(snapshotObj, beginOffset + i * TAGGED_SIZE, HandleTaggedField(fieldAddr, queue, data)); + } + SetObjectSlotField(snapshotObj, Program::METHODS_DATA_OFFSET, 0); // methods + SetObjectSlotField(snapshotObj, Program::NUMBER_METHODS_OFFSET, 0); // method_number +} + +void SnapShotSerialize::DynProgramDeserialize(uint64_t *objectHeader, [[maybe_unused]] size_t objectSize) +{ + auto object = reinterpret_cast(objectHeader); + // handle object_header + DeserializeHandleClassWord(object); + + auto objBodySize = Program::METHODS_DATA_OFFSET - OBJECT_HEADER_SIZE; + ASSERT(objBodySize % TAGGED_SIZE == 0); + int numOfFields = static_cast(objBodySize / TAGGED_SIZE); + size_t addr = ToUintPtr(objectHeader) + OBJECT_HEADER_SIZE; + for (int i = 0; i < numOfFields; i++) { + auto fieldAddr = reinterpret_cast(addr + i * TAGGED_SIZE); + DeserializeHandleTaggedField(fieldAddr); + } + + auto program = reinterpret_cast(objectHeader); + program->SetMethodsData(nullptr); + program->SetNumberMethods(0); +} + +void SnapShotSerialize::SerializePandaFileMethod() +{ + SlotBit slot(0); + slot.SetObjectType(MASK_METHOD_SPACE_BEGIN); + slot.SetObjectSize(pandaMethod_.size()); + + ObjectFactory *factory = vm_->GetFactory(); + // panda method space begin + uintptr_t snapshotObj = factory->NewSpaceBySnapShotAllocator(sizeof(uint64_t)); + if (snapshotObj == 0) { + LOG(ERROR, RUNTIME) << "SnapShotAllocator OOM"; + return; + } + SetObjectSlotField(snapshotObj, 0, slot.GetValue()); // methods + + // panda methods + for (auto &it : pandaMethod_) { + // write method + size_t methodObjSize = METHOD_SIZE; + uintptr_t methodObj = factory->NewSpaceBySnapShotAllocator(methodObjSize); + if (methodObj == 0) { + LOG(ERROR, RUNTIME) << "SnapShotAllocator OOM"; + return; + } + if (memcpy_s(ToVoidPtr(methodObj), methodObjSize, ToVoidPtr(it), METHOD_SIZE) != EOK) { + LOG_ECMA(FATAL) << "memcpy_s failed"; + UNREACHABLE(); + } + } +} + +void SnapShotSerialize::RegisterNativeMethod() // NOLINT(readability-function-size) +{ + constexpr int size = sizeof(g_nativeTable) / sizeof(uintptr_t); + ASSERT(size == NATIVE_METHOD_SIZE + PROGRAM_NATIVE_METHOD_BEGIN); + for (int i = 0; i < size; i++) { + SetAddressToSlot(i, g_nativeTable[i]); + } +} + +void SnapShotSerialize::GeneratedNativeMethod() // NOLINT(readability-function-size) +{ + for (int i = 0; i < NATIVE_METHOD_SIZE; i++) { + SetAddressToSlot(i, g_nativeTable[i]); + vm_->GetMethodForNativeFunction(reinterpret_cast(g_nativeTable[i])); + } +} +} // namespace panda::ecmascript diff --git a/runtime/snapshot/mem/snapshot_serialize.h b/runtime/snapshot/mem/snapshot_serialize.h new file mode 100644 index 000000000..62d96df50 --- /dev/null +++ b/runtime/snapshot/mem/snapshot_serialize.h @@ -0,0 +1,119 @@ +/* + * Copyright (c) 2021-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_SNAPSHOT_MEM_SNAPSHOT_SERIALIZE_H +#define ECMASCRIPT_SNAPSHOT_MEM_SNAPSHOT_SERIALIZE_H + +#include +#include +#include + +#include "libpandabase/macros.h" +#include "plugins/ecmascript/runtime/snapshot/mem/slot_bit.h" +#include "plugins/ecmascript/runtime/js_tagged_value.h" + +namespace panda::ecmascript { +class EcmaVM; + +class SnapShotSerialize final { +public: + explicit SnapShotSerialize(EcmaVM *vm, bool serialize); + ~SnapShotSerialize(); + + void Serialize(TaggedObject *objectHeader, CQueue *queue, + std::unordered_map *data); + void RedirectSlot(const panda_file::File *pf); + void SerializePandaFileMethod(); + + void SetProgramSerializeStart() + { + programSerialize_ = true; + } + + void RegisterNativeMethod(); + void GeneratedNativeMethod(); + +private: + void SetObjectSlotField(uintptr_t obj, size_t offset, uint64_t value); + void SetObjectSlotFieldUint32(uintptr_t obj, size_t offset, uint32_t value); + + void NativePointerSerialize(TaggedObject *objectHeader, uintptr_t snapshotObj); + void JSObjectSerialize(TaggedObject *objectHeader, uintptr_t snapshotObj, size_t objectSize, + CQueue *queue, + std::unordered_map *data); + void JSFunctionBaseSerialize(TaggedObject *objectHeader, uintptr_t snapshotObj, size_t objectSize, + CQueue *queue, + std::unordered_map *data); + void JSProxySerialize(TaggedObject *objectHeader, uintptr_t snapshotObj, size_t objectSize, + CQueue *queue, + std::unordered_map *data); + void DynClassSerialize(TaggedObject *objectHeader, uintptr_t snapshotObj, size_t objectSize, + CQueue *queue, + std::unordered_map *data); + void DynArraySerialize(TaggedObject *objectHeader, uintptr_t snapshotObj, CQueue *queue, + std::unordered_map *data); + void DynStringSerialize(TaggedObject *objectHeader, uintptr_t snapshotObj); + void DynProgramSerialize(TaggedObject *objectHeader, uintptr_t snapshotObj, CQueue *queue, + std::unordered_map *data); + + void NativePointerDeserialize(uint64_t *objectHeader); + void JSObjectDeserialize(uint64_t *objectHeader, size_t objectSize); + void JSFunctionBaseDeserialize(uint64_t *objectHeader, size_t objectSize); + void JSProxyDeserialize(uint64_t *objectHeader, size_t objectSize); + void DynClassDeserialize(uint64_t *objectHeader); + void DynStringDeserialize(uint64_t *objectHeader); + void DynArrayDeserialize(uint64_t *objectHeader); + void DynProgramDeserialize(uint64_t *objectHeader, size_t objectSize); + + SlotBit HandleObjectHeader(TaggedObject *objectHeader, uint8_t objectType, size_t objectSize, + CQueue *queue, + std::unordered_map *data); + uint64_t HandleTaggedField(JSTaggedType *tagged, CQueue *queue, + std::unordered_map *data); + void DeserializeHandleTaggedField(uint64_t *value); + void DeserializeHandleClassWord(TaggedObject *object); + void ExtendObjectArray(); + + void SetAddressToSlot(size_t index, uintptr_t value) + { + auto addr = reinterpret_cast(addressSlot_ + index * ADDRESS_SIZE); + // NOLINTNEXTLINE(clang-analyzer-core.CallAndMessage) + *addr = value; + } + + template + T GetAddress(size_t index) + { + return *reinterpret_cast(addressSlot_ + index * ADDRESS_SIZE); + } + + SlotBit NativePointerToSlotBit(void *nativePointer); + void *NativePointerSlotBitToAddr(SlotBit native); + void DeserializeRangeTaggedField(size_t beginAddr, int numOfFields); + + EcmaVM *vm_{nullptr}; + bool serialize_{false}; + bool programSerialize_{false}; + int count_{0}; + int objectArraySize_{0}; + uintptr_t addressSlot_; + CVector pandaMethod_; + + NO_COPY_SEMANTIC(SnapShotSerialize); + NO_MOVE_SEMANTIC(SnapShotSerialize); +}; +} // namespace panda::ecmascript + +#endif // ECMASCRIPT_SNAPSHOT_MEM_SNAPSHOT_SERIALIZE_H diff --git a/runtime/symbol_table-inl.h b/runtime/symbol_table-inl.h new file mode 100644 index 000000000..1160e8e0d --- /dev/null +++ b/runtime/symbol_table-inl.h @@ -0,0 +1,73 @@ +/* + * Copyright (c) 2021-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_SYMBOL_TABLE_INL_H +#define ECMASCRIPT_SYMBOL_TABLE_INL_H + +#include "symbol_table.h" +#include "js_symbol.h" +#include "tagged_hash_table-inl.h" +#include "libpandabase/utils/hash.h" + +namespace panda::ecmascript { +uint32_t SymbolTable::Hash(const JSTaggedValue &obj) +{ + if (obj.IsHeapObject()) { + if (obj.IsString()) { + auto *nameString = static_cast(obj.GetTaggedObject()); + return nameString->GetHashcode(); + } + return JSSymbol::ComputeHash(); + } + UNREACHABLE(); +} + +bool SymbolTable::IsMatch(const JSTaggedValue &name, const JSTaggedValue &other) +{ + if (name.IsHole() || name.IsUndefined()) { + return false; + } + + auto *nameString = static_cast(name.GetTaggedObject()); + auto *otherString = static_cast(other.GetTaggedObject()); + return EcmaString::StringsAreEqual(nameString, otherString); +} + +bool SymbolTable::ContainsKey([[maybe_unused]] JSThread *thread, const JSTaggedValue &key) +{ + int entry = FindEntry(key); + return entry != -1; +} + +JSTaggedValue SymbolTable::GetSymbol(const JSTaggedValue &key) +{ + int entry = FindEntry(key); + ASSERT(entry != -1); + return GetValue(entry); +} + +JSTaggedValue SymbolTable::FindSymbol(JSThread *thread, const JSTaggedValue &value) +{ + JSSymbol *symbol = JSSymbol::Cast(value.GetTaggedObject()); + JSTaggedValue des = symbol->GetDescription(); + if (!des.IsUndefined()) { + if (ContainsKey(thread, des)) { + return des; + } + } + return JSTaggedValue::Undefined(); +} +} // namespace panda::ecmascript +#endif // ECMASCRIPT_SYMBOL_TABLE_INL_H diff --git a/runtime/symbol_table.h b/runtime/symbol_table.h new file mode 100644 index 000000000..0b36377ee --- /dev/null +++ b/runtime/symbol_table.h @@ -0,0 +1,66 @@ +/* + * Copyright (c) 2021-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_SYMBOL_TABLE_H +#define ECMASCRIPT_SYMBOL_TABLE_H + +#include "plugins/ecmascript/runtime/tagged_hash_table.h" +#include "plugins/ecmascript/runtime/ecma_string.h" +#include "plugins/ecmascript/runtime/js_thread.h" + +namespace panda::ecmascript { +class SymbolTable : public TaggedHashTable { +public: + using HashTable = TaggedHashTable; + static SymbolTable *Cast(ObjectHeader *object) + { + return reinterpret_cast(object); + } + inline static int GetKeyIndex(int entry) + { + return HashTable::TABLE_HEADER_SIZE + entry * GetEntrySize() + ENTRY_KEY_INDEX; + } + inline static int GetValueIndex(int entry) + { + return HashTable::TABLE_HEADER_SIZE + entry * GetEntrySize() + ENTRY_VALUE_INDEX; + } + inline static int GetEntryIndex(int entry) + { + return HashTable::TABLE_HEADER_SIZE + entry * GetEntrySize(); + } + inline static int GetEntrySize() + { + return ENTRY_SIZE; + } + static inline bool IsMatch(const JSTaggedValue &name, const JSTaggedValue &other); + static inline uint32_t Hash(const JSTaggedValue &obj); + + static const int DEFAULT_ELEMENTS_NUMBER = 64; + static JSHandle Create(JSThread *thread, int numberOfElements = DEFAULT_ELEMENTS_NUMBER) + { + return HashTable::Create(thread, numberOfElements); + } + + inline bool ContainsKey(JSThread *thread, const JSTaggedValue &key); + + inline JSTaggedValue GetSymbol(const JSTaggedValue &key); + + inline JSTaggedValue FindSymbol(JSThread *thread, const JSTaggedValue &value); + static constexpr int ENTRY_KEY_INDEX = 0; + static constexpr int ENTRY_VALUE_INDEX = 1; + static constexpr int ENTRY_SIZE = 2; +}; +} // namespace panda::ecmascript +#endif // ECMASCRIPT_SYMBOL_TABLE_H diff --git a/runtime/tagged_array-inl.h b/runtime/tagged_array-inl.h new file mode 100644 index 000000000..00217f8ee --- /dev/null +++ b/runtime/tagged_array-inl.h @@ -0,0 +1,164 @@ +/* + * Copyright (c) 2021-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_TAGGED_ARRAY_INL_H +#define ECMASCRIPT_TAGGED_ARRAY_INL_H + +#include "plugins/ecmascript/runtime/js_tagged_value-inl.h" +#include "plugins/ecmascript/runtime/js_thread.h" +#include "plugins/ecmascript/runtime/tagged_array.h" + +namespace panda::ecmascript { +inline JSTaggedValue TaggedArray::Get(uint32_t idx) const +{ + ASSERT(idx < GetLength()); + // Note: Here we can't statically decide the element type is a primitive or heap object, especially for + // dynamically-typed languages like JavaScript. So we simply skip the read-barrier. + size_t offset = JSTaggedValue::TaggedTypeSize() * idx; + // NOLINTNEXTLINE(readability-braces-around-statements, bugprone-suspicious-semicolon) + return JSTaggedValue(Barriers::GetDynValue(this, DATA_OFFSET + offset)); +} + +inline JSTaggedValue TaggedArray::Get([[maybe_unused]] const JSThread *thread, uint32_t idx) const +{ + return Get(idx); +} + +inline uint32_t TaggedArray::GetIdx(const JSTaggedValue &value) const +{ + uint32_t length = GetLength(); + + for (uint32_t i = 0; i < length; i++) { + if (JSTaggedValue::SameValue(Get(i), value)) { + return i; + } + } + return TaggedArray::MAX_ARRAY_INDEX; +} + +template +inline void TaggedArray::Set(const JSThread *thread, uint32_t idx, const JSHandle &value) +{ + ASSERT(idx < GetLength()); + size_t offset = JSTaggedValue::TaggedTypeSize() * idx; + + ObjectAccessor::SetDynValue(thread, this, DATA_OFFSET + offset, value.GetTaggedValue().GetRawData()); +} + +inline void TaggedArray::Set(const JSThread *thread, uint32_t idx, const JSTaggedValue &value) +{ + ASSERT(idx < GetLength()); + size_t offset = JSTaggedValue::TaggedTypeSize() * idx; + + ObjectAccessor::SetDynValue(thread, this, DATA_OFFSET + offset, value.GetRawData()); +} + +JSHandle TaggedArray::Append(const JSThread *thread, const JSHandle &first, + const JSHandle &second) +{ + uint32_t firstLength = first->GetLength(); + uint32_t secondLength = second->GetLength(); + uint32_t length = firstLength + secondLength; + ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); + JSHandle argument = factory->NewTaggedArray(length); + uint32_t index = 0; + for (; index < firstLength; ++index) { + argument->Set(thread, index, first->Get(index)); + } + for (; index < length; ++index) { + argument->Set(thread, index, second->Get(index - firstLength)); + } + return argument; +} + +JSHandle TaggedArray::AppendSkipHole(const JSThread *thread, const JSHandle &first, + const JSHandle &second, uint32_t copyLength) +{ + uint32_t firstLength = first->GetLength(); + uint32_t secondLength = second->GetLength(); + ASSERT(firstLength + secondLength >= copyLength); + + ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); + JSHandle argument = factory->NewTaggedArray(copyLength); + uint32_t index = 0; + for (; index < firstLength; ++index) { + JSTaggedValue val = first->Get(index); + if (val.IsHole()) { + break; + } + argument->Set(thread, index, val); + ASSERT(copyLength >= index); + } + for (uint32_t i = 0; i < secondLength; ++i) { + JSTaggedValue val = second->Get(i); + if (val.IsHole()) { + break; + } + argument->Set(thread, index++, val); + ASSERT(copyLength >= index); + } + return argument; +} + +inline bool TaggedArray::HasDuplicateEntry() const +{ + uint32_t length = GetLength(); + for (uint32_t i = 0; i < length; i++) { + for (uint32_t j = i + 1; j < length; j++) { + if (JSTaggedValue::SameValue(Get(i), Get(j))) { + return true; + } + } + } + return false; +} + +void TaggedArray::InitializeWithSpecialValue(JSTaggedValue initValue, uint32_t length) +{ + ASSERT(initValue.IsSpecial()); + SetLength(length); + for (uint32_t i = 0; i < length; i++) { + size_t offset = JSTaggedValue::TaggedTypeSize() * i; + // Without barrier because 'this' is just allocated object and it doesn't contains references. + ObjectAccessor::SetDynValueWithoutBarrier(this, DATA_OFFSET + offset, initValue.GetRawData()); + } +} + +inline JSHandle TaggedArray::SetCapacity(const JSThread *thread, const JSHandle &array, + uint32_t capa) +{ + ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); + uint32_t oldLength = array->GetLength(); + JSHandle newArray = factory->CopyArray(array, oldLength, capa); + return newArray; +} + +inline bool TaggedArray::IsDictionaryMode() const +{ + return GetClass()->IsDictionary(); +} + +void TaggedArray::Trim(JSThread *thread, uint32_t newLength) +{ + ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); + uint32_t oldLength = GetLength(); + ASSERT(oldLength > newLength); + size_t trimBytes = (oldLength - newLength) * JSTaggedValue::TaggedTypeSize(); + size_t size = TaggedArray::ComputeSize(JSTaggedValue::TaggedTypeSize(), newLength); + factory->FillFreeObject(ToUintPtr(this) + size, trimBytes, RemoveSlots::YES); + SetLength(newLength); +} +} // namespace panda::ecmascript +#endif // ECMASCRIPT_TAGGED_ARRAY_INL_H diff --git a/runtime/tagged_array.h b/runtime/tagged_array.h new file mode 100644 index 000000000..c1f2761f3 --- /dev/null +++ b/runtime/tagged_array.h @@ -0,0 +1,99 @@ +/* + * Copyright (c) 2021-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_TAGGED_ARRAY_H +#define ECMASCRIPT_TAGGED_ARRAY_H + +#include "plugins/ecmascript/runtime/js_hclass.h" +#include "plugins/ecmascript/runtime/js_tagged_value.h" +#include "plugins/ecmascript/runtime/js_thread.h" + +namespace panda::ecmascript { +class ObjectFactory; + +class TaggedArray : public TaggedObject { +public: + static constexpr uint32_t MAX_ARRAY_INDEX = std::numeric_limits::max(); + static constexpr uint32_t MAX_END_UNUSED = 4; + + inline static TaggedArray *Cast(ObjectHeader *obj) + { + ASSERT(JSTaggedValue(obj).IsTaggedArray()); + return static_cast(obj); + } + + JSTaggedValue Get(uint32_t idx) const; + + uint32_t GetIdx(const JSTaggedValue &value) const; + + template + void Set(const JSThread *thread, uint32_t idx, const JSHandle &value); + + JSTaggedValue Get(const JSThread *thread, uint32_t idx) const; + + void Set(const JSThread *thread, uint32_t idx, const JSTaggedValue &value); + + static inline JSHandle Append(const JSThread *thread, const JSHandle &first, + const JSHandle &second); + static inline JSHandle AppendSkipHole(const JSThread *thread, const JSHandle &first, + const JSHandle &second, uint32_t copyLength); + + static inline size_t ComputeSize(size_t elemSize, uint32_t length) + { + ASSERT(elemSize != 0); + size_t size = DATA_OFFSET + elemSize * length; +#ifdef PANDA_TARGET_32 + size_t sizeLimit = (std::numeric_limits::max() - DATA_OFFSET) / elemSize; + if (UNLIKELY(sizeLimit < static_cast(length))) { + return 0; + } +#endif + return size; + } + + inline JSTaggedType *GetData() const + { + return reinterpret_cast(ToUintPtr(this) + DATA_OFFSET); + } + + inline bool IsDictionaryMode() const; + + bool HasDuplicateEntry() const; + + static JSHandle SetCapacity(const JSThread *thread, const JSHandle &array, + uint32_t capa); + + inline void InitializeWithSpecialValue(JSTaggedValue initValue, uint32_t length); + + static inline bool ShouldTrim([[maybe_unused]] JSThread *thread, uint32_t oldLength, uint32_t newLength) + { + return (oldLength - newLength > MAX_END_UNUSED); + } + inline void Trim(JSThread *thread, uint32_t newLength); + + static constexpr size_t LENGTH_OFFSET = TaggedObjectSize(); + SET_GET_PRIMITIVE_FIELD(Length, uint32_t, LENGTH_OFFSET, DATA_OFFSET); + static constexpr size_t SIZE = DATA_OFFSET; // Empty Array size + + DECL_VISIT_ARRAY(DATA_OFFSET, GetLength()); + +private: + friend class ObjectFactory; +}; + +static_assert(TaggedArray::LENGTH_OFFSET == sizeof(TaggedObject)); +static_assert((TaggedArray::DATA_OFFSET % static_cast(MemAlignment::MEM_ALIGN_OBJECT)) == 0); +} // namespace panda::ecmascript +#endif // ECMASCRIPT_TAGGED_ARRAY_H diff --git a/runtime/tagged_dictionary.cpp b/runtime/tagged_dictionary.cpp new file mode 100644 index 000000000..2c8ea7ea4 --- /dev/null +++ b/runtime/tagged_dictionary.cpp @@ -0,0 +1,250 @@ +/* + * Copyright (c) 2021-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 "tagged_dictionary.h" +#include "tagged_hash_table-inl.h" + +namespace panda::ecmascript { +int NameDictionary::Hash(const JSTaggedValue &key) +{ + if (key.IsHeapObject()) { + JSTaggedValue jsKey(key); + if (jsKey.IsSymbol()) { + auto symbolString = JSSymbol::Cast(key.GetTaggedObject()); + return static_cast(symbolString->GetHashField()).GetInt(); + } + if (jsKey.IsString()) { + auto keyString = reinterpret_cast(key.GetTaggedObject()); + return keyString->GetHashcode(); + } + } + // key must be object + UNREACHABLE(); +} + +bool NameDictionary::IsMatch(const JSTaggedValue &key, const JSTaggedValue &other) +{ + return key == other; +} + +void NameDictionary::GetAllKeys(const JSThread *thread, int offset, TaggedArray *keyArray) const +{ + int arrayIndex = 0; + int size = Size(); + CVector> sortArr; + for (int hashIndex = 0; hashIndex < size; hashIndex++) { + JSTaggedValue key = GetKey(hashIndex); + if (!key.IsUndefined() && !key.IsHole()) { + PropertyAttributes attr = GetAttributes(hashIndex); + std::pair pair(key, attr); + sortArr.push_back(pair); + } + } + std::sort(sortArr.begin(), sortArr.end(), CompKey); + for (const auto &entry : sortArr) { + keyArray->Set(thread, arrayIndex + offset, entry.first); + arrayIndex++; + } +} + +void NameDictionary::GetAllEnumKeys(const JSThread *thread, int offset, TaggedArray *keyArray, uint32_t *keys) const +{ + uint32_t arrayIndex = 0; + int size = Size(); + CVector> sortArr; + for (int hashIndex = 0; hashIndex < size; hashIndex++) { + JSTaggedValue key = GetKey(hashIndex); + if (key.IsString()) { + PropertyAttributes attr = GetAttributes(hashIndex); + if (attr.IsEnumerable()) { + std::pair pair(key, attr); + sortArr.push_back(pair); + } + } + } + std::sort(sortArr.begin(), sortArr.end(), CompKey); + for (auto entry : sortArr) { + keyArray->Set(thread, arrayIndex + offset, entry.first); + arrayIndex++; + } + *keys += arrayIndex; +} + +JSHandle NameDictionary::Create(const JSThread *thread, int numberOfElements) +{ + return OrderHashTableT::Create(thread, numberOfElements); +} + +PropertyAttributes NameDictionary::GetAttributes(int entry) const +{ + int index = GetEntryIndex(entry) + ENTRY_DETAILS_INDEX; + return PropertyAttributes(Get(index).GetInt()); +} + +void NameDictionary::SetAttributes(const JSThread *thread, int entry, const PropertyAttributes &metaData) +{ + int index = GetEntryIndex(entry) + ENTRY_DETAILS_INDEX; + Set(thread, index, metaData.GetTaggedValue()); +} + +void NameDictionary::SetEntry(const JSThread *thread, int entry, const JSTaggedValue &key, const JSTaggedValue &value, + const PropertyAttributes &metaData) +{ + SetKey(thread, entry, key); + SetValue(thread, entry, value); + SetAttributes(thread, entry, metaData); +} + +void NameDictionary::UpdateValueAndAttributes(const JSThread *thread, int entry, const JSTaggedValue &value, + const PropertyAttributes &metaData) +{ + SetValue(thread, entry, value); + SetAttributes(thread, entry, metaData); +} + +void NameDictionary::UpdateValue(const JSThread *thread, int entry, const JSTaggedValue &value) +{ + SetValue(thread, entry, value); +} + +void NameDictionary::ClearEntry(const JSThread *thread, int entry) +{ + JSTaggedValue hole = JSTaggedValue::Hole(); + PropertyAttributes metaData; + SetEntry(thread, entry, hole, hole, metaData); +} + +int NumberDictionary::Hash(const JSTaggedValue &key) +{ + if (key.IsInt()) { + int keyValue = key.GetInt(); + return GetHash32(reinterpret_cast(&keyValue), sizeof(keyValue) / sizeof(uint8_t)); + } + // key must be object + UNREACHABLE(); +} + +bool NumberDictionary::IsMatch(const JSTaggedValue &key, const JSTaggedValue &other) +{ + if (key.IsHole() || key.IsUndefined()) { + return false; + } + + if (key.IsInt()) { + if (other.IsInt()) { + return key.GetInt() == other.GetInt(); + } + return false; + } + // key must be integer + UNREACHABLE(); +} + +void NumberDictionary::GetAllKeys(const JSThread *thread, const JSHandle &obj, int offset, + const JSHandle &keyArray) +{ + ASSERT_PRINT(offset + obj->EntriesCount() <= static_cast(keyArray->GetLength()), + "keyArray capacity is not enough for dictionary"); + int arrayIndex = 0; + int size = obj->Size(); + CVector sortArr; + for (int hashIndex = 0; hashIndex < size; hashIndex++) { + JSTaggedValue key = obj->GetKey(hashIndex); + if (!key.IsUndefined() && !key.IsHole()) { + sortArr.push_back(JSTaggedValue(static_cast(key.GetInt()))); + } + } + std::sort(sortArr.begin(), sortArr.end(), CompKey); + for (auto entry : sortArr) { + JSHandle keyHandle(thread, entry); + JSHandle str = JSTaggedValue::ToString(const_cast(thread), keyHandle); + ASSERT_NO_ABRUPT_COMPLETION(thread); + keyArray->Set(thread, arrayIndex + offset, str.GetTaggedValue()); + arrayIndex++; + } +} + +void NumberDictionary::GetAllEnumKeys(const JSThread *thread, const JSHandle &obj, int offset, + const JSHandle &keyArray, uint32_t *keys) +{ + ASSERT_PRINT(offset + obj->EntriesCount() <= static_cast(keyArray->GetLength()), + "keyArray capacity is not enough for dictionary"); + uint32_t arrayIndex = 0; + int size = obj->Size(); + CVector sortArr; + for (int hashIndex = 0; hashIndex < size; hashIndex++) { + JSTaggedValue key = obj->GetKey(hashIndex); + if (!key.IsUndefined() && !key.IsHole()) { + PropertyAttributes attr = obj->GetAttributes(hashIndex); + if (attr.IsEnumerable()) { + sortArr.push_back(JSTaggedValue(static_cast(key.GetInt()))); + } + } + } + std::sort(sortArr.begin(), sortArr.end(), CompKey); + for (auto entry : sortArr) { + JSHandle key_handle(thread, entry); + JSHandle str = JSTaggedValue::ToString(const_cast(thread), key_handle); + ASSERT_NO_ABRUPT_COMPLETION(thread); + keyArray->Set(thread, arrayIndex + offset, str.GetTaggedValue()); + arrayIndex++; + } + *keys += arrayIndex; +} + +JSHandle NumberDictionary::Create(const JSThread *thread, int numberOfElements) +{ + return OrderHashTableT::Create(thread, numberOfElements); +} + +PropertyAttributes NumberDictionary::GetAttributes(int entry) const +{ + int index = GetEntryIndex(entry) + ENTRY_DETAILS_INDEX; + return PropertyAttributes(Get(index).GetInt()); +} + +void NumberDictionary::SetAttributes(const JSThread *thread, int entry, const PropertyAttributes &metaData) +{ + int index = GetEntryIndex(entry) + ENTRY_DETAILS_INDEX; + Set(thread, index, metaData.GetTaggedValue()); +} + +void NumberDictionary::SetEntry(const JSThread *thread, int entry, const JSTaggedValue &key, const JSTaggedValue &value, + const PropertyAttributes &metaData) +{ + SetKey(thread, entry, key); + SetValue(thread, entry, value); + SetAttributes(thread, entry, metaData); +} + +void NumberDictionary::UpdateValueAndAttributes(const JSThread *thread, int entry, const JSTaggedValue &value, + const PropertyAttributes &metaData) +{ + SetValue(thread, entry, value); + SetAttributes(thread, entry, metaData); +} + +void NumberDictionary::UpdateValue(const JSThread *thread, int entry, const JSTaggedValue &value) +{ + SetValue(thread, entry, value); +} + +void NumberDictionary::ClearEntry(const JSThread *thread, int entry) +{ + JSTaggedValue hole = JSTaggedValue::Hole(); + PropertyAttributes metaData; + SetEntry(thread, entry, hole, hole, metaData); +} +} // namespace panda::ecmascript diff --git a/runtime/tagged_dictionary.h b/runtime/tagged_dictionary.h new file mode 100644 index 000000000..fe5660bfd --- /dev/null +++ b/runtime/tagged_dictionary.h @@ -0,0 +1,133 @@ +/* + * Copyright (c) 2021-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_TAGGED_DICTIONARY_H +#define ECMASCRIPT_TAGGED_DICTIONARY_H + +#include "plugins/ecmascript/runtime/tagged_hash_table.h" + +namespace panda::ecmascript { +class NameDictionary : public OrderTaggedHashTable { +public: + using OrderHashTableT = OrderTaggedHashTable; + inline static int GetKeyIndex(int entry) + { + return OrderHashTableT::TABLE_HEADER_SIZE + entry * GetEntrySize() + ENTRY_KEY_INDEX; + } + inline static int GetValueIndex(int entry) + { + return OrderHashTableT::TABLE_HEADER_SIZE + entry * GetEntrySize() + ENTRY_VALUE_INDEX; + } + inline static int GetEntryIndex(int entry) + { + return OrderHashTableT::TABLE_HEADER_SIZE + entry * GetEntrySize(); + } + inline static int GetEntrySize() + { + return ENTRY_SIZE; + } + static int Hash(const JSTaggedValue &key); + static bool IsMatch(const JSTaggedValue &key, const JSTaggedValue &other); + static JSHandle Create(const JSThread *thread, + int numberOfElements = OrderHashTableT::DEFAULT_ELEMENTS_NUMBER); + // Returns the property metaData for the property at entry. + PropertyAttributes GetAttributes(int entry) const; + void SetAttributes(const JSThread *thread, int entry, const PropertyAttributes &metaData); + void SetEntry(const JSThread *thread, int entry, const JSTaggedValue &key, const JSTaggedValue &value, + const PropertyAttributes &metaData); + void UpdateValueAndAttributes(const JSThread *thread, int entry, const JSTaggedValue &value, + const PropertyAttributes &metaData); + void UpdateValue(const JSThread *thread, int entry, const JSTaggedValue &value); + void UpdateAttributes(int entry, const PropertyAttributes &metaData); + void ClearEntry(const JSThread *thread, int entry); + void GetAllKeys(const JSThread *thread, int offset, TaggedArray *keyArray) const; + void GetAllEnumKeys(const JSThread *thread, int offset, TaggedArray *keyArray, uint32_t *keys) const; + static inline bool CompKey(const std::pair &a, + const std::pair &b) + { + // ES6 9.4.5.6 + // 3. For each own property key P of O such that Type(P) is String and P is + // not an array index, in ascending chronological order of property creation, do + // a. Add P as the last element of keys. + // 4. For each own property key P of O such that Type(P) is Symbol, + // in ascending chronological order of property creation, do + // a. Add P as the last element of keys. + + if ((a.first.IsString() && b.first.IsString()) || (a.first.IsSymbol() && b.first.IsSymbol())) { + return a.second.GetDictionaryOrder() < b.second.GetDictionaryOrder(); + } + + return a.first.IsString() && b.first.IsSymbol(); + } + DECL_DUMP() + + static constexpr int ENTRY_KEY_INDEX = 0; + static constexpr int ENTRY_VALUE_INDEX = 1; + static constexpr int ENTRY_DETAILS_INDEX = 2; + static constexpr int ENTRY_SIZE = 3; +}; + +class NumberDictionary : public OrderTaggedHashTable { +public: + using OrderHashTableT = OrderTaggedHashTable; + inline static int GetKeyIndex(int entry) + { + return OrderHashTableT::TABLE_HEADER_SIZE + entry * GetEntrySize() + ENTRY_KEY_INDEX; + } + inline static int GetValueIndex(int entry) + { + return OrderHashTableT::TABLE_HEADER_SIZE + entry * GetEntrySize() + ENTRY_VALUE_INDEX; + } + inline static int GetEntryIndex(int entry) + { + return OrderHashTableT::TABLE_HEADER_SIZE + entry * GetEntrySize(); + } + inline static int GetEntrySize() + { + return ENTRY_SIZE; + } + static int Hash(const JSTaggedValue &key); + static bool IsMatch(const JSTaggedValue &key, const JSTaggedValue &other); + static JSHandle Create(const JSThread *thread, + int numberOfElements = OrderHashTableT::DEFAULT_ELEMENTS_NUMBER); + // Returns the property metaData for the property at entry. + PropertyAttributes GetAttributes(int entry) const; + void SetAttributes(const JSThread *thread, int entry, const PropertyAttributes &metaData); + void SetEntry([[maybe_unused]] const JSThread *thread, int entry, const JSTaggedValue &key, + const JSTaggedValue &value, const PropertyAttributes &metaData); + void UpdateValueAndAttributes(const JSThread *thread, int entry, const JSTaggedValue &value, + const PropertyAttributes &metaData); + void UpdateValue(const JSThread *thread, int entry, const JSTaggedValue &value); + void UpdateAttributes(int entry, const PropertyAttributes &metaData); + void ClearEntry(const JSThread *thread, int entry); + + static void GetAllKeys(const JSThread *thread, const JSHandle &obj, int offset, + const JSHandle &keyArray); + static void GetAllEnumKeys(const JSThread *thread, const JSHandle &obj, int offset, + const JSHandle &keyArray, uint32_t *keys); + static inline bool CompKey(const JSTaggedValue &a, const JSTaggedValue &b) + { + ASSERT(a.IsNumber() && b.IsNumber()); + return a.GetNumber() < b.GetNumber(); + } + DECL_DUMP() + + static constexpr int ENTRY_KEY_INDEX = 0; + static constexpr int ENTRY_VALUE_INDEX = 1; + static constexpr int ENTRY_DETAILS_INDEX = 2; + static constexpr int ENTRY_SIZE = 3; +}; +} // namespace panda::ecmascript +#endif // ECMASCRIPT_NEW_DICTIONARY_H diff --git a/runtime/tagged_hash_table-inl.h b/runtime/tagged_hash_table-inl.h new file mode 100644 index 000000000..9eb05ca8a --- /dev/null +++ b/runtime/tagged_hash_table-inl.h @@ -0,0 +1,469 @@ +/* + * Copyright (c) 2021-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_TAGGED_HASH_TABLE_INL_H +#define ECMASCRIPT_TAGGED_HASH_TABLE_INL_H + +#include "plugins/ecmascript/runtime/object_factory.h" +#include "plugins/ecmascript/runtime/tagged_array-inl.h" +#include "plugins/ecmascript/runtime/tagged_hash_table.h" + +namespace panda::ecmascript { +template +int TaggedHashTable::EntriesCount() const +{ + return Get(NUMBER_OF_ENTRIES_INDEX).GetInt(); +} + +template +int TaggedHashTable::HoleEntriesCount() const +{ + return Get(NUMBER_OF_HOLE_ENTRIES_INDEX).GetInt(); +} + +template +int TaggedHashTable::Size() const +{ + return Get(SIZE_INDEX).GetInt(); +} + +template +void TaggedHashTable::IncreaseEntries(const JSThread *thread) +{ + SetEntriesCount(thread, EntriesCount() + 1); +} + +template +void TaggedHashTable::IncreaseHoleEntriesCount(const JSThread *thread, int number) +{ + SetEntriesCount(thread, EntriesCount() - number); + SetHoleEntriesCount(thread, HoleEntriesCount() + number); +} + +template +void TaggedHashTable::SetEntriesCount(const JSThread *thread, int nof) +{ + Set(thread, NUMBER_OF_ENTRIES_INDEX, JSTaggedValue(nof)); +} + +template +void TaggedHashTable::SetHoleEntriesCount(const JSThread *thread, int nod) +{ + Set(thread, NUMBER_OF_HOLE_ENTRIES_INDEX, JSTaggedValue(nod)); +} + +template +void TaggedHashTable::SetHashTableSize(const JSThread *thread, int size) +{ + Set(thread, SIZE_INDEX, JSTaggedValue(size)); +} + +template +void TaggedHashTable::GetAllKeys(const JSThread *thread, int offset, TaggedArray *keyArray) const +{ + ASSERT_PRINT(offset + EntriesCount() <= static_cast(keyArray->GetLength()), + "keyArray size is not enough for dictionary"); + int arrayIndex = 0; + int size = Size(); + for (int hashIndex = 0; hashIndex < size; hashIndex++) { + JSTaggedValue key = GetKey(hashIndex); + if (!key.IsUndefined() && !key.IsHole()) { + keyArray->Set(thread, arrayIndex + offset, key); + arrayIndex++; + } + } +} + +// Find entry for key otherwise return -1. +template +int TaggedHashTable::FindEntry(const JSTaggedValue &key) +{ + size_t size = Size(); + int count = 1; + JSTaggedValue keyValue; + int hash = Derived::Hash(key); + + for (int entry = GetFirstPosition(hash, size);; entry = GetNextPosition(entry, count++, size)) { + keyValue = GetKey(entry); + if (keyValue.IsHole()) { + continue; + } + if (keyValue.IsUndefined()) { + return -1; + } + if (Derived::IsMatch(key, keyValue)) { + return entry; + } + } + return -1; +} + +// static +template +int TaggedHashTable::RecalculateTableSize(int currentSize, int atLeastSize) +{ + // When the filled entries is greater than a quart of currentSize + // it need not to shrink + if (atLeastSize > (currentSize / 4)) { // 4 : quarter + return currentSize; + } + // Recalculate table size + int newSize = ComputeHashTableSize(atLeastSize); + ASSERT_PRINT(newSize > atLeastSize, "new size must greater than atLeastSize"); + // Don't go lower than room for MIN_SHRINK_SIZE elements. + if (newSize < MIN_SHRINK_SIZE) { + return currentSize; + } + return newSize; +} + +// static +template +JSHandle TaggedHashTable::Shrink(const JSThread *thread, const JSHandle &table, + int additionalSize) +{ + int newSize = RecalculateTableSize(table->Size(), table->EntriesCount() + additionalSize); + if (newSize == table->Size()) { + return table; + } + + JSHandle newTable = TaggedHashTable::Create(thread, newSize); + if (newTable.IsEmpty()) { + return table; + } + + table->Rehash(thread, *newTable); + return newTable; +} + +template +bool TaggedHashTable::IsNeedGrowHashTable(int numOfAddEntries) +{ + int entriesCount = EntriesCount(); + int numOfDelEntries = HoleEntriesCount(); + int currentSize = Size(); + int numberFilled = entriesCount + numOfAddEntries; + // needn't to grow table: + // 1. after adding number entries, table have half free entries. + // 2. deleted entries are less than half of the free entries. + const int halfFree = 2; + if ((numberFilled < currentSize) && ((numOfDelEntries <= (currentSize - numberFilled) / halfFree))) { + int neededFree = numberFilled / halfFree; + if (numberFilled + neededFree <= currentSize) { + return false; + } + } + return true; +} + +template +void TaggedHashTable::AddElement(const JSThread *thread, int entry, const JSHandle &key, + const JSHandle &value) +{ + this->SetKey(thread, entry, key.GetTaggedValue()); + this->SetValue(thread, entry, value.GetTaggedValue()); + this->IncreaseEntries(thread); +} + +template +void TaggedHashTable::RemoveElement(const JSThread *thread, int entry) +{ + JSTaggedValue defaultValue(JSTaggedValue::Hole()); + this->SetKey(thread, entry, defaultValue); + this->SetValue(thread, entry, defaultValue); + this->IncreaseHoleEntriesCount(thread); +} + +template +JSHandle TaggedHashTable::Insert(const JSThread *thread, JSHandle &table, + const JSHandle &key, + const JSHandle &value) +{ + // Make sure the key object has an identity hash code. + int hash = Derived::Hash(key.GetTaggedValue()); + int entry = table->FindEntry(key.GetTaggedValue()); + if (entry != -1) { + table->SetValue(thread, entry, value.GetTaggedValue()); + return table; + } + + JSHandle newTable = GrowHashTable(thread, table); + newTable->AddElement(thread, newTable->FindInsertIndex(hash), key, value); + return newTable; +} + +template +JSHandle TaggedHashTable::Remove(const JSThread *thread, JSHandle &table, + const JSHandle &key) +{ + int entry = table->FindEntry(key.GetTaggedValue()); + if (entry == -1) { + return table; + } + + table->RemoveElement(thread, entry); + return Derived::Shrink(thread, *table); +} + +template +void TaggedHashTable::Rehash(const JSThread *thread, Derived *newTable) +{ + if ((newTable == nullptr) || (newTable->Size() < EntriesCount())) { + return; + } + int currentSize = this->Size(); + // Rehash elements to new table + for (int i = 0; i < currentSize; i++) { + int fromIndex = Derived::GetKeyIndex(i); + JSTaggedValue k = this->GetKey(i); + if (!IsKey(k)) { + continue; + } + int hash = Derived::Hash(k); + int insertionIndex = Derived::GetKeyIndex(newTable->FindInsertIndex(hash)); + JSTaggedValue tv = Get(fromIndex); + newTable->Set(thread, insertionIndex, tv); + for (int j = 1; j < Derived::GetEntrySize(); j++) { + tv = Get(fromIndex + j); + newTable->Set(thread, insertionIndex + j, tv); + } + } + newTable->SetEntriesCount(thread, EntriesCount()); + newTable->SetHoleEntriesCount(thread, 0); +} + +// static +template +int TaggedHashTable::ComputeHashTableSize(uint32_t atLeastSize) +{ + // increase size for hash-collision + uint32_t rawSize = atLeastSize + (atLeastSize >> 1UL); + int newSize = static_cast(helpers::math::GetPowerOfTwoValue32(rawSize)); + return (newSize > MIN_SIZE) ? newSize : MIN_SIZE; +} + +template +JSHandle TaggedHashTable::GrowHashTable(const JSThread *thread, const JSHandle &table, + int numOfAddedElements) +{ + if (!table->IsNeedGrowHashTable(numOfAddedElements)) { + return table; + } + int newSize = ComputeHashTableSize(table->Size() + numOfAddedElements); + int length = Derived::GetEntryIndex(newSize); + JSHandle newTable(thread->GetEcmaVM()->GetFactory()->NewDictionaryArray(length)); + newTable->SetHashTableSize(thread, newSize); + table->Rehash(thread, *newTable); + return newTable; +} + +template +JSHandle TaggedHashTable::Create(const JSThread *thread, int entriesCount) +{ + ASSERT_PRINT((entriesCount > 0), "the size must be greater than zero"); + auto size = static_cast(entriesCount); + ASSERT_PRINT(helpers::math::IsPowerOfTwo(static_cast(entriesCount)), "the size must be power of two"); + + int length = Derived::GetEntryIndex(entriesCount); + + JSHandle table(thread->GetEcmaVM()->GetFactory()->NewDictionaryArray(length)); + table->SetEntriesCount(thread, 0); + table->SetHoleEntriesCount(thread, 0); + table->SetHashTableSize(thread, size); + return table; +} + +template +void TaggedHashTable::SetKey(const JSThread *thread, int entry, const JSTaggedValue &key) +{ + int index = Derived::GetKeyIndex(entry); + if (UNLIKELY(index < 0 || index > static_cast(GetLength()))) { + return; + } + Set(thread, index, key); +} + +template +JSTaggedValue TaggedHashTable::GetKey(int entry) const +{ + int index = Derived::GetKeyIndex(entry); + if (UNLIKELY((index < 0 || index > static_cast(GetLength())))) { + return JSTaggedValue::Undefined(); + } + return Get(index); +} + +template +void TaggedHashTable::SetValue(const JSThread *thread, int entry, const JSTaggedValue &value) +{ + int index = Derived::GetValueIndex(entry); + if (UNLIKELY((index < 0 || index > static_cast(GetLength())))) { + return; + } + Set(thread, index, value); +} + +template +JSTaggedValue TaggedHashTable::GetValue(int entry) const +{ + int index = Derived::GetValueIndex(entry); + if (UNLIKELY((index < 0 || index > static_cast(GetLength())))) { + return JSTaggedValue::Undefined(); + } + return Get(index); +} + +template +int TaggedHashTable::FindInsertIndex(int hash) +{ + int size = Size(); + int count = 1; + // GrowHashTable will guarantee the hash table is never full. + for (int entry = GetFirstPosition(hash, size);; entry = GetNextPosition(entry, count++, size)) { + if (!IsKey(GetKey(entry))) { + return entry; + } + } +} + +template +JSHandle OrderTaggedHashTable::Create(const JSThread *thread, int numberOfElements) +{ + JSHandle dict = HashTableT::Create(thread, numberOfElements); + dict->SetNextEnumerationIndex(thread, PropertyAttributes::INTIAL_PROPERTY_INDEX); + return dict; +} + +template +JSHandle OrderTaggedHashTable::PutIfAbsent(const JSThread *thread, const JSHandle &table, + const JSHandle &key, + const JSHandle &value, + const PropertyAttributes &metaData) +{ + int hash = Derived::Hash(key.GetTaggedValue()); + + /* no need to add key if exist */ + int entry = table->FindEntry(key.GetTaggedValue()); + if (entry != -1) { + return table; + } + int enumIndex = table->NextEnumerationIndex(thread); + PropertyAttributes attr(metaData); + attr.SetDictionaryOrder(enumIndex); + // Check whether the table should be growed. + JSHandle newTable = HashTableT::GrowHashTable(thread, table); + + // Compute the key object. + entry = newTable->FindInsertIndex(hash); + newTable->SetEntry(thread, entry, key.GetTaggedValue(), value.GetTaggedValue(), attr); + + newTable->IncreaseEntries(thread); + newTable->SetNextEnumerationIndex(thread, enumIndex + 1); + return newTable; +} + +template +JSHandle OrderTaggedHashTable::Put(const JSThread *thread, const JSHandle &table, + const JSHandle &key, + const JSHandle &value, + const PropertyAttributes &metaData) +{ + int hash = Derived::Hash(key.GetTaggedValue()); + int enumIndex = table->NextEnumerationIndex(thread); + PropertyAttributes attr(metaData); + attr.SetDictionaryOrder(enumIndex); + int entry = table->FindEntry(key.GetTaggedValue()); + if (entry != -1) { + table->SetEntry(thread, entry, key.GetTaggedValue(), value.GetTaggedValue(), attr); + return table; + } + // Check whether the table should be extended. + JSHandle newTable = HashTableT::GrowHashTable(thread, table); + + // Compute the key object. + entry = newTable->FindInsertIndex(hash); + newTable->SetEntry(thread, entry, key.GetTaggedValue(), value.GetTaggedValue(), attr); + + newTable->IncreaseEntries(thread); + newTable->SetNextEnumerationIndex(thread, enumIndex + 1); + return newTable; +} + +template +void TaggedHashTable::GetAllKeysIntoVector([[maybe_unused]] const JSThread *thread, + std::vector &vector) const +{ + int capacity = Size(); + for (int hashIndex = 0; hashIndex < capacity; hashIndex++) { + JSTaggedValue key = GetKey(hashIndex); + if (!key.IsUndefined() && !key.IsHole()) { + vector.push_back(key); + } + } +} + +template +JSHandle OrderTaggedHashTable::Remove(const JSThread *thread, const JSHandle &table, + int entry) +{ + if (!(table->IsKey(table->GetKey(entry)))) { + return table; + } + table->ClearEntry(thread, entry); + table->IncreaseHoleEntriesCount(thread); + return Shrink(thread, table); +} + +template +int OrderTaggedHashTable::NextEnumerationIndex(const JSThread *thread) +{ + int index = GetNextEnumerationIndex(); + auto table = Derived::Cast(this); + + if (!PropertyAttributes::IsValidIndex(index)) { + std::vector indexOrder = GetEnumerationOrder(); + int length = indexOrder.size(); + for (int i = 0; i < length; i++) { + int oldIndex = indexOrder[i]; + int enumIndex = PropertyAttributes::INTIAL_PROPERTY_INDEX + i; + PropertyAttributes attr = table->GetAttributes(oldIndex); + attr.SetDictionaryOrder(enumIndex); + table->SetAttributes(thread, oldIndex, attr); + } + index = PropertyAttributes::INTIAL_PROPERTY_INDEX + length; + } + return index; +} + +template +std::vector OrderTaggedHashTable::GetEnumerationOrder() +{ + std::vector result; + auto table = Derived::Cast(this); + int size = table->Size(); + for (int i = 0; i < size; i++) { + if (table->IsKey(table->GetKey(i))) { + result.push_back(i); + } + } + std::sort(result.begin(), result.end(), [table](int a, int b) { + PropertyAttributes attrA = table->GetAttributes(a); + PropertyAttributes attrB = table->GetAttributes(b); + return attrA.GetDictionaryOrder() < attrB.GetDictionaryOrder(); + }); + return result; +} +} // namespace panda::ecmascript +#endif // ECMASCRIPT_TAGGED_HASH_TABLE_INL_H diff --git a/runtime/tagged_hash_table.h b/runtime/tagged_hash_table.h new file mode 100644 index 000000000..64881618c --- /dev/null +++ b/runtime/tagged_hash_table.h @@ -0,0 +1,164 @@ +/* + * Copyright (c) 2021-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_TAGGED_HASH_TABLE_H +#define ECMASCRIPT_TAGGED_HASH_TABLE_H + +#include + +#include "js_handle.h" +#include "tagged_array.h" + +namespace panda::ecmascript { +template +class TaggedHashTable : public TaggedArray { +public: + inline int EntriesCount() const; + + inline int HoleEntriesCount() const; + + inline int Size() const; + + inline void IncreaseEntries(const JSThread *thread); + + inline void IncreaseHoleEntriesCount(const JSThread *thread, int number = 1); + + inline static int ComputeHashTableSize(uint32_t atLeastSize); + + static JSHandle GrowHashTable(const JSThread *thread, const JSHandle &table, + int numOfAddedElements = 1); + + static JSHandle Create(const JSThread *thread, int entriesCount); + + static JSHandle Insert(const JSThread *thread, JSHandle &table, + const JSHandle &key, const JSHandle &value); + + static JSHandle Remove(const JSThread *thread, JSHandle &table, + const JSHandle &key); + + inline static int RecalculateTableSize(int currentSize, int atLeastSize); + + inline static JSHandle Shrink(const JSThread *thread, const JSHandle &table, int additionalSize); + + bool IsNeedGrowHashTable(int numOfAddEntries); + + JSTaggedValue GetKey(int entry) const; + + JSTaggedValue GetValue(int entry) const; + + inline void GetAllKeys(const JSThread *thread, int offset, TaggedArray *keyArray) const; + + inline void GetAllKeysIntoVector(const JSThread *thread, std::vector &vector) const; + + inline void Swap(const JSThread *thread, int src, int dst); + + // Find entry for key otherwise return -1. + inline int FindEntry(const JSTaggedValue &key); + + inline int FindInsertIndex(int hash); + + inline void SetKey(const JSThread *thread, int entry, const JSTaggedValue &key); + + inline void SetValue(const JSThread *thread, int entry, const JSTaggedValue &value); + + static constexpr int MIN_SHRINK_SIZE = 16; + static constexpr int MIN_SIZE = 4; + static constexpr int NUMBER_OF_ENTRIES_INDEX = 0; + static constexpr int NUMBER_OF_HOLE_ENTRIES_INDEX = 1; + static constexpr int SIZE_INDEX = 2; + static constexpr int TABLE_HEADER_SIZE = 3; + +protected: + inline bool IsKey(const JSTaggedValue &key) const + { + return !key.IsHole() && !key.IsUndefined(); + }; + + inline static uint32_t GetFirstPosition(uint32_t hash, uint32_t size) + { + return hash & (size - 1); + } + + inline static uint32_t GetNextPosition(uint32_t last, uint32_t number, uint32_t size) + { + return (last + (number * (number + 1)) / 2) & (size - 1); // 2 : half + } + + inline void SetEntriesCount(const JSThread *thread, int nof); + + inline void SetHoleEntriesCount(const JSThread *thread, int nod); + + // Sets the size of the hash table. + inline void SetHashTableSize(const JSThread *thread, int size); + + inline static int GetHeadSizeOfTable(); + inline static int GetEntrySize(); + inline static int GetKeyOffset(); + inline static int GetValueOffset(); + + inline void AddElement(const JSThread *thread, int entry, const JSHandle &key, + const JSHandle &value); + + inline void RemoveElement(const JSThread *thread, int entry); + + // Rehash element to new_table + void Rehash(const JSThread *thread, Derived *newTable); +}; + +template +class OrderTaggedHashTable : public TaggedHashTable { +public: + using HashTableT = TaggedHashTable; + static Derived *Cast(TaggedObject *object) + { + return reinterpret_cast(object); + } + // Attempt to shrink the table after deletion of key. + static JSHandle Shrink(const JSThread *thread, const JSHandle &table) + { + int index = table->GetNextEnumerationIndex(); + JSHandle newTable = HashTableT::Shrink(thread, table, 0); + newTable->SetNextEnumerationIndex(thread, index); + return newTable; + } + + static JSHandle Create(const JSThread *thread, int numberOfElements = DEFAULT_ELEMENTS_NUMBER); + static JSHandle PutIfAbsent(const JSThread *thread, const JSHandle &table, + const JSHandle &key, const JSHandle &value, + const PropertyAttributes &metaData); + static JSHandle Put(const JSThread *thread, const JSHandle &table, + const JSHandle &key, const JSHandle &value, + const PropertyAttributes &metaData); + static JSHandle Remove(const JSThread *thread, const JSHandle &table, int entry); + + inline void SetNextEnumerationIndex(const JSThread *thread, int index) + { + HashTableT::Set(thread, NEXT_ENUMERATION_INDEX, JSTaggedValue(index)); + } + inline int GetNextEnumerationIndex() const + { + return HashTableT::Get(NEXT_ENUMERATION_INDEX).GetInt(); + } + + inline int NextEnumerationIndex(const JSThread *thread); + + inline std::vector GetEnumerationOrder(); + + static const int NEXT_ENUMERATION_INDEX = HashTableT::SIZE_INDEX + 1; + static const int DEFAULT_ELEMENTS_NUMBER = 128; + static constexpr int TABLE_HEADER_SIZE = 4; +}; +} // namespace panda::ecmascript +#endif // ECMASCRIPT_NEW_HASH_TABLE_H \ No newline at end of file diff --git a/runtime/tagged_queue-inl.h b/runtime/tagged_queue-inl.h new file mode 100644 index 000000000..61f6013e1 --- /dev/null +++ b/runtime/tagged_queue-inl.h @@ -0,0 +1,48 @@ +/* + * Copyright (c) 2021-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_TAGGED_QUEUE_INL_H +#define ECMASCRIPT_TAGGED_QUEUE_INL_H + +#include "plugins/ecmascript/runtime/tagged_queue.h" + +namespace panda::ecmascript { +inline TaggedQueue *TaggedQueue::Create(JSThread *thread, uint32_t capacity, JSTaggedValue initVal) +{ + uint32_t length = QueueToArrayIndex(capacity); + + auto queue = TaggedQueue::Cast(*thread->GetEcmaVM()->GetFactory()->NewTaggedArray(length, initVal)); + queue->SetStart(thread, JSTaggedValue(0)); // equal to 0 when add 1. + queue->SetEnd(thread, JSTaggedValue(0)); + queue->SetCapacity(thread, JSTaggedValue(capacity)); + return queue; +} + +inline JSTaggedValue TaggedQueue::Pop(JSThread *thread) +{ + if (Empty()) { + return JSTaggedValue::Hole(); + } + + uint32_t start = GetStart().GetArrayLength(); + JSTaggedValue value = Get(start); + + uint32_t capacity = GetCapacity().GetArrayLength(); + ASSERT(capacity != 0); + SetStart(thread, JSTaggedValue((start + 1) % capacity)); + return value; +} +} // namespace panda::ecmascript +#endif // ECMASCRIPT_TAGGED_QUEUE_INL_H diff --git a/runtime/tagged_queue.h b/runtime/tagged_queue.h new file mode 100644 index 000000000..091fb37b6 --- /dev/null +++ b/runtime/tagged_queue.h @@ -0,0 +1,181 @@ +/* + * Copyright (c) 2021-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_TAGGED_QUEUE_H +#define ECMASCRIPT_TAGGED_QUEUE_H + +#include "plugins/ecmascript/runtime/js_tagged_value.h" +#include "plugins/ecmascript/runtime/js_thread.h" +#include "plugins/ecmascript/runtime/tagged_array.h" +#include "plugins/ecmascript/runtime/tagged_array-inl.h" + +namespace panda::ecmascript { +class TaggedQueue : public TaggedArray { +public: + static TaggedQueue *Cast(ObjectHeader *object) + { + return reinterpret_cast(object); + } + + JSTaggedValue Pop(JSThread *thread); + static TaggedQueue *Push(const JSThread *thread, const JSHandle &queue, + const JSHandle &value) + { + uint32_t capacity = queue->GetCapacity().GetArrayLength(); + if (capacity == 0) { + // If there is no capacity, directly create a queue whose capacity is MIN_CAPACITY. Add elements. + ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); + JSHandle newQueue = factory->NewTaggedQueue(MIN_CAPACITY); + newQueue->Set(thread, 0, value.GetTaggedValue()); + newQueue->SetCapacity(thread, JSTaggedValue(MIN_CAPACITY)); + newQueue->SetStart(thread, JSTaggedValue(0)); + newQueue->SetEnd(thread, JSTaggedValue(1)); + return *newQueue; + } + + uint32_t start = queue->GetStart().GetArrayLength(); + uint32_t end = queue->GetEnd().GetArrayLength(); + uint32_t size = queue->Size(); + if ((end + 1) % capacity == start) { + // The original queue is full and needs to be expanded. + if (capacity == MAX_QUEUE_INDEX) { + // can't grow anymore + return *queue; + } + ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); + // Grow Array for 1.5 times + uint32_t newCapacity = capacity + (capacity >> 1U); + newCapacity = newCapacity < capacity ? MAX_QUEUE_INDEX : newCapacity; + JSHandle newQueue = factory->NewTaggedQueue(newCapacity); + uint32_t newEnd = 0; + for (uint32_t i = start; newEnd < size; i = (i + 1) % capacity) { + newQueue->Set(thread, newEnd, queue->Get(i)); + newEnd++; + } + + newQueue->SetCapacity(thread, JSTaggedValue(newCapacity)); + newQueue->SetStart(thread, JSTaggedValue(0)); + newQueue->Set(thread, newEnd++, value.GetTaggedValue()); + newQueue->SetEnd(thread, JSTaggedValue(newEnd)); + return *newQueue; + } + queue->Set(thread, end, value.GetTaggedValue()); + queue->SetEnd(thread, JSTaggedValue((end + 1) % capacity)); + return *queue; + } + + // Only for fixed-length queue, without grow capacity + static inline void PushFixedQueue(const JSThread *thread, const JSHandle &queue, + const JSHandle &value) + { + uint32_t end = queue->GetEnd().GetArrayLength(); + uint32_t capacity = queue->GetCapacity().GetArrayLength(); + ASSERT(capacity != 0); + queue->Set(thread, end, value.GetTaggedValue()); + queue->SetEnd(thread, JSTaggedValue((end + 1) % capacity)); + } + + inline bool Empty() + { + return GetStart() == GetEnd(); + } + + inline JSTaggedValue Front() + { + if (Empty()) { + return JSTaggedValue::Hole(); + } + uint32_t start = GetStart().GetArrayLength(); + return JSTaggedValue(Get(start)); + } + + inline JSTaggedValue Back() + { + if (Empty()) { + return JSTaggedValue::Hole(); + } + return JSTaggedValue(Get(GetEnd().GetArrayLength() - 1)); + } + + inline uint32_t Size() + { + uint32_t capacity = GetCapacity().GetArrayLength(); + if (capacity == 0) { + return 0; + } + uint32_t end = GetEnd().GetArrayLength(); + uint32_t start = GetStart().GetArrayLength(); + return (end - start + capacity) % capacity; + } + + inline JSTaggedValue Get(uint32_t index) const + { + return TaggedArray::Get(QueueToArrayIndex(index)); + } + + inline void Set(const JSThread *thread, uint32_t index, JSTaggedValue value) + { + return TaggedArray::Set(thread, QueueToArrayIndex(index), value); + } + + inline JSTaggedValue GetStart() const + { + return TaggedArray::Get(START_INDEX); + } + + static const uint32_t MIN_CAPACITY = 2; + static const uint32_t CAPACITY_INDEX = 0; + static const uint32_t START_INDEX = 1; + static const uint32_t END_INDEX = 2; + static const uint32_t ELEMENTS_START_INDEX = 3; + static const uint32_t MAX_QUEUE_INDEX = TaggedArray::MAX_ARRAY_INDEX - ELEMENTS_START_INDEX; + +private: + friend class ObjectFactory; + + inline static constexpr uint32_t QueueToArrayIndex(uint32_t index) + { + return index + ELEMENTS_START_INDEX; + } + + inline void SetCapacity(const JSThread *thread, JSTaggedValue capacity) + { + TaggedArray::Set(thread, CAPACITY_INDEX, capacity); + } + + inline JSTaggedValue GetCapacity() const + { + return TaggedArray::Get(CAPACITY_INDEX); + } + + inline void SetStart(const JSThread *thread, JSTaggedValue start) + { + TaggedArray::Set(thread, START_INDEX, start); + } + + inline void SetEnd(const JSThread *thread, JSTaggedValue end) + { + TaggedArray::Set(thread, END_INDEX, end); + } + + inline JSTaggedValue GetEnd() const + { + return TaggedArray::Get(END_INDEX); + } + + static TaggedQueue *Create(JSThread *thread, uint32_t capacity, JSTaggedValue initVal = JSTaggedValue::Hole()); +}; +} // namespace panda::ecmascript +#endif // ECMASCRIPT_TAGGED_QUEUE_H diff --git a/runtime/template_map.h b/runtime/template_map.h new file mode 100644 index 000000000..6a09ac57f --- /dev/null +++ b/runtime/template_map.h @@ -0,0 +1,67 @@ +/* + * Copyright (c) 2021-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_TEMPLATE_MAP_H +#define ECMASCRIPT_TEMPLATE_MAP_H + +#include "plugins/ecmascript/runtime/tagged_hash_table-inl.h" +#include "plugins/ecmascript/runtime/js_array.h" + +namespace panda::ecmascript { +class TemplateMap : public TaggedHashTable { +public: + using HashTable = TaggedHashTable; + static inline bool IsMatch(const JSTaggedValue &key, const JSTaggedValue &other) + { + return key == other; + } + static inline int Hash(const JSTaggedValue &obj) + { + ASSERT(obj.IsJSArray()); + JSArray *array = JSArray::Cast(obj.GetHeapObject()); + uint32_t len = array->GetArrayLength(); + return len; + } + inline static int GetKeyIndex(int entry) + { + return HashTable::TABLE_HEADER_SIZE + entry * GetEntrySize() + ENTRY_KEY_INDEX; + } + inline static int GetValueIndex(int entry) + { + return HashTable::TABLE_HEADER_SIZE + entry * GetEntrySize() + ENTRY_VALUE_INDEX; + } + inline static int GetEntryIndex(int entry) + { + return HashTable::TABLE_HEADER_SIZE + entry * GetEntrySize(); + } + inline static int GetEntrySize() + { + return ENTRY_SIZE; + } + static TemplateMap *Cast(ObjectHeader *object) + { + return reinterpret_cast(object); + } + static const int DEFAULT_ELEMENTS_NUMBER = 16; + static JSHandle Create(JSThread *thread, int numberOfElements = DEFAULT_ELEMENTS_NUMBER) + { + return HashTable::Create(thread, numberOfElements); + } + static const int ENTRY_SIZE = 2; + static const int ENTRY_KEY_INDEX = 0; + static const int ENTRY_VALUE_INDEX = 1; +}; +} // namespace panda::ecmascript +#endif // ECMASCRIPT_TEMPLATE_MAP_H diff --git a/runtime/template_string.cpp b/runtime/template_string.cpp new file mode 100644 index 000000000..2e201e53b --- /dev/null +++ b/runtime/template_string.cpp @@ -0,0 +1,62 @@ +/* + * Copyright (c) 2021-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 "plugins/ecmascript/runtime/template_string.h" +#include "plugins/ecmascript/runtime/ecma_vm.h" +#include "plugins/ecmascript/runtime/global_env.h" +#include "plugins/ecmascript/runtime/js_array.h" +#include "plugins/ecmascript/runtime/js_tagged_number.h" +#include "plugins/ecmascript/runtime/js_tagged_value.h" +#include "plugins/ecmascript/runtime/object_factory.h" +#include "plugins/ecmascript/runtime/template_map.h" + +namespace panda::ecmascript { +JSHandle TemplateString::GetTemplateObject(JSThread *thread, JSHandle templateLiteral) +{ + JSHandle env = thread->GetEcmaVM()->GetGlobalEnv(); + JSHandle rawStringsTag = JSObject::GetProperty(thread, templateLiteral, 0).GetValue(); + JSHandle templateMapTag = env->GetTemplateMap(); + JSHandle templateMap(templateMapTag); + int32_t element = templateMap->FindEntry(rawStringsTag.GetTaggedValue()); + if (element != -1) { + return JSHandle(thread, templateMap->GetValue(element)); + } + JSHandle cookedStringsTag = JSObject::GetProperty(thread, templateLiteral, 1).GetValue(); + JSHandle cookedStrings(cookedStringsTag); + int32_t count = cookedStrings->GetArrayLength(); + auto countNum = JSTaggedNumber(count); + JSHandle templateArr = JSArray::ArrayCreate(thread, countNum); + JSHandle rawArr = JSArray::ArrayCreate(thread, countNum); + JSHandle templateObj(templateArr); + JSHandle rawObj(rawArr); + for (int32_t i = 0; i < count; i++) { + JSHandle cookedValue = JSObject::GetProperty(thread, cookedStringsTag, i).GetValue(); + PropertyDescriptor descCooked(thread, cookedValue, true, false, false); + JSArray::DefineOwnProperty(thread, templateObj, i, descCooked); + JSHandle rawValue = JSObject::GetProperty(thread, rawStringsTag, i).GetValue(); + PropertyDescriptor descRaw(thread, rawValue, true, false, false); + JSArray::DefineOwnProperty(thread, rawObj, i, descRaw); + } + JSObject::SetIntegrityLevel(thread, rawObj, IntegrityLevel::FROZEN); + ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); + JSHandle raw(factory->NewFromCanBeCompressString("raw")); + PropertyDescriptor desc(thread, rawArr, false, false, false); + JSArray::DefineOwnProperty(thread, templateObj, raw, desc); + JSObject::SetIntegrityLevel(thread, templateObj, IntegrityLevel::FROZEN); + TemplateMap::Insert(thread, templateMap, rawStringsTag, templateArr); + env->SetTemplateMap(thread, templateMap.GetTaggedValue()); + return templateArr; +} +} // namespace panda::ecmascript diff --git a/runtime/template_string.h b/runtime/template_string.h new file mode 100644 index 000000000..a2e8c40c9 --- /dev/null +++ b/runtime/template_string.h @@ -0,0 +1,27 @@ +/* + * Copyright (c) 2021-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_TEMPLATE_STRING_H +#define ECMASCRIPT_TEMPLATE_STRING_H + +#include "plugins/ecmascript/runtime/js_array.h" + +namespace panda::ecmascript { +class TemplateString { +public: + static JSHandle GetTemplateObject(JSThread *thread, JSHandle templateLiteral); +}; +} // namespace panda::ecmascript +#endif // ECMASCRIPT_TEMPLATE_STRING_H diff --git a/runtime/templates/intrinsics_gen.cpp.erb b/runtime/templates/intrinsics_gen.cpp.erb new file mode 100644 index 000000000..fb7188433 --- /dev/null +++ b/runtime/templates/intrinsics_gen.cpp.erb @@ -0,0 +1,49 @@ +/* + * Copyright (c) 2021-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. + */ + +namespace panda::ecmascript::intrinsics { + +// Autogenerated file -- DO NOT EDIT! + +% Runtime::intrinsics.select(&:has_impl?).uniq { |i| i.impl }.each do |intrinsic| +% funcname = intrinsic.impl.rpartition('::').last +% params = intrinsic.signature.args.map { |t| get_effective_type(t) }.flatten +% params = params.each_with_index.map {|cpp_type, index| cpp_type + " " + "arg#{index}" } +% ret_type = get_ret_effective_type(intrinsic.signature.ret) +% use_thread = !intrinsic.respond_to?(:use_thread) || intrinsic.use_thread +% nargs = intrinsic.signature.args.size +% arg_list = (0...nargs).map { |i| "arg#{i}" } +% arg_list.insert(0, 'GetJSThread()') if use_thread +% arg_list = arg_list.join(", ") +extern "C" <%= ret_type %> <%= funcname %>Slow(<%= params.join(", ") %>) { +% if ret_type == 'void' + <%= intrinsic.impl %>(<%= arg_list %>); +% else + return <%= intrinsic.impl %>(<%= arg_list %>); +% end +} + +% next if !use_thread +<%= ret_type %> <%= funcname %>(<%= params.join(", ") %>) { +% if ret_type == 'void' + <%= intrinsic.impl %>(<%= arg_list %>); +% else + return <%= intrinsic.impl %>(<%= arg_list %>); +% end +} + +% end + +} // namespace panda::ecmascript::intrinsics diff --git a/runtime/tooling/pt_ecmascript_extension.cpp b/runtime/tooling/pt_ecmascript_extension.cpp new file mode 100644 index 000000000..bdc353fbe --- /dev/null +++ b/runtime/tooling/pt_ecmascript_extension.cpp @@ -0,0 +1,34 @@ +/* + * 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 "plugins/ecmascript/runtime/include/tooling/pt_ecmascript_extension.h" +#include "plugins/ecmascript/runtime/js_handle.h" +#include "plugins/ecmascript/runtime/global_env.h" + +namespace panda::tooling::ecmascript { +using JSThread = panda::ecmascript::JSThread; + +/* static */ +VRegValue PtEcmaScriptExtension::TaggedValueToVRegValue(JSTaggedValue value) +{ + return VRegValue(bit_cast(value.GetRawData())); +} + +/* static */ +JSTaggedValue PtEcmaScriptExtension::VRegValueToTaggedValue(VRegValue value) +{ + return JSTaggedValue(bit_cast(value.GetValue())); +} +} // namespace panda::tooling::ecmascript diff --git a/runtime/tooling/pt_js_extractor.h b/runtime/tooling/pt_js_extractor.h new file mode 100644 index 000000000..c65b73c14 --- /dev/null +++ b/runtime/tooling/pt_js_extractor.h @@ -0,0 +1,86 @@ +/* + * Copyright (c) 2021-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 PANDA_TOOLING_JS_EXTRACTOR_H +#define PANDA_TOOLING_JS_EXTRACTOR_H + +#include "plugins/ecmascript/runtime/js_method.h" +#include "plugins/ecmascript/runtime/js_thread.h" +#include "plugins/ecmascript/runtime/mem/c_containers.h" +#include "libpandafile/debug_info_extractor.h" +#include "libpandabase/macros.h" +#include "include/tooling/debug_interface.h" + +namespace panda::tooling::ecmascript { +using panda::panda_file::DebugInfoExtractor; +using panda::panda_file::File; + +class PtJSExtractor : public DebugInfoExtractor { +public: + explicit PtJSExtractor(const File *pf) : DebugInfoExtractor(pf) {} + virtual ~PtJSExtractor() = default; + + template + bool MatchWithLine(const Callback &cb, int32_t line) + { + auto methods = GetMethodIdList(); + for (const auto &method : methods) { + auto table = GetLineNumberTable(method); + for (const auto &pair : table) { + if (static_cast(pair.line) == line) { + return cb(method, pair.offset); + } + } + } + return false; + } + + template + bool MatchWithOffset(const Callback &cb, File::EntityId methodId, uint32_t offset) + { + auto lineTable = GetLineNumberTable(methodId); + auto columnTable = GetColumnNumberTable(methodId); + size_t line = 0; + size_t column = 0; + + for (const auto &pair : lineTable) { + if (offset < pair.offset) { + break; + } + if (offset == pair.offset) { + line = pair.line; + break; + } + line = pair.line; + } + + for (const auto &pair : columnTable) { + if (offset < pair.offset) { + break; + } + if (offset == pair.offset) { + column = pair.column; + break; + } + column = pair.column; + } + return cb(line, column); + } + + NO_COPY_SEMANTIC(PtJSExtractor); + NO_MOVE_SEMANTIC(PtJSExtractor); +}; +} // namespace panda::tooling::ecmascript +#endif diff --git a/runtime/transitions_dictionary.h b/runtime/transitions_dictionary.h new file mode 100644 index 000000000..e3fa3126a --- /dev/null +++ b/runtime/transitions_dictionary.h @@ -0,0 +1,138 @@ +/* + * Copyright (c) 2021-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_TRANSITIONS_DICTIONARY_H +#define ECMASCRIPT_TRANSITIONS_DICTIONARY_H + +#include "plugins/ecmascript/runtime/tagged_hash_table-inl.h" +#include "plugins/ecmascript/runtime/js_symbol.h" +#include "plugins/ecmascript/runtime/accessor_data.h" +#include "plugins/ecmascript/runtime/js_tagged_number.h" + +namespace panda::ecmascript { +class TransitionsDictionary : public TaggedHashTable { +public: + using HashTableT = TaggedHashTable; + static inline bool IsMatch([[maybe_unused]] const JSTaggedValue &key, + [[maybe_unused]] const JSTaggedValue &otherKey) + { + UNREACHABLE(); + } + static inline int Hash([[maybe_unused]] const JSTaggedValue &key) + { + UNREACHABLE(); + } + + static inline bool IsMatch(const JSTaggedValue &key, const JSTaggedValue &metaData, const JSTaggedValue &otherKey, + const JSTaggedValue &otherDetails) + { + return key == otherKey && metaData == otherDetails; + } + + static inline int Hash(const JSTaggedValue &key, const JSTaggedValue &metaData) + { + ASSERT(key.IsStringOrSymbol()); + + int hash = 0; + if (key.IsString()) { + hash = EcmaString::Cast(key.GetTaggedObject())->GetHashcode(); + } else if (key.IsSymbol()) { + hash = static_cast(JSSymbol::Cast(key.GetTaggedObject())->GetHashField()).GetInt(); + } + int metaDataHash = metaData.IsInt() ? metaData.GetInt() : static_cast(metaData.GetRawData()); + return hash + metaDataHash; + } + + inline static int GetKeyIndex(int entry) + { + return HashTableT::TABLE_HEADER_SIZE + entry * GetEntrySize() + ENTRY_KEY_INDEX; + } + inline static int GetValueIndex(int entry) + { + return HashTableT::TABLE_HEADER_SIZE + entry * GetEntrySize() + ENTRY_VALUE_INDEX; + } + inline static int GetEntryIndex(int entry) + { + return HashTableT::TABLE_HEADER_SIZE + entry * GetEntrySize(); + } + inline static int GetEntrySize() + { + return ENTRY_SIZE; + } + + static TransitionsDictionary *Cast(TaggedObject *object) + { + return reinterpret_cast(object); + } + + static constexpr int DEFAULT_ELEMENTS_NUMBER = 16; + static JSHandle Create(const JSThread *thread, + int numberOfElements = DEFAULT_ELEMENTS_NUMBER) + { + return HashTableT::Create(thread, numberOfElements); + } + + // Attempt to shrink the dictionary after deletion of key. + inline static JSHandle Shrink(const JSThread *thread, + const JSHandle &dictionary) + { + return HashTableT::Shrink(thread, dictionary, 0); + } + + inline JSTaggedValue GetAttributes(int entry) const + { + int index = GetEntryIndex(entry) + ENTRY_DETAILS_INDEX; + return HashTableT::Get(index); + } + inline void SetAttributes(const JSThread *thread, int entry, JSTaggedValue metaData) + { + int index = GetEntryIndex(entry) + ENTRY_DETAILS_INDEX; + HashTableT::Set(thread, index, metaData); + } + + inline void SetEntry(const JSThread *thread, int entry, const JSTaggedValue &key, const JSTaggedValue &value, + const JSTaggedValue &metaData) + { + SetKey(thread, entry, key); + SetValue(thread, entry, value); + SetAttributes(thread, entry, metaData); + } + + inline void RemoveElement(const JSThread *thread, int entry) + { + SetKey(thread, entry, JSTaggedValue::Hole()); + SetValue(thread, entry, JSTaggedValue::Hole()); + SetAttributes(thread, entry, JSTaggedValue::Hole()); + IncreaseHoleEntriesCount(thread); + } + + int FindEntry(const JSTaggedValue &key, const JSTaggedValue &metaData); + static JSHandle PutIfAbsent(const JSThread *thread, + const JSHandle &dictionary, + const JSHandle &key, + const JSHandle &value, + const JSHandle &metaData); + static JSHandle Remove(const JSThread *thread, const JSHandle &table, + const JSHandle &key, const JSTaggedValue &metaData); + void Rehash(const JSThread *thread, TransitionsDictionary *newTable); + + static constexpr int ENTRY_SIZE = 3; + static constexpr int ENTRY_KEY_INDEX = 0; + static constexpr int ENTRY_VALUE_INDEX = 1; + static constexpr int ENTRY_DETAILS_INDEX = 2; + DECL_DUMP() +}; +} // namespace panda::ecmascript +#endif diff --git a/runtime/vmstat/caller_stat.cpp b/runtime/vmstat/caller_stat.cpp new file mode 100644 index 000000000..d03570835 --- /dev/null +++ b/runtime/vmstat/caller_stat.cpp @@ -0,0 +1,71 @@ +/* + * Copyright (c) 2021-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 "caller_stat.h" + +namespace panda::ecmascript { +void PandaRuntimeTimer::Start(PandaRuntimeCallerStat *callerStat, PandaRuntimeTimer *parent) +{ + parent_ = parent; + callerStat_ = callerStat; + uint64_t nowTime = Now(); + if (parent != nullptr) { + parent_->Pause(nowTime); + } + Resume(nowTime); +} + +PandaRuntimeTimer *PandaRuntimeTimer::Stop() +{ + uint64_t nowTime = Now(); + Pause(nowTime); + if (parent_ != nullptr) { + parent_->Resume(nowTime); + } + UpdateCallerState(); + elapsed_ = 0; + return parent_; +} + +void PandaRuntimeTimer::Pause(uint64_t now) +{ + if (!IsStarted()) { + return; + } + elapsed_ += (now - start_); + start_ = 0; +} + +void PandaRuntimeTimer::Resume(uint64_t now) +{ + if (IsStarted()) { + return; + } + start_ = now; +} + +void PandaRuntimeTimer::Snapshot() +{ + uint64_t nowTime = Now(); + Pause(nowTime); + + PandaRuntimeTimer *timer = this; + while (timer != nullptr) { + timer->UpdateCallerState(); + timer = timer->parent_; + } + Resume(nowTime); +} +} // namespace panda::ecmascript \ No newline at end of file diff --git a/runtime/vmstat/caller_stat.h b/runtime/vmstat/caller_stat.h new file mode 100644 index 000000000..7ac969e09 --- /dev/null +++ b/runtime/vmstat/caller_stat.h @@ -0,0 +1,120 @@ +/* + * Copyright (c) 2021-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_VMSTAT_CALLER_STAT_H +#define ECMASCRIPT_VMSTAT_CALLER_STAT_H + +#include +#include +#include // NOLINTNEXTLINE(modernize-deprecated-headers) + +#include "plugins/ecmascript/runtime/mem/c_string.h" +#include "libpandabase/macros.h" + +namespace panda::ecmascript { +class EcmaRuntimeStat; +class PandaRuntimeCallerStat { +public: + // NOLINTNEXTLINE(modernize-pass-by-value) + explicit PandaRuntimeCallerStat(const CString &name) : name_(name) {} + PandaRuntimeCallerStat() = default; + virtual ~PandaRuntimeCallerStat() = default; + + DEFAULT_NOEXCEPT_MOVE_SEMANTIC(PandaRuntimeCallerStat); + DEFAULT_COPY_SEMANTIC(PandaRuntimeCallerStat); + + void UpdateState(uint64_t elapsed) + { + totalCount_++; + totalTime_ += elapsed; + maxTime_ = elapsed < maxTime_ ? maxTime_ : elapsed; + } + const char *Name() const + { + return name_.c_str(); + } + uint64_t TotalCount() const + { + return totalCount_; + } + uint64_t TotalTime() const + { + return totalTime_; + } + uint64_t MaxTime() const + { + return maxTime_; + } + + void Reset() + { + totalCount_ = 0; + totalTime_ = 0; + maxTime_ = 0; + } + +private: + CString name_{}; + uint64_t totalCount_{0}; + uint64_t totalTime_{0}; + uint64_t maxTime_{0}; +}; + +class PandaRuntimeTimer { +public: + void Start(PandaRuntimeCallerStat *callerStat, PandaRuntimeTimer *parent); + inline static uint64_t Now() + { + struct timespec timeNow = {0, 0}; + clock_gettime(CLOCK_REALTIME, &timeNow); + return timeNow.tv_sec * NANOSECONDSINSECOND + timeNow.tv_nsec; + } + + uint64_t Elapsed() const + { + return elapsed_; + } + + inline bool IsStarted() const + { + return start_ != 0; + } + + inline void SetParent(PandaRuntimeTimer *parent) + { + parent_ = parent; + } + + void Snapshot(); + + inline void UpdateCallerState() + { + callerStat_->UpdateState(elapsed_); + } + +private: + static constexpr uint64_t NANOSECONDSINSECOND = 1000000000; + PandaRuntimeTimer *Stop(); + void Pause(uint64_t now); + void Resume(uint64_t now); + PandaRuntimeCallerStat *callerStat_{nullptr}; + PandaRuntimeTimer *parent_{nullptr}; + uint64_t start_{0}; + uint64_t elapsed_{0}; + + friend class EcmaRuntimeStat; +}; +} // namespace panda::ecmascript +#endif diff --git a/runtime/vmstat/runtime_stat.cpp b/runtime/vmstat/runtime_stat.cpp new file mode 100644 index 000000000..9d0659878 --- /dev/null +++ b/runtime/vmstat/runtime_stat.cpp @@ -0,0 +1,96 @@ +/* + * Copyright (c) 2021-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 "runtime_stat.h" + +#include + +#include "plugins/ecmascript/runtime/ecma_macros.h" +#include "plugins/ecmascript/runtime/runtime_call_id.h" + +namespace panda::ecmascript { +// NOLINTNEXTLINE(modernize-avoid-c-arrays) +EcmaRuntimeStat::EcmaRuntimeStat(const char *const runtimeCallerNames[], int count) +{ + for (int i = 0; i < count; i++) { + // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-pointer-arithmetic) + callerStat_.emplace_back(PandaRuntimeCallerStat(CString(runtimeCallerNames[i]))); + } +} + +void EcmaRuntimeStat::StartCount(PandaRuntimeTimer *timer, int callerId) +{ + if (currentTimer_ != nullptr) { + timer->SetParent(currentTimer_); + } + PandaRuntimeTimer *parent = currentTimer_; + currentTimer_ = timer; + PandaRuntimeCallerStat *callerStat = &callerStat_[callerId]; + timer->Start(callerStat, parent); +} + +void EcmaRuntimeStat::StopCount(const PandaRuntimeTimer *nowTimer) +{ + if (nowTimer != currentTimer_) { + return; + } + PandaRuntimeTimer *parentTimer = currentTimer_->Stop(); + currentTimer_ = parentTimer; +} + +void EcmaRuntimeStat::Print() const +{ + if (currentTimer_ != nullptr) { + currentTimer_->Snapshot(); + } + LOG_ECMA(ERROR) << GetAllStats(); +} + +void EcmaRuntimeStat::ResetAllCount() +{ + while (currentTimer_ != nullptr) { + StopCount(currentTimer_); + } + for (auto &runCallerStat : callerStat_) { + runCallerStat.Reset(); + } +} + +CString EcmaRuntimeStat::GetAllStats() const +{ + CStringStream statistic; + statistic << "panda runtime stat:" << std::endl; + static constexpr int nameRightAdjustment = 50; + static constexpr int numberRightAdjustment = 20; + statistic << std::right << std::setw(nameRightAdjustment) << "InterPreter && GC && C++ Builtin Function" + << std::setw(numberRightAdjustment) << "Time(ns)" << std::setw(numberRightAdjustment) << "Count" + << std::setw(numberRightAdjustment) << "MaxTime(ns)" + << std::setw(numberRightAdjustment) << "AverageTime(ns)" << std::endl; + + statistic << "===========================================================================================" + << "=======================================" << std::endl; + for (auto &runCallerStat : callerStat_) { + if (runCallerStat.TotalCount() != 0) { + statistic << std::right << std::setw(nameRightAdjustment) << runCallerStat.Name() + << std::setw(numberRightAdjustment) << runCallerStat.TotalTime() + << std::setw(numberRightAdjustment) << runCallerStat.TotalCount() + << std::setw(numberRightAdjustment) << runCallerStat.MaxTime() + << std::setw(numberRightAdjustment) << runCallerStat.TotalTime() / runCallerStat.TotalCount() + << std::endl; + } + } + return statistic.str(); +} +} // namespace panda::ecmascript diff --git a/runtime/vmstat/runtime_stat.h b/runtime/vmstat/runtime_stat.h new file mode 100644 index 000000000..56f80b0d6 --- /dev/null +++ b/runtime/vmstat/runtime_stat.h @@ -0,0 +1,80 @@ +/* + * Copyright (c) 2021-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_VMSTAT_RUNTIME_STAT_H +#define ECMASCRIPT_VMSTAT_RUNTIME_STAT_H + +#include "plugins/ecmascript/runtime/ecma_vm.h" +#include "plugins/ecmascript/runtime/js_thread.h" +#include "plugins/ecmascript/runtime/mem/c_containers.h" +#include "plugins/ecmascript/runtime/vmstat/caller_stat.h" + +namespace panda::ecmascript { +class EcmaRuntimeStat { +public: + // NOLINTNEXTLINE(modernize-avoid-c-arrays) + explicit EcmaRuntimeStat(const char * const runtimeCallerNames[], int count); + EcmaRuntimeStat() = default; + virtual ~EcmaRuntimeStat() = default; + + DEFAULT_NOEXCEPT_MOVE_SEMANTIC(EcmaRuntimeStat); + DEFAULT_COPY_SEMANTIC(EcmaRuntimeStat); + + void StartCount(PandaRuntimeTimer *timer, int callerId); + void StopCount(const PandaRuntimeTimer *timer); + CString GetAllStats() const; + void ResetAllCount(); + void Print() const; + +private: + PandaRuntimeTimer *currentTimer_ = nullptr; + CVector callerStat_{}; +}; + +class RuntimeTimerScope { +public: + explicit RuntimeTimerScope(JSThread *thread, int callerId, EcmaRuntimeStat *stat) + { + bool statEnabled = thread->GetEcmaVM()->IsRuntimeStatEnabled(); + if (!statEnabled || stat == nullptr) { + return; + } + stats_ = stat; + stats_->StartCount(&timer_, callerId); + } + RuntimeTimerScope(const EcmaVM *vm, int callerId, EcmaRuntimeStat *stat) + { + bool statEnabled = vm->IsRuntimeStatEnabled(); + if (!statEnabled || stat == nullptr) { + return; + } + stats_ = stat; + stats_->StartCount(&timer_, callerId); + } + ~RuntimeTimerScope() + { + if (stats_ != nullptr) { + stats_->StopCount(&timer_); + } + } + NO_COPY_SEMANTIC(RuntimeTimerScope); + NO_MOVE_SEMANTIC(RuntimeTimerScope); + +private: + PandaRuntimeTimer timer_{}; + EcmaRuntimeStat *stats_{nullptr}; +}; +} // namespace panda::ecmascript +#endif diff --git a/runtime/weak_vector-inl.h b/runtime/weak_vector-inl.h new file mode 100644 index 000000000..1aa93ab0b --- /dev/null +++ b/runtime/weak_vector-inl.h @@ -0,0 +1,61 @@ +/* + * Copyright (c) 2021-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_WEAK_VECTOR_INL_H +#define ECMASCRIPT_WEAK_VECTOR_INL_H + +#include "weak_vector.h" +#include "tagged_array-inl.h" + +namespace panda::ecmascript { +uint32_t WeakVector::GetEnd() const +{ + return TaggedArray::Get(END_INDEX).GetArrayLength(); +} + +bool WeakVector::Full() const +{ + return GetEnd() == GetCapacity(); +} + +bool WeakVector::Empty() const +{ + return GetEnd() == 0; +} + +uint32_t WeakVector::GetCapacity() const +{ + return TaggedArray::GetLength() - ELEMENTS_START_INDEX; +} + +JSTaggedValue WeakVector::Get(uint32_t index) const +{ + ASSERT(index < GetCapacity()); + return TaggedArray::Get(VectorToArrayIndex(index)); +} + +void WeakVector::Set(const JSThread *thread, uint32_t index, JSTaggedValue value) +{ + ASSERT(index < GetCapacity()); + TaggedArray::Set(thread, VectorToArrayIndex(index), value); +} + +void WeakVector::SetEnd(const JSThread *thread, uint32_t end) +{ + ASSERT(end <= GetCapacity()); + TaggedArray::Set(thread, END_INDEX, JSTaggedValue(end)); +} +} // namespace panda::ecmascript +#endif // ECMASCRIPT_WEAK_VECTOR_INL_H \ No newline at end of file diff --git a/runtime/weak_vector.cpp b/runtime/weak_vector.cpp new file mode 100644 index 000000000..1684cc98d --- /dev/null +++ b/runtime/weak_vector.cpp @@ -0,0 +1,72 @@ +/* + * Copyright (c) 2021-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 "weak_vector.h" +#include "plugins/ecmascript/runtime/object_factory.h" +#include "plugins/ecmascript/runtime/weak_vector-inl.h" + +namespace panda::ecmascript { +JSHandle WeakVector::Create(const JSThread *thread, uint32_t capacity) +{ + ASSERT(capacity < MAX_VECTOR_INDEX); + + uint32_t length = VectorToArrayIndex(capacity); + JSHandle vector = JSHandle(thread->GetEcmaVM()->GetFactory()->NewTaggedArray(length)); + + vector->SetEnd(thread, 0); + return vector; +} + +bool WeakVector::Delete(const JSThread *thread, uint32_t index) +{ + uint32_t end = GetEnd(); + if (index < end) { + Set(thread, index, JSTaggedValue::Hole()); + return true; + } + return false; +} + +JSHandle WeakVector::Grow(const JSThread *thread, const JSHandle &old, uint32_t newCapacity) +{ + uint32_t oldCapacity = old->GetCapacity(); + ASSERT(newCapacity > oldCapacity); + if (oldCapacity == MAX_VECTOR_INDEX) { + return old; + } + + if (newCapacity > MAX_VECTOR_INDEX) { + newCapacity = MAX_VECTOR_INDEX; + } + + ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); + JSHandle newVec = factory->CopyArray(JSHandle(old), VectorToArrayIndex(oldCapacity), + VectorToArrayIndex(newCapacity)); + + return JSHandle(newVec); +} + +uint32_t WeakVector::PushBack(const JSThread *thread, JSTaggedValue value) +{ + uint32_t end = GetEnd(); + if (end == GetCapacity()) { + return TaggedArray::MAX_ARRAY_INDEX; + } + + Set(thread, end, value); + SetEnd(thread, end + 1); + return end; +} +} // namespace panda::ecmascript diff --git a/runtime/weak_vector.h b/runtime/weak_vector.h new file mode 100644 index 000000000..2495a7c7f --- /dev/null +++ b/runtime/weak_vector.h @@ -0,0 +1,64 @@ +/* + * Copyright (c) 2021-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_WEAK_VECTOR_H +#define ECMASCRIPT_WEAK_VECTOR_H + +#include "plugins/ecmascript/runtime/js_handle.h" +#include "plugins/ecmascript/runtime/js_thread.h" +#include "plugins/ecmascript/runtime/tagged_array.h" + +namespace panda::ecmascript { +class WeakVector : public TaggedArray { +public: + static WeakVector *Cast(ObjectHeader *object) + { + return static_cast(object); + } + + static constexpr uint32_t DEFALUT_CAPACITY = 4; + static JSHandle Create(const JSThread *thread, uint32_t capacity = DEFALUT_CAPACITY); + static JSHandle Grow(const JSThread *thread, const JSHandle &old, uint32_t newCapacity); + uint32_t PushBack(const JSThread *thread, JSTaggedValue value); + // just set index value to Hole + bool Delete(const JSThread *thread, uint32_t index); + + inline uint32_t GetEnd() const; + + inline bool Full() const; + + inline bool Empty() const; + + inline uint32_t GetCapacity() const; + + inline JSTaggedValue Get(uint32_t index) const; + + inline void Set(const JSThread *thread, uint32_t index, JSTaggedValue value); + +private: + static const uint32_t MIN_CAPACITY = 2; + static const uint32_t END_INDEX = 0; + static const uint32_t ELEMENTS_START_INDEX = 1; + static const uint32_t MAX_VECTOR_INDEX = TaggedArray::MAX_ARRAY_INDEX - ELEMENTS_START_INDEX; + + inline static constexpr uint32_t VectorToArrayIndex(uint32_t index) + { + return index + ELEMENTS_START_INDEX; + } + + inline void SetEnd(const JSThread *thread, uint32_t end); +}; +} // namespace panda::ecmascript +#endif // ECMASCRIPT_WEAK_VECTOR_H diff --git a/runtime_options.yaml b/runtime_options.yaml new file mode 100644 index 000000000..95c135d5d --- /dev/null +++ b/runtime_options.yaml @@ -0,0 +1,187 @@ +# Copyright (c) 2022-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. + +options: +- name: boot-intrinsic-spaces + type: arg_list_t + default: + - ecmascript + possible_values: + - ecmascript + description: Load specified intrinsic spaces + delimiter: ":" + +- name: boot-class-spaces + type: arg_list_t + default: + - core + possible_values: + - core + - ecmascript + description: Load specified class spaces + delimiter: ":" + +- name: runtime-type + type: std::string + default: core + possible_values: + - core + - ecmascript + description: Runtime type + +- name: load-runtimes + type: arg_list_t + default: + - core + possible_values: + - core + - ecmascript + description: Load specified class and intrinsic spaces and define runtime type + delimiter: ":" + +- name: run-gc-in-place + lang: + - ecmascript + type: bool + default: false + description: Enable/disable running GC only in place + +- name: log-components + type: arg_list_t + default: + - all + possible_values: + - all + - ecmascript + description: '[DEPRECATED] Option was moved to libpandabase/options.yaml, use base_options::Options instead of runtime options.' + delimiter: ":" + deprecated: true + +- name: gc-type + lang: + - ecmascript + type: std::string + default: g1-gc + possible_values: + - epsilon + - stw + - gen-gc + - g1-gc + description: Type of used GC + +- name: gc-trigger-type + lang: + - ecmascript + type: std::string + default: heap-trigger-test + possible_values: + - heap-trigger-test + - heap-trigger + - adaptive-heap-trigger + - trigger-heap-occupancy + - no-gc-for-start-up + - debug + - debug-never + description: Type of used GC trigger + +- name: skip-startup-gc-count + lang: + - ecmascript + type: uint32_t + default: 10 + description: No stw gc count for no-gc-for-start-up + +- name: gc-debug-trigger-start + lang: + - ecmascript + type: uint64_t + default: 0 + description: First iteration to start returning true in debug trigger + +- name: gc-dump-heap + lang: + - ecmascript + type: bool + default: false + description: Dump heap before and after GC + +- name: reference-processor-enable + type: bool + default: true + description: Enables/disables ReferenceProcessor + +- name: gc-enable-tracing + lang: + - ecmascript + type: bool + default: false + description: enables/disables tracing gc + +- name: native-gc-trigger-type + lang: + - ecmascript + type: std::string + default: simple-strategy + possible_values: + - no-native-gc-trigger + - simple-strategy + description: Type of native gc trigger + +- name: enable-fast-heap-verifier + lang: + - ecmascript + type: bool + default: true + description: Enable fast heap verifier, which is faster than ordinary heap verifier but uses more internal memory + +- name: fail-on-heap-verification + lang: + - ecmascript + type: bool + default: false + description: if enabled then fail execution if heap verifier found heap corruption + +- name: pre-gc-heap-verify-enabled + lang: + - ecmascript + type: bool + default: false + description: whether verify heap before GC + +- name: into-gc-heap-verify-enabled + lang: + - ecmascript + type: bool + default: false + description: whether verify heap during GC + +- name: post-gc-heap-verify-enabled + lang: + - ecmascript + type: bool + default: false + description: whether verify heap after GC + +- name: concurrent-gc-enabled + lang: + - ecmascript + type: bool + default: true + description: whether concurrent GC is enabled + +- name: enable-paralled-young-gc + lang: + - ecmascript + type: bool + default: true + description: enable-paralled-young-gc diff --git a/subproject_sources.gn b/subproject_sources.gn new file mode 100644 index 000000000..466f34311 --- /dev/null +++ b/subproject_sources.gn @@ -0,0 +1,36 @@ +# Copyright (c) 2021-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. + +srcs_assembler_path = "assembler/assembler_sources.gn" +srcs_isa_path = "isa/isa_sources.gn" +srcs_runtime_path = "runtime/runtime_sources.gn" +srcs_bytecode_optimizer_path = "bytecode_optimizer/bytecodeopt_sources.gn" +srcs_compiler_path = "compiler/compiler_sources.gn" +option_yaml_path = "ecmascript_plugin_options.yaml" +inst_templates_yaml_path = "compiler/optimizer/ir_builder/ecmascript_inst_templates.yaml" +runtime_option_yaml_path = "runtime_options.yaml" +irtoc_plugins = "irtoc_scripts/irtoc_scripts.gn" +arkruntime_header_sub_deps = [ + ":ecma_intrinsics_gen_arkruntime", + ":ecmastblib_inline_h", +] +arkcompiler_deps = [ + ":isa_gen_ecma_compiler_ecmascript_inst_builder_gen_cpp", +] +arkbytecodeopt_deps = [ + ":isa_gen_ecma_bytecodeopt_ecmascript_codegen_intrinsics_gen_inc", +] +arkruntime_deps = [ + "../../ark-third-party/icu:shared_icui18n", + "../../ark-third-party/icu:shared_icuuc", +] diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt new file mode 100644 index 000000000..a4d8dfd61 --- /dev/null +++ b/tests/CMakeLists.txt @@ -0,0 +1,273 @@ +# Copyright (c) 2021-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. + +function(add_test_file_ecma) + set(prefix ARG) + set(noValues DISABLE_LIMIT_STD_ALLOC SKIP_AOT SKIP_OSR SKIP_VERIFICATION VERIFIER_FAIL_TEST) + set(singleValues FILE DEBUG_LOG_MESSAGE DEBUG_LOG_MESSAGE_ENFORCED_JIT GC_OPTIONS) + set(multiValues EXPECTED_STDOUT VERIFIER_EXPECTED_STDOUT VERIFIER_SEARCH_STDERR ARGUMENTS RUNTIME_OPTIONS COMPILER_OPTIONS PRLIMIT_OPTIONS) + cmake_parse_arguments(${prefix} + "${noValues}" + "${singleValues}" + "${multiValues}" + ${ARGN}) + + + if (NOT DEFINED ARG_FILE) + message(FATAL_ERROR "Mandatory FILE argument is not defined.") + endif() + + get_filename_component(target "${ARG_FILE}" NAME) + get_filename_component(path "${ARG_FILE}" DIRECTORY) + get_filename_component(suite ${path} NAME) + + if (NOT ARG_DISABLE_LIMIT_STD_ALLOC) + set(ARG_RUNTIME_OPTIONS "${ARG_RUNTIME_OPTIONS} --limit-standard-alloc=true") + endif() + + if(DEFINED ARG_GC_OPTIONS) + set(ARG_RUNTIME_OPTIONS "${ARG_RUNTIME_OPTIONS}" "${ARG_GC_OPTIONS}") + endif() + + if (PANDA_ENABLE_EVENTS) + set(ARG_RUNTIME_OPTIONS "${ARG_RUNTIME_OPTIONS} --events-output=csv") + endif() + + #TODO: add test suite to check with PANDA_COMPILER_TARGET + if (((PANDA_TARGET_ARM64) OR (PANDA_TARGET_ARM32) OR (PANDA_TARGET_AMD64)) AND DEFINED ARG_DEBUG_LOG_MESSAGE_ENFORCED_JIT) + set(CUR_RUNTIME_OPTIONS "${ARG_RUNTIME_OPTIONS}" "--log-level=debug") + set(CUR_JIT_DEBUG_LOG_MESSAGE "${ARG_DEBUG_LOG_MESSAGE_ENFORCED_JIT}") + elseif(DEFINED ARG_DEBUG_LOG_MESSAGE) + set(CUR_RUNTIME_OPTIONS "${ARG_RUNTIME_OPTIONS}" "--log-level=debug") + set(CUR_JIT_DEBUG_LOG_MESSAGE "${ARG_DEBUG_LOG_MESSAGE}") + set(CUR_DEBUG_LOG_MESSAGE "${ARG_DEBUG_LOG_MESSAGE}") + else() + set(CUR_RUNTIME_OPTIONS "${ARG_RUNTIME_OPTIONS}") + endif() + + set(language_context "ecmascript") + set(ARG_SKIP_AOT TRUE) # aot mode is not supported yet + set(ARG_SKIP_VERIFICATION TRUE) #verification is not supported yet + set(ARG_SKIP_OSR TRUE) #osr is not supported yet + set(CUR_RUNTIME_OPTIONS "${ARG_RUNTIME_OPTIONS}" "--run-gc-in-place=true") # ecma vm doesn't support concurrent GC + + if (ARG_VERIFIER_FAIL_TEST) + set(VERIFIER_FAIL_TEST VERIFIER_FAIL_TEST) + else() + set(VERIFIER_FAIL_TEST) + endif() + + if (NOT ARG_SKIP_VERIFICATION) + verifier_add_test_run( + FILE "${ARG_FILE}" + TARGET ${target}-verifier + SUBDIR ${suite}-verifier + ${VERIFIER_FAIL_TEST} + EXPECTED_STDOUT "${ARG_VERIFIER_EXPECTED_STDOUT}" + SEARCH_STDERR "${ARG_VERIFIER_SEARCH_STDERR}" + LANGUAGE_CONTEXT "${language_context}" + ) + add_dependencies(${suite} ${target}-verifier) + endif() + + panda_add_test_run( + FILE "${ARG_FILE}" + TARGET ${target}-stw + SUBDIR ${suite}-stw + EXPECTED_STDOUT "${ARG_EXPECTED_STDOUT}" + SEARCH_DEBUG_STDERR ${CUR_DEBUG_LOG_MESSAGE} + RUNTIME_OPTIONS "${CUR_RUNTIME_OPTIONS}" "--compiler-enable-jit=false --gc-type=stw" + COMPILER_OPTIONS "${ARG_COMPILER_OPTIONS}" + ARGUMENTS "${ARG_ARGUMENTS}" + LANGUAGE_CONTEXT "${language_context}" + AOT_MODE FALSE + PRLIMIT_OPTIONS "${ARG_PRLIMIT_OPTIONS}" + ) + add_dependencies(${suite} ${target}-stw) + + + panda_add_test_run( + FILE "${ARG_FILE}" + TARGET ${target}-gengc + SUBDIR ${suite}-gengc + EXPECTED_STDOUT "${ARG_EXPECTED_STDOUT}" + SEARCH_DEBUG_STDERR ${CUR_DEBUG_LOG_MESSAGE} + RUNTIME_OPTIONS "${CUR_RUNTIME_OPTIONS}" "--compiler-enable-jit=false --gc-type=gen-gc" + COMPILER_OPTIONS "${ARG_COMPILER_OPTIONS}" + ARGUMENTS "${ARG_ARGUMENTS}" + LANGUAGE_CONTEXT "${language_context}" + AOT_MODE FALSE + PRLIMIT_OPTIONS "${ARG_PRLIMIT_OPTIONS}" + ) + add_dependencies(${suite} ${target}-gengc) + + # TODO(dtrubenkov): remove this if after enabling G1GC + if (PANDA_ENABLE_G1GC_TESTS) + panda_add_test_run( + FILE "${ARG_FILE}" + TARGET ${target}-g1gc + SUBDIR ${suite}-g1gc + EXPECTED_STDOUT "${ARG_EXPECTED_STDOUT}" + SEARCH_DEBUG_STDERR ${CUR_DEBUG_LOG_MESSAGE} + RUNTIME_OPTIONS "${CUR_RUNTIME_OPTIONS}" "--compiler-enable-jit=false --gc-type=g1-gc" + COMPILER_OPTIONS "${ARG_COMPILER_OPTIONS}" + ARGUMENTS "${ARG_ARGUMENTS}" + LANGUAGE_CONTEXT "${language_context}" + AOT_MODE FALSE + PRLIMIT_OPTIONS "${ARG_PRLIMIT_OPTIONS}" + ) + add_dependencies(${suite} ${target}-g1gc) + endif() + + # Check if JIT-compilation wasn't turned off explicitly: + if (PANDA_COMPILER_ENABLE AND NOT CUR_RUNTIME_OPTIONS MATCHES "^.*--compiler-enable-jit=false.*$") + panda_add_test_run( + FILE "${ARG_FILE}" + TARGET ${target}-enforce-jit-compiler + SUBDIR ${suite}-enforce-jit-compiler + EXPECTED_STDOUT "${ARG_EXPECTED_STDOUT}" + SEARCH_DEBUG_STDERR ${CUR_JIT_DEBUG_LOG_MESSAGE} + RUNTIME_OPTIONS "${CUR_RUNTIME_OPTIONS}" "--compiler-enable-jit=true --compiler-hotness-threshold=0 --no-async-jit=true" + COMPILER_OPTIONS "${ARG_COMPILER_OPTIONS}" + ARGUMENTS "${ARG_ARGUMENTS}" + LANGUAGE_CONTEXT "${language_context}" + AOT_MODE FALSE + PRLIMIT_OPTIONS "${ARG_PRLIMIT_OPTIONS}" + ) + add_dependencies(${suite} ${target}-enforce-jit-compiler) + add_dependencies(cts-jit-tests ${target}-enforce-jit-compiler) + + if (PANDA_TARGET_ARM64 AND NOT ARG_SKIP_OSR) + panda_add_test_run( + FILE "${ARG_FILE}" + TARGET ${target}-osr-jit + SUBDIR ${suite}-osr-jit + EXPECTED_STDOUT "${ARG_EXPECTED_STDOUT}" + RUNTIME_OPTIONS "${CUR_RUNTIME_OPTIONS}" "--compiler-enable-jit=true --compiler-hotness-threshold=2 --compiler-enable-osr=true" + COMPILER_OPTIONS "${ARG_COMPILER_OPTIONS}" + ARGUMENTS "${ARG_ARGUMENTS}" + LANGUAGE_CONTEXT "${language_context}" + AOT_MODE FALSE + PRLIMIT_OPTIONS "${ARG_PRLIMIT_OPTIONS}" + ) + add_dependencies(${suite} ${target}-osr-jit) + add_dependencies(cts-jit-tests ${target}-osr-jit) + endif() + endif() + + if (PANDA_TARGET_AMD64 AND PANDA_CI_TESTING_MODE STREQUAL "Nightly") + panda_add_test_run( + FILE "${ARG_FILE}" + TARGET ${target}-arm32 + SUBDIR ${suite}-arm32 + EXPECTED_STDOUT "${ARG_EXPECTED_STDOUT}" + SEARCH_DEBUG_STDERR ${CUR_DEBUG_LOG_MESSAGE} + RUNTIME_OPTIONS "${CUR_RUNTIME_OPTIONS}" "--compiler-cross-arch=arm" + COMPILER_OPTIONS "${ARG_COMPILER_OPTIONS}" + ARGUMENTS "${ARG_ARGUMENTS}" + LANGUAGE_CONTEXT "${language_context}" + AOT_MODE FALSE + PRLIMIT_OPTIONS "${ARG_PRLIMIT_OPTIONS}" + ) + add_dependencies(${suite} ${target}-arm32) + + panda_add_test_run( + FILE "${ARG_FILE}" + TARGET ${target}-arm32-enforce-jit-compiler + SUBDIR ${suite}-arm32-enforce-jit-compiler + EXPECTED_STDOUT "${ARG_EXPECTED_STDOUT}" + SEARCH_DEBUG_STDERR ${CUR_JIT_DEBUG_LOG_MESSAGE} + RUNTIME_OPTIONS "${CUR_RUNTIME_OPTIONS}" "--compiler-hotness-threshold=0 --no-async-jit=true --compiler-cross-arch=arm" + COMPILER_OPTIONS "${ARG_COMPILER_OPTIONS}" + ARGUMENTS "${ARG_ARGUMENTS}" + LANGUAGE_CONTEXT "${language_context}" + AOT_MODE FALSE + PRLIMIT_OPTIONS "${ARG_PRLIMIT_OPTIONS}" + ) + add_dependencies(${suite} ${target}-arm32-enforce-jit-compiler) + + panda_add_test_run( + FILE "${ARG_FILE}" + TARGET ${target}-arm64 + SUBDIR ${suite}-arm64 + EXPECTED_STDOUT "${ARG_EXPECTED_STDOUT}" + SEARCH_DEBUG_STDERR ${CUR_DEBUG_LOG_MESSAGE} + RUNTIME_OPTIONS "${CUR_RUNTIME_OPTIONS}" "--compiler-cross-arch=arm64" + COMPILER_OPTIONS "${ARG_COMPILER_OPTIONS}" + ARGUMENTS "${ARG_ARGUMENTS}" + LANGUAGE_CONTEXT "${language_context}" + AOT_MODE FALSE + PRLIMIT_OPTIONS "${ARG_PRLIMIT_OPTIONS}" + ) + add_dependencies(${suite} ${target}-arm64) + + panda_add_test_run( + FILE "${ARG_FILE}" + TARGET ${target}-arm64-enforce-jit-compiler + SUBDIR ${suite}-arm64-enforce-jit-compiler + EXPECTED_STDOUT "${ARG_EXPECTED_STDOUT}" + SEARCH_DEBUG_STDERR ${CUR_JIT_DEBUG_LOG_MESSAGE} + RUNTIME_OPTIONS "${CUR_RUNTIME_OPTIONS}" "--compiler-hotness-threshold=0 --no-async-jit=true --compiler-cross-arch=arm64" + COMPILER_OPTIONS "${ARG_COMPILER_OPTIONS}" + ARGUMENTS "${ARG_ARGUMENTS}" + LANGUAGE_CONTEXT "${language_context}" + AOT_MODE FALSE + PRLIMIT_OPTIONS "${ARG_PRLIMIT_OPTIONS}" + ) + add_dependencies(${suite} ${target}-arm64-enforce-jit-compiler) + endif() + + if ((PANDA_TARGET_ARM64 OR PANDA_TARGET_AMD64) AND NOT ARG_SKIP_AOT) + panda_add_test_run( + FILE "${ARG_FILE}" + TARGET ${target}-aot + SUBDIR ${suite}-aot + EXPECTED_STDOUT "${ARG_EXPECTED_STDOUT}" + RUNTIME_OPTIONS "${CUR_RUNTIME_OPTIONS}" + COMPILER_OPTIONS "${ARG_COMPILER_OPTIONS}" + GC_OPTIONS "${ARG_GC_OPTIONS}" + ARGUMENTS "${ARG_ARGUMENTS}" + LANGUAGE_CONTEXT "${language_context}" + AOT_MODE TRUE + PRLIMIT_OPTIONS "${ARG_PRLIMIT_OPTIONS}" + ) + add_dependencies(${suite} ${target}-aot) + add_dependencies(cts-aot-tests ${target}-aot) + endif() + +endfunction() + +if(CMAKE_CROSSCOMPILING) + ExternalProject_Get_Property(panda_host_tools binary_dir) + set(es2panda_target build_host_tools) + set(es2panda_bin "${binary_dir}/plugins/ecmascript/es2panda/aot/es2panda") +else() + set(es2panda_target es2panda) + set(es2panda_bin $) +endif() + +if (CMAKE_CROSSCOMPILING AND PANDA_TARGET_ARM64) +# ecmascript tests +# add_test_file_ecma(FILE "${CMAKE_CURRENT_SOURCE_DIR}/ecmascript-tests/js-bitops-bitwise-and.pa" COMPILER_OPTIONS --compiler-hotness-threshold=0) +endif() + +add_subdirectory(runtime) + +if(PANDA_WITH_COMPILER) + add_subdirectory(compiler) + add_subdirectory(bytecode_optimizer) +endif() + +add_subdirectory(checked) +add_subdirectory(assembler) +add_subdirectory(disassembler) diff --git a/tests/assembler/CMakeLists.txt b/tests/assembler/CMakeLists.txt new file mode 100644 index 000000000..912203cf2 --- /dev/null +++ b/tests/assembler/CMakeLists.txt @@ -0,0 +1,29 @@ +# Copyright (c) 2021-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. + +panda_add_gtest( + NAME assembler_tests_ecma + SOURCES + parser_test_ecmascript.cpp + emitter_test_ecmascript.cpp + INCLUDE_DIRS + ${PANDA_ROOT}/assembler + LIBRARIES + arkbase arkassembler + SANITIZERS + ${PANDA_SANITIZERS_LIST} +) + +if(TARGET assembler_tests_ecma) + target_compile_options(assembler_tests_ecma PUBLIC "-Wno-ignored-attributes") +endif() diff --git a/tests/assembler/emitter_test_ecmascript.cpp b/tests/assembler/emitter_test_ecmascript.cpp new file mode 100644 index 000000000..514613e54 --- /dev/null +++ b/tests/assembler/emitter_test_ecmascript.cpp @@ -0,0 +1,106 @@ +/* + * Copyright (c) 2021-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 +#include +#include + +#include +#include +#include "annotation_data_accessor.h" +#include "assembly-emitter.h" +#include "assembly-parser.h" +#include "class_data_accessor-inl.h" +#include "code_data_accessor-inl.h" +#include "debug_data_accessor-inl.h" +#include "debug_info_extractor.h" +#include "field_data_accessor-inl.h" +#include "file_items.h" +#include "lexer.h" +#include "method_data_accessor-inl.h" +#include "param_annotations_data_accessor.h" +#include "proto_data_accessor-inl.h" +#include "utils/span.h" +#include "utils/leb128.h" +#include "utils/utf.h" + +namespace panda::test { + +using namespace panda::pandasm; + +static const uint8_t *GetTypeDescriptor(const std::string &name, std::string *storage) +{ + *storage = "L" + name + ";"; + std::replace(storage->begin(), storage->end(), '.', '/'); + return utf::CStringAsMutf8(storage->c_str()); +} + +TEST(emittertests, get_GLOBAL_lang_for_JS_func) +{ + Parser p; + auto source = R"( + .language ECMAScript + + .function any main() { + return.dyn + } + )"; + + auto res = p.Parse(source); + ASSERT_EQ(p.ShowError().err, Error::ErrorType::ERR_NONE); + + auto pf = AsmEmitter::Emit(res.Value()); + ASSERT_NE(pf, nullptr); + + std::string descriptor; + + auto class_id = pf->GetClassId(GetTypeDescriptor("_GLOBAL", &descriptor)); + ASSERT_TRUE(class_id.IsValid()); + + panda_file::ClassDataAccessor cda(*pf, class_id); + + ASSERT_TRUE(cda.GetSourceLang().has_value()); + ASSERT_EQ(cda.GetSourceLang(), panda_file::SourceLang::ECMASCRIPT); +} + +TEST(emittertests, ecmascript_debuginfo) +{ + Parser p; + + auto source = R"( + .language ECMAScript + .function void main() { + return.void + } + )"; + + std::string source_filename = "source.pa"; + auto res = p.Parse(source, source_filename); + ASSERT_EQ(p.ShowError().err, Error::ErrorType::ERR_NONE); + + auto pf = AsmEmitter::Emit(res.Value()); + ASSERT_NE(pf, nullptr); + + panda_file::DebugInfoExtractor extractor(pf.get()); + auto methods = extractor.GetMethodIdList(); + ASSERT_EQ(methods.size(), 1); + auto lineTable = extractor.GetLineNumberTable(methods[0]); + auto columnTable = extractor.GetColumnNumberTable(methods[0]); + + EXPECT_EQ(lineTable.size(), 1); + EXPECT_EQ(columnTable.size(), 1); +} + +} // namespace panda::test diff --git a/tests/assembler/parser_test_ecmascript.cpp b/tests/assembler/parser_test_ecmascript.cpp new file mode 100644 index 000000000..d42dd2f1c --- /dev/null +++ b/tests/assembler/parser_test_ecmascript.cpp @@ -0,0 +1,43 @@ +/* + * Copyright (c) 2021-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 "operand_types_print.h" + +#include +#include + +using namespace panda::pandasm; + +TEST(parsertests, calli_dyn_3args) +{ + { + Parser p; + std::string source = R"( + .language ECMAScript + + # a0 - function, a1 - this + .function any main(any a0, any a1) { + calli.dyn.short 1, a0, a1 + return.dyn + } + )"; + + auto res = p.Parse(source); + + Error e = p.ShowError(); + + ASSERT_EQ(e.err, Error::ErrorType::ERR_NONE); + } +} \ No newline at end of file diff --git a/tests/bytecode_optimizer/CMakeLists.txt b/tests/bytecode_optimizer/CMakeLists.txt new file mode 100644 index 000000000..2ccbb48a9 --- /dev/null +++ b/tests/bytecode_optimizer/CMakeLists.txt @@ -0,0 +1,39 @@ +# Copyright (c) 2021-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. + +set(PANDA_BYTECODE_OPT_TESTS_ECMA_LIBRARIES arkbytecodeopt arkfile arkbase) +if (NOT (PANDA_TARGET_MOBILE OR PANDA_TARGET_OHOS OR PANDA_ENABLE_FUZZBENCH)) + list(APPEND PANDA_BYTECODE_OPT_TESTS_ECMA_LIBRARIES stdc++fs) +endif() + +set(BYTECODE_OPT_TEST_ECMA_SOURCES + codegen_test_ecmascript.cpp + reg_encoder_test_ecmascript.cpp +) + +panda_add_gtest( + CONTAINS_MAIN + NAME bytecodeopt_unit_tests_ecma + SOURCES + ${BYTECODE_OPT_TEST_ECMA_SOURCES} + INCLUDE_DIRS + ${PANDA_ROOT}/bytecode_optimizer/ + ${PANDA_ROOT}/bytecode_optimizer/tests + ${PANDA_ROOT} + LIBRARIES + ${PANDA_BYTECODE_OPT_TESTS_ECMA_LIBRARIES} + SANITIZERS + ${PANDA_SANITIZERS_LIST} +) + +add_dependencies(bytecode_optimizer_coverage bytecodeopt_unit_tests_ecma) diff --git a/tests/bytecode_optimizer/codegen_test_ecmascript.cpp b/tests/bytecode_optimizer/codegen_test_ecmascript.cpp new file mode 100644 index 000000000..37d16e4e2 --- /dev/null +++ b/tests/bytecode_optimizer/codegen_test_ecmascript.cpp @@ -0,0 +1,149 @@ +/* + * Copyright (c) 2021-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 + +#include "assembler/assembly-emitter.h" +#include "assembler/assembly-function.h" +#include "assembler/assembly-parser.h" +#include "assembler/assembly-program.h" +#include "codegen.h" +#include "compiler/optimizer/optimizations/cleanup.h" +#include "compiler/optimizer/optimizations/lowering.h" +#include "compiler/optimizer/optimizations/regalloc/reg_alloc_linear_scan.h" +#include "optimize_bytecode.h" +#include "tests/common.h" + +namespace panda::bytecodeopt::test { + +TEST_F(CommonTest, CodegenEcmaInt) +{ + auto graph = CreateEmptyGraph(); + graph->SetDynamicMethod(); + GRAPH(graph) + { + CONSTANT(0, 0).i32(); + + BASIC_BLOCK(2, -1) + { + INST(1, Opcode::CastValueToAnyType).any().AnyType(compiler::AnyBaseType::ECMASCRIPT_INT_TYPE).Inputs(0); + INST(2, Opcode::Return).any().Inputs(1); + } + } + + EXPECT_TRUE(graph->RunPass(compiler::EmptyRegMask())); + auto function = pandasm::Function(std::string(), panda::panda_file::SourceLang::ECMASCRIPT); + EXPECT_TRUE(graph->RunPass(&function, nullptr)); +} + +TEST_F(CommonTest, CodegenEcmaDouble) +{ + auto graph = CreateEmptyGraph(); + graph->SetDynamicMethod(); + GRAPH(graph) + { + CONSTANT(0, 0).f64(); + + BASIC_BLOCK(2, -1) + { + INST(1, Opcode::CastValueToAnyType).any().AnyType(compiler::AnyBaseType::ECMASCRIPT_DOUBLE_TYPE).Inputs(0); + INST(2, Opcode::Return).any().Inputs(1); + } + } + + EXPECT_TRUE(graph->RunPass(compiler::EmptyRegMask())); + auto function = pandasm::Function(std::string(), panda::panda_file::SourceLang::ECMASCRIPT); + EXPECT_TRUE(graph->RunPass(&function, nullptr)); +} + +TEST_F(CommonTest, CodegenEcmaTaggedUndefined) +{ + auto graph = CreateEmptyGraph(); + graph->SetDynamicMethod(); + GRAPH(graph) + { + CONSTANT(0, 0).i32(); + + BASIC_BLOCK(2, -1) + { + INST(1, Opcode::CastValueToAnyType) + .any() + .AnyType(compiler::AnyBaseType::ECMASCRIPT_UNDEFINED_TYPE) + .Inputs(0); + INST(2, Opcode::Return).any().Inputs(1); + } + } + + EXPECT_TRUE(graph->RunPass(compiler::EmptyRegMask())); + auto function = pandasm::Function(std::string(), panda::panda_file::SourceLang::ECMASCRIPT); + EXPECT_TRUE(graph->RunPass(&function, nullptr)); +} + +TEST_F(CommonTest, CodegenEcmaTaggedTrue) +{ + auto graph = CreateEmptyGraph(); + graph->SetDynamicMethod(); + GRAPH(graph) + { + CONSTANT(0, 1).i64(); + + BASIC_BLOCK(2, -1) + { + INST(1, Opcode::CastValueToAnyType).any().AnyType(compiler::AnyBaseType::ECMASCRIPT_BOOLEAN_TYPE).Inputs(0); + INST(2, Opcode::Return).any().Inputs(1); + } + } + + EXPECT_TRUE(graph->RunPass(compiler::EmptyRegMask())); + auto function = pandasm::Function(std::string(), panda::panda_file::SourceLang::ECMASCRIPT); + EXPECT_TRUE(graph->RunPass(&function, nullptr)); +} + +TEST_F(CommonTest, CodegenEcma_jtrue) +{ + auto graph = CreateEmptyGraph(); + graph->SetDynamicMethod(); + GRAPH(graph) + { + PARAMETER(0, 0).any(); + CONSTANT(1, 1).i64(); + + BASIC_BLOCK(2, 3, 4) + { + INST(2, Opcode::CastValueToAnyType).any().AnyType(compiler::AnyBaseType::ECMASCRIPT_BOOLEAN_TYPE).Inputs(1); + INST(3, Opcode::If).SrcType(compiler::DataType::ANY).CC(compiler::CC_EQ).Inputs(0, 2); + } + BASIC_BLOCK(3, -1) + { + INST(4, Opcode::Return).any().Inputs(0); + } + BASIC_BLOCK(4, -1) + { + INST(5, Opcode::Return).any().Inputs(2); + } + } + + EXPECT_TRUE(graph->RunPass(compiler::EmptyRegMask())); + auto function = pandasm::Function(std::string(), panda::panda_file::SourceLang::ECMASCRIPT); + EXPECT_TRUE(graph->RunPass(&function, nullptr)); +} + +} // namespace panda::bytecodeopt::test + +int main(int argc, char **argv) +{ + ::testing::InitGoogleTest(&argc, argv); + return RUN_ALL_TESTS(); +} diff --git a/tests/bytecode_optimizer/reg_encoder_test_ecmascript.cpp b/tests/bytecode_optimizer/reg_encoder_test_ecmascript.cpp new file mode 100644 index 000000000..8adbc6872 --- /dev/null +++ b/tests/bytecode_optimizer/reg_encoder_test_ecmascript.cpp @@ -0,0 +1,134 @@ +/* + * Copyright (c) 2021-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 "tests/common.h" +#include "bytecode_optimizer/reg_encoder.h" + +namespace panda::bytecodeopt::test { + +TEST_F(CommonTest, RegEncoderEcmaSuperCallIntrinsic) +{ + auto graph = CreateEmptyGraph(); + graph->SetDynamicMethod(); + ArenaVector reg_mask(254, false, graph->GetLocalAllocator()->Adapter()); + graph->InitUsedRegs(®_mask); + GRAPH(graph) + { + PARAMETER(0, 0).any().DstReg(compiler::ACC_REG_ID - 3); + PARAMETER(1, 1).any().DstReg(compiler::ACC_REG_ID - 2); + PARAMETER(2, 2).any().DstReg(compiler::ACC_REG_ID - 1); + BASIC_BLOCK(2, -1) + { + INST(3, Opcode::SaveState).NoVregs(); + INST(4, Opcode::Intrinsic) + .any() + .IntrinsicId(compiler::RuntimeInterface::IntrinsicId::INTRINSIC_LDTRUE) + .Inputs({{compiler::DataType::NO_TYPE, 3}}) + .DstReg(compiler::ACC_REG_ID); + INST(5, Opcode::SaveState).NoVregs(); + INST(6, Opcode::Intrinsic) + .any() + .IntrinsicId(compiler::RuntimeInterface::IntrinsicId::INTRINSIC_SUPER_CALL) + .Inputs({{compiler::DataType::ANY, 0}, + {compiler::DataType::ANY, 1}, + {compiler::DataType::ANY, 2}, + {compiler::DataType::ANY, 4}, + {compiler::DataType::NO_TYPE, 5}}) + .DstReg(compiler::ACC_REG_ID); + INST(7, Opcode::Return).any().Inputs(6); + } + } + + graph->InitDefaultLocations(); + + INS(4).SetFlag(compiler::inst_flags::ACC_WRITE); + + INS(6).SetSrcReg(3, compiler::ACC_REG_ID); + INS(6).SetFlag(compiler::inst_flags::ACC_READ); + + EXPECT_TRUE(graph->RunPass()); + + auto expected = CreateEmptyGraph(); + expected->SetDynamicMethod(); + GRAPH(expected) + { + PARAMETER(0, 0).any(); + PARAMETER(1, 1).any(); + PARAMETER(2, 2).any(); + BASIC_BLOCK(2, -1) + { + INST(3, Opcode::SaveState).NoVregs(); + INST(4, Opcode::Intrinsic) + .any() + .IntrinsicId(compiler::RuntimeInterface::IntrinsicId::INTRINSIC_LDTRUE) + .Inputs({{compiler::DataType::NO_TYPE, 3}}) + .DstReg(compiler::ACC_REG_ID); + INST(5, Opcode::SaveState).NoVregs(); + INST(8, Opcode::SpillFill); + INST(6, Opcode::Intrinsic) + .any() + .IntrinsicId(compiler::RuntimeInterface::IntrinsicId::INTRINSIC_SUPER_CALL) + .Inputs({{compiler::DataType::ANY, 0}, + {compiler::DataType::ANY, 1}, + {compiler::DataType::ANY, 2}, + {compiler::DataType::ANY, 4}, + {compiler::DataType::NO_TYPE, 5}}) + .DstReg(compiler::ACC_REG_ID); + INST(7, Opcode::Return).any().Inputs(6); + } + } + + EXPECT_TRUE(GraphComparator().Compare(graph, expected)); + + auto expected_data = std::vector{std::make_tuple(compiler::LocationType::REGISTER, + compiler::LocationType::REGISTER, + compiler::INVALID_REG_ID, 0U), + std::make_tuple(compiler::LocationType::REGISTER, + compiler::LocationType::REGISTER, + compiler::INVALID_REG_ID, 1U), + std::make_tuple(compiler::LocationType::REGISTER, + compiler::LocationType::REGISTER, + compiler::INVALID_REG_ID, 2U)}; + + size_t spill_fills = 0; + for (auto block : graph->GetVectorBlocks()) { + if (block == nullptr) { + continue; + } + for (auto inst : block->AllInsts()) { + if (inst->GetOpcode() != Opcode::SpillFill) { + continue; + } + + auto sf = inst->CastToSpillFill(); + auto &data = sf->GetSpillFills(); + ASSERT_EQ(data.size(), expected_data.size()); + + for (size_t i = 0; i < data.size(); i++) { + auto &sf_data = data[i]; + auto &[src_type, dst_type, src, dst] = expected_data[i]; + EXPECT_EQ(sf_data.SrcType(), src_type); + EXPECT_EQ(sf_data.DstType(), dst_type); + EXPECT_EQ(sf_data.SrcValue(), src); + EXPECT_EQ(sf_data.DstValue(), dst); + } + + spill_fills++; + } + } + EXPECT_EQ(spill_fills, 1); +} + +} // namespace panda::bytecodeopt::test diff --git a/tests/checked/CMakeLists.txt b/tests/checked/CMakeLists.txt new file mode 100644 index 000000000..548930d2b --- /dev/null +++ b/tests/checked/CMakeLists.txt @@ -0,0 +1,119 @@ +# 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. + +function(compile_file_ecma) + set(prefix ARG) + set(singleValues FILE OUTPUT_FILE WORKING_DIR) + cmake_parse_arguments(${prefix} "" "${singleValues}" "${multiValues}" ${ARGN}) + + get_filename_component(FILE_NAME_WE "${ARG_FILE}" NAME_WE) + get_filename_component(SOURCE_DIR "${ARG_FILE}" DIRECTORY) + + get_filename_component(FILE_TYPE "${ARG_FILE}" EXT) + if (${FILE_TYPE} MATCHES "js") + add_custom_command(OUTPUT "${ARG_OUTPUT_FILE}" + COMMENT "Run es2panda for ${ARG_FILE}" + COMMAND ${es2panda_bin} --opt-level 0 --output ${ARG_OUTPUT_FILE} "${ARG_FILE}" + DEPENDS es2panda "${ARG_FILE}" + WORKING_DIRECTORY "${ARG_WORKING_DIR}") + else() + # Compile assembly file + add_custom_command(OUTPUT "${ARG_OUTPUT_FILE}" + COMMENT "Building ${TEST_NAME}" + COMMAND ${PANDA_RUN_PREFIX} $ --log-file ${BUILD_LOG} ${ARG_FILE} ${ARG_OUTPUT_FILE} + DEPENDS ${assembler} "${ARG_FILE}" + WORKING_DIRECTORY "${ARG_WORKING_DIR}") + endif() +endfunction() + +function(panda_add_checked_test_ecma) + set(prefix ARG) + set(singleValues NAME FILE SUPPORT_RELEASE LIBCORE) + set(multiValues AUX_FILES EXT_FILES) + cmake_parse_arguments(${prefix} "" "${singleValues}" "${multiValues}" ${ARGN}) + + # Events does not work in Release mode + if (NOT DEFINED ARG_SUPPORT_RELEASE AND "${CMAKE_BUILD_TYPE}" MATCHES "Release") + return() + endif() + + if ("${ARG_NAME}" STREQUAL "") + get_filename_component(TEST_NAME "${ARG_FILE}" NAME_WE) + set(TEST_NAME "${TEST_NAME}.checked") + else() + set(TEST_NAME "${ARG_NAME}.checked") + endif() + set(CHECKER "${PANDA_ROOT}/tests/checked/checker.rb") + set(TEST_DIR "${CMAKE_CURRENT_BINARY_DIR}/${TEST_NAME}") + set(BINARY_FILE "${TEST_DIR}/test.abc") + set(BUILD_LOG "${TEST_DIR}/build.log") + + if (NOT DEFINED ARG_FILE) + message(FATAL_ERROR "Mandatory FILE argument is not defined.") + endif() + + file(MAKE_DIRECTORY "${TEST_DIR}") + + compile_file_ecma(FILE ${ARG_FILE} OUTPUT_FILE ${BINARY_FILE} WORKING_DIR ${TEST_DIR}) + + set(EXT_FILES "") + foreach(APP_FILE ${ARG_EXT_FILES}) + get_filename_component(APP_FILE_BASE "${APP_FILE}" NAME_WE) + set(APP_FILE_OUTPUT "${TEST_DIR}/${APP_FILE_BASE}.abc") + compile_file_ecma(FILE ${APP_FILE} OUTPUT_FILE "${APP_FILE_OUTPUT}" WORKING_DIR ${TEST_DIR}) + # TODO(msherstennikov): pass external files via --app-panda-files (now it doesn't work) + list(APPEND stdlibs "${APP_FILE_OUTPUT}") + list(APPEND EXT_FILES "${APP_FILE_OUTPUT}") + endforeach() + + get_filename_component(TEST_TYPE "${ARG_FILE}" EXT) + if (${TEST_TYPE} MATCHES "js") + set(COMMAND_TOKEN "//!") + else() + set(COMMAND_TOKEN "#!") + endif() + + set(OPTIONS "--load-runtimes=ecmascript" "--run-gc-in-place") + + if (NOT ${CMAKE_BUILD_TYPE} MATCHES "Debug") + set(RELEASE_OPT "--release") + endif() + + # Run checker + add_custom_target(${TEST_NAME} + COMMAND ${CHECKER} --source ${ARG_FILE} + --panda $ + --paoc $ + --run-prefix \"${PANDA_RUN_PREFIX}\" + --test-file ${BINARY_FILE} + --panda-options \"${OPTIONS}\" + --paoc-options \"${OPTIONS}\" + --command-token \"${COMMAND_TOKEN}\" + ${RELEASE_OPT} + WORKING_DIRECTORY ${TEST_DIR} + COMMENT "Running ${TEST_NAME} checked test" + DEPENDS ${BINARY_FILE} ${EXT_FILES}) + + add_dependencies(checked_tests ${TEST_NAME}) + + foreach(AUX_FILE ${ARG_AUX_FILES}) + get_filename_component(AUX_NAME "${AUX_FILE}" NAME_WE) + set(AUX_NAME "${AUX_NAME}.checked") + add_dependencies(${TEST_NAME} ${AUX_NAME}) + endforeach() + +endfunction() + +if (NOT PANDA_TARGET_ARM32) + panda_add_checked_test_ecma(FILE ${CMAKE_CURRENT_SOURCE_DIR}/type_resolving.js SUPPORT_RELEASE true) +endif() diff --git a/tests/checked/type_resolving.js b/tests/checked/type_resolving.js new file mode 100644 index 000000000..e6ffed9af --- /dev/null +++ b/tests/checked/type_resolving.js @@ -0,0 +1,592 @@ +/* + * Copyright (c) 2021-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. + */ + +//! CHECKER Test arithmetic aot +//! RUN_PAOC options: "--compiler-regex '_GLOBAL::test_(\\w+_[if](_[if]|)|toboolean)'" +//! check_aot = lambda do |op, intrinsic, form, inst| +//! METHOD "test_#{op.downcase}_#{form}" +//! PASS_BEFORE "TypesResolving" +//! INST "Intrinsic.#{intrinsic}" +//! INST_NOT inst +//! PASS_AFTER "TypesResolving" +//! INST_NOT "Intrinsic.#{intrinsic}" +//! INST inst +//! end +//! check_aot.call(:Add, 'Add2Dyn', 'i_i', "AddOverflowCheck") +//! check_aot.call(:Add, 'Add2Dyn', 'i_f', /f64 +Add /) +//! check_aot.call(:Add, 'Add2Dyn', 'f_i', /f64 +Add /) +//! check_aot.call(:Add, 'Add2Dyn', 'f_f', /f64 +Add /) +//! check_aot.call(:Sub, 'Sub2Dyn', 'i_i', "SubOverflowCheck") +//! check_aot.call(:Sub, 'Sub2Dyn', 'i_f', /f64 +Sub /) +//! check_aot.call(:Sub, 'Sub2Dyn', 'f_i', /f64 +Sub /) +//! check_aot.call(:Sub, 'Sub2Dyn', 'f_f', /f64 +Sub /) +//! check_aot.call(:And, 'And2Dyn', 'i_i', /i32 +And /) +//! check_aot.call(:And, 'And2Dyn', 'i_f', /i32 +And /) +//! check_aot.call(:And, 'And2Dyn', 'f_i', /i32 +And /) +//! check_aot.call(:And, 'And2Dyn', 'f_f', /i32 +And /) +//! check_aot.call(:Or, 'Or2Dyn', 'i_i', /i32 +Or /) +//! check_aot.call(:Or, 'Or2Dyn', 'i_f', /i32 +Or /) +//! check_aot.call(:Or, 'Or2Dyn', 'f_i', /i32 +Or /) +//! check_aot.call(:Or, 'Or2Dyn', 'f_f', /i32 +Or /) +//! check_aot.call(:Xor, 'Xor2Dyn', 'i_i', /i32 +Xor /) +//! check_aot.call(:Xor, 'Xor2Dyn', 'i_f', /i32 +Xor /) +//! check_aot.call(:Xor, 'Xor2Dyn', 'f_i', /i32 +Xor /) +//! check_aot.call(:Xor, 'Xor2Dyn', 'f_f', /i32 +Xor /) +//! check_aot.call(:Shl, 'Shl2Dyn', 'i_i', /i32 +Shl /) +//! check_aot.call(:Shl, 'Shl2Dyn', 'i_f', /i32 +Shl /) +//! check_aot.call(:Shl, 'Shl2Dyn', 'f_i', /i32 +Shl /) +//! check_aot.call(:Shl, 'Shl2Dyn', 'f_f', /i32 +Shl /) +//! check_aot.call(:Shr, 'Shr2Dyn', 'i_i', /i32 +AShr /) +//! check_aot.call(:Shr, 'Shr2Dyn', 'i_f', /i32 +AShr /) +//! check_aot.call(:Shr, 'Shr2Dyn', 'f_i', /i32 +AShr /) +//! check_aot.call(:Shr, 'Shr2Dyn', 'f_f', /i32 +AShr /) +//! check_aot.call(:Mul, 'Mul2Dyn', 'i_i', /f64 +Mul /) +//! check_aot.call(:Mul, 'Mul2Dyn', 'i_f', /f64 +Mul /) +//! check_aot.call(:Mul, 'Mul2Dyn', 'f_i', /f64 +Mul /) +//! check_aot.call(:Mul, 'Mul2Dyn', 'f_f', /f64 +Mul /) +//! check_aot.call(:Div, 'Div2Dyn', 'i_i', /f64 +Div /) +//! check_aot.call(:Div, 'Div2Dyn', 'i_f', /f64 +Div /) +//! check_aot.call(:Div, 'Div2Dyn', 'f_i', /f64 +Div /) +//! check_aot.call(:Div, 'Div2Dyn', 'f_f', /f64 +Div /) +//! check_aot.call(:Not, 'NotDyn', 'i', /i32 +Not/) +//! check_aot.call(:Not, 'NotDyn', 'f', /i32 +Not/) +//! check_aot.call(:Neg, 'NegDyn', 'i', /f64 +Neg/) +//! check_aot.call(:Neg, 'NegDyn', 'f', /f64 +Neg/) +//! check_aot.call(:Inc, 'IncDyn', 'i', /AddOverflowCheck/) +//! check_aot.call(:Inc, 'IncDyn', 'f', /f64 +Add/) +//! check_aot.call(:Dec, 'DecDyn', 'i', /SubOverflowCheck/) +//! check_aot.call(:Dec, 'DecDyn', 'f', /f64 +Sub/) +//! +//! forms = [['i_i', 'i32'], ['i_f', 'f64'], ['f_i', 'f64'], ['f_f', 'f64']] +//! insts = [['eq', 'Eq'], ['ne', 'NotEq'], ['ge', 'GreaterEq'], ['le', 'LessEq'], ['gt', 'Greater'], ['lt', 'Less']] +//! forms.each do |form, type| +//! METHOD "test_cmp_#{form}" +//! PASS_BEFORE "TypesResolving" +//! insts.each do |op, intrinsic| +//! INST "Intrinsic.#{intrinsic}Dyn" +//! end +//! PASS_AFTER "TypesResolving" +//! insts.each do |op, intrinsic| +//! INST_NOT "Intrinsic.#{intrinsic}Dyn" +//! INST "Compare #{op.upcase} #{type}" +//! end +//! end +//! +//! METHOD "test_toboolean" +//! PASS_BEFORE "TypesResolving" +//! INST_COUNT "Intrinsic.Toboolean", 5 +//! PASS_AFTER "TypesResolving" +//! INST_COUNT "Intrinsic.Toboolean", 2 +//! +//! RUN options: "--interpreter-type irtoc", entry: "_GLOBAL::func_main_0", force_jit: false +//! +//! EVENT_NOT /Deoptimization.*/ +//! [:add, :sub, :and, :or, :xor, :shl, :shr, :mul, :div, :cmp].each do |op| +//! ['i_i', 'i_f', 'f_i', 'f_f'].each { |form| +//! EVENT /AotEntrypointFound,_GLOBAL::func_test_#{op}_#{form}_\d+/ +//! } +//! end +//! +//! [:not, :neg, :inc, :dec].each do |op| +//! EVENT /AotEntrypointFound,_GLOBAL::func_test_#{op}_i_\d+/ +//! EVENT /AotEntrypointFound,_GLOBAL::func_test_#{op}_f_\d+/ +//! end +//! EVENT /AotEntrypointFound,_GLOBAL::func_test_toboolean_\d+/ + +//! CHECKER Test arithmetics in interpreter +//! RUN options: "--interpreter-type irtoc", entry: "_GLOBAL::func_main_0", force_jit: false +//! EVENT_NOT /AotEntrypointFound.*/ + +function test_add_i_i() { + let a = 1; + let b = 2; + return a + b; +} + +function test_add_i_f() { + let a = 3; + let b = 4.1; + return a + b; +} + +function test_add_f_i() { + let a = 5.2; + let b = 6; + return a + b; +} + +function test_add_f_f() { + let a = 7.3; + let b = 8.4; + return a + b; +} + +function test_add() { + let res = 0; + res += test_add_i_i(); + res += test_add_i_f(); + res += test_add_f_i(); + res += test_add_f_f(); + if (res != 37) { + throw "test_add is failed"; + } +} + +function test_sub_i_i() { + let a = 1; + let b = 2; + return b - a; +} + +function test_sub_i_f() { + let a = 3; + let b = 4.1; + return b - a; +} + +function test_sub_f_i() { + let a = 5.2; + let b = 6; + return b - a; +} + +function test_sub_f_f() { + let a = 7.3; + let b = 8.4; + return b - a; +} + +function test_sub() { + let res = 0; + res += test_sub_i_i(); + res += test_sub_i_f(); + res += test_sub_f_i(); + res += test_sub_f_f(); + if (res != 4) { + throw "test_sub is failed"; + } +} + +function test_and_i_i() { + let a = 3; + let b = 2; + return a & b; +} + +function test_and_i_f() { + let a = 3; + let b = 5.1; + return a & b; +} + +function test_and_f_i() { + let a = 5.2; + let b = 6; + return a & b; +} + +function test_and_f_f() { + let a = 7.3; + let b = 9.4; + return a & b; +} + +function test_and() { + let res = 0; + res += test_and_i_i(); + res += test_and_i_f(); + res += test_and_f_i(); + res += test_and_f_f(); + if (res != 8) { + throw "test_and is failed"; + } +} + +function test_or_i_i() { + let a = 2; + let b = 4; + return a | b; // 6 +} + +function test_or_i_f() { + let a = 3; + let b = 5.1; + return a | b; // 7 +} + +function test_or_f_i() { + let a = 5.2; + let b = 6; + return a | b; // 7 +} + +function test_or_f_f() { + let a = 7.3; + let b = 9.4; + return a | b; // 15 +} + +function test_or() { + let res = 0; + res += test_or_i_i(); + res += test_or_i_f(); + res += test_or_f_i(); + res += test_or_f_f(); + if (res != 35) { + throw "test_or is failed"; + } +} + +function test_xor_i_i() { + let a = 1; + let b = 4; + return a ^ b; // 5 +} + +function test_xor_i_f() { + let a = 3; + let b = 5.1; + return a ^ b; // 6 +} + +function test_xor_f_i() { + let a = 5.2; + let b = 6; + return a ^ b; // 3 +} + +function test_xor_f_f() { + let a = 7.3; + let b = 9.4; + return a ^ b; // 14 +} + +function test_xor() { + let res = 0; + res += test_xor_i_i(); + res += test_xor_i_f(); + res += test_xor_f_i(); + res += test_xor_f_f(); + if (res != 28) { + throw "test_xor is failed"; + } +} + +function test_shl_i_i() { + let a = 1; + let b = 2; + return a << b; // 4 +} + +function test_shl_i_f() { + let a = 1; + let b = 2.7; + return a << b; // 4 +} + +function test_shl_f_i() { + let a = 3.2; + let b = 2; + return a << b; // 12 +} + +function test_shl_f_f() { + let a = 3.2; + let b = 4.4; + return a << b; // 48 +} + +function test_shl() { + let res = 0; + res += test_shl_i_i(); + res += test_shl_i_f(); + res += test_shl_f_i(); + res += test_shl_f_f(); + if (res != 68) { + throw "test_shl is failed"; + } +} + +function test_shr_i_i() { + let a = 4; + let b = 1; + return a >> b; // 2 +} + +function test_shr_i_f() { + let a = 6; + let b = 2.7; + return a >> b; // 1 +} + +function test_shr_f_i() { + let a = 7.2; + let b = 2; + return a >> b; // 1 +} + +function test_shr_f_f() { + let a = 12.2; + let b = 2.4; + return a >> b; // 3 +} + +function test_shr() { + let res = 0; + res += test_shr_i_i(); + res += test_shr_i_f(); + res += test_shr_f_i(); + res += test_shr_f_f(); + if (res != 7) { + throw "test_shr is failed"; + } +} + +function test_mul_i_i() { + let a = 2; + let b = 4; + return a * b; // 8 +} + +function test_mul_i_f() { + let a = 2; + let b = 2.7; + return a * b; // 5.4 +} + +function test_mul_f_i() { + let a = 3.2; + let b = 2; + return a * b; // 6.4 +} + +function test_mul_f_f() { + let a = 3.2; + let b = 4.4; + return a * b; // 14.08 +} + +function test_mul() { + let res = 0; + res += test_mul_i_i(); + res += test_mul_i_f(); + res += test_mul_f_i(); + res += test_mul_f_f(); + if (!(res >= 33.7 && res <= 33.9)) { + throw "test_mul is failed"; + } +} + +function test_div_i_i() { + let a = 4; + let b = 2; + return a / b; // 2 +} + +function test_div_i_f() { + let a = 5; + let b = 2.5; + return a / b; // 2 +} + +function test_div_f_i() { + let a = 4.8; + let b = 2; + return a / b; // 2.4 +} + +function test_div_f_f() { + let a = 4.4; + let b = 2.2; + return a / b; // 2 +} + +function test_div() { + let res = 0; + res += test_div_i_i(); + res += test_div_i_f(); + res += test_div_f_i(); + res += test_div_f_f(); + if (res != 8.4) { + throw "test_div is failed"; + } +} + +function test_not_i() { + let a = 1; + return ~a; // -2 +} + +function test_not_f() { + let a = 3.5; + return ~a; // -4 +} + +function test_not() { + let res = 0; + res += test_not_i(); + res += test_not_f(); + if (res != -6) { + throw "test_not is failed"; + } +} + +function test_neg_i() { + let a = 1; + return -a; +} + +function test_neg_f() { + let a = -3.5; + return -a; +} + +function test_neg() { + let res = 0; + res += test_neg_i(); + res += test_neg_f(); + if (res != 2.5) { + throw "test_neg is failed"; + } +} + +function test_inc_i() { + let a = 1; + return ++a; +} + +function test_inc_f() { + let a = -3.5; + return ++a; +} + +function test_inc() { + let res = 0; + res += test_inc_i(); + res += test_inc_f(); + if (res != -0.5) { + throw "test_inc is failed"; + } +} + +function test_dec_i() { + let a = 10; + return --a; +} + +function test_dec_f() { + let a = 13.5; + return --a; +} + +function test_dec() { + let res = 0; + res += test_dec_i(); + res += test_dec_f(); + if (res != 21.5) { + throw "test_dec is failed"; + } +} + +function test_cmp_i_i() { + let a = 1; + let b = 2; + let res = 0; + if (a == b) res += 1; + if (a != b) res += 2; + if (a >= b) res += 3; + if (a <= b) res += 4; + if (a > b) res += 5; + if (a < b) res += 6; + return res; // 12 +} + +function test_cmp_i_f() { + let a = 3; + let b = 2.5; + let res = 0; + if (a == b) res += 11; + if (a != b) res += 12; + if (a >= b) res += 13; + if (a <= b) res += 14; + if (a > b) res += 15; + if (a < b) res += 16; + return res; // 40 +} + +function test_cmp_f_i() { + let a = -5.2; + let b = 2; + let res = 0; + if (a == b) res += 21; + if (a != b) res += 22; + if (a >= b) res += 23; + if (a <= b) res += 24; + if (a > b) res += 25; + if (a < b) res += 26; + return res; // 72 +} + +function test_cmp_f_f() { + let a = -1.3; + let b = 2.5; + let res = 0; + if (a == b) res += 31; + if (a != b) res += 32; + if (a >= b) res += 33; + if (a <= b) res += 34; + if (a > b) res += 35; + if (a < b) res += 36; + return res; // 102 +} + +function test_cmp() { + let res = 0; + res += test_cmp_i_i(); + res += test_cmp_i_f(); + res += test_cmp_f_i(); + res += test_cmp_f_f(); + if (res != 226) { + throw "test_add is failed"; + } +} + +function test_toboolean() { + let res = 0; + if (2) res += 1; + if (2.5) res += 2; + if ('test') res += 3; + if (0) res += 4; + if ('') res += 6; + if (res != 6) { + throw "test_toboolean is failed"; + } +} + +test_add(); +test_sub(); +test_and(); +test_or(); +test_xor(); +test_shl(); +test_shr(); +test_mul(); +test_div(); +test_not(); +test_neg(); +test_inc(); +test_dec(); +test_cmp(); +test_toboolean(); diff --git a/tests/compiler/CMakeLists.txt b/tests/compiler/CMakeLists.txt new file mode 100644 index 000000000..f8338b5e0 --- /dev/null +++ b/tests/compiler/CMakeLists.txt @@ -0,0 +1,73 @@ +# Copyright (c) 2021-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. + +set(PANDA_COMPILER_ECMA_TESTS_LIBRARIES arkcompiler arkbase arkassembler arkruntime arkaotmanager aot_builder) + +set(INCLUDE_ECMA_DIRS + ${PANDA_ROOT} + ${PANDA_ROOT}/compiler + ${PANDA_ROOT}/compiler/tests +) + +set(PANDA_COMPILER_ECMA_TESTS_SOURCES + unit_ecma_test.cpp + branch_elimination_ecma_test.cpp + checks_elimination_ecma_test.cpp + ir_builder_ecma_test.cpp + lowering_ecma_test.cpp + peepholes_ecma_test.cpp + types_resolving_ecma_tests.cpp + vn_test_ecma.cpp +) + +if(NOT PANDA_MINIMAL_VIXL AND PANDA_COMPILER_ENABLE) + panda_add_gtest( + CONTAINS_MAIN + NAME compiler_unit_tests_ecma + SOURCES + ${PANDA_COMPILER_ECMA_TESTS_SOURCES} + LIBRARIES + ${PANDA_COMPILER_ECMA_TESTS_LIBRARIES} + SANITIZERS + ${PANDA_SANITIZERS_LIST} + INCLUDE_DIRS + ${INCLUDE_ECMA_DIRS} + ) +endif() + +# AMD64 and X86 - for unit tests +if(NOT PANDA_MINIMAL_VIXL AND PANDA_TARGET_AMD64) + set(PANDA_CODEGEN_ECMA_TESTS_SOURCES + unit_ecma_test.cpp + ) + if (PANDA_COMPILER_TARGET_AARCH64) + list(APPEND PANDA_CODEGEN_ECMA_TESTS_SOURCES + codegen_ecma_test.cpp + ) + set_source_files_properties(codegen_ecma_test.cpp PROPERTIES COMPILE_FLAGS -Wno-shadow) + endif() + + panda_add_gtest( + CONTAINS_MAIN + NAME compiler_codegen_tests_ecma + SOURCES + ${PANDA_CODEGEN_ECMA_TESTS_SOURCES} + LIBRARIES + ${PANDA_COMPILER_ECMA_TESTS_LIBRARIES} + SANITIZERS + ${PANDA_SANITIZERS_LIST} + INCLUDE_DIRS + ${INCLUDE_ECMA_DIRS} + ) + +endif() diff --git a/tests/compiler/branch_elimination_ecma_test.cpp b/tests/compiler/branch_elimination_ecma_test.cpp new file mode 100644 index 000000000..a19834dfc --- /dev/null +++ b/tests/compiler/branch_elimination_ecma_test.cpp @@ -0,0 +1,533 @@ +/* + * Copyright (c) 2021-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 "unit_ecma_test.h" +#include "optimizer/ir/datatype.h" +#include "optimizer/ir/graph_cloner.h" +#include "optimizer/optimizations/cleanup.h" +#include "optimizer/optimizations/branch_elimination.h" + +namespace panda::compiler { +class IrBranchEliminationTest : public AsmTest { +public: + IrBranchEliminationTest() {} +}; + +TEST_F(IrBranchEliminationTest, EliminateCompareAnyTypeVNDiffTypesTrue) +{ + auto graph = CreateGraphWithDefaultRuntime(); + graph->SetDynamicMethod(); + GRAPH(graph) + { + PARAMETER(0, 0).any(); + PARAMETER(4, 1).s32(); + PARAMETER(8, 2).s32(); + PARAMETER(10, 3).s32(); + + BASIC_BLOCK(2, 3, 4) + { + INST(2, Opcode::CompareAnyType).b().AnyType(AnyBaseType::ECMASCRIPT_INT_TYPE).Inputs(0); + INST(3, Opcode::IfImm).SrcType(DataType::BOOL).CC(CC_EQ).Imm(0).Inputs(2); + } + + BASIC_BLOCK(3, -1) + { + INST(5, Opcode::Return).s32().Inputs(4); + } + + BASIC_BLOCK(4, 6, 5) + { + INST(6, Opcode::CompareAnyType).b().AnyType(AnyBaseType::ECMASCRIPT_DOUBLE_TYPE).Inputs(0); + INST(7, Opcode::IfImm).SrcType(DataType::BOOL).CC(CC_EQ).Imm(0).Inputs(6); + } + + BASIC_BLOCK(5, -1) + { + INST(9, Opcode::Return).s32().Inputs(8); + } + + BASIC_BLOCK(6, -1) + { + INST(11, Opcode::Return).s32().Inputs(10); + } + } + + ASSERT_TRUE(graph->RunPass()); + ASSERT_TRUE(graph->RunPass()); + + GraphChecker(graph).Check(); + + auto graph_opt = CreateGraphWithDefaultRuntime(); + graph_opt->SetDynamicMethod(); + GRAPH(graph_opt) + { + PARAMETER(0, 0).any(); + PARAMETER(4, 1).s32(); + PARAMETER(10, 3).s32(); + + BASIC_BLOCK(2, 3, 6) + { + INST(2, Opcode::CompareAnyType).b().AnyType(AnyBaseType::ECMASCRIPT_INT_TYPE).Inputs(0); + INST(3, Opcode::IfImm).SrcType(DataType::BOOL).CC(CC_EQ).Imm(0).Inputs(2); + } + + BASIC_BLOCK(3, -1) + { + INST(5, Opcode::Return).s32().Inputs(4); + } + + BASIC_BLOCK(6, -1) + { + INST(11, Opcode::Return).s32().Inputs(10); + } + } + + EXPECT_TRUE(GraphComparator().Compare(graph, graph_opt)); +} + +TEST_F(IrBranchEliminationTest, EliminateCompareAnyTypeVNDiffTypesFalse_1) +{ + auto graph = CreateGraphWithDefaultRuntime(); + graph->SetDynamicMethod(); + GRAPH(graph) + { + PARAMETER(0, 0).any(); + PARAMETER(4, 1).s32(); + PARAMETER(8, 2).s32(); + PARAMETER(10, 3).s32(); + + BASIC_BLOCK(2, 4, 3) + { + INST(2, Opcode::CompareAnyType).b().AnyType(AnyBaseType::ECMASCRIPT_INT_TYPE).Inputs(0); + INST(3, Opcode::IfImm).SrcType(DataType::BOOL).CC(CC_EQ).Imm(0).Inputs(2); + } + + BASIC_BLOCK(3, -1) + { + INST(5, Opcode::Return).s32().Inputs(4); + } + + BASIC_BLOCK(4, 6, 5) + { + INST(6, Opcode::CompareAnyType).b().AnyType(AnyBaseType::ECMASCRIPT_DOUBLE_TYPE).Inputs(0); + INST(7, Opcode::IfImm).SrcType(DataType::BOOL).CC(CC_EQ).Imm(0).Inputs(6); + } + + BASIC_BLOCK(5, -1) + { + INST(9, Opcode::Return).s32().Inputs(8); + } + + BASIC_BLOCK(6, -1) + { + INST(11, Opcode::Return).s32().Inputs(10); + } + } + + auto graph_opt = GraphCloner(graph, graph->GetAllocator(), graph->GetLocalAllocator()).CloneGraph(); + + ASSERT_FALSE(graph->RunPass()); + ASSERT_FALSE(graph->RunPass()); + + GraphChecker(graph).Check(); + + EXPECT_TRUE(GraphComparator().Compare(graph, graph_opt)); +} + +TEST_F(IrBranchEliminationTest, EliminateCompareAnyTypeVNDiffTypesFalse_2) +{ + auto graph = CreateGraphWithDefaultRuntime(); + graph->SetDynamicMethod(); + GRAPH(graph) + { + PARAMETER(0, 0).any(); + PARAMETER(4, 1).s32(); + PARAMETER(8, 2).s32(); + PARAMETER(10, 3).s32(); + + BASIC_BLOCK(2, 3, 4) + { + INST(2, Opcode::CompareAnyType).b().AnyType(AnyBaseType::ECMASCRIPT_INT_TYPE).Inputs(0); + INST(3, Opcode::IfImm).SrcType(DataType::BOOL).CC(CC_NE).Imm(0).Inputs(2); + } + + BASIC_BLOCK(3, -1) + { + INST(5, Opcode::Return).s32().Inputs(4); + } + + BASIC_BLOCK(4, 6, 5) + { + INST(6, Opcode::CompareAnyType).b().AnyType(AnyBaseType::ECMASCRIPT_DOUBLE_TYPE).Inputs(0); + INST(7, Opcode::IfImm).SrcType(DataType::BOOL).CC(CC_EQ).Imm(0).Inputs(6); + } + + BASIC_BLOCK(5, -1) + { + INST(9, Opcode::Return).s32().Inputs(8); + } + + BASIC_BLOCK(6, -1) + { + INST(11, Opcode::Return).s32().Inputs(10); + } + } + + auto graph_opt = GraphCloner(graph, graph->GetAllocator(), graph->GetLocalAllocator()).CloneGraph(); + + ASSERT_FALSE(graph->RunPass()); + ASSERT_FALSE(graph->RunPass()); + + GraphChecker(graph).Check(); + + EXPECT_TRUE(GraphComparator().Compare(graph, graph_opt)); +} + +TEST_F(IrBranchEliminationTest, EliminateCompareAnyTypeVNEqTypesTrue_1) +{ + auto graph = CreateGraphWithDefaultRuntime(); + graph->SetDynamicMethod(); + GRAPH(graph) + { + PARAMETER(0, 0).any(); + PARAMETER(4, 1).s32(); + PARAMETER(8, 2).s32(); + PARAMETER(10, 3).s32(); + + BASIC_BLOCK(2, 3, 4) + { + INST(2, Opcode::CompareAnyType).b().AnyType(AnyBaseType::ECMASCRIPT_INT_TYPE).Inputs(0); + INST(3, Opcode::IfImm).SrcType(DataType::BOOL).CC(CC_EQ).Imm(0).Inputs(2); + } + + BASIC_BLOCK(3, -1) + { + INST(5, Opcode::Return).s32().Inputs(4); + } + + BASIC_BLOCK(4, 6, 5) + { + INST(6, Opcode::CompareAnyType).b().AnyType(AnyBaseType::ECMASCRIPT_INT_TYPE).Inputs(0); + INST(7, Opcode::IfImm).SrcType(DataType::BOOL).CC(CC_EQ).Imm(0).Inputs(6); + } + + BASIC_BLOCK(5, -1) + { + INST(9, Opcode::Return).s32().Inputs(8); + } + + BASIC_BLOCK(6, -1) + { + INST(11, Opcode::Return).s32().Inputs(10); + } + } + + ASSERT_TRUE(graph->RunPass()); + ASSERT_TRUE(graph->RunPass()); + + GraphChecker(graph).Check(); + + auto graph_opt = CreateGraphWithDefaultRuntime(); + graph_opt->SetDynamicMethod(); + GRAPH(graph_opt) + { + PARAMETER(0, 0).any(); + PARAMETER(4, 1).s32(); + PARAMETER(8, 2).s32(); + + BASIC_BLOCK(2, 3, 5) + { + INST(2, Opcode::CompareAnyType).b().AnyType(AnyBaseType::ECMASCRIPT_INT_TYPE).Inputs(0); + INST(3, Opcode::IfImm).SrcType(DataType::BOOL).CC(CC_EQ).Imm(0).Inputs(2); + } + + BASIC_BLOCK(3, -1) + { + INST(5, Opcode::Return).s32().Inputs(4); + } + + BASIC_BLOCK(5, -1) + { + INST(9, Opcode::Return).s32().Inputs(8); + } + } + + EXPECT_TRUE(GraphComparator().Compare(graph, graph_opt)); +} + +TEST_F(IrBranchEliminationTest, EliminateCompareAnyTypeVNEqTypesTrue_2) +{ + auto graph = CreateGraphWithDefaultRuntime(); + graph->SetDynamicMethod(); + GRAPH(graph) + { + PARAMETER(0, 0).any(); + PARAMETER(4, 1).s32(); + PARAMETER(8, 2).s32(); + PARAMETER(10, 3).s32(); + + BASIC_BLOCK(2, 4, 3) + { + INST(2, Opcode::CompareAnyType).b().AnyType(AnyBaseType::ECMASCRIPT_INT_TYPE).Inputs(0); + INST(3, Opcode::IfImm).SrcType(DataType::BOOL).CC(CC_EQ).Imm(0).Inputs(2); + } + + BASIC_BLOCK(3, -1) + { + INST(5, Opcode::Return).s32().Inputs(4); + } + + BASIC_BLOCK(4, 6, 5) + { + INST(6, Opcode::CompareAnyType).b().AnyType(AnyBaseType::ECMASCRIPT_INT_TYPE).Inputs(0); + INST(7, Opcode::IfImm).SrcType(DataType::BOOL).CC(CC_EQ).Imm(0).Inputs(6); + } + + BASIC_BLOCK(5, -1) + { + INST(9, Opcode::Return).s32().Inputs(8); + } + + BASIC_BLOCK(6, -1) + { + INST(11, Opcode::Return).s32().Inputs(10); + } + } + + ASSERT_TRUE(graph->RunPass()); + ASSERT_TRUE(graph->RunPass()); + + GraphChecker(graph).Check(); + + auto graph_opt = CreateGraphWithDefaultRuntime(); + graph_opt->SetDynamicMethod(); + GRAPH(graph_opt) + { + PARAMETER(0, 0).any(); + PARAMETER(4, 1).s32(); + PARAMETER(10, 3).s32(); + + BASIC_BLOCK(2, 6, 3) + { + INST(2, Opcode::CompareAnyType).b().AnyType(AnyBaseType::ECMASCRIPT_INT_TYPE).Inputs(0); + INST(3, Opcode::IfImm).SrcType(DataType::BOOL).CC(CC_EQ).Imm(0).Inputs(2); + } + + BASIC_BLOCK(3, -1) + { + INST(5, Opcode::Return).s32().Inputs(4); + } + + BASIC_BLOCK(6, -1) + { + INST(11, Opcode::Return).s32().Inputs(10); + } + } + + EXPECT_TRUE(GraphComparator().Compare(graph, graph_opt)); +} + +TEST_F(IrBranchEliminationTest, EliminateCompareAnyTypeVNEqTypesTrue_3) +{ + auto graph = CreateGraphWithDefaultRuntime(); + graph->SetDynamicMethod(); + GRAPH(graph) + { + PARAMETER(0, 0).any(); + PARAMETER(4, 1).s32(); + PARAMETER(8, 2).s32(); + PARAMETER(10, 3).s32(); + + BASIC_BLOCK(2, 3, 4) + { + INST(2, Opcode::CompareAnyType).b().AnyType(AnyBaseType::ECMASCRIPT_INT_TYPE).Inputs(0); + INST(3, Opcode::IfImm).SrcType(DataType::BOOL).CC(CC_EQ).Imm(0).Inputs(2); + } + + BASIC_BLOCK(3, -1) + { + INST(5, Opcode::Return).s32().Inputs(4); + } + + BASIC_BLOCK(4, 6, 5) + { + INST(7, Opcode::IfImm).SrcType(DataType::BOOL).CC(CC_EQ).Imm(0).Inputs(2); + } + + BASIC_BLOCK(5, -1) + { + INST(9, Opcode::Return).s32().Inputs(8); + } + + BASIC_BLOCK(6, -1) + { + INST(11, Opcode::Return).s32().Inputs(10); + } + } + + ASSERT_TRUE(graph->RunPass()); + ASSERT_TRUE(graph->RunPass()); + + GraphChecker(graph).Check(); + + auto graph_opt = CreateGraphWithDefaultRuntime(); + graph_opt->SetDynamicMethod(); + GRAPH(graph_opt) + { + PARAMETER(0, 0).any(); + PARAMETER(4, 1).s32(); + PARAMETER(8, 2).s32(); + + BASIC_BLOCK(2, 3, 5) + { + INST(2, Opcode::CompareAnyType).b().AnyType(AnyBaseType::ECMASCRIPT_INT_TYPE).Inputs(0); + INST(3, Opcode::IfImm).SrcType(DataType::BOOL).CC(CC_EQ).Imm(0).Inputs(2); + } + + BASIC_BLOCK(3, -1) + { + INST(5, Opcode::Return).s32().Inputs(4); + } + + BASIC_BLOCK(5, -1) + { + INST(9, Opcode::Return).s32().Inputs(8); + } + } + + EXPECT_TRUE(GraphComparator().Compare(graph, graph_opt)); +} + +TEST_F(IrBranchEliminationTest, EliminateCompareAnyTypeSubtype_1) +{ + auto graph = CreateGraphWithDefaultRuntime(); + graph->SetDynamicMethod(); + GRAPH(graph) + { + PARAMETER(0, 0).any(); + PARAMETER(4, 1).s32(); + PARAMETER(8, 2).s32(); + PARAMETER(10, 3).s32(); + + BASIC_BLOCK(2, 3, 4) + { + INST(2, Opcode::CompareAnyType).b().AnyType(AnyBaseType::ECMASCRIPT_STRING_TYPE).Inputs(0); + INST(3, Opcode::IfImm).SrcType(DataType::BOOL).CC(CC_EQ).Imm(0).Inputs(2); + } + + BASIC_BLOCK(3, -1) + { + INST(5, Opcode::Return).s32().Inputs(4); + } + + BASIC_BLOCK(4, 6, 5) + { + INST(6, Opcode::CompareAnyType).b().AnyType(AnyBaseType::ECMASCRIPT_HEAP_OBJECT_TYPE).Inputs(0); + INST(7, Opcode::IfImm).SrcType(DataType::BOOL).CC(CC_EQ).Imm(0).Inputs(6); + } + + BASIC_BLOCK(5, -1) + { + INST(9, Opcode::Return).s32().Inputs(8); + } + + BASIC_BLOCK(6, -1) + { + INST(11, Opcode::Return).s32().Inputs(10); + } + } + + ASSERT_TRUE(graph->RunPass()); + ASSERT_TRUE(graph->RunPass()); + + GraphChecker(graph).Check(); + + auto graph_opt = CreateGraphWithDefaultRuntime(); + graph_opt->SetDynamicMethod(); + GRAPH(graph_opt) + { + PARAMETER(0, 0).any(); + PARAMETER(4, 1).s32(); + PARAMETER(8, 2).s32(); + + BASIC_BLOCK(2, 3, 6) + { + INST(2, Opcode::CompareAnyType).b().AnyType(AnyBaseType::ECMASCRIPT_STRING_TYPE).Inputs(0); + INST(3, Opcode::IfImm).SrcType(DataType::BOOL).CC(CC_EQ).Imm(0).Inputs(2); + } + + BASIC_BLOCK(3, -1) + { + INST(5, Opcode::Return).s32().Inputs(4); + } + + BASIC_BLOCK(6, -1) + { + INST(11, Opcode::Return).s32().Inputs(8); + } + } + + EXPECT_TRUE(GraphComparator().Compare(graph, graph_opt)); +} + +TEST_F(IrBranchEliminationTest, EliminateCompareAnyTypeSubtype_2) +{ + auto graph = CreateGraphWithDefaultRuntime(); + graph->SetDynamicMethod(); + GRAPH(graph) + { + PARAMETER(0, 0).any(); + PARAMETER(4, 1).s32(); + PARAMETER(8, 2).s32(); + PARAMETER(10, 3).s32(); + + BASIC_BLOCK(2, 3, 4) + { + INST(2, Opcode::CompareAnyType).b().AnyType(AnyBaseType::ECMASCRIPT_HEAP_OBJECT_TYPE).Inputs(0); + INST(3, Opcode::IfImm).SrcType(DataType::BOOL).CC(CC_EQ).Imm(0).Inputs(2); + } + + BASIC_BLOCK(3, -1) + { + INST(5, Opcode::Return).s32().Inputs(4); + } + + BASIC_BLOCK(4, 6, 5) + { + INST(6, Opcode::CompareAnyType).b().AnyType(AnyBaseType::ECMASCRIPT_STRING_TYPE).Inputs(0); + INST(7, Opcode::IfImm).SrcType(DataType::BOOL).CC(CC_EQ).Imm(0).Inputs(6); + } + + BASIC_BLOCK(5, -1) + { + INST(9, Opcode::Return).s32().Inputs(8); + } + + BASIC_BLOCK(6, -1) + { + INST(11, Opcode::Return).s32().Inputs(10); + } + } + + auto graph_opt = GraphCloner(graph, graph->GetAllocator(), graph->GetLocalAllocator()).CloneGraph(); + + ASSERT_FALSE(graph->RunPass()); + ASSERT_FALSE(graph->RunPass()); + + GraphChecker(graph).Check(); + + EXPECT_TRUE(GraphComparator().Compare(graph, graph_opt)); +} + +} // namespace panda::compiler diff --git a/tests/compiler/checks_elimination_ecma_test.cpp b/tests/compiler/checks_elimination_ecma_test.cpp new file mode 100644 index 000000000..457291111 --- /dev/null +++ b/tests/compiler/checks_elimination_ecma_test.cpp @@ -0,0 +1,399 @@ +/* + * Copyright (c) 2021-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 "unit_ecma_test.h" +#include "optimizer/ir/datatype.h" +#include "optimizer/ir/graph_cloner.h" +#include "optimizer/optimizations/cleanup.h" +#include "optimizer/optimizations/checks_elimination.h" + +namespace panda::compiler { +class CheckEliminationEcmaTest : public AsmTest { +public: + CheckEliminationEcmaTest() {} +}; + +TEST_F(CheckEliminationEcmaTest, EliminateAnyTypeCheckAfterCastValueToAnyType) +{ + auto graph = CreateGraphDynWithDefaultRuntime(); + GRAPH(graph) + { + PARAMETER(0, 0).any(); + CONSTANT(1, 1.0); + + BASIC_BLOCK(2, -1) + { + INST(10, Opcode::CastValueToAnyType).any().AnyType(AnyBaseType::ECMASCRIPT_DOUBLE_TYPE).Inputs(1); + INST(2, Opcode::SaveState).Inputs(0, 10).SrcVregs({0, 1}); + INST(3, Opcode::AnyTypeCheck).any().AnyType(AnyBaseType::ECMASCRIPT_DOUBLE_TYPE).Inputs(0, 2); + INST(4, Opcode::AnyTypeCheck).any().AnyType(AnyBaseType::ECMASCRIPT_DOUBLE_TYPE).Inputs(10, 2); + INST(5, Opcode::CastAnyTypeValue).f64().AnyType(AnyBaseType::ECMASCRIPT_DOUBLE_TYPE).Inputs(3); + INST(6, Opcode::CastAnyTypeValue).f64().AnyType(AnyBaseType::ECMASCRIPT_DOUBLE_TYPE).Inputs(4); + INST(7, Opcode::Add).f64().Inputs(5, 6); + INST(8, Opcode::CastValueToAnyType).any().AnyType(AnyBaseType::ECMASCRIPT_DOUBLE_TYPE).Inputs(7); + INST(9, Opcode::Return).any().Inputs(8); + } + } + + ASSERT_TRUE(graph->RunPass()); + ASSERT_TRUE(graph->RunPass()); + + GraphChecker(graph).Check(); + + auto graph_opt = CreateGraphDynWithDefaultRuntime(); + GRAPH(graph_opt) + { + PARAMETER(0, 0).any(); + CONSTANT(1, 1.0); + + BASIC_BLOCK(2, -1) + { + INST(10, Opcode::CastValueToAnyType).any().AnyType(AnyBaseType::ECMASCRIPT_DOUBLE_TYPE).Inputs(1); + INST(2, Opcode::SaveState).Inputs(0, 10).SrcVregs({0, 1}); + INST(3, Opcode::AnyTypeCheck).any().AnyType(AnyBaseType::ECMASCRIPT_DOUBLE_TYPE).Inputs(0, 2); + INST(5, Opcode::CastAnyTypeValue).f64().AnyType(AnyBaseType::ECMASCRIPT_DOUBLE_TYPE).Inputs(3); + INST(6, Opcode::CastAnyTypeValue).f64().AnyType(AnyBaseType::ECMASCRIPT_DOUBLE_TYPE).Inputs(10); + INST(7, Opcode::Add).f64().Inputs(5, 6); + INST(8, Opcode::CastValueToAnyType).any().AnyType(AnyBaseType::ECMASCRIPT_DOUBLE_TYPE).Inputs(7); + INST(9, Opcode::Return).any().Inputs(8); + } + } + + EXPECT_TRUE(GraphComparator().Compare(graph, graph_opt)); +} + +TEST_F(CheckEliminationEcmaTest, EliminateDuplicateAnyTypeCheck) +{ + auto graph = CreateGraphDynWithDefaultRuntime(); + GRAPH(graph) + { + PARAMETER(0, 0).any(); + + BASIC_BLOCK(2, -1) + { + INST(2, Opcode::SaveState).Inputs(0).SrcVregs({0}); + INST(3, Opcode::AnyTypeCheck).any().AnyType(AnyBaseType::ECMASCRIPT_DOUBLE_TYPE).Inputs(0, 2); + INST(10, Opcode::AnyTypeCheck).any().AnyType(AnyBaseType::ECMASCRIPT_DOUBLE_TYPE).Inputs(3, 2); + INST(4, Opcode::AnyTypeCheck).any().AnyType(AnyBaseType::ECMASCRIPT_DOUBLE_TYPE).Inputs(0, 2); + INST(5, Opcode::CastAnyTypeValue).f64().AnyType(AnyBaseType::ECMASCRIPT_DOUBLE_TYPE).Inputs(10); + INST(6, Opcode::CastAnyTypeValue).f64().AnyType(AnyBaseType::ECMASCRIPT_DOUBLE_TYPE).Inputs(4); + INST(7, Opcode::Add).f64().Inputs(5, 6); + INST(8, Opcode::CastValueToAnyType).any().AnyType(AnyBaseType::ECMASCRIPT_DOUBLE_TYPE).Inputs(7); + INST(9, Opcode::Return).any().Inputs(8); + } + } + + ASSERT_TRUE(graph->RunPass()); + ASSERT_TRUE(graph->RunPass()); + + GraphChecker(graph).Check(); + + auto graph_opt = CreateGraphDynWithDefaultRuntime(); + + GRAPH(graph_opt) + { + PARAMETER(0, 0).any(); + + BASIC_BLOCK(2, -1) + { + INST(2, Opcode::SaveState).Inputs(0).SrcVregs({0}); + INST(3, Opcode::AnyTypeCheck).any().AnyType(AnyBaseType::ECMASCRIPT_DOUBLE_TYPE).Inputs(0, 2); + INST(5, Opcode::CastAnyTypeValue).f64().AnyType(AnyBaseType::ECMASCRIPT_DOUBLE_TYPE).Inputs(3); + INST(6, Opcode::CastAnyTypeValue).f64().AnyType(AnyBaseType::ECMASCRIPT_DOUBLE_TYPE).Inputs(3); + INST(7, Opcode::Add).f64().Inputs(5, 6); + INST(8, Opcode::CastValueToAnyType).any().AnyType(AnyBaseType::ECMASCRIPT_DOUBLE_TYPE).Inputs(7); + INST(9, Opcode::Return).any().Inputs(8); + } + } + + EXPECT_TRUE(GraphComparator().Compare(graph, graph_opt)); +} + +TEST_F(CheckEliminationEcmaTest, EliminateDuplicateAnyTypeCheckCase3) +{ + auto graph = CreateGraphDynWithDefaultRuntime(); + GRAPH(graph) + { + PARAMETER(0, 0).any(); + + BASIC_BLOCK(2, -1) + { + INST(2, Opcode::SaveState).Inputs(0).SrcVregs({0}); + + INST(3, Opcode::AnyTypeCheck).any().AnyType(AnyBaseType::ECMASCRIPT_STRING_TYPE).Inputs(0, 2); + INST(4, Opcode::AnyTypeCheck).any().AnyType(AnyBaseType::ECMASCRIPT_OBJECT_TYPE).Inputs(0, 2); + + INST(5, Opcode::CastAnyTypeValue).f64().AnyType(AnyBaseType::ECMASCRIPT_STRING_TYPE).Inputs(3); + INST(6, Opcode::CastAnyTypeValue).f64().AnyType(AnyBaseType::ECMASCRIPT_STRING_TYPE).Inputs(4); + INST(7, Opcode::Add).f64().Inputs(5, 6); + INST(8, Opcode::CastValueToAnyType).any().AnyType(AnyBaseType::ECMASCRIPT_DOUBLE_TYPE).Inputs(7); + INST(9, Opcode::Return).any().Inputs(8); + } + } + + ASSERT_TRUE(graph->RunPass()); + ASSERT_TRUE(graph->RunPass()); + + GraphChecker(graph).Check(); + + auto graph_opt = CreateGraphDynWithDefaultRuntime(); + + GRAPH(graph_opt) + { + PARAMETER(0, 0).any(); + + BASIC_BLOCK(2, -1) + { + INST(2, Opcode::SaveState).Inputs(0).SrcVregs({0}); + + INST(3, Opcode::AnyTypeCheck).any().AnyType(AnyBaseType::ECMASCRIPT_STRING_TYPE).Inputs(0, 2); + + INST(5, Opcode::CastAnyTypeValue).f64().AnyType(AnyBaseType::ECMASCRIPT_STRING_TYPE).Inputs(3); + INST(6, Opcode::CastAnyTypeValue).f64().AnyType(AnyBaseType::ECMASCRIPT_STRING_TYPE).Inputs(3); + INST(7, Opcode::Add).f64().Inputs(5, 6); + INST(8, Opcode::CastValueToAnyType).any().AnyType(AnyBaseType::ECMASCRIPT_DOUBLE_TYPE).Inputs(7); + INST(9, Opcode::Return).any().Inputs(8); + } + } + + EXPECT_TRUE(GraphComparator().Compare(graph, graph_opt)); +} + +TEST_F(CheckEliminationEcmaTest, EliminateDuplicateAnyTypeCheckCase4) +{ + auto graph = CreateGraphDynWithDefaultRuntime(); + GRAPH(graph) + { + PARAMETER(0, 0).any(); + + BASIC_BLOCK(2, -1) + { + INST(2, Opcode::SaveState).Inputs(0).SrcVregs({0}); + + INST(3, Opcode::AnyTypeCheck).any().AnyType(AnyBaseType::ECMASCRIPT_INT_TYPE).Inputs(0, 2); + INST(4, Opcode::AnyTypeCheck).any().AnyType(AnyBaseType::ECMASCRIPT_DOUBLE_TYPE).Inputs(0, 2); + + INST(5, Opcode::CastAnyTypeValue).i32().AnyType(AnyBaseType::ECMASCRIPT_INT_TYPE).Inputs(3); + INST(6, Opcode::CastAnyTypeValue).i32().AnyType(AnyBaseType::ECMASCRIPT_INT_TYPE).Inputs(4); + INST(7, Opcode::Add).i32().Inputs(5, 6); + INST(8, Opcode::CastValueToAnyType).any().AnyType(AnyBaseType::ECMASCRIPT_INT_TYPE).Inputs(7); + INST(9, Opcode::Return).any().Inputs(8); + } + } + + ASSERT_TRUE(graph->RunPass()); + GraphChecker(graph).Check(); + + auto graph_opt = CreateGraphDynWithDefaultRuntime(); + + GRAPH(graph_opt) + { + PARAMETER(0, 0).any(); + + BASIC_BLOCK(2, -1) + { + INST(2, Opcode::SaveState).Inputs(0).SrcVregs({0}); + + INST(3, Opcode::AnyTypeCheck).any().AnyType(AnyBaseType::ECMASCRIPT_INT_TYPE).Inputs(0, 2); + + INST(4, Opcode::Deoptimize).DeoptimizeType(DeoptimizeType::ANY_TYPE_CHECK).Inputs(2); + } + } + + EXPECT_TRUE(GraphComparator().Compare(graph, graph_opt)); +} + +TEST_F(CheckEliminationEcmaTest, NotEliminateAnyTypeCheck) +{ + auto graph = CreateGraphDynWithDefaultRuntime(); + GRAPH(graph) + { + PARAMETER(0, 0).any(); + PARAMETER(10, 1).any(); + PARAMETER(11, 2).any(); + + BASIC_BLOCK(2, 3, 4) + { + INST(12, Opcode::CompareAnyType).b().AnyType(AnyBaseType::ECMASCRIPT_INT_TYPE).Inputs(11); + INST(13, Opcode::IfImm).SrcType(DataType::BOOL).CC(CC_EQ).Imm(0).Inputs(12); + } + BASIC_BLOCK(3, -1) + { + INST(2, Opcode::SaveState).Inputs(0, 10).SrcVregs({0, 1}); + INST(3, Opcode::AnyTypeCheck).any().AnyType(AnyBaseType::ECMASCRIPT_DOUBLE_TYPE).Inputs(0, 2); + INST(4, Opcode::AnyTypeCheck).any().AnyType(AnyBaseType::ECMASCRIPT_DOUBLE_TYPE).Inputs(10, 2); + INST(5, Opcode::CastAnyTypeValue).f64().AnyType(AnyBaseType::ECMASCRIPT_DOUBLE_TYPE).Inputs(3); + INST(6, Opcode::CastAnyTypeValue).f64().AnyType(AnyBaseType::ECMASCRIPT_DOUBLE_TYPE).Inputs(4); + INST(7, Opcode::Add).f64().Inputs(5, 6); + INST(8, Opcode::CastValueToAnyType).any().AnyType(AnyBaseType::ECMASCRIPT_DOUBLE_TYPE).Inputs(7); + INST(9, Opcode::Return).any().Inputs(8); + } + BASIC_BLOCK(4, -1) + { + INST(22, Opcode::SaveState).Inputs(0, 10).SrcVregs({0, 1}); + INST(23, Opcode::AnyTypeCheck).any().AnyType(AnyBaseType::ECMASCRIPT_DOUBLE_TYPE).Inputs(0, 22); + INST(24, Opcode::AnyTypeCheck).any().AnyType(AnyBaseType::ECMASCRIPT_DOUBLE_TYPE).Inputs(10, 22); + INST(25, Opcode::CastAnyTypeValue).f64().AnyType(AnyBaseType::ECMASCRIPT_DOUBLE_TYPE).Inputs(23); + INST(26, Opcode::CastAnyTypeValue).f64().AnyType(AnyBaseType::ECMASCRIPT_DOUBLE_TYPE).Inputs(24); + INST(27, Opcode::Sub).f64().Inputs(25, 26); + INST(28, Opcode::CastValueToAnyType).any().AnyType(AnyBaseType::ECMASCRIPT_DOUBLE_TYPE).Inputs(27); + INST(29, Opcode::Return).any().Inputs(28); + } + } + + auto graph_opt = GraphCloner(graph, graph->GetAllocator(), graph->GetLocalAllocator()).CloneGraph(); + + ASSERT_FALSE(graph->RunPass()); + ASSERT_FALSE(graph->RunPass()); + + GraphChecker(graph).Check(); + + EXPECT_TRUE(GraphComparator().Compare(graph, graph_opt)); +} + +TEST_F(CheckEliminationEcmaTest, MoveAnyTypeCheckFromLoop) +{ + auto graph = CreateGraphDynWithDefaultRuntime(); + GRAPH(graph) + { + PARAMETER(0, 0).any(); + CONSTANT(5, 1); + BASIC_BLOCK(2, 3) + { + INST(1, Opcode::SaveState).Inputs(0).SrcVregs({0}).Pc(10U); + INST(7, Opcode::Intrinsic).v0id().Inputs({{DataType::NO_TYPE, 1}}).Pc(10U); + } + BASIC_BLOCK(3, 3, 4) + { + INST(2, Opcode::SaveState).Inputs(0).SrcVregs({0}).Pc(20U); + INST(3, Opcode::AnyTypeCheck).any().AnyType(AnyBaseType::ECMASCRIPT_DOUBLE_TYPE).Inputs(0, 2).Pc(20U); + INST(6, Opcode::IfImm).SrcType(DataType::BOOL).CC(CC_NE).Imm(0).Inputs(5).Pc(20U); + } + BASIC_BLOCK(4, -1) + { + INST(4, Opcode::ReturnVoid).v0id().Pc(30U); + } + } + + ASSERT_TRUE(graph->RunPass()); + ASSERT_TRUE(graph->RunPass()); + GraphChecker(graph).Check(); + + auto &save_state = INS(1); + Inst *check = nullptr; + for (auto user_it = save_state.GetUsers().begin(); user_it != save_state.GetUsers().end();) { + if (user_it->GetInst()->GetOpcode() == Opcode::AnyTypeCheck) { + check = user_it->GetInst(); + break; + } + } + ASSERT_NE(check, nullptr); + ASSERT_EQ(check->GetPc(), save_state.GetPc()); + + auto graph_opt = CreateGraphDynWithDefaultRuntime(); + + GRAPH(graph_opt) + { + PARAMETER(0, 0).any(); + CONSTANT(5, 1); + BASIC_BLOCK(2, 3) + { + INST(1, Opcode::SaveState).Inputs(0).SrcVregs({0}); + INST(30, Opcode::AnyTypeCheck).any().AnyType(AnyBaseType::ECMASCRIPT_DOUBLE_TYPE).Inputs(0, 1); + INST(7, Opcode::Intrinsic).v0id().Inputs({{DataType::NO_TYPE, 1}}); + } + BASIC_BLOCK(3, 3, 4) + { + INST(6, Opcode::IfImm).SrcType(DataType::BOOL).CC(CC_NE).Imm(0).Inputs(5); + } + BASIC_BLOCK(4, -1) + { + INST(4, Opcode::ReturnVoid).v0id(); + } + } + + EXPECT_TRUE(GraphComparator().Compare(graph, graph_opt)); +} + +TEST_F(CheckEliminationEcmaTest, MoveAnyTypeCheckFromLoop2) +{ + // not applied, AnyTypeCheck not moved, because it isn't dominate on all back edges. + auto graph = CreateGraphDynWithDefaultRuntime(); + GRAPH(graph) + { + PARAMETER(0, 0).any(); + CONSTANT(5, 1); + BASIC_BLOCK(2, 4, 5) + { + INST(6, Opcode::IfImm).SrcType(DataType::BOOL).CC(CC_NE).Imm(0).Inputs(5); + } + BASIC_BLOCK(4, 2, 6) + { + INST(2, Opcode::SaveState).Inputs(0).SrcVregs({0}); + INST(3, Opcode::AnyTypeCheck).any().AnyType(AnyBaseType::ECMASCRIPT_DOUBLE_TYPE).Inputs(0, 2); + INST(7, Opcode::IfImm).SrcType(DataType::BOOL).CC(CC_NE).Imm(0).Inputs(5); + } + BASIC_BLOCK(5, 2, 6) + { + INST(8, Opcode::IfImm).SrcType(DataType::BOOL).CC(CC_NE).Imm(0).Inputs(5); + } + BASIC_BLOCK(6, -1) + { + INST(4, Opcode::ReturnVoid).v0id(); + } + } + + auto graph_opt = GraphCloner(graph, graph->GetAllocator(), graph->GetLocalAllocator()).CloneGraph(); + + ASSERT_FALSE(graph->RunPass()); + GraphChecker(graph).Check(); + EXPECT_TRUE(GraphComparator().Compare(graph, graph_opt)); +} + +TEST_F(CheckEliminationEcmaTest, EliminateAnyTypeCheckWithUndefinedType) +{ + auto graph = CreateGraphDynWithDefaultRuntime(); + GRAPH(graph) + { + PARAMETER(0, 0).any(); + + BASIC_BLOCK(2, -1) + { + INST(2, Opcode::SaveState).Inputs(0).SrcVregs({0}); + + INST(3, Opcode::AnyTypeCheck).any().AnyType(AnyBaseType::UNDEFINED_TYPE).Inputs(0, 2); + + INST(4, Opcode::Return).any().Inputs(3); + } + } + + ASSERT_TRUE(graph->RunPass()); + ASSERT_TRUE(graph->RunPass()); + GraphChecker(graph).Check(); + + auto graph_opt = CreateGraphDynWithDefaultRuntime(); + + GRAPH(graph_opt) + { + PARAMETER(0, 0).any(); + + BASIC_BLOCK(2, -1) + { + INST(4, Opcode::Return).any().Inputs(0); + } + } + + EXPECT_TRUE(GraphComparator().Compare(graph, graph_opt)); +} +} // namespace panda::compiler diff --git a/tests/compiler/codegen_ecma_test.cpp b/tests/compiler/codegen_ecma_test.cpp new file mode 100644 index 000000000..8df013bb0 --- /dev/null +++ b/tests/compiler/codegen_ecma_test.cpp @@ -0,0 +1,393 @@ +/* + * Copyright (c) 2021-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 +#include "optimizer/ir/inst.h" +#include "optimizer/ir/basicblock.h" +#include "optimizer/optimizations/if_conversion.h" +#include "optimizer/optimizations/lowering.h" +#include "optimizer/optimizations/regalloc/reg_alloc.h" + +#include "libpandabase/macros.h" +#include "gtest/gtest.h" +#include "unit_ecma_test.h" +#include "optimizer/code_generator/codegen.h" +#include "vixl_exec_module.h" +#include "runtime/include/coretypes/tagged_value.h" +#include "ir-dyn-base-types.h" + +namespace panda::compiler { +class CodegenEcmaTest : public GraphTest { +public: + CodegenEcmaTest() : exec_module_(GetAllocator(), GetGraph()->GetRuntime()) {} + ~CodegenEcmaTest() override {} + + void TestCompareAnyType(AnyBaseType any_type, uint64_t val, bool expected_value); + void TestAnyTypeCheck(AnyBaseType any_type, uint64_t val); + template + void CastAnyTypeValue(AnyBaseType boxed_type, TaggedValue boxed_value, T expected_unboxed_value); + template + void CastValueToAnyType(T unboxed_value, AnyBaseType boxed_type, uint64_t expected_boxed_value); + template + void CheckReturnValue(Graph *graph, T expected_value); + +private: + VixlExecModule exec_module_; +}; + +static bool RunCodegen(Graph *graph) +{ + if (!graph->RunPass()) { + return false; + } + return true; +} + +template +void CodegenEcmaTest::CheckReturnValue(Graph *graph, T expected_value) +{ + SetNumVirtRegs(0); + ASSERT_TRUE(RegAlloc(graph)); + ASSERT_TRUE(RunCodegen(graph)); + + auto code_entry = reinterpret_cast(graph->GetData().Data()); + auto code_exit = code_entry + graph->GetData().Size(); + + ASSERT(code_entry != nullptr && code_exit != nullptr); + + exec_module_.SetInstructions(code_entry, code_exit); + exec_module_.SetDump(false); + + exec_module_.Execute(); + auto rv = exec_module_.GetRetValue(); + EXPECT_EQ(rv, expected_value); +} + +void CodegenEcmaTest::TestCompareAnyType(AnyBaseType any_type, uint64_t val, bool expected_value) +{ + auto graph = GetGraph(); + graph->SetDynamicMethod(); + GRAPH(graph) + { + CONSTANT(0, val).any(); + + BASIC_BLOCK(2, -1) + { + INST(2, Opcode::CompareAnyType).b().AnyType(any_type).Inputs(0); + INST(3, Opcode::Return).b().Inputs(2); + } + } + + CheckReturnValue(graph, expected_value); +} + +TEST_F(CodegenEcmaTest, CompareAnyTypeInstBoolTrue_1) +{ + TestCompareAnyType(AnyBaseType::ECMASCRIPT_BOOLEAN_TYPE, TaggedValue::VALUE_TRUE, true); +} + +TEST_F(CodegenEcmaTest, CompareAnyTypeInstBoolTrue_2) +{ + TestCompareAnyType(AnyBaseType::ECMASCRIPT_BOOLEAN_TYPE, TaggedValue::VALUE_FALSE, true); +} + +TEST_F(CodegenEcmaTest, CompareAnyTypeInstBoolFalse) +{ + TestCompareAnyType(AnyBaseType::ECMASCRIPT_BOOLEAN_TYPE, TaggedValue::VALUE_TRUE + 1, false); +} + +TEST_F(CodegenEcmaTest, CompareAnyTypeInstDoubleTrue) +{ + TestCompareAnyType(AnyBaseType::ECMASCRIPT_DOUBLE_TYPE, TaggedValue::DOUBLE_ENCODE_OFFSET, true); +} + +TEST_F(CodegenEcmaTest, CompareAnyTypeInstDoubleFalse_1) +{ + TestCompareAnyType(AnyBaseType::ECMASCRIPT_DOUBLE_TYPE, TaggedValue::TAG_INT, false); +} + +TEST_F(CodegenEcmaTest, CompareAnyTypeInstDoubleFalse_2) +{ + TestCompareAnyType(AnyBaseType::ECMASCRIPT_DOUBLE_TYPE, TaggedValue::TAG_OBJECT + 1, false); +} + +TEST_F(CodegenEcmaTest, CompareAnyTypeInstIntTrue) +{ + TestCompareAnyType(AnyBaseType::ECMASCRIPT_INT_TYPE, TaggedValue::TAG_INT, true); +} + +TEST_F(CodegenEcmaTest, CompareAnyTypeInstIntFalse) +{ + TestCompareAnyType(AnyBaseType::ECMASCRIPT_INT_TYPE, TaggedValue::DOUBLE_ENCODE_OFFSET, false); +} + +TEST_F(CodegenEcmaTest, CompareAnyTypeInstNullTrue) +{ + TestCompareAnyType(AnyBaseType::ECMASCRIPT_NULL_TYPE, TaggedValue::VALUE_NULL, true); +} + +TEST_F(CodegenEcmaTest, CompareAnyTypeInstNullFalse) +{ + TestCompareAnyType(AnyBaseType::ECMASCRIPT_NULL_TYPE, TaggedValue::TAG_INT, false); +} + +TEST_F(CodegenEcmaTest, CompareAnyTypeInstObjectTrue) +{ + TestCompareAnyType(AnyBaseType::ECMASCRIPT_OBJECT_TYPE, TaggedValue::TAG_OBJECT + 1, true); +} + +TEST_F(CodegenEcmaTest, CompareAnyTypeInstObjectFalse) +{ + TestCompareAnyType(AnyBaseType::ECMASCRIPT_OBJECT_TYPE, TaggedValue::TAG_INT, false); +} + +TEST_F(CodegenEcmaTest, CompareAnyTypeInstStringFalse) +{ + // Not implemented. + TestCompareAnyType(AnyBaseType::ECMASCRIPT_STRING_TYPE, TaggedValue::TAG_OBJECT + 1, false); +} + +TEST_F(CodegenEcmaTest, CompareAnyTypeInstUndefinedTrue) +{ + TestCompareAnyType(AnyBaseType::ECMASCRIPT_UNDEFINED_TYPE, TaggedValue::VALUE_UNDEFINED, true); +} + +TEST_F(CodegenEcmaTest, CompareAnyTypeInstUndefinedFalse) +{ + TestCompareAnyType(AnyBaseType::ECMASCRIPT_UNDEFINED_TYPE, TaggedValue::TAG_INT, false); +} + +template +void CodegenEcmaTest::CastAnyTypeValue(AnyBaseType boxed_type, TaggedValue boxed_value, T expected_unboxed_value) +{ + auto graph = GetGraph(); + graph->SetDynamicMethod(); + GRAPH(graph) + { + CONSTANT(0, boxed_value.GetRawData()).any(); + + BASIC_BLOCK(2, -1) + { + INST(2, Opcode::CastAnyTypeValue).AnyType(boxed_type).Inputs(0); + INS(2).SetType(AnyBaseTypeToDataType(boxed_type)); + INS(2).SetFlag(inst_flags::Flags::NO_CSE); + INS(2).SetFlag(inst_flags::Flags::NO_HOIST); + INST(3, Opcode::Return).Inputs(2); + INS(3).SetType(AnyBaseTypeToDataType(boxed_type)); + } + } + + CheckReturnValue(graph, expected_unboxed_value); +} + +TEST_F(CodegenEcmaTest, DISABLED_CastAnyTypeValueInstNull) +{ + // Unclear way for using should be fixed after real example. + CastAnyTypeValue(AnyBaseType::ECMASCRIPT_NULL_TYPE, TaggedValue::Null(), 0); +} + +TEST_F(CodegenEcmaTest, DISABLED_CastAnyTypeValueInstUndefined) +{ + // Unclear way for using should be fixed after real example. + CastAnyTypeValue(AnyBaseType::ECMASCRIPT_UNDEFINED_TYPE, TaggedValue::Undefined(), 0); +} + +TEST_F(CodegenEcmaTest, CastAnyTypeValueInstFalse) +{ + CastAnyTypeValue(AnyBaseType::ECMASCRIPT_BOOLEAN_TYPE, TaggedValue::False(), false); +} + +TEST_F(CodegenEcmaTest, CastAnyTypeValueInstTrue) +{ + CastAnyTypeValue(AnyBaseType::ECMASCRIPT_BOOLEAN_TYPE, TaggedValue::True(), true); +} + +TEST_F(CodegenEcmaTest, CastAnyTypeValueInstInt) +{ + constexpr int32_t payload = 42; + auto value = TaggedValue(payload); + + ASSERT_TRUE(value.IsInt()); + ASSERT_TRUE(value.GetInt() == payload); + CastAnyTypeValue(AnyBaseType::ECMASCRIPT_INT_TYPE, value, payload); +} + +TEST_F(CodegenEcmaTest, CastAnyTypeValueInstDouble) +{ + constexpr double payload = 42.0; + auto value = TaggedValue(payload); + + ASSERT_TRUE(value.IsDouble()); + ASSERT_TRUE(value.GetDouble() == payload); + CastAnyTypeValue(AnyBaseType::ECMASCRIPT_DOUBLE_TYPE, value, payload); +} + +TEST_F(CodegenEcmaTest, CastAnyTypeValueInstObject) +{ + // As long as tagging remains the same for all heap objects (strings, array, objects, etc.), + // this single test is enough. + const auto *payload_ptr = reinterpret_cast(0xbee8); + const auto payload_int = reinterpret_cast(payload_ptr); + auto value = TaggedValue(payload_ptr); + + ASSERT_TRUE(value.IsHeapObject()); + ASSERT_TRUE(value.GetHeapObject() == payload_ptr); + ASSERT_TRUE(reinterpret_cast(value.GetHeapObject()) == static_cast(payload_int)); + CastAnyTypeValue(AnyBaseType::ECMASCRIPT_OBJECT_TYPE, value, payload_int); +} + +template +void CodegenEcmaTest::CastValueToAnyType(T unboxed_value, AnyBaseType boxed_type, uint64_t expected_boxed_value) +{ + auto graph = GetGraph(); + graph->SetDynamicMethod(); + GRAPH(graph) + { + CONSTANT(0, unboxed_value); + BASIC_BLOCK(2, -1) + { + INST(2, Opcode::CastValueToAnyType).any().AnyType(boxed_type).Inputs(0); + INS(2).SetFlag(inst_flags::Flags::NO_CSE); + INS(2).SetFlag(inst_flags::Flags::NO_HOIST); + INST(3, Opcode::Return).any().Inputs(2); + } + } + + CheckReturnValue(graph, expected_boxed_value); +} + +TEST_F(CodegenEcmaTest, DISABLED_CastValueToAnyTypeInstNull) +{ + // Unclear way for using should be fixed after real example. + CastValueToAnyType(0, AnyBaseType::ECMASCRIPT_NULL_TYPE, TaggedValue::Null().GetRawData()); +} + +TEST_F(CodegenEcmaTest, DISABLED_CastValueToAnyTypeInstUndefined) +{ + // Unclear way for using should be fixed after real example. + CastValueToAnyType(0, AnyBaseType::ECMASCRIPT_UNDEFINED_TYPE, TaggedValue::Undefined().GetRawData()); +} + +TEST_F(CodegenEcmaTest, CastValueToAnyTypeInstFalse) +{ + CastValueToAnyType(false, AnyBaseType::ECMASCRIPT_BOOLEAN_TYPE, TaggedValue::False().GetRawData()); +} + +TEST_F(CodegenEcmaTest, CastValueToAnyTypeInstTrue) +{ + CastValueToAnyType(true, AnyBaseType::ECMASCRIPT_BOOLEAN_TYPE, TaggedValue::True().GetRawData()); +} + +TEST_F(CodegenEcmaTest, CastValueToAnyTypeInstInt) +{ + constexpr int32_t payload = 42; + auto value = TaggedValue(payload); + + ASSERT_TRUE(value.IsInt()); + ASSERT_TRUE(value.GetInt() == payload); + CastValueToAnyType(payload, AnyBaseType::ECMASCRIPT_INT_TYPE, value.GetRawData()); +} + +TEST_F(CodegenEcmaTest, CastValueToAnyTypeInstDouble) +{ + constexpr double payload = 42.0; + auto value = TaggedValue(payload); + + ASSERT_TRUE(value.IsDouble()); + ASSERT_TRUE(value.GetDouble() == payload); + CastValueToAnyType(payload, AnyBaseType::ECMASCRIPT_DOUBLE_TYPE, value.GetRawData()); +} + +TEST_F(CodegenEcmaTest, CastValueToAnyTypeInstObject) +{ + // As long as tagging remains the same for all heap objects (strings, array, objects, etc.), + // this single test is enough. + const auto *payload_ptr = reinterpret_cast(0xbee8); + const auto payload_int = reinterpret_cast(payload_ptr); + auto value = TaggedValue(payload_ptr); + + ASSERT_TRUE(value.IsHeapObject()); + ASSERT_TRUE(value.GetHeapObject() == payload_ptr); + ASSERT_TRUE(reinterpret_cast(value.GetHeapObject()) == static_cast(payload_int)); + CastValueToAnyType(payload_int, AnyBaseType::ECMASCRIPT_OBJECT_TYPE, value.GetRawData()); +} + +void CodegenEcmaTest::TestAnyTypeCheck(AnyBaseType any_type, uint64_t val) +{ + auto graph = CreateEmptyGraph(); + graph->SetDynamicMethod(); + GRAPH(graph) + { + CONSTANT(0, val).any(); + CONSTANT(1, 1); + + BASIC_BLOCK(2, -1) + { + INST(4, Opcode::SaveState).Inputs(0).SrcVregs({0}); + INST(2, Opcode::AnyTypeCheck).b().AnyType(any_type).Inputs(0, 4); + INST(3, Opcode::Return).b().Inputs(1); + } + } + + CheckReturnValue(graph, 1); +} + +TEST_F(CodegenEcmaTest, AnyTypeCheckInstBoolTrue_1) +{ + TestAnyTypeCheck(AnyBaseType::ECMASCRIPT_BOOLEAN_TYPE, TaggedValue::VALUE_TRUE); +} + +TEST_F(CodegenEcmaTest, AnyTypeCheckInstBoolTrue_2) +{ + TestAnyTypeCheck(AnyBaseType::ECMASCRIPT_BOOLEAN_TYPE, TaggedValue::VALUE_FALSE); +} + +TEST_F(CodegenEcmaTest, AnyTypeCheckInstDoubleTrue) +{ + TestAnyTypeCheck(AnyBaseType::ECMASCRIPT_DOUBLE_TYPE, TaggedValue::DOUBLE_ENCODE_OFFSET); +} + +TEST_F(CodegenEcmaTest, AnyTypeCheckInstIntTrue) +{ + TestAnyTypeCheck(AnyBaseType::ECMASCRIPT_INT_TYPE, TaggedValue::TAG_INT); +} + +TEST_F(CodegenEcmaTest, AnyTypeCheckInstNullTrue) +{ + TestAnyTypeCheck(AnyBaseType::ECMASCRIPT_NULL_TYPE, TaggedValue::VALUE_NULL); +} + +TEST_F(CodegenEcmaTest, AnyTypeCheckInstObjectTrue) +{ + TestAnyTypeCheck(AnyBaseType::ECMASCRIPT_OBJECT_TYPE, TaggedValue::TAG_OBJECT + 1); +} + +TEST_F(CodegenEcmaTest, AnyTypeCheckInstUndefinedTrue) +{ + TestAnyTypeCheck(AnyBaseType::ECMASCRIPT_UNDEFINED_TYPE, TaggedValue::VALUE_UNDEFINED); +} + +TEST_F(CodegenEcmaTest, AnyTypeCheckInstCommonUndefined) +{ + TestAnyTypeCheck(AnyBaseType::UNDEFINED_TYPE, TaggedValue::VALUE_FALSE); + TestAnyTypeCheck(AnyBaseType::UNDEFINED_TYPE, TaggedValue::VALUE_TRUE); + TestAnyTypeCheck(AnyBaseType::UNDEFINED_TYPE, TaggedValue::DOUBLE_ENCODE_OFFSET); + TestAnyTypeCheck(AnyBaseType::UNDEFINED_TYPE, TaggedValue::TAG_INT); + TestAnyTypeCheck(AnyBaseType::UNDEFINED_TYPE, TaggedValue::TAG_OBJECT + 1); + TestAnyTypeCheck(AnyBaseType::UNDEFINED_TYPE, TaggedValue::VALUE_NULL); + TestAnyTypeCheck(AnyBaseType::UNDEFINED_TYPE, TaggedValue::VALUE_UNDEFINED); +} + +} // namespace panda::compiler diff --git a/tests/compiler/ir_builder_ecma_test.cpp b/tests/compiler/ir_builder_ecma_test.cpp new file mode 100644 index 000000000..8b6a07fb8 --- /dev/null +++ b/tests/compiler/ir_builder_ecma_test.cpp @@ -0,0 +1,97 @@ +/* + * Copyright (c) 2021-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 "optimizer/ir/datatype.h" +#include "unit_ecma_test.h" + +namespace panda::compiler { +class IrBuilderTest : public AsmTest { +public: + IrBuilderTest() {} +}; + +TEST_F(IrBuilderTest, CompareAnyType) +{ + // no crash. + auto graph = CreateGraphDynWithDefaultRuntime(); + GRAPH(graph) + { + PARAMETER(0, 0); + INS(0).SetType(DataType::Type::ANY); + + BASIC_BLOCK(2, -1) + { + INST(2, Opcode::CompareAnyType).b().AnyType(AnyBaseType::ECMASCRIPT_INT_TYPE).Inputs(0); + INST(3, Opcode::Return).s32().Inputs(2); + } + } + + const CompareAnyTypeInst *cati = INS(2).CastToCompareAnyType(); + + ASSERT_TRUE(cati != nullptr); + EXPECT_TRUE(cati->GetInputType(0) == DataType::Type::ANY); + EXPECT_TRUE(cati->GetType() == DataType::BOOL); + EXPECT_TRUE(cati->GetAnyType() == AnyBaseType::ECMASCRIPT_INT_TYPE); +} + +TEST_F(IrBuilderTest, CastAnyTypeValue) +{ + // no crash. + auto graph = CreateGraphDynWithDefaultRuntime(); + GRAPH(graph) + { + PARAMETER(0, 0); + INS(0).SetType(DataType::Type::ANY); + + BASIC_BLOCK(2, -1) + { + INST(2, Opcode::CastAnyTypeValue).f64().AnyType(AnyBaseType::ECMASCRIPT_DOUBLE_TYPE).Inputs(0); + INST(3, Opcode::Return).f64().Inputs(2); + } + } + + const CastAnyTypeValueInst *catvi = INS(2).CastToCastAnyTypeValue(); + + ASSERT_TRUE(catvi != nullptr); + EXPECT_TRUE(catvi->GetInputType(0) == DataType::Type::ANY); + EXPECT_TRUE(catvi->GetDeducedType() == DataType::Type::FLOAT64); + EXPECT_TRUE(catvi->GetAnyType() == AnyBaseType::ECMASCRIPT_DOUBLE_TYPE); +} + +TEST_F(IrBuilderTest, CastValueToAnyType) +{ + // no crash. + auto graph = CreateGraphDynWithDefaultRuntime(); + GRAPH(graph) + { + PARAMETER(0, 0); + INS(0).SetType(DataType::Type::INT32); + + BASIC_BLOCK(2, -1) + { + INST(2, Opcode::CastValueToAnyType).any().AnyType(AnyBaseType::ECMASCRIPT_INT_TYPE).Inputs(0); + INST(3, Opcode::Return).any().Inputs(2); + } + } + + const CastValueToAnyTypeInst *cvai = INS(2).CastToCastValueToAnyType(); + + ASSERT_TRUE(cvai != nullptr); + EXPECT_TRUE(cvai->GetInputType(0) == DataType::Type::INT32); + EXPECT_TRUE(cvai->GetType() == DataType::Type::ANY); + EXPECT_TRUE(cvai->GetAnyType() == AnyBaseType::ECMASCRIPT_INT_TYPE); +} + +} // namespace panda::compiler diff --git a/tests/compiler/lowering_ecma_test.cpp b/tests/compiler/lowering_ecma_test.cpp new file mode 100644 index 000000000..0ecea76f9 --- /dev/null +++ b/tests/compiler/lowering_ecma_test.cpp @@ -0,0 +1,103 @@ +/* + * 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 "unit_ecma_test.h" +#include "optimizer/ir/datatype.h" +#include "optimizer/ir/graph_cloner.h" +#include "optimizer/optimizations/cleanup.h" +#include "optimizer/optimizations/lowering.h" + +namespace panda::compiler { +class LoweringEcmaTest : public AsmTest { +public: + LoweringEcmaTest() {} +}; + +TEST_F(LoweringEcmaTest, CastValueToAnyTypeWithConst) +{ + auto graph = CreateGraphDynWithDefaultRuntime(); + GRAPH(graph) + { + CONSTANT(0, 0); + + BASIC_BLOCK(2, -1) + { + INST(4, Opcode::SaveState).NoVregs(); + INST(1, Opcode::CastValueToAnyType).any().AnyType(AnyBaseType::ECMASCRIPT_BOOLEAN_TYPE).Inputs(0); + INST(2, Opcode::Intrinsic).any().Inputs({{DataType::ANY, 1}, {DataType::NO_TYPE, 4}}); + INST(3, Opcode::Return).any().Inputs(2); + } + } + auto graph_opt = CreateGraphDynWithDefaultRuntime(); + GRAPH(graph_opt) + { + CONSTANT(5, 0x6).any(); + + BASIC_BLOCK(2, -1) + { + INST(4, Opcode::SaveState).NoVregs(); + INST(2, Opcode::Intrinsic).any().Inputs({{DataType::ANY, 5}, {DataType::NO_TYPE, 4}}); + INST(3, Opcode::Return).any().Inputs(2); + } + } + + ASSERT_TRUE(graph->RunPass()); + ASSERT_TRUE(graph->RunPass()); + GraphChecker(graph).Check(); + + EXPECT_TRUE(GraphComparator().Compare(graph, graph_opt)); +} + +TEST_F(LoweringEcmaTest, CastValueToAnyTypeWithSaveState) +{ + auto graph = CreateGraphDynWithDefaultRuntime(); + GRAPH(graph) + { + PARAMETER(0, 0).any(); + CONSTANT(1, 1.1); + + BASIC_BLOCK(2, -1) + { + INST(2, Opcode::CastAnyTypeValue).f64().AnyType(AnyBaseType::ECMASCRIPT_DOUBLE_TYPE).Inputs(0); + INST(3, Opcode::Add).f64().Inputs(2, 1); + INST(4, Opcode::CastValueToAnyType).any().AnyType(AnyBaseType::ECMASCRIPT_DOUBLE_TYPE).Inputs(3); + INST(5, Opcode::SaveState).Inputs(4).SrcVregs({0}); + INST(6, Opcode::Intrinsic).any().Inputs({{DataType::NO_TYPE, 5}}); + INST(7, Opcode::Return).any().Inputs(6); + } + } + auto graph_opt = CreateGraphDynWithDefaultRuntime(); + GRAPH(graph_opt) + { + PARAMETER(0, 0).any(); + CONSTANT(1, 1.1); + + BASIC_BLOCK(2, -1) + { + INST(2, Opcode::CastAnyTypeValue).f64().AnyType(AnyBaseType::ECMASCRIPT_DOUBLE_TYPE).Inputs(0); + INST(3, Opcode::Add).f64().Inputs(2, 1); + INST(5, Opcode::SaveState).Inputs(3).SrcVregs({0}); + INST(6, Opcode::Intrinsic).any().Inputs({{DataType::NO_TYPE, 5}}); + INST(7, Opcode::Return).any().Inputs(6); + } + } + + ASSERT_TRUE(graph->RunPass()); + ASSERT_TRUE(graph->RunPass()); + GraphChecker(graph).Check(); + + EXPECT_TRUE(GraphComparator().Compare(graph, graph_opt)); +} +} // namespace panda::compiler diff --git a/tests/compiler/peepholes_ecma_test.cpp b/tests/compiler/peepholes_ecma_test.cpp new file mode 100644 index 000000000..bd223f72d --- /dev/null +++ b/tests/compiler/peepholes_ecma_test.cpp @@ -0,0 +1,382 @@ +/* + * Copyright (c) 2021-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 "unit_ecma_test.h" +#include "optimizer/ir/datatype.h" +#include "optimizer/ir/graph_cloner.h" +#include "optimizer/optimizations/cleanup.h" +#include "optimizer/optimizations/peepholes.h" + +namespace panda::compiler { +class PeepholesTest : public AsmTest { +public: + PeepholesTest() {} +}; + +TEST_F(PeepholesTest, CastTrueSimple) +{ + auto graph = CreateGraphDynWithDefaultRuntime(); + GRAPH(graph) + { + PARAMETER(0, 0).any(); + PARAMETER(1, 1).s32(); + + BASIC_BLOCK(2, -1) + { + INST(2, Opcode::CastAnyTypeValue).s32().AnyType(AnyBaseType::ECMASCRIPT_INT_TYPE).Inputs(0); + INST(3, Opcode::Add).s32().Inputs(1, 2); + INST(4, Opcode::CastValueToAnyType).any().AnyType(AnyBaseType::ECMASCRIPT_INT_TYPE).Inputs(3); + + INST(5, Opcode::CastAnyTypeValue).s32().AnyType(AnyBaseType::ECMASCRIPT_INT_TYPE).Inputs(4); + INST(6, Opcode::Add).s32().Inputs(1, 5); + INST(7, Opcode::CastValueToAnyType).any().AnyType(AnyBaseType::ECMASCRIPT_INT_TYPE).Inputs(6); + + INST(8, Opcode::Return).any().Inputs(7); + } + } + + ASSERT_TRUE(graph->RunPass()); + ASSERT_TRUE(graph->RunPass()); + GraphChecker(graph).Check(); + + auto graph_opt = CreateGraphDynWithDefaultRuntime(); + GRAPH(graph_opt) + { + PARAMETER(0, 0).any(); + PARAMETER(1, 1).s32(); + + BASIC_BLOCK(2, -1) + { + INST(2, Opcode::CastAnyTypeValue).s32().AnyType(AnyBaseType::ECMASCRIPT_INT_TYPE).Inputs(0); + INST(3, Opcode::Add).s32().Inputs(1, 2); + + INST(6, Opcode::Add).s32().Inputs(1, 3); + INST(7, Opcode::CastValueToAnyType).any().AnyType(AnyBaseType::ECMASCRIPT_INT_TYPE).Inputs(6); + + INST(8, Opcode::Return).any().Inputs(7); + } + } + + EXPECT_TRUE(GraphComparator().Compare(graph, graph_opt)); +} + +TEST_F(PeepholesTest, CastTrueComplex) +{ + auto graph = CreateGraphDynWithDefaultRuntime(); + GRAPH(graph) + { + PARAMETER(0, 0).any(); + PARAMETER(1, 1).s32(); + + BASIC_BLOCK(2, 3, 4) + { + INST(2, Opcode::CastAnyTypeValue).s32().AnyType(AnyBaseType::ECMASCRIPT_INT_TYPE).Inputs(0); + INST(3, Opcode::Add).s32().Inputs(1, 2); + INST(4, Opcode::CastValueToAnyType).any().AnyType(AnyBaseType::ECMASCRIPT_INT_TYPE).Inputs(3); + INST(13, Opcode::SaveState).NoVregs(); + INST(5, Opcode::CallDynamic).any().InputsAutoType(4, 13); + INST(6, Opcode::CompareAnyType).b().AnyType(AnyBaseType::ECMASCRIPT_INT_TYPE).Inputs(5); + INST(7, Opcode::IfImm).SrcType(DataType::BOOL).CC(CC_EQ).Imm(0).Inputs(6); + } + + BASIC_BLOCK(3, -1) + { + INST(8, Opcode::Return).any().Inputs(0); + } + + BASIC_BLOCK(4, -1) + { + INST(9, Opcode::CastAnyTypeValue).s32().AnyType(AnyBaseType::ECMASCRIPT_INT_TYPE).Inputs(4); + INST(10, Opcode::Add).s32().Inputs(1, 9); + INST(11, Opcode::CastValueToAnyType).any().AnyType(AnyBaseType::ECMASCRIPT_INT_TYPE).Inputs(10); + + INST(12, Opcode::Return).any().Inputs(11); + } + } + + ASSERT_TRUE(graph->RunPass()); + ASSERT_TRUE(graph->RunPass()); + GraphChecker(graph).Check(); + + auto graph_opt = CreateGraphDynWithDefaultRuntime(); + GRAPH(graph_opt) + { + PARAMETER(0, 0).any(); + PARAMETER(1, 1).s32(); + + BASIC_BLOCK(2, 3, 4) + { + INST(2, Opcode::CastAnyTypeValue).s32().AnyType(AnyBaseType::ECMASCRIPT_INT_TYPE).Inputs(0); + INST(3, Opcode::Add).s32().Inputs(1, 2); + INST(4, Opcode::CastValueToAnyType).any().AnyType(AnyBaseType::ECMASCRIPT_INT_TYPE).Inputs(3); + INST(13, Opcode::SaveState).NoVregs(); + INST(5, Opcode::CallDynamic).any().InputsAutoType(4, 13); + INST(6, Opcode::CompareAnyType).b().AnyType(AnyBaseType::ECMASCRIPT_INT_TYPE).Inputs(5); + INST(7, Opcode::IfImm).SrcType(DataType::BOOL).CC(CC_EQ).Imm(0).Inputs(6); + } + + BASIC_BLOCK(3, -1) + { + INST(8, Opcode::Return).any().Inputs(0); + } + + BASIC_BLOCK(4, -1) + { + INST(10, Opcode::Add).s32().Inputs(1, 3); + INST(11, Opcode::CastValueToAnyType).any().AnyType(AnyBaseType::ECMASCRIPT_INT_TYPE).Inputs(10); + + INST(12, Opcode::Return).any().Inputs(11); + } + } + + EXPECT_TRUE(GraphComparator().Compare(graph, graph_opt)); +} + +TEST_F(PeepholesTest, CastFalse) +{ + auto graph = CreateGraphDynWithDefaultRuntime(); + GRAPH(graph) + { + PARAMETER(0, 0).any(); + PARAMETER(1, 1).s32(); + + BASIC_BLOCK(2, -1) + { + INST(2, Opcode::CastAnyTypeValue).s32().AnyType(AnyBaseType::ECMASCRIPT_INT_TYPE).Inputs(0); + INST(3, Opcode::Add).s32().Inputs(1, 2); + INST(4, Opcode::CastValueToAnyType).any().AnyType(AnyBaseType::ECMASCRIPT_INT_TYPE).Inputs(3); + INST(5, Opcode::Return).any().Inputs(4); + } + } + + auto graph_opt = GraphCloner(graph, graph->GetAllocator(), graph->GetLocalAllocator()).CloneGraph(); + + ASSERT_FALSE(graph->RunPass()); + ASSERT_FALSE(graph->RunPass()); + GraphChecker(graph).Check(); + + EXPECT_TRUE(GraphComparator().Compare(graph, graph_opt)); +} + +TEST_F(PeepholesTest, CompareCastToAnyAndCastToAny) +{ + auto graph = CreateGraphDynWithDefaultRuntime(); + GRAPH(graph) + { + PARAMETER(0, 0).any(); + PARAMETER(1, 1).any(); + + BASIC_BLOCK(2, -1) + { + INST(2, Opcode::CastAnyTypeValue).i32().AnyType(AnyBaseType::ECMASCRIPT_INT_TYPE).Inputs(0); + INST(3, Opcode::CastAnyTypeValue).i32().AnyType(AnyBaseType::ECMASCRIPT_INT_TYPE).Inputs(1); + INST(4, Opcode::Compare).b().CC(CC_LT).Inputs(2, 3).SrcType(DataType::INT32); + INST(5, Opcode::CastValueToAnyType).any().AnyType(AnyBaseType::ECMASCRIPT_BOOLEAN_TYPE).Inputs(4); + INST(6, Opcode::Compare).b().CC(CC_GE).Inputs(2, 3).SrcType(DataType::INT32); + INST(7, Opcode::CastValueToAnyType).any().AnyType(AnyBaseType::ECMASCRIPT_BOOLEAN_TYPE).Inputs(6); + INST(8, Opcode::Compare).b().CC(CC_EQ).Inputs(5, 7).SrcType(DataType::ANY); + INST(9, Opcode::CastValueToAnyType).any().AnyType(AnyBaseType::ECMASCRIPT_BOOLEAN_TYPE).Inputs(8); + INST(10, Opcode::Return).any().Inputs(9); + } + } + + ASSERT_TRUE(graph->RunPass()); + ASSERT_TRUE(graph->RunPass()); + GraphChecker(graph).Check(); + + auto graph_opt = CreateGraphDynWithDefaultRuntime(); + GRAPH(graph_opt) + { + PARAMETER(0, 0).any(); + PARAMETER(1, 1).any(); + + BASIC_BLOCK(2, -1) + { + INST(2, Opcode::CastAnyTypeValue).i32().AnyType(AnyBaseType::ECMASCRIPT_INT_TYPE).Inputs(0); + INST(3, Opcode::CastAnyTypeValue).i32().AnyType(AnyBaseType::ECMASCRIPT_INT_TYPE).Inputs(1); + INST(4, Opcode::Compare).b().CC(CC_LT).Inputs(2, 3).SrcType(DataType::INT32); + INST(6, Opcode::Compare).b().CC(CC_GE).Inputs(2, 3).SrcType(DataType::INT32); + INST(8, Opcode::Compare).b().CC(CC_EQ).Inputs(4, 6).SrcType(DataType::BOOL); + INST(9, Opcode::CastValueToAnyType).any().AnyType(AnyBaseType::ECMASCRIPT_BOOLEAN_TYPE).Inputs(8); + INST(10, Opcode::Return).any().Inputs(9); + } + } + + EXPECT_TRUE(GraphComparator().Compare(graph, graph_opt)); +} + +TEST_F(PeepholesTest, CompareCastToAnyAndCastToAnyNotApply) +{ + auto graph = CreateGraphDynWithDefaultRuntime(); + GRAPH(graph) + { + PARAMETER(0, 0).any(); + PARAMETER(1, 1).any(); + + BASIC_BLOCK(2, -1) + { + INST(2, Opcode::CastAnyTypeValue).i32().AnyType(AnyBaseType::ECMASCRIPT_INT_TYPE).Inputs(0); + INST(3, Opcode::CastAnyTypeValue).i32().AnyType(AnyBaseType::ECMASCRIPT_INT_TYPE).Inputs(1); + INST(4, Opcode::Compare).b().CC(CC_LT).Inputs(2, 3).SrcType(DataType::INT32); + INST(5, Opcode::CastValueToAnyType).any().AnyType(AnyBaseType::ECMASCRIPT_BOOLEAN_TYPE).Inputs(4); + INST(6, Opcode::Add).i32().Inputs(2, 3); + INST(7, Opcode::CastValueToAnyType).any().AnyType(AnyBaseType::ECMASCRIPT_INT_TYPE).Inputs(6); + INST(8, Opcode::Compare).b().CC(CC_EQ).Inputs(5, 7).SrcType(DataType::ANY); + INST(9, Opcode::CastValueToAnyType).any().AnyType(AnyBaseType::ECMASCRIPT_BOOLEAN_TYPE).Inputs(8); + INST(10, Opcode::Return).any().Inputs(9); + } + } + auto graph_opt = GraphCloner(graph, graph->GetAllocator(), graph->GetLocalAllocator()).CloneGraph(); + + ASSERT_FALSE(graph->RunPass()); + ASSERT_FALSE(graph->RunPass()); + GraphChecker(graph).Check(); + + EXPECT_TRUE(GraphComparator().Compare(graph, graph_opt)); +} + +TEST_F(PeepholesTest, CompareCastToAnyAndConstTrue) +{ + auto graph = CreateGraphDynWithDefaultRuntime(); + GRAPH(graph) + { + PARAMETER(0, 0).any(); + PARAMETER(1, 1).any(); + CONSTANT(9, TaggedValue::VALUE_TRUE).any(); + + BASIC_BLOCK(2, -1) + { + INST(2, Opcode::CastAnyTypeValue).i32().AnyType(AnyBaseType::ECMASCRIPT_INT_TYPE).Inputs(0); + INST(3, Opcode::CastAnyTypeValue).i32().AnyType(AnyBaseType::ECMASCRIPT_INT_TYPE).Inputs(1); + INST(4, Opcode::Compare).b().CC(CC_LT).Inputs(2, 3).SrcType(DataType::INT32); + INST(5, Opcode::CastValueToAnyType).any().AnyType(AnyBaseType::ECMASCRIPT_BOOLEAN_TYPE).Inputs(4); + INST(6, Opcode::Compare).b().CC(CC_EQ).Inputs(5, 9).SrcType(DataType::ANY); + INST(7, Opcode::CastValueToAnyType).any().AnyType(AnyBaseType::ECMASCRIPT_BOOLEAN_TYPE).Inputs(6); + INST(8, Opcode::Return).any().Inputs(7); + } + } + + ASSERT_TRUE(graph->RunPass()); + ASSERT_TRUE(graph->RunPass()); + GraphChecker(graph).Check(); + + auto graph_opt = CreateGraphDynWithDefaultRuntime(); + GRAPH(graph_opt) + { + PARAMETER(0, 0).any(); + PARAMETER(1, 1).any(); + CONSTANT(19, 1); + + BASIC_BLOCK(2, -1) + { + INST(2, Opcode::CastAnyTypeValue).i32().AnyType(AnyBaseType::ECMASCRIPT_INT_TYPE).Inputs(0); + INST(3, Opcode::CastAnyTypeValue).i32().AnyType(AnyBaseType::ECMASCRIPT_INT_TYPE).Inputs(1); + INST(4, Opcode::Compare).b().CC(CC_LT).Inputs(2, 3).SrcType(DataType::INT32); + INST(6, Opcode::Compare).b().CC(CC_EQ).Inputs(4, 19).SrcType(DataType::BOOL); + INST(7, Opcode::CastValueToAnyType).any().AnyType(AnyBaseType::ECMASCRIPT_BOOLEAN_TYPE).Inputs(6); + INST(8, Opcode::Return).any().Inputs(7); + } + } + + EXPECT_TRUE(GraphComparator().Compare(graph, graph_opt)); +} + +TEST_F(PeepholesTest, CompareCastToAnyAndConstFalse) +{ + auto graph = CreateGraphDynWithDefaultRuntime(); + GRAPH(graph) + { + PARAMETER(0, 0).any(); + PARAMETER(1, 1).any(); + CONSTANT(9, TaggedValue::VALUE_FALSE).any(); + + BASIC_BLOCK(2, -1) + { + INST(2, Opcode::CastAnyTypeValue).i32().AnyType(AnyBaseType::ECMASCRIPT_INT_TYPE).Inputs(0); + INST(3, Opcode::CastAnyTypeValue).i32().AnyType(AnyBaseType::ECMASCRIPT_INT_TYPE).Inputs(1); + INST(4, Opcode::Compare).b().CC(CC_LT).Inputs(2, 3).SrcType(DataType::INT32); + INST(5, Opcode::CastValueToAnyType).any().AnyType(AnyBaseType::ECMASCRIPT_BOOLEAN_TYPE).Inputs(4); + INST(6, Opcode::Compare).b().CC(CC_EQ).Inputs(5, 9).SrcType(DataType::ANY); + INST(7, Opcode::CastValueToAnyType).any().AnyType(AnyBaseType::ECMASCRIPT_BOOLEAN_TYPE).Inputs(6); + INST(8, Opcode::Return).any().Inputs(7); + } + } + + ASSERT_TRUE(graph->RunPass()); + ASSERT_TRUE(graph->RunPass()); + GraphChecker(graph).Check(); + + auto graph_opt = CreateGraphDynWithDefaultRuntime(); + GRAPH(graph_opt) + { + PARAMETER(0, 0).any(); + PARAMETER(1, 1).any(); + CONSTANT(19, 0); + + BASIC_BLOCK(2, -1) + { + INST(2, Opcode::CastAnyTypeValue).i32().AnyType(AnyBaseType::ECMASCRIPT_INT_TYPE).Inputs(0); + INST(3, Opcode::CastAnyTypeValue).i32().AnyType(AnyBaseType::ECMASCRIPT_INT_TYPE).Inputs(1); + INST(4, Opcode::Compare).b().CC(CC_LT).Inputs(2, 3).SrcType(DataType::INT32); + INST(6, Opcode::Compare).b().CC(CC_EQ).Inputs(4, 19).SrcType(DataType::BOOL); + INST(7, Opcode::CastValueToAnyType).any().AnyType(AnyBaseType::ECMASCRIPT_BOOLEAN_TYPE).Inputs(6); + INST(8, Opcode::Return).any().Inputs(7); + } + } + + EXPECT_TRUE(GraphComparator().Compare(graph, graph_opt)); +} + +TEST_F(PeepholesTest, CompareCastToAnyAndConst) +{ + auto graph = CreateGraphDynWithDefaultRuntime(); + GRAPH(graph) + { + PARAMETER(0, 0).any(); + PARAMETER(1, 1).any(); + CONSTANT(9, 10000).any(); + + BASIC_BLOCK(2, -1) + { + INST(2, Opcode::CastAnyTypeValue).i32().AnyType(AnyBaseType::ECMASCRIPT_INT_TYPE).Inputs(0); + INST(3, Opcode::CastAnyTypeValue).i32().AnyType(AnyBaseType::ECMASCRIPT_INT_TYPE).Inputs(1); + INST(4, Opcode::Compare).b().CC(CC_LT).Inputs(2, 3).SrcType(DataType::INT32); + INST(5, Opcode::CastValueToAnyType).any().AnyType(AnyBaseType::ECMASCRIPT_BOOLEAN_TYPE).Inputs(4); + INST(6, Opcode::Compare).b().CC(CC_EQ).Inputs(5, 9).SrcType(DataType::ANY); + INST(7, Opcode::CastValueToAnyType).any().AnyType(AnyBaseType::ECMASCRIPT_BOOLEAN_TYPE).Inputs(6); + INST(8, Opcode::Return).any().Inputs(7); + } + } + + ASSERT_TRUE(graph->RunPass()); + ASSERT_TRUE(graph->RunPass()); + GraphChecker(graph).Check(); + + auto graph_opt = CreateGraphDynWithDefaultRuntime(); + GRAPH(graph_opt) + { + CONSTANT(19, 0); + + BASIC_BLOCK(2, -1) + { + INST(7, Opcode::CastValueToAnyType).any().AnyType(AnyBaseType::ECMASCRIPT_BOOLEAN_TYPE).Inputs(19); + INST(8, Opcode::Return).any().Inputs(7); + } + } + + EXPECT_TRUE(GraphComparator().Compare(graph, graph_opt)); +} + +} // namespace panda::compiler diff --git a/tests/compiler/types_resolving_ecma_tests.cpp b/tests/compiler/types_resolving_ecma_tests.cpp new file mode 100644 index 000000000..1f51194d0 --- /dev/null +++ b/tests/compiler/types_resolving_ecma_tests.cpp @@ -0,0 +1,476 @@ +/* + * Copyright (c) 2021-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 "unit_ecma_test.h" +#include "optimizer/ir/datatype.h" +#include "optimizer/ir/graph_cloner.h" +#include "optimizer/optimizations/cleanup.h" +#include "optimizer/optimizations/types_resolving.h" + +namespace panda::compiler { +class TypeResolvingTest : public AsmTest { +public: + TypeResolvingTest() {} + + Graph *ConstructGraphWithIntrinsic(AnyBaseType type, RuntimeInterface::IntrinsicId id); + Graph *ConstructGraphWithIntrinsic(AnyBaseType type1, AnyBaseType type2, RuntimeInterface::IntrinsicId id); + Graph *ConstructGraphWithOpcode1(AnyBaseType type, Opcode opcode); + Graph *ConstructGraphWithOpcode2(AnyBaseType type, Opcode opcode); +}; + +Graph *TypeResolvingTest::ConstructGraphWithIntrinsic(AnyBaseType type, RuntimeInterface::IntrinsicId id) +{ + auto graph = CreateGraphDynWithDefaultRuntime(); + GRAPH(graph) + { + PARAMETER(0, 0).any(); + PARAMETER(1, 1).any(); + + BASIC_BLOCK(2, -1) + { + INST(2, Opcode::SaveState).Inputs(0, 1).SrcVregs({0, 1}); + INST(4, Opcode::AnyTypeCheck).any().AnyType(type).Inputs(0, 2); + INST(5, Opcode::Intrinsic) + .any() + .IntrinsicId(id) + .Inlined() + .Inputs({{DataType::ANY, 4}, {DataType::NO_TYPE, 2}}); + INST(6, Opcode::Return).any().Inputs(5); + } + } + return graph; +} + +Graph *TypeResolvingTest::ConstructGraphWithOpcode1(AnyBaseType type, Opcode opcode) +{ + auto graph = CreateGraphDynWithDefaultRuntime(); + auto date_type = AnyBaseTypeToDataType(type); + GRAPH(graph) + { + PARAMETER(0, 0).any(); + PARAMETER(1, 1).any(); + + BASIC_BLOCK(2, -1) + { + INST(2, Opcode::SaveState).Inputs(0, 1).SrcVregs({0, 1}); + INST(3, Opcode::AnyTypeCheck).any().AnyType(type).Inputs(0, 2); + INST(5, Opcode::CastAnyTypeValue).SetType(date_type).AnyType(type).Inputs(3); + INST(7, opcode).SetType(date_type).Inputs(5); + INST(8, Opcode::CastValueToAnyType).any().AnyType(type).Inputs(7); + INST(9, Opcode::Return).any().Inputs(8); + } + } + return graph; +} + +Graph *TypeResolvingTest::ConstructGraphWithOpcode2(AnyBaseType type, Opcode opcode) +{ + auto graph = CreateGraphDynWithDefaultRuntime(); + auto date_type = AnyBaseTypeToDataType(type); + GRAPH(graph) + { + PARAMETER(0, 0).any(); + PARAMETER(1, 1).any(); + + BASIC_BLOCK(2, -1) + { + INST(2, Opcode::SaveState).Inputs(0, 1).SrcVregs({0, 1}); + INST(3, Opcode::AnyTypeCheck).any().AnyType(type).Inputs(0, 2); + INST(4, Opcode::AnyTypeCheck).any().AnyType(type).Inputs(1, 2); + INST(5, Opcode::CastAnyTypeValue).SetType(date_type).AnyType(type).Inputs(3); + INST(6, Opcode::CastAnyTypeValue).SetType(date_type).AnyType(type).Inputs(4); + INST(7, opcode).SetType(date_type).Inputs(5, 6); + INST(8, Opcode::CastValueToAnyType).any().AnyType(type).Inputs(7); + INST(9, Opcode::Return).any().Inputs(8); + } + } + return graph; +} + +Graph *TypeResolvingTest::ConstructGraphWithIntrinsic(AnyBaseType type1, AnyBaseType type2, + RuntimeInterface::IntrinsicId id) +{ + auto graph = CreateGraphDynWithDefaultRuntime(); + GRAPH(graph) + { + PARAMETER(0, 0).any(); + PARAMETER(1, 1).any(); + + BASIC_BLOCK(2, -1) + { + INST(2, Opcode::SaveState).Inputs(0, 1).SrcVregs({0, 1}); + INST(3, Opcode::AnyTypeCheck).any().AnyType(type1).Inputs(0, 2); + INST(4, Opcode::AnyTypeCheck).any().AnyType(type2).Inputs(1, 2); + INST(5, Opcode::Intrinsic) + .any() + .IntrinsicId(id) + .Inlined() + .Inputs({{DataType::ANY, 3}, {DataType::ANY, 4}, {DataType::NO_TYPE, 2}}); + INST(6, Opcode::Return).any().Inputs(5); + } + } + return graph; +} + +TEST_F(TypeResolvingTest, ToNumber) +{ + // Case with Int + { + auto graph = ConstructGraphWithIntrinsic(AnyBaseType::ECMASCRIPT_INT_TYPE, + RuntimeInterface::IntrinsicId::INTRINSIC_TONUMBER); + ASSERT_TRUE(graph->RunPass()); + ASSERT_TRUE(graph->RunPass()); + GraphChecker(graph).Check(); + + auto graph_opt = CreateGraphDynWithDefaultRuntime(); + GRAPH(graph_opt) + { + PARAMETER(0, 0).any(); + PARAMETER(1, 1).any(); + + BASIC_BLOCK(2, -1) + { + INST(2, Opcode::SaveState).Inputs(0, 1).SrcVregs({0, 1}); + INST(3, Opcode::AnyTypeCheck).any().AnyType(AnyBaseType::ECMASCRIPT_INT_TYPE).Inputs(0, 2); + INST(9, Opcode::Return).any().Inputs(3); + } + } + + ASSERT_TRUE(GraphComparator().Compare(graph, graph_opt)); + } + + // Case with Double + { + auto graph = ConstructGraphWithIntrinsic(AnyBaseType::ECMASCRIPT_DOUBLE_TYPE, + RuntimeInterface::IntrinsicId::INTRINSIC_TONUMBER); + ASSERT_TRUE(graph->RunPass()); + ASSERT_TRUE(graph->RunPass()); + GraphChecker(graph).Check(); + + auto graph_opt = CreateGraphDynWithDefaultRuntime(); + GRAPH(graph_opt) + { + PARAMETER(0, 0).any(); + PARAMETER(1, 1).any(); + + BASIC_BLOCK(2, -1) + { + INST(2, Opcode::SaveState).Inputs(0, 1).SrcVregs({0, 1}); + INST(3, Opcode::AnyTypeCheck).any().AnyType(AnyBaseType::ECMASCRIPT_DOUBLE_TYPE).Inputs(0, 2); + INST(9, Opcode::Return).any().Inputs(3); + } + } + + ASSERT_TRUE(GraphComparator().Compare(graph, graph_opt)); + } + + // Case with Object + { + auto graph = ConstructGraphWithIntrinsic(AnyBaseType::ECMASCRIPT_OBJECT_TYPE, + RuntimeInterface::IntrinsicId::INTRINSIC_TONUMBER); + auto clone = GraphCloner(graph, graph->GetAllocator(), graph->GetLocalAllocator()).CloneGraph(); + ASSERT_FALSE(graph->RunPass()); + ASSERT_FALSE(graph->RunPass()); + GraphChecker(graph).Check(); + + ASSERT_TRUE(GraphComparator().Compare(graph, clone)); + } +} + +TEST_F(TypeResolvingTest, ResolvePhi) +{ + auto graph = CreateGraphDynWithDefaultRuntime(); + GRAPH(graph) + { + CONSTANT(0, 0).i64(); + CONSTANT(1, 1).i64(); + PARAMETER(2, 0).any(); + + BASIC_BLOCK(2, 3, 4) + { + INST(3, Opcode::CastValueToAnyType).any().AnyType(AnyBaseType::ECMASCRIPT_INT_TYPE).Inputs(0); + INST(4, Opcode::CastValueToAnyType).any().AnyType(AnyBaseType::ECMASCRIPT_INT_TYPE).Inputs(1); + INST(6, Opcode::CastAnyTypeValue).b().AnyType(AnyBaseType::ECMASCRIPT_BOOLEAN_TYPE).Inputs(2); + INST(7, Opcode::IfImm).SrcType(DataType::BOOL).CC(CC_NE).Imm(0).Inputs(6); + } + BASIC_BLOCK(3, 4) {} + + BASIC_BLOCK(4, -1) + { + INST(8, Opcode::Phi).any().Inputs(3, 4); + INST(9, Opcode::Return).any().Inputs(8); + } + } + ASSERT_TRUE(graph->RunPass()); + ASSERT_TRUE(graph->RunPass()); + GraphChecker(graph).Check(); + + auto graph_opt = CreateGraphDynWithDefaultRuntime(); + GRAPH(graph_opt) + { + CONSTANT(0, 0).i64(); + CONSTANT(1, 1).i64(); + PARAMETER(2, 0).any(); + + BASIC_BLOCK(2, 3, 4) + { + INST(6, Opcode::CastAnyTypeValue).b().AnyType(AnyBaseType::ECMASCRIPT_BOOLEAN_TYPE).Inputs(2); + INST(7, Opcode::IfImm).SrcType(DataType::BOOL).CC(CC_NE).Imm(0).Inputs(6); + } + BASIC_BLOCK(3, 4) {} + + BASIC_BLOCK(4, -1) + { + INST(8, Opcode::Phi).i32().Inputs(0, 1); + INST(10, Opcode::CastValueToAnyType).any().AnyType(AnyBaseType::ECMASCRIPT_INT_TYPE).Inputs(8); + INST(9, Opcode::Return).any().Inputs(10); + } + } + + ASSERT_TRUE(GraphComparator().Compare(graph, graph_opt)); +} + +TEST_F(TypeResolvingTest, Resolve2Phi) +{ + auto graph = CreateGraphDynWithDefaultRuntime(); + GRAPH(graph) + { + CONSTANT(0, 0).i64(); + CONSTANT(1, 1).i64(); + CONSTANT(21, 1).i64(); + PARAMETER(2, 0).any(); + PARAMETER(22, 1).any(); + + BASIC_BLOCK(2, 3, 4) + { + INST(3, Opcode::CastValueToAnyType).any().AnyType(AnyBaseType::ECMASCRIPT_INT_TYPE).Inputs(0); + INST(4, Opcode::CastValueToAnyType).any().AnyType(AnyBaseType::ECMASCRIPT_INT_TYPE).Inputs(1); + INST(10, Opcode::CastValueToAnyType).any().AnyType(AnyBaseType::ECMASCRIPT_INT_TYPE).Inputs(21); + INST(6, Opcode::CastAnyTypeValue).b().AnyType(AnyBaseType::ECMASCRIPT_BOOLEAN_TYPE).Inputs(2); + INST(7, Opcode::IfImm).SrcType(DataType::BOOL).CC(CC_NE).Imm(0).Inputs(6); + } + BASIC_BLOCK(3, 4) {} + + BASIC_BLOCK(4, 5, 6) + { + INST(13, Opcode::Phi).any().Inputs(3, 4); + INST(11, Opcode::CastAnyTypeValue).b().AnyType(AnyBaseType::ECMASCRIPT_BOOLEAN_TYPE).Inputs(22); + INST(12, Opcode::IfImm).SrcType(DataType::BOOL).CC(CC_NE).Imm(0).Inputs(11); + } + BASIC_BLOCK(6, 5) {} + + BASIC_BLOCK(5, -1) + { + INST(8, Opcode::Phi).any().Inputs(13, 10); + INST(9, Opcode::Return).any().Inputs(8); + } + } + ASSERT_TRUE(graph->RunPass()); + ASSERT_TRUE(graph->RunPass()); + GraphChecker(graph).Check(); + + auto graph_opt = CreateGraphDynWithDefaultRuntime(); + GRAPH(graph_opt) + { + CONSTANT(0, 0).i64(); + CONSTANT(1, 1).i64(); + CONSTANT(21, 1).i64(); + PARAMETER(2, 0).any(); + PARAMETER(22, 1).any(); + + BASIC_BLOCK(2, 3, 4) + { + INST(6, Opcode::CastAnyTypeValue).b().AnyType(AnyBaseType::ECMASCRIPT_BOOLEAN_TYPE).Inputs(2); + INST(7, Opcode::IfImm).SrcType(DataType::BOOL).CC(CC_NE).Imm(0).Inputs(6); + } + BASIC_BLOCK(3, 4) {} + + BASIC_BLOCK(4, 5, 6) + { + INST(13, Opcode::Phi).i32().Inputs(0, 1); + INST(11, Opcode::CastAnyTypeValue).b().AnyType(AnyBaseType::ECMASCRIPT_BOOLEAN_TYPE).Inputs(22); + INST(12, Opcode::IfImm).SrcType(DataType::BOOL).CC(CC_NE).Imm(0).Inputs(11); + } + BASIC_BLOCK(6, 5) {} + + BASIC_BLOCK(5, -1) + { + INST(8, Opcode::Phi).i32().Inputs(13, 21); + INST(25, Opcode::CastValueToAnyType).any().AnyType(AnyBaseType::ECMASCRIPT_INT_TYPE).Inputs(8); + INST(9, Opcode::Return).any().Inputs(25); + } + } + + ASSERT_TRUE(GraphComparator().Compare(graph, graph_opt)); +} + +TEST_F(TypeResolvingTest, ResolvePhiNotApply) +{ + auto graph = CreateGraphDynWithDefaultRuntime(); + GRAPH(graph) + { + CONSTANT(0, 0).i64(); + CONSTANT(1, 1.1).f64(); + PARAMETER(2, 0).any(); + + BASIC_BLOCK(2, 3, 4) + { + INST(3, Opcode::CastValueToAnyType).any().AnyType(AnyBaseType::ECMASCRIPT_INT_TYPE).Inputs(0); + INST(4, Opcode::CastValueToAnyType).any().AnyType(AnyBaseType::ECMASCRIPT_DOUBLE_TYPE).Inputs(1); + INST(6, Opcode::CastAnyTypeValue).b().AnyType(AnyBaseType::ECMASCRIPT_BOOLEAN_TYPE).Inputs(2); + INST(7, Opcode::IfImm).SrcType(DataType::BOOL).CC(CC_NE).Imm(0).Inputs(6); + } + BASIC_BLOCK(3, 4) {} + + BASIC_BLOCK(4, -1) + { + INST(8, Opcode::Phi).any().Inputs(3, 4); + INST(9, Opcode::Return).any().Inputs(8); + } + } + auto graph_opt = GraphCloner(graph, graph->GetAllocator(), graph->GetLocalAllocator()).CloneGraph(); + + ASSERT_FALSE(graph->RunPass()); + ASSERT_FALSE(graph->RunPass()); + GraphChecker(graph).Check(); + + ASSERT_TRUE(GraphComparator().Compare(graph, graph_opt)); +} + +TEST_F(TypeResolvingTest, ResolvePhiUserNotAnyType) +{ + auto graph = CreateGraphDynWithDefaultRuntime(); + GRAPH(graph) + { + CONSTANT(0, 0).f64(); + CONSTANT(1, 1).f64(); + PARAMETER(2, 0).any(); + + BASIC_BLOCK(2, 3, 4) + { + INST(3, Opcode::CastValueToAnyType).any().AnyType(AnyBaseType::ECMASCRIPT_DOUBLE_TYPE).Inputs(0); + INST(4, Opcode::CastValueToAnyType).any().AnyType(AnyBaseType::ECMASCRIPT_DOUBLE_TYPE).Inputs(1); + INST(6, Opcode::CastAnyTypeValue).b().AnyType(AnyBaseType::ECMASCRIPT_BOOLEAN_TYPE).Inputs(2); + INST(7, Opcode::IfImm).SrcType(DataType::BOOL).CC(CC_NE).Imm(0).Inputs(6); + } + BASIC_BLOCK(3, 4) {} + + BASIC_BLOCK(4, -1) + { + INST(8, Opcode::Phi).any().Inputs(3, 4); + INST(9, Opcode::CompareAnyType).AnyType(AnyBaseType::ECMASCRIPT_INT_TYPE).b().Inputs(8); + INST(10, Opcode::Return).b().Inputs(9); + } + } + ASSERT_TRUE(graph->RunPass()); + ASSERT_TRUE(graph->RunPass()); + GraphChecker(graph).Check(); + + auto graph_opt = CreateGraphDynWithDefaultRuntime(); + GRAPH(graph_opt) + { + CONSTANT(0, 0).f64(); + CONSTANT(1, 1).f64(); + PARAMETER(2, 0).any(); + + BASIC_BLOCK(2, 3, 4) + { + INST(6, Opcode::CastAnyTypeValue).b().AnyType(AnyBaseType::ECMASCRIPT_BOOLEAN_TYPE).Inputs(2); + INST(7, Opcode::IfImm).SrcType(DataType::BOOL).CC(CC_NE).Imm(0).Inputs(6); + } + BASIC_BLOCK(3, 4) {} + + BASIC_BLOCK(4, -1) + { + INST(8, Opcode::Phi).f64().Inputs(0, 1); + INST(11, Opcode::CastValueToAnyType).any().AnyType(AnyBaseType::ECMASCRIPT_DOUBLE_TYPE).Inputs(8); + INST(9, Opcode::CompareAnyType).AnyType(AnyBaseType::ECMASCRIPT_INT_TYPE).b().Inputs(11); + INST(10, Opcode::Return).b().Inputs(9); + } + } + + ASSERT_TRUE(GraphComparator().Compare(graph, graph_opt)); +} + +TEST_F(TypeResolvingTest, ResolveLoopPhi) +{ + auto graph = CreateGraphDynWithDefaultRuntime(); + + // for (let x = 0; x < 10;) + // { + // if (x < 5) { + // x++; + // } + // } + GRAPH(graph) + { + CONSTANT(0, 0).i64(); + CONSTANT(1, 10).i64(); + CONSTANT(2, 5).i64(); + + BASIC_BLOCK(2, 3) + { + INST(3, Opcode::CastValueToAnyType).any().AnyType(AnyBaseType::ECMASCRIPT_INT_TYPE).Inputs(0); + } + BASIC_BLOCK(3, 4, 5) + { + INST(4, Opcode::Phi).any().Inputs(3, 4, 19); + INST(5, Opcode::Phi).any().Inputs(3, 14, 4); + INST(6, Opcode::CastValueToAnyType).any().AnyType(AnyBaseType::ECMASCRIPT_INT_TYPE).Inputs(1); + INST(7, Opcode::SaveState).NoVregs(); + INST(8, Opcode::Intrinsic) + .any() + .IntrinsicId(RuntimeInterface::IntrinsicId::INTRINSIC_LESS_DYN) + .Inputs({{DataType::ANY, 4}, {DataType::ANY, 6}, {DataType::NO_TYPE, 7}}); + INST(9, Opcode::CastValueToAnyType).any().AnyType(AnyBaseType::ECMASCRIPT_BOOLEAN_TYPE).Inputs(0); + INST(10, Opcode::Compare).b().CC(CC_EQ).Inputs(8, 9); + INST(11, Opcode::IfImm).SrcType(DataType::BOOL).CC(CC_NE).Imm(0).Inputs(10); + } + BASIC_BLOCK(4, -1) + { + INST(20, Opcode::CastValueToAnyType).any().AnyType(AnyBaseType::ECMASCRIPT_UNDEFINED_TYPE).Inputs(0); + INST(21, Opcode::Return).any().Inputs(20); + } + BASIC_BLOCK(5, 3, 6) + { + INST(12, Opcode::CastValueToAnyType).any().AnyType(AnyBaseType::ECMASCRIPT_INT_TYPE).Inputs(2); + INST(13, Opcode::SaveState).NoVregs(); + INST(14, Opcode::Intrinsic) + .any() + .IntrinsicId(RuntimeInterface::IntrinsicId::INTRINSIC_LESS_DYN) + .Inputs({{DataType::ANY, 4}, {DataType::ANY, 12}, {DataType::NO_TYPE, 13}}); + INST(15, Opcode::CastValueToAnyType).any().AnyType(AnyBaseType::ECMASCRIPT_BOOLEAN_TYPE).Inputs(0); + INST(16, Opcode::Compare).b().CC(CC_EQ).Inputs(14, 15); + INST(17, Opcode::IfImm).SrcType(DataType::BOOL).CC(CC_NE).Imm(0).Inputs(16); + } + BASIC_BLOCK(6, 3) + { + INST(18, Opcode::SaveState).NoVregs(); + INST(19, Opcode::Intrinsic) + .any() + .Inlined() + .IntrinsicId(RuntimeInterface::IntrinsicId::INTRINSIC_INC_DYN) + .Inputs({{DataType::ANY, 4}, {DataType::NO_TYPE, 18}}); + } + } + ASSERT_TRUE(graph->RunPass()); + ASSERT_TRUE(graph->RunPass()); + GraphChecker(graph).Check(); + + auto &phi = INS(4); + ASSERT_EQ(phi.GetType(), DataType::INT32); + ASSERT_EQ(phi.GetInput(1U).GetInst(), &phi); +} +} // namespace panda::compiler diff --git a/tests/compiler/unit_ecma_test.cpp b/tests/compiler/unit_ecma_test.cpp new file mode 100644 index 000000000..bcfff0094 --- /dev/null +++ b/tests/compiler/unit_ecma_test.cpp @@ -0,0 +1,175 @@ +/* + * Copyright (c) 2021-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 "macros.h" +#if defined(PANDA_TARGET_MOBILE) +#elif defined(USE_STD_FILESYSTEM) +#include +#else +#include +#endif +#include "unit_ecma_test.h" +#include "optimizer/ir_builder/ir_builder.h" +#include "optimizer/optimizations/cleanup.h" +#include "optimizer/code_generator/encode.h" +#include "include/class_linker.h" +#include "assembly-parser.h" +#include "include/runtime.h" +#include "compiler.h" +#include "utils/expected.h" +#include "compiler_options.h" + +#include "utils/utf.h" + +namespace panda::compiler { +void PandaRuntimeTest::Initialize([[maybe_unused]] int argc, char **argv) +{ + ASSERT(argc > 0); + exec_path_ = argv[0]; +} + +PandaRuntimeTest::PandaRuntimeTest() +{ + ASSERT(exec_path_ != nullptr); +#if !defined(PANDA_TARGET_MOBILE) +#if defined(USE_STD_FILESYSTEM) + std::filesystem::path exec_name(exec_path_); +#else + std::experimental::filesystem::path exec_name(exec_path_); +#endif // defined(USE_STD_FILESYSTEM) + std::string pandastdlib_path = exec_name.parent_path() / "../pandastdlib/arkstdlib.abc"; +#else + std::string exec_name = "compiler_unit_tests"; + std::string pandastdlib_path = "../pandastdlib/arkstdlib.abc"; +#endif + panda::RuntimeOptions runtime_options(exec_name); + runtime_options.SetBootPandaFiles({pandastdlib_path}); + runtime_options.SetLoadRuntimes({"ecmascript"}); + runtime_options.SetHeapSizeLimit(50_MB); + runtime_options.SetEnableAn(true); + runtime_options.SetGcType("epsilon"); + Logger::InitializeStdLogging(Logger::Level::ERROR, Logger::Component::COMPILER); + EXPECT_TRUE(panda::Runtime::Create(runtime_options)); + + allocator_ = new ArenaAllocator(panda::SpaceType::SPACE_TYPE_INTERNAL); + local_allocator_ = new ArenaAllocator(panda::SpaceType::SPACE_TYPE_INTERNAL); + builder_ = new IrConstructor(); + + graph_ = CreateGraph(); +} + +PandaRuntimeTest::~PandaRuntimeTest() +{ + delete builder_; + delete allocator_; + delete local_allocator_; + panda::Runtime::Destroy(); +} + +class EcmaDefaultRuntimeInterface : public RuntimeInterface { +public: + SourceLanguage GetMethodSourceLanguage([[maybe_unused]] MethodPtr method) const override + { + return SourceLanguage::ECMASCRIPT; + } +}; + +RuntimeInterface *PandaRuntimeTest::GetDefaultRuntime() +{ + static EcmaDefaultRuntimeInterface runtime_interface; + return &runtime_interface; +} + +std::unique_ptr AsmTest::ParseToFile(const char *source, const char *file_name) +{ + panda::pandasm::Parser parser; + auto res = parser.Parse(source, file_name); + if (parser.ShowError().err != pandasm::Error::ErrorType::ERR_NONE) { + std::cerr << "Parse failed: " << parser.ShowError().message << std::endl + << parser.ShowError().whole_line << std::endl; + ADD_FAILURE(); + return nullptr; + } + return pandasm::AsmEmitter::Emit(res.Value()); +} + +bool AsmTest::Parse(const char *source, const char *file_name) +{ + auto pfile = ParseToFile(source, file_name); + if (pfile == nullptr) { + ADD_FAILURE(); + return false; + } + GetClassLinker()->AddPandaFile(std::move(pfile)); + return true; +} + +Graph *AsmTest::BuildGraph(const char *method_name, Graph *graph) +{ + auto loader = GetClassLinker(); + PandaString storage; + auto extension = loader->GetExtension(panda_file::SourceLang::ECMASCRIPT); + auto *thread = MTManagedThread::GetCurrent(); + thread->ManagedCodeBegin(); + auto klass = extension->GetClass(ClassHelper::GetDescriptor(utf::CStringAsMutf8("_GLOBAL"), &storage)); + thread->ManagedCodeEnd(); + + auto method = klass->GetDirectMethod(utf::CStringAsMutf8(method_name)); + if (method == nullptr) { + ADD_FAILURE(); + return nullptr; + } + if (graph == nullptr) { + graph = CreateGraph(); + } + graph->SetMethod(method); + if (!graph->RunPass()) { + ADD_FAILURE(); + return nullptr; + } + return graph; +} + +void AsmTest::CleanUp(Graph *graph) +{ + graph->RunPass(); +} + +CommonTest::~CommonTest() +{ + // Look at examples in encoder_constructors tests. + // Used for destroy MasmHolder. + Encoder *encoder = Encoder::Create(allocator_, arch_, false); + if (encoder != nullptr) { + encoder->~Encoder(); + } + delete builder_; + delete allocator_; + delete object_allocator_; + delete local_allocator_; + PoolManager::Finalize(); + + panda::mem::MemConfig::Finalize(); +} +} // namespace panda::compiler + +int main(int argc, char **argv) +{ + ::testing::InitGoogleTest(&argc, argv); + + panda::compiler::PandaRuntimeTest::Initialize(argc, argv); + panda::compiler::options.SetCompilerUseSafepoint(false); + return RUN_ALL_TESTS(); +} diff --git a/tests/compiler/unit_ecma_test.h b/tests/compiler/unit_ecma_test.h new file mode 100644 index 000000000..13ee35f21 --- /dev/null +++ b/tests/compiler/unit_ecma_test.h @@ -0,0 +1,24 @@ +/* + * Copyright (c) 2021-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 COMPILER_TESTS_UNIT_ECMA_TEST_H_ +#define COMPILER_TESTS_UNIT_ECMA_TEST_H_ + +#include "unit_test.h" + +namespace panda::compiler { +} // namespace panda::compiler + +#endif // COMPILER_TESTS_UNIT_ECMA_TEST_H_ diff --git a/tests/compiler/vn_test_ecma.cpp b/tests/compiler/vn_test_ecma.cpp new file mode 100644 index 000000000..684ab8d9a --- /dev/null +++ b/tests/compiler/vn_test_ecma.cpp @@ -0,0 +1,210 @@ +/* + * Copyright (c) 2021-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 "unit_ecma_test.h" +#include "optimizer/ir/datatype.h" +#include "optimizer/ir/graph_cloner.h" +#include "optimizer/optimizations/cleanup.h" +#include "optimizer/optimizations/vn.h" + +namespace panda::compiler { +class VNTest : public AsmTest { +public: + VNTest() {} +}; + +TEST_F(VNTest, CompareAnyTypeVNTrue) +{ + auto graph = CreateGraphDynWithDefaultRuntime(); + GRAPH(graph) + { + PARAMETER(0, 0).any(); + + BASIC_BLOCK(2, 3, 4) + { + INST(2, Opcode::CompareAnyType).b().AnyType(AnyBaseType::ECMASCRIPT_INT_TYPE).Inputs(0); + INST(3, Opcode::IfImm).SrcType(DataType::BOOL).CC(CC_EQ).Imm(0).Inputs(2); + } + + BASIC_BLOCK(3, -1) + { + CONSTANT(4, 42); + INST(5, Opcode::Return).s32().Inputs(4); + } + + BASIC_BLOCK(4, -1) + { + INST(6, Opcode::CompareAnyType).b().AnyType(AnyBaseType::ECMASCRIPT_INT_TYPE).Inputs(0); + INST(7, Opcode::Return).s32().Inputs(6); + } + } + + ASSERT_TRUE(graph->RunPass()); + ASSERT_TRUE(graph->RunPass()); + GraphChecker(graph).Check(); + + auto graph_opt = CreateGraphDynWithDefaultRuntime(); + GRAPH(graph_opt) + { + PARAMETER(0, 0).any(); + + BASIC_BLOCK(2, 3, 4) + { + INST(2, Opcode::CompareAnyType).b().AnyType(AnyBaseType::ECMASCRIPT_INT_TYPE).Inputs(0); + INST(3, Opcode::IfImm).SrcType(DataType::BOOL).CC(CC_EQ).Imm(0).Inputs(2); + } + + BASIC_BLOCK(3, -1) + { + CONSTANT(4, 42); + INST(5, Opcode::Return).s32().Inputs(4); + } + + BASIC_BLOCK(4, -1) + { + INST(7, Opcode::Return).s32().Inputs(2); + } + } + + EXPECT_TRUE(GraphComparator().Compare(graph, graph_opt)); +} + +TEST_F(VNTest, CompareAnyTypeVNFalse) +{ + auto graph = CreateGraphDynWithDefaultRuntime(); + GRAPH(graph) + { + PARAMETER(0, 0).any(); + + BASIC_BLOCK(2, 3, 4) + { + INST(2, Opcode::CompareAnyType).b().AnyType(AnyBaseType::ECMASCRIPT_INT_TYPE).Inputs(0); + INST(3, Opcode::IfImm).SrcType(DataType::BOOL).CC(CC_EQ).Imm(0).Inputs(2); + } + + BASIC_BLOCK(3, -1) + { + CONSTANT(4, 42); + INST(5, Opcode::Return).s32().Inputs(4); + } + + BASIC_BLOCK(4, -1) + { + INST(6, Opcode::CompareAnyType).b().AnyType(AnyBaseType::ECMASCRIPT_BOOLEAN_TYPE).Inputs(0); + INST(7, Opcode::Return).s32().Inputs(6); + } + } + + auto graph_opt = GraphCloner(graph, graph->GetAllocator(), graph->GetLocalAllocator()).CloneGraph(); + + ASSERT_FALSE(graph->RunPass()); + ASSERT_FALSE(graph->RunPass()); + GraphChecker(graph).Check(); + + EXPECT_TRUE(GraphComparator().Compare(graph, graph_opt)); +} + +TEST_F(VNTest, CastAnyTypeVNTrue) +{ + auto graph = CreateGraphDynWithDefaultRuntime(); + GRAPH(graph) + { + PARAMETER(0, 0).any(); + + BASIC_BLOCK(2, 3, 4) + { + INST(2, Opcode::CastAnyTypeValue).b().AnyType(AnyBaseType::ECMASCRIPT_BOOLEAN_TYPE).Inputs(0); + INST(3, Opcode::IfImm).SrcType(DataType::BOOL).CC(CC_EQ).Imm(0).Inputs(2); + } + + BASIC_BLOCK(3, -1) + { + CONSTANT(4, 42); + INST(5, Opcode::Return).s32().Inputs(4); + } + + BASIC_BLOCK(4, -1) + { + INST(6, Opcode::CastAnyTypeValue).b().AnyType(AnyBaseType::ECMASCRIPT_BOOLEAN_TYPE).Inputs(0); + INST(7, Opcode::Return).s32().Inputs(6); + } + } + + ASSERT_TRUE(graph->RunPass()); + ASSERT_TRUE(graph->RunPass()); + GraphChecker(graph).Check(); + + auto graph_opt = CreateGraphDynWithDefaultRuntime(); + GRAPH(graph_opt) + { + PARAMETER(0, 0).any(); + + BASIC_BLOCK(2, 3, 4) + { + INST(2, Opcode::CastAnyTypeValue).b().AnyType(AnyBaseType::ECMASCRIPT_BOOLEAN_TYPE).Inputs(0); + INST(3, Opcode::IfImm).SrcType(DataType::BOOL).CC(CC_EQ).Imm(0).Inputs(2); + } + + BASIC_BLOCK(3, -1) + { + CONSTANT(4, 42); + INST(5, Opcode::Return).s32().Inputs(4); + } + + BASIC_BLOCK(4, -1) + { + INST(7, Opcode::Return).s32().Inputs(2); + } + } + + EXPECT_TRUE(GraphComparator().Compare(graph, graph_opt)); +} + +TEST_F(VNTest, CastAnyTypeVNFalse) +{ + auto graph = CreateGraphDynWithDefaultRuntime(); + GRAPH(graph) + { + PARAMETER(0, 0).any(); + + BASIC_BLOCK(2, 3, 4) + { + INST(2, Opcode::CastAnyTypeValue).b().AnyType(AnyBaseType::ECMASCRIPT_BOOLEAN_TYPE).Inputs(0); + INST(3, Opcode::IfImm).SrcType(DataType::BOOL).CC(CC_EQ).Imm(0).Inputs(2); + } + + BASIC_BLOCK(3, -1) + { + CONSTANT(4, 42); + INST(5, Opcode::Return).s32().Inputs(4); + } + + BASIC_BLOCK(4, -1) + { + INST(6, Opcode::CastAnyTypeValue).s32().AnyType(AnyBaseType::ECMASCRIPT_INT_TYPE).Inputs(0); + INST(7, Opcode::Return).s32().Inputs(6); + } + } + + auto graph_opt = GraphCloner(graph, graph->GetAllocator(), graph->GetLocalAllocator()).CloneGraph(); + + ASSERT_FALSE(graph->RunPass()); + ASSERT_FALSE(graph->RunPass()); + GraphChecker(graph).Check(); + + EXPECT_TRUE(GraphComparator().Compare(graph, graph_opt)); +} + +} // namespace panda::compiler diff --git a/tests/disassembler/CMakeLists.txt b/tests/disassembler/CMakeLists.txt new file mode 100644 index 000000000..eb9a022a1 --- /dev/null +++ b/tests/disassembler/CMakeLists.txt @@ -0,0 +1,69 @@ +# Copyright (c) 2021-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. + +# disasm_bin directory +set(DISASM_BIN_DIR ${PANDA_BINARY_ROOT}/disassembler/bin) + +# disasm_tests directory +set(DISASM_ECMA_TESTS_DIR ${CMAKE_CURRENT_SOURCE_DIR}/sources) + +set(LITERALS_TEST_ECMA_SRC ${CMAKE_CURRENT_BINARY_DIR}/disasm_test_lit_ecma.cpp) +configure_file(${CMAKE_CURRENT_SOURCE_DIR}/disasm_test_lit_ecma.cpp.in ${LITERALS_TEST_ECMA_SRC} @ONLY) + +panda_add_gtest( + NAME + disasm_tests_ecma + SOURCES + ${LITERALS_TEST_ECMA_SRC} + LIBRARIES + arkdisassembler + INCLUDE_DIRS + ${PANDA_ROOT}/disassembler + SANITIZERS + ${PANDA_SANITIZERS_LIST} +) + +function(ecma_compile_pre_build) + set(options) + set(singleValueArgs TARGET FILE_SRC FILE_DST) + set(multiValueArgs) + + cmake_parse_arguments(CPB "${options}" "${singleValueArgs}" "${multiValueArgs}" ${ARGN}) + + if (NOT DEFINED CPB_TARGET) + message(FATAL_ERROR "TARGET argument is missing") + endif() + + if (NOT DEFINED CPB_FILE_SRC) + message(FATAL_ERROR "FILE_SRC argument is missing") + endif() + + if (NOT DEFINED CPB_FILE_DST) + message(FATAL_ERROR "FILE_DST argument is missing") + endif() + + add_custom_command(OUTPUT "${CPB_FILE_DST}" + COMMENT "Compiling ${CPB_FILE_SRC} ( ${CPB_TARGET} ) in c_p_b function" + COMMAND ${es2panda_bin} "${CPB_FILE_SRC}" --output "${CPB_FILE_DST}" + DEPENDS "${CPB_FILE_SRC}" ${es2panda_target}) + + add_custom_target(${CPB_TARGET} + COMMENT "reached ${CPB_TARGET}" + DEPENDS "${CPB_FILE_DST}") +endfunction() + +ecma_compile_pre_build(TARGET disasm_binaries_ecma_lit + FILE_SRC ${DISASM_ECMA_TESTS_DIR}/lit.js + FILE_DST ${DISASM_BIN_DIR}/lit.bc) + +add_dependencies(disasm_tests_ecma disasm_binaries_ecma_lit) diff --git a/tests/disassembler/disasm_test_lit_ecma.cpp.in b/tests/disassembler/disasm_test_lit_ecma.cpp.in new file mode 100644 index 000000000..5204c2ed4 --- /dev/null +++ b/tests/disassembler/disasm_test_lit_ecma.cpp.in @@ -0,0 +1,37 @@ +/* + * Copyright (c) 2021-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 +#include + +#include +#include "disassembler.h" + +#cmakedefine DISASM_BIN_DIR "@DISASM_BIN_DIR@/" + +using namespace panda::disasm; + +TEST(instructions_test, test_lit_ecma) +{ + Disassembler d {}; + + std::stringstream ss {}; + d.Disassemble(std::string(DISASM_BIN_DIR) + "lit.bc"); + d.Serialize(ss); + + EXPECT_TRUE(ss.str().find(".language ECMAScript") != std::string::npos); +} + +#undef DISASM_BIN_DIR diff --git a/tests/disassembler/sources/lit.js b/tests/disassembler/sources/lit.js new file mode 100644 index 000000000..bfdccfb3d --- /dev/null +++ b/tests/disassembler/sources/lit.js @@ -0,0 +1,16 @@ +/* + * Copyright (c) 2021-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. + */ + +print({"a": "b"}) diff --git a/tests/ecmascript-tests/js-bitops-bitwise-and.js b/tests/ecmascript-tests/js-bitops-bitwise-and.js new file mode 100644 index 000000000..59861da08 --- /dev/null +++ b/tests/ecmascript-tests/js-bitops-bitwise-and.js @@ -0,0 +1,41 @@ +/* + * Copyright (C) 2007 Apple Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +// #ifndef RUNNER +var n = 10 +// #else +// #put_params +// #endif + +bitwiseAndValue = 4294967296; +for (var i = 0; i < n; i++) + bitwiseAndValue = bitwiseAndValue & i; + +var result = bitwiseAndValue; + +var expected = 0; +if (result != expected) + throw "ERROR: bad result: expected " + expected + " but got " + result; + diff --git a/tests/ecmascript-tests/js-bitops-bitwise-and.pa b/tests/ecmascript-tests/js-bitops-bitwise-and.pa new file mode 100644 index 000000000..14e75c7ee --- /dev/null +++ b/tests/ecmascript-tests/js-bitops-bitwise-and.pa @@ -0,0 +1,154 @@ +# Copyright (c) 2021-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. + +.language ECMAScript + +.record Ecmascript.Intrinsics +.function any Ecmascript.Intrinsics.ldglobal() +.function any Ecmascript.Intrinsics.ldundefined() +.function void Ecmascript.Intrinsics.stobjDyn(any a0, any a1, any a2) +.function any Ecmascript.Intrinsics.ldobjDyn(any a0, any a1) +.function any Ecmascript.Intrinsics.lessDyn(any a0, any a1) +.function any Ecmascript.Intrinsics.trygetobjprop(any a0, any a1) +.function any Ecmascript.Intrinsics.and2Dyn(any a0, any a1) +.function any Ecmascript.Intrinsics.incDyn(any a0) +.function any Ecmascript.Intrinsics.tonumber(any a0) +.function any Ecmascript.Intrinsics.noteqDyn(any a0, any a1) +.function any Ecmascript.Intrinsics.add2Dyn(any a0, any a1) +.function void Ecmascript.Intrinsics.throwDyn(any a0) + +.function any main(any a0, any a1, any a2) { + mov.dyn v2, a2 + mov.dyn v1, a1 + mov.dyn v0, a0 + call.short Ecmascript.Intrinsics.ldglobal + sta.dyn v11 + call.short Ecmascript.Intrinsics.ldundefined + sta.dyn v10 + lda.str "n" + sta.dyn v3 + call Ecmascript.Intrinsics.stobjDyn, v11, v3, v10 + lda.str "i" + sta.dyn v3 + call Ecmascript.Intrinsics.stobjDyn, v11, v3, v10 + lda.str "result" + sta.dyn v3 + call Ecmascript.Intrinsics.stobjDyn, v11, v3, v10 + lda.str "expected" + sta.dyn v3 + call Ecmascript.Intrinsics.stobjDyn, v11, v3, v10 + ldai.dyn 10 + sta.dyn v3 + lda.str "n" + sta.dyn v4 + call Ecmascript.Intrinsics.stobjDyn, v11, v4, v3 + lda.dyn v3 + fldai.dyn 4294967296 + sta.dyn v3 + lda.str "bitwiseAndValue" + sta.dyn v4 + call Ecmascript.Intrinsics.stobjDyn, v11, v4, v3 + lda.dyn v3 + ldai.dyn 0 + sta.dyn v3 + lda.str "i" + sta.dyn v4 + call Ecmascript.Intrinsics.stobjDyn, v11, v4, v3 + lda.dyn v3 +LABEL_0: + lda.str "i" + sta.dyn v5 + call.short Ecmascript.Intrinsics.ldobjDyn, v11, v5 + sta.dyn v3 + lda.str "n" + sta.dyn v5 + call.short Ecmascript.Intrinsics.ldobjDyn, v11, v5 + sta.dyn v4 + call.short Ecmascript.Intrinsics.lessDyn, v3, v4 + jeqz LABEL_1 + lda.str "bitwiseAndValue" + sta.dyn v5 + call.short Ecmascript.Intrinsics.trygetobjprop, v11, v5 + sta.dyn v3 + lda.str "i" + sta.dyn v5 + call.short Ecmascript.Intrinsics.ldobjDyn, v11, v5 + sta.dyn v4 + call.short Ecmascript.Intrinsics.and2Dyn, v3, v4 + sta.dyn v3 + lda.str "bitwiseAndValue" + sta.dyn v4 + call Ecmascript.Intrinsics.stobjDyn, v11, v4, v3 + lda.dyn v3 +LABEL_2: + lda.str "i" + sta.dyn v4 + call.short Ecmascript.Intrinsics.ldobjDyn, v11, v4 + sta.dyn v3 + call.short Ecmascript.Intrinsics.incDyn, v3 + sta.dyn v4 + lda.str "i" + sta.dyn v5 + call Ecmascript.Intrinsics.stobjDyn, v11, v5, v4 + lda.dyn v4 + call.short Ecmascript.Intrinsics.tonumber, v3 + jmp LABEL_0 +LABEL_1: + lda.str "bitwiseAndValue" + sta.dyn v3 + call.short Ecmascript.Intrinsics.trygetobjprop, v11, v3 + sta.dyn v3 + lda.str "result" + sta.dyn v4 + call Ecmascript.Intrinsics.stobjDyn, v11, v4, v3 + lda.dyn v3 + ldai.dyn 0 + sta.dyn v3 + lda.str "expected" + sta.dyn v4 + call Ecmascript.Intrinsics.stobjDyn, v11, v4, v3 + lda.dyn v3 + lda.str "result" + sta.dyn v5 + call.short Ecmascript.Intrinsics.ldobjDyn, v11, v5 + sta.dyn v3 + lda.str "expected" + sta.dyn v5 + call.short Ecmascript.Intrinsics.ldobjDyn, v11, v5 + sta.dyn v4 + call.short Ecmascript.Intrinsics.noteqDyn, v3, v4 + jeqz LABEL_3 + lda.str "ERROR: bad result: expected " + sta.dyn v7 + lda.str "expected" + sta.dyn v9 + call.short Ecmascript.Intrinsics.ldobjDyn, v11, v9 + sta.dyn v8 + call.short Ecmascript.Intrinsics.add2Dyn, v7, v8 + sta.dyn v5 + lda.str " but got " + sta.dyn v6 + call.short Ecmascript.Intrinsics.add2Dyn, v5, v6 + sta.dyn v3 + lda.str "result" + sta.dyn v5 + call.short Ecmascript.Intrinsics.ldobjDyn, v11, v5 + sta.dyn v4 + call.short Ecmascript.Intrinsics.add2Dyn, v3, v4 + sta.dyn v3 + call.short Ecmascript.Intrinsics.throwDyn, v3 +LABEL_3: + lda.dyn v10 + return.dyn +} + diff --git a/tests/runtime/CMakeLists.txt b/tests/runtime/CMakeLists.txt new file mode 100644 index 000000000..b5515696e --- /dev/null +++ b/tests/runtime/CMakeLists.txt @@ -0,0 +1,668 @@ +# Copyright (c) 2021-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. + +add_subdirectory(tooling) +add_subdirectory(common) +add_subdirectory(napi) +add_subdirectory(mem) +add_subdirectory(irtoc) + +## for performance consideration, each test suite should not be oversize, neither too small ## +## the principle is make a solo suite for case over 1000 lines ## +set(ECMASCRIPT_TESTS_SOURCES + snapshot/snapshot_test.cpp + common/name_dictionary_test.cpp + common/native_pointer_test.cpp + common/object_factory_test.cpp + common/linked_hash_table_test.cpp + #common/dump_test.cpp + common/assert_scope_test.cpp + common/js_verification_test.cpp + common/large_object_test.cpp + common/glue_regs_test.cpp + common/test_helper.cpp +) + +set(ECMASCRIPT_TEST_SOURCES-2 + #common/concurrent_marking_test.cpp + #common/concurrent_sweep_test.cpp + #common/gc_test.cpp + #common/mem_controller_test.cpp + #common/weak_ref_gen_gc_test.cpp + #common/weak_ref_stw_gc_test.cpp + common/ecma_module_test.cpp + common/ecma_string_test.cpp + common/huge_object_test.cpp + common/js_arguments_test.cpp + common/js_array_iterator_test.cpp + common/js_dataview_test.cpp + #common/js_serializer_test.cpp + common/js_set_iterator_test.cpp + common/js_symbol_test.cpp + common/js_typed_array_test.cpp + common/lexical_env_test.cpp + common/symbol_table_test.cpp + common/test_helper.cpp +) + +set(ECMASCRIPT_TAGGEDVALUE_TESTS_SOURCES + common/tagged_value_test.cpp + common/test_helper.cpp +) + +set(ECMASCRIPT_BUILTINS_BASIC_TESTS_SOURCES + common/builtins_test.cpp + builtins/builtins_boolean_test.cpp + builtins/builtins_symbol_test.cpp + builtins/builtins_set_test.cpp + builtins/builtins_map_test.cpp + builtins/builtins_weak_map_test.cpp + builtins/builtins_weak_set_test.cpp + builtins/builtins_iterator_test.cpp + builtins/builtins_proxy_test.cpp + common/test_helper.cpp +) + +set(ECMASCRIPT_BUILTINS_BASIC_TESTS_SOURCES-1 + builtins/builtins_function_test.cpp + builtins/builtins_number_test.cpp + common/test_helper.cpp +) + +set(ECMASCRIPT_BUILTINS_REGEXP_TESTS + builtins/builtins_regexp_test.cpp + common/test_helper.cpp +) + +set(ECMASCRIPT_BUILTINS_OBJECT_TESTS_SOURCES + builtins/builtins_object_test.cpp + common/test_helper.cpp +) + +set(ECMASCRIPT_BUILTINS_ERRORS_TESTS_SOURCES + builtins/builtins_errors_test.cpp + common/test_helper.cpp +) + +set(ECMASCRIPT_BUILTINS_DATE_SOURCES + builtins/builtins_date_test.cpp + common/test_helper.cpp +) + +set(ECMASCRIPT_ES_BASIC_TESTS_SOURCES + common/js_function_test.cpp + common/js_date_test.cpp + common/js_array_test.cpp + common/js_primitive_ref_test.cpp + common/test_helper.cpp +) + +set(ECMASCRIPT_ES_BASIC_TESTS_SOURCES-1 + common/js_proxy_test.cpp + common/test_helper.cpp +) + +set(ECMASCRIPT_ES_BASIC_TESTS_SOURCES-2 + common/js_set_test.cpp + common/js_map_test.cpp + common/js_tagged_queue_test.cpp + common/js_forin_iterator_test.cpp + common/js_handle_test.cpp + common/js_iterator_test.cpp + common/js_promise_test.cpp + common/test_helper.cpp +) + +set(ECMASCRIPT_ES_BASIC_OBJECT_TESTS_SOURCES + common/js_object_test.cpp + common/test_helper.cpp +) + +set(ECMASCRIPT_BUILTINS_MATH_TESTS_SOURCES + builtins/builtins_math_test.cpp + common/test_helper.cpp +) + +set(ECMASCRIPT_BUILTINS_PROMISE_TESTS_SOURCES + builtins/builtins_promise_test.cpp + common/test_helper.cpp +) + +set(ECMASCRIPT_BUILTINS_JSON_TESTS_SOURCES + builtins/builtins_json_test.cpp + common/test_helper.cpp +) + +set(ECMASCRIPT_BUILTINS_STRING_TESTS_SOURCES + builtins/builtins_string_test.cpp + common/test_helper.cpp +) + +set(ECMASCRIPT_BUILTINS_ARRAY_TESTS_SOURCES + builtins/builtins_array_test.cpp + common/test_helper.cpp +) + +set(ECMASCRIPT_BUILTINS_REFLECT_TESTS_SOURCES + builtins/builtins_reflect_test.cpp + common/test_helper.cpp +) + +set(ECMASCRIPT_BUILTINS_ARRAYBUFFER_TESTS_SOURCES + builtins/builtins_arraybuffer_test.cpp + common/test_helper.cpp +) + +set(ECMASCRIPT_BUILTINS_DATAVIEW_TESTS_SOURCES + builtins/builtins_dataview_test.cpp + common/test_helper.cpp +) + +set(ECMASCRIPT_REGEXP_TESTS_SOURCES + regexp/dyn_buffer_test.cpp + regexp/regexp_test.cpp + common/test_helper.cpp +) + +set(ECMASCRIPT_BUILTINS_TYPEDARRAY_TESTS_SOURCES + builtins/builtins_typedarray_test.cpp + common/test_helper.cpp +) + +set(ECMASCRIPT_HPROF_TESTS_SOURCES + hprof/hprof_test.cpp + hprof/heap_tracker_test.cpp + common/test_helper.cpp +) + +#0 +panda_add_gtest( + NO_CORES +NAME arkruntime4ecmascript_tests + SOURCES + ${ECMASCRIPT_TESTS_SOURCES} + LIBRARIES + arkruntime arkassembler + INCLUDE_DIRS + ${PANDA_ECMASCRIPT_PLUGIN_SOURCE}/tests/runtime/common + SANITIZERS + ${PANDA_SANITIZERS_LIST} +) +if(TARGET arkruntime4ecmascript_tests) + target_include_directories(arkruntime4ecmascript_tests + PUBLIC ${CMAKE_CURRENT_SOURCE_DIR} + ) + target_compile_options(arkruntime4ecmascript_tests PUBLIC "-Wno-ignored-attributes") +endif() +#1 +panda_add_gtest( + NO_CORES +NAME arkruntime4ecmascript_taggedvalue_tests + SOURCES + ${ECMASCRIPT_TAGGEDVALUE_TESTS_SOURCES} + LIBRARIES + arkruntime arkassembler + INCLUDE_DIRS + ${PANDA_ECMASCRIPT_PLUGIN_SOURCE}/tests/runtime/common + SANITIZERS + ${PANDA_SANITIZERS_LIST} +) +if(TARGET arkruntime4ecmascript_taggedvalue_tests) + target_include_directories(arkruntime4ecmascript_taggedvalue_tests + PUBLIC ${CMAKE_CURRENT_SOURCE_DIR} + ) + target_compile_options(arkruntime4ecmascript_taggedvalue_tests PUBLIC "-Wno-ignored-attributes") +endif() +#2 +panda_add_gtest( + NO_CORES +NAME arkruntime4ecmascript_builtins_basic_tests + SOURCES + ${ECMASCRIPT_BUILTINS_BASIC_TESTS_SOURCES} + LIBRARIES + arkruntime arkassembler + INCLUDE_DIRS + ${PANDA_ECMASCRIPT_PLUGIN_SOURCE}/tests/runtime/common + SANITIZERS + ${PANDA_SANITIZERS_LIST} +) +if(TARGET arkruntime4ecmascript_builtins_basic_tests) + target_include_directories(arkruntime4ecmascript_builtins_basic_tests + PUBLIC ${CMAKE_CURRENT_SOURCE_DIR} + ) + target_compile_options(arkruntime4ecmascript_builtins_basic_tests PUBLIC "-Wno-ignored-attributes") +endif() +#3.1 +panda_add_gtest( + NO_CORES +NAME arkruntime4ecmascript_builtins_regexp_tests + SOURCES + ${ECMASCRIPT_BUILTINS_REGEXP_TESTS} + LIBRARIES + arkruntime arkassembler + INCLUDE_DIRS + ${PANDA_ECMASCRIPT_PLUGIN_SOURCE}/tests/runtime/common + SANITIZERS + ${PANDA_SANITIZERS_LIST} +) +if(TARGET arkruntime4ecmascript_builtins_regexp_tests) + target_include_directories(arkruntime4ecmascript_builtins_regexp_tests + PUBLIC ${CMAKE_CURRENT_SOURCE_DIR} + ) + target_compile_options(arkruntime4ecmascript_builtins_regexp_tests PUBLIC "-Wno-ignored-attributes") +endif() +#3.2 +panda_add_gtest( + NO_CORES +NAME arkruntime4ecmascript_builtins_basic_tests-1 + SOURCES + ${ECMASCRIPT_BUILTINS_BASIC_TESTS_SOURCES-1} + LIBRARIES + arkruntime arkassembler + INCLUDE_DIRS + ${PANDA_ECMASCRIPT_PLUGIN_SOURCE}/tests/runtime/common + SANITIZERS + ${PANDA_SANITIZERS_LIST} +) +if(TARGET arkruntime4ecmascript_builtins_basic_tests-1) + target_include_directories(arkruntime4ecmascript_builtins_basic_tests-1 + PUBLIC ${CMAKE_CURRENT_SOURCE_DIR} + ) + target_compile_options(arkruntime4ecmascript_builtins_basic_tests-1 PUBLIC "-Wno-ignored-attributes") +endif() +#4 +panda_add_gtest( + NO_CORES +NAME arkruntime4ecmascript_builtins_object_tests + SOURCES + ${ECMASCRIPT_BUILTINS_OBJECT_TESTS_SOURCES} + LIBRARIES + arkruntime arkassembler + INCLUDE_DIRS + ${PANDA_ECMASCRIPT_PLUGIN_SOURCE}/tests/runtime/common + SANITIZERS + ${PANDA_SANITIZERS_LIST} +) +if(TARGET arkruntime4ecmascript_builtins_object_tests) + target_include_directories(arkruntime4ecmascript_builtins_object_tests + PUBLIC ${CMAKE_CURRENT_SOURCE_DIR} + ) + target_compile_options(arkruntime4ecmascript_builtins_object_tests PUBLIC "-Wno-ignored-attributes") +endif() +#5 +panda_add_gtest( + NO_CORES +NAME arkruntime4ecmascript_builtins_errors_tests + SOURCES + ${ECMASCRIPT_BUILTINS_ERRORS_TESTS_SOURCES} + LIBRARIES + arkruntime arkassembler + INCLUDE_DIRS + ${PANDA_ECMASCRIPT_PLUGIN_SOURCE}/tests/runtime/common + SANITIZERS + ${PANDA_SANITIZERS_LIST} +) +if(TARGET arkruntime4ecmascript_builtins_errors_tests) + target_include_directories(arkruntime4ecmascript_builtins_errors_tests + PUBLIC ${CMAKE_CURRENT_SOURCE_DIR} + ) + target_compile_options(arkruntime4ecmascript_builtins_errors_tests PUBLIC "-Wno-ignored-attributes") +endif() +#6 +panda_add_gtest( + NO_CORES +NAME arkruntime4ecmascript_builtins_date_tests + SOURCES + ${ECMASCRIPT_BUILTINS_DATE_SOURCES} + LIBRARIES + arkruntime arkassembler + INCLUDE_DIRS + ${PANDA_ECMASCRIPT_PLUGIN_SOURCE}/tests/runtime/common + SANITIZERS + ${PANDA_SANITIZERS_LIST} +) +if(TARGET arkruntime4ecmascript_builtins_date_tests) + target_include_directories(arkruntime4ecmascript_builtins_date_tests + PUBLIC ${CMAKE_CURRENT_SOURCE_DIR} + ) + target_compile_options(arkruntime4ecmascript_builtins_date_tests PUBLIC "-Wno-ignored-attributes") +endif() +#7 +panda_add_gtest( + NO_CORES +NAME arkruntime4ecmascript_es_basic_tests + SOURCES + ${ECMASCRIPT_ES_BASIC_TESTS_SOURCES} + LIBRARIES + arkruntime arkassembler + INCLUDE_DIRS + ${PANDA_ECMASCRIPT_PLUGIN_SOURCE}/tests/runtime/common + SANITIZERS + ${PANDA_SANITIZERS_LIST} +) +if(TARGET arkruntime4ecmascript_es_basic_tests) + target_include_directories(arkruntime4ecmascript_es_basic_tests + PUBLIC ${CMAKE_CURRENT_SOURCE_DIR} + ) + target_compile_options(arkruntime4ecmascript_es_basic_tests PUBLIC "-Wno-ignored-attributes") +endif() +#7.1 +panda_add_gtest( + NO_CORES +NAME arkruntime4ecmascript_es_basic_tests-1 + SOURCES + ${ECMASCRIPT_ES_BASIC_TESTS_SOURCES-1} + LIBRARIES + arkruntime arkassembler + INCLUDE_DIRS + ${PANDA_ECMASCRIPT_PLUGIN_SOURCE}/tests/runtime/common + SANITIZERS + ${PANDA_SANITIZERS_LIST} +) +if(TARGET arkruntime4ecmascript_es_basic_tests-1) + target_include_directories(arkruntime4ecmascript_es_basic_tests-1 + PUBLIC ${CMAKE_CURRENT_SOURCE_DIR} + ) + target_compile_options(arkruntime4ecmascript_es_basic_tests-1 PUBLIC "-Wno-ignored-attributes") +endif() +#7.2 +panda_add_gtest( + NO_CORES +NAME arkruntime4ecmascript_es_basic_tests-2 + SOURCES + ${ECMASCRIPT_ES_BASIC_TESTS_SOURCES-2} + LIBRARIES + arkruntime arkassembler + INCLUDE_DIRS + ${PANDA_ECMASCRIPT_PLUGIN_SOURCE}/tests/runtime/common + SANITIZERS + ${PANDA_SANITIZERS_LIST} +) +if(TARGET arkruntime4ecmascript_es_basic_tests-2) + target_include_directories(arkruntime4ecmascript_es_basic_tests-2 + PUBLIC ${CMAKE_CURRENT_SOURCE_DIR} + ) + target_compile_options(arkruntime4ecmascript_es_basic_tests-2 PUBLIC "-Wno-ignored-attributes") +endif() +#8 +panda_add_gtest( + NO_CORES +NAME arkruntime4ecmascript_es_basic_object_tests + SOURCES + ${ECMASCRIPT_ES_BASIC_OBJECT_TESTS_SOURCES} + LIBRARIES + arkruntime arkassembler + INCLUDE_DIRS + ${PANDA_ECMASCRIPT_PLUGIN_SOURCE}/tests/runtime/common + SANITIZERS + ${PANDA_SANITIZERS_LIST} +) +if(TARGET arkruntime4ecmascript_es_basic_object_tests) + target_include_directories(arkruntime4ecmascript_es_basic_object_tests + PUBLIC ${CMAKE_CURRENT_SOURCE_DIR} + ) + target_compile_options(arkruntime4ecmascript_es_basic_object_tests PUBLIC "-Wno-ignored-attributes") +endif() +#9 +panda_add_gtest( + NO_CORES +NAME arkruntime4ecmascript_builtins_math_tests + SOURCES +${ECMASCRIPT_BUILTINS_MATH_TESTS_SOURCES} + LIBRARIES + arkruntime arkassembler + INCLUDE_DIRS + ${PANDA_ECMASCRIPT_PLUGIN_SOURCE}/tests/runtime/common + SANITIZERS + ${PANDA_SANITIZERS_LIST} +) +if(TARGET arkruntime4ecmascript_builtins_math_tests) + target_include_directories(arkruntime4ecmascript_builtins_math_tests + PUBLIC ${CMAKE_CURRENT_SOURCE_DIR} + ) + target_compile_options(arkruntime4ecmascript_builtins_math_tests PUBLIC "-Wno-ignored-attributes") +endif() +#10 +panda_add_gtest( + NO_CORES +NAME arkruntime4ecmascript_builtins_json_tests + SOURCES +${ECMASCRIPT_BUILTINS_JSON_TESTS_SOURCES} + LIBRARIES + arkruntime arkassembler + INCLUDE_DIRS + ${PANDA_ECMASCRIPT_PLUGIN_SOURCE}/tests/runtime/common + SANITIZERS + ${PANDA_SANITIZERS_LIST} +) +if(TARGET arkruntime4ecmascript_builtins_json_tests) + target_include_directories(arkruntime4ecmascript_builtins_json_tests + PUBLIC ${CMAKE_CURRENT_SOURCE_DIR} + ) + target_compile_options(arkruntime4ecmascript_builtins_json_tests PUBLIC "-Wno-ignored-attributes") +endif() +#11 +panda_add_gtest( + NO_CORES + NAME arkruntime4ecmascript_builtins_string_tests + SOURCES + ${ECMASCRIPT_BUILTINS_STRING_TESTS_SOURCES} + LIBRARIES + arkruntime arkassembler + INCLUDE_DIRS + ${PANDA_ECMASCRIPT_PLUGIN_SOURCE}/tests/runtime/common + SANITIZERS + ${PANDA_SANITIZERS_LIST} +) +if(TARGET arkruntime4ecmascript_builtins_string_tests) + target_include_directories(arkruntime4ecmascript_builtins_string_tests + PUBLIC ${CMAKE_CURRENT_SOURCE_DIR} + ) + target_compile_options(arkruntime4ecmascript_builtins_string_tests PUBLIC "-Wno-ignored-attributes") +endif() +#12 +panda_add_gtest( + NO_CORES + NAME arkruntime4ecmascript_builtins_array_tests + SOURCES + ${ECMASCRIPT_BUILTINS_ARRAY_TESTS_SOURCES} + LIBRARIES + arkruntime arkassembler + INCLUDE_DIRS + ${PANDA_ECMASCRIPT_PLUGIN_SOURCE}/tests/runtime/common + SANITIZERS + ${PANDA_SANITIZERS_LIST} +) +if(TARGET arkruntime4ecmascript_builtins_array_tests) + target_include_directories(arkruntime4ecmascript_builtins_array_tests + PUBLIC ${CMAKE_CURRENT_SOURCE_DIR} + ) + target_compile_options(arkruntime4ecmascript_builtins_array_tests PUBLIC "-Wno-ignored-attributes") +endif() +#13 +panda_add_gtest( + NO_CORES + NAME arkruntime4ecmascript_builtins_reflect_tests + SOURCES + ${ECMASCRIPT_BUILTINS_REFLECT_TESTS_SOURCES} + LIBRARIES + arkruntime arkassembler + INCLUDE_DIRS + ${PANDA_ECMASCRIPT_PLUGIN_SOURCE}/tests/runtime/common + SANITIZERS + ${PANDA_SANITIZERS_LIST} +) +if(TARGET arkruntime4ecmascript_builtins_reflect_tests) + target_include_directories(arkruntime4ecmascript_builtins_reflect_tests + PUBLIC ${CMAKE_CURRENT_SOURCE_DIR} + ) + target_compile_options(arkruntime4ecmascript_builtins_reflect_tests PUBLIC "-Wno-ignored-attributes") +endif() +#14 +panda_add_gtest( + NO_CORES + NAME arkruntime4ecmascript_builtins_arraybuffer_tests + SOURCES + ${ECMASCRIPT_BUILTINS_ARRAYBUFFER_TESTS_SOURCES} + LIBRARIES + arkruntime arkassembler + INCLUDE_DIRS + ${PANDA_ECMASCRIPT_PLUGIN_SOURCE}/tests/runtime/common + SANITIZERS + ${PANDA_SANITIZERS_LIST} +) +if(TARGET arkruntime4ecmascript_builtins_arraybuffer_tests) + target_include_directories(arkruntime4ecmascript_builtins_arraybuffer_tests + PUBLIC ${CMAKE_CURRENT_SOURCE_DIR} + ) + target_compile_options(arkruntime4ecmascript_builtins_arraybuffer_tests PUBLIC "-Wno-ignored-attributes") +endif() +#15 +panda_add_gtest( + NO_CORES + NAME arkruntime4ecmascript_regexp_tests + SOURCES + ${ECMASCRIPT_REGEXP_TESTS_SOURCES} + LIBRARIES + arkruntime arkassembler + INCLUDE_DIRS + ${PANDA_ECMASCRIPT_PLUGIN_SOURCE}/tests/runtime/common + SANITIZERS + ${PANDA_SANITIZERS_LIST} +) +if(TARGET arkruntime4ecmascript_regexp_tests) + target_include_directories(arkruntime4ecmascript_regexp_tests + PUBLIC ${CMAKE_CURRENT_SOURCE_DIR} + ) + target_compile_options(arkruntime4ecmascript_regexp_tests PUBLIC "-Wno-ignored-attributes") +endif() +#16 +panda_add_gtest( + NO_CORES + NAME arkruntime4ecmascript_builtins_dataview_tests + SOURCES + ${ECMASCRIPT_BUILTINS_DATAVIEW_TESTS_SOURCES} + LIBRARIES + arkruntime arkassembler + INCLUDE_DIRS + ${PANDA_ECMASCRIPT_PLUGIN_SOURCE}/tests/runtime/common + SANITIZERS + ${PANDA_SANITIZERS_LIST} +) +if(TARGET arkruntime4ecmascript_builtins_dataview_tests) + target_include_directories(arkruntime4ecmascript_builtins_dataview_tests + PUBLIC ${CMAKE_CURRENT_SOURCE_DIR} + ) + target_compile_options(arkruntime4ecmascript_builtins_dataview_tests PUBLIC "-Wno-ignored-attributes") +endif() +#17 +panda_add_gtest( + NO_CORES + NAME arkruntime4ecmascript_builtins_typedarray_tests + SOURCES + ${ECMASCRIPT_BUILTINS_TYPEDARRAY_TESTS_SOURCES} + LIBRARIES + arkruntime arkassembler + INCLUDE_DIRS + ${PANDA_ECMASCRIPT_PLUGIN_SOURCE}/tests/runtime/common + SANITIZERS + ${PANDA_SANITIZERS_LIST} +) +if(TARGET arkruntime4ecmascript_builtins_typedarray_tests) + target_include_directories(arkruntime4ecmascript_builtins_typedarray_tests + PUBLIC ${CMAKE_CURRENT_SOURCE_DIR} + ) + target_compile_options(arkruntime4ecmascript_builtins_typedarray_tests PUBLIC "-Wno-ignored-attributes") +endif() +#18 +panda_add_gtest( + NO_CORES + NAME arkruntime4ecmascript_hprof_tests + SOURCES + ${ECMASCRIPT_HPROF_TESTS_SOURCES} + LIBRARIES + arkruntime arkassembler + INCLUDE_DIRS + ${PANDA_ECMASCRIPT_PLUGIN_SOURCE}/tests/runtime/common + SANITIZERS + ${PANDA_SANITIZERS_LIST} +) +if(TARGET arkruntime4ecmascript_hprof_tests) + target_include_directories(arkruntime4ecmascript_hprof_tests + PUBLIC ${CMAKE_CURRENT_SOURCE_DIR} + ) + target_compile_options(arkruntime4ecmascript_hprof_tests PUBLIC "-Wno-ignored-attributes") +endif() +#19 +panda_add_gtest( + NO_CORES +NAME arkruntime4ecmascript_separate_jsvm + SOURCES + common/separate_jsvm_test.cpp + LIBRARIES + arkruntime arkassembler + INCLUDE_DIRS + ${PANDA_ECMASCRIPT_PLUGIN_SOURCE}/tests/runtime/common + SANITIZERS + ${PANDA_SANITIZERS_LIST} +) +#20 +panda_add_gtest( + NO_CORES + NAME arkruntime4ecmascript_mem_tests + SOURCES + mem/object_helpers_test.cpp + mem/g1gc_barrier_test.cpp + LIBRARIES + arkruntime arkassembler + SANITIZERS + ${PANDA_SANITIZERS_LIST} +) +#21 +panda_add_gtest( + NO_CORES +NAME arkruntime4ecmascript_builtins_promise_tests + SOURCES +${ECMASCRIPT_BUILTINS_PROMISE_TESTS_SOURCES} + LIBRARIES + arkruntime arkassembler + INCLUDE_DIRS + ${PANDA_ECMASCRIPT_PLUGIN_SOURCE}/tests/runtime/common + SANITIZERS + ${PANDA_SANITIZERS_LIST} +) +if(TARGET arkruntime4ecmascript_builtins_promise_tests) + target_include_directories(arkruntime4ecmascript_builtins_promise_tests + PUBLIC ${CMAKE_CURRENT_SOURCE_DIR} + ) + target_compile_options(arkruntime4ecmascript_builtins_promise_tests PUBLIC "-Wno-ignored-attributes") +endif() +#22 +panda_add_gtest( + NO_CORES +NAME arkruntime4ecmascript_tests2 + SOURCES + ${ECMASCRIPT_TEST_SOURCES-2} + LIBRARIES + arkruntime arkassembler + INCLUDE_DIRS + ${PANDA_ECMASCRIPT_PLUGIN_SOURCE}/tests/runtime/common + SANITIZERS + ${PANDA_SANITIZERS_LIST} +) +if(TARGET arkruntime4ecmascript_tests2) + target_include_directories(arkruntime4ecmascript_tests2 + PUBLIC ${CMAKE_CURRENT_SOURCE_DIR} + ) + target_compile_options(arkruntime4ecmascript_tests2 PUBLIC "-Wno-ignored-attributes") +endif() diff --git a/tests/runtime/builtins/builtins_array_test.cpp b/tests/runtime/builtins/builtins_array_test.cpp new file mode 100644 index 000000000..47e1e536d --- /dev/null +++ b/tests/runtime/builtins/builtins_array_test.cpp @@ -0,0 +1,1724 @@ +/* + * Copyright (c) 2021-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 "plugins/ecmascript/runtime/builtins/builtins_array.h" + +#include "plugins/ecmascript/runtime/ecma_string.h" +#include "plugins/ecmascript/runtime/ecma_vm.h" +#include "plugins/ecmascript/runtime/global_env.h" +#include "plugins/ecmascript/runtime/js_array.h" +#include "plugins/ecmascript/runtime/js_array_iterator.h" + +#include "plugins/ecmascript/runtime/js_handle.h" +#include "plugins/ecmascript/runtime/js_hclass.h" +#include "plugins/ecmascript/runtime/js_object-inl.h" +#include "plugins/ecmascript/runtime/js_tagged_value-inl.h" +#include "plugins/ecmascript/runtime/js_tagged_value.h" +#include "plugins/ecmascript/runtime/js_thread.h" + +#include "plugins/ecmascript/runtime/object_factory.h" +#include "plugins/ecmascript/runtime/object_operator.h" +#include "plugins/ecmascript/tests/runtime/common/test_helper.h" +#include "utils/bit_utils.h" + +using namespace panda::ecmascript; +using namespace panda::ecmascript::builtins; +using namespace panda::ecmascript::base; + +namespace panda::test { +using Array = ecmascript::builtins::BuiltinsArray; +class BuiltinsArrayTest : public testing::Test { +public: + static void SetUpTestCase() + { + GTEST_LOG_(INFO) << "SetUpTestCase"; + } + + static void TearDownTestCase() + { + GTEST_LOG_(INFO) << "TearDownCase"; + } + + void SetUp() override + { + TestHelper::CreateEcmaVMWithScope(instance, thread, scope); + } + + void TearDown() override + { + TestHelper::DestroyEcmaVMWithScope(instance, scope); + } + + PandaVM *instance {nullptr}; + EcmaHandleScope *scope {nullptr}; + JSThread *thread {nullptr}; + + class TestClass : public base::BuiltinsBase { + public: + static JSTaggedValue TestForEachFunc(EcmaRuntimeCallInfo *argv) + { + JSHandle key = GetCallArg(argv, 0); + if (key->IsUndefined()) { + return JSTaggedValue::Undefined(); + } + JSArray *jsArray = JSArray::Cast(GetThis(argv)->GetTaggedObject()); + int length = jsArray->GetArrayLength() + 1; + jsArray->SetArrayLength(argv->GetThread(), length); + return JSTaggedValue::Undefined(); + } + + static JSTaggedValue TestEveryFunc(EcmaRuntimeCallInfo *argv) + { + uint32_t argc = argv->GetArgsNumber(); + if (argc > 0) { + if (GetCallArg(argv, 0)->GetInt() > 10) { // 10 : test case + return GetTaggedBoolean(true); + } + } + return GetTaggedBoolean(false); + } + + static JSTaggedValue TestMapFunc(EcmaRuntimeCallInfo *argv) + { + int accumulator = GetCallArg(argv, 0)->GetInt(); + accumulator = accumulator * 2; // 2 : mapped to 2 times the original value + return BuiltinsBase::GetTaggedInt(accumulator); + } + + static JSTaggedValue TestFindFunc(EcmaRuntimeCallInfo *argv) + { + uint32_t argc = argv->GetArgsNumber(); + if (argc > 0) { + // 10 : test case + if (GetCallArg(argv, 0)->GetInt() > 10) { + return GetTaggedBoolean(true); + } + } + return GetTaggedBoolean(false); + } + + static JSTaggedValue TestFindIndexFunc(EcmaRuntimeCallInfo *argv) + { + uint32_t argc = argv->GetArgsNumber(); + if (argc > 0) { + // 10 : test case + if (GetCallArg(argv, 0)->GetInt() > 10) { + return GetTaggedBoolean(true); + } + } + return GetTaggedBoolean(false); + } + + static JSTaggedValue TestReduceFunc(EcmaRuntimeCallInfo *argv) + { + int accumulator = GetCallArg(argv, 0)->GetInt(); + accumulator = accumulator + GetCallArg(argv, 1)->GetInt(); + return BuiltinsBase::GetTaggedInt(accumulator); + } + + static JSTaggedValue TestReduceRightFunc(EcmaRuntimeCallInfo *argv) + { + int accumulator = GetCallArg(argv, 0)->GetInt(); + accumulator = accumulator + GetCallArg(argv, 1)->GetInt(); + return BuiltinsBase::GetTaggedInt(accumulator); + } + + static JSTaggedValue TestSomeFunc(EcmaRuntimeCallInfo *argv) + { + uint32_t argc = argv->GetArgsNumber(); + if (argc > 0) { + if (GetCallArg(argv, 0)->GetInt() > 10) { // 10 : test case + return GetTaggedBoolean(true); + } + } + return GetTaggedBoolean(false); + } + + // x => [x*2] + static JSTaggedValue TestFlatMapFunc(EcmaRuntimeCallInfo *argv) //« element, sourceIndex, source » + { + auto thread = argv->GetThread(); + + // element = [x] + JSTaggedValue element = GetCallArg(argv, 0).GetTaggedValue(); + JSHandle key0(thread, JSTaggedValue(0)); + + // val = x + JSTaggedValue val = JSArray::GetProperty(thread, JSHandle(thread, element), key0) + .GetValue() + .GetTaggedValue(); + int accumulator = val.GetNumber(); + accumulator *= 2; + + return BuiltinsBase::GetTaggedInt(accumulator); + } + }; +}; + +JSTaggedValue CreateBuiltinsJSObject(JSThread *thread, const CString keyCStr) +{ + EcmaVM *ecmaVM = thread->GetEcmaVM(); + JSHandle globalEnv = ecmaVM->GetGlobalEnv(); + + JSHandle dynclass = globalEnv->GetObjectFunction(); + ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); + + JSHandle obj(factory->NewJSObjectByConstructor(JSHandle(dynclass), dynclass)); + JSHandle key(factory->NewFromCanBeCompressString(&keyCStr[0])); + JSHandle value(thread, JSTaggedValue(1)); + JSObject::SetProperty(thread, obj, key, value); + return obj.GetTaggedValue(); +} + +TEST_F(BuiltinsArrayTest, ArrayConstructor) +{ + auto ecmaVM = thread->GetEcmaVM(); + JSHandle env = ecmaVM->GetGlobalEnv(); + JSHandle array(env->GetArrayFunction()); + JSHandle globalObject(thread, env->GetGlobalObject()); + + auto ecmaRuntimeCallInfo1 = TestHelper::CreateEcmaRuntimeCallInfo(thread, JSTaggedValue::Undefined(), 10); + ecmaRuntimeCallInfo1->SetFunction(array.GetTaggedValue()); + ecmaRuntimeCallInfo1->SetThis(globalObject.GetTaggedValue()); + ecmaRuntimeCallInfo1->SetCallArg(0, JSTaggedValue(static_cast(1))); + ecmaRuntimeCallInfo1->SetCallArg(1, JSTaggedValue(static_cast(3))); + ecmaRuntimeCallInfo1->SetCallArg(2, JSTaggedValue(static_cast(5))); + + [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread, ecmaRuntimeCallInfo1.get()); + JSTaggedValue result = Array::ArrayConstructor(ecmaRuntimeCallInfo1.get()); + TestHelper::TearDownFrame(thread, prev); + JSTaggedValue value(static_cast(result.GetRawData())); + ASSERT_TRUE(value.IsECMAObject()); + PropertyDescriptor descRes(thread); + JSHandle valueHandle(thread, value); + JSHandle key0(thread, JSTaggedValue(0)); + JSHandle key1(thread, JSTaggedValue(1)); + JSHandle key2(thread, JSTaggedValue(2)); + JSObject::GetOwnProperty(thread, valueHandle, key0, descRes); + ASSERT_EQ(descRes.GetValue().GetTaggedValue(), JSTaggedValue(1)); + JSObject::GetOwnProperty(thread, valueHandle, key1, descRes); + ASSERT_EQ(descRes.GetValue().GetTaggedValue(), JSTaggedValue(3)); + JSObject::GetOwnProperty(thread, valueHandle, key2, descRes); + ASSERT_EQ(descRes.GetValue().GetTaggedValue(), JSTaggedValue(5)); +} + +// 22.1.2.1 Array.from ( items [ , mapfn [ , thisArg ] ] ) +TEST_F(BuiltinsArrayTest, From) +{ + JSHandle lengthKeyHandle = thread->GlobalConstants()->GetHandledLengthString(); + JSArray *arr = JSArray::Cast(JSArray::ArrayCreate(thread, JSTaggedNumber(0))->GetTaggedObject()); + EXPECT_TRUE(arr != nullptr); + JSHandle obj(thread, arr); + EXPECT_EQ(JSArray::GetProperty(thread, JSHandle(obj), lengthKeyHandle).GetValue()->GetInt(), 0); + + JSHandle key0(thread, JSTaggedValue(0)); + PropertyDescriptor desc0(thread, JSHandle(thread, JSTaggedValue(1)), true, true, true); + JSArray::DefineOwnProperty(thread, obj, key0, desc0); + JSHandle key1(thread, JSTaggedValue(1)); + PropertyDescriptor desc1(thread, JSHandle(thread, JSTaggedValue(2)), true, true, true); + JSArray::DefineOwnProperty(thread, obj, key1, desc1); + JSHandle key2(thread, JSTaggedValue(2)); + PropertyDescriptor desc2(thread, JSHandle(thread, JSTaggedValue(3)), true, true, true); + JSArray::DefineOwnProperty(thread, obj, key2, desc2); + JSHandle key3(thread, JSTaggedValue(3)); + PropertyDescriptor desc3(thread, JSHandle(thread, JSTaggedValue(4)), true, true, true); + JSArray::DefineOwnProperty(thread, obj, key3, desc3); + JSHandle key4(thread, JSTaggedValue(4)); + PropertyDescriptor desc4(thread, JSHandle(thread, JSTaggedValue(5)), true, true, true); + JSArray::DefineOwnProperty(thread, obj, key4, desc4); + + auto ecmaRuntimeCallInfo1 = TestHelper::CreateEcmaRuntimeCallInfo(thread, JSTaggedValue::Undefined(), 6); + ecmaRuntimeCallInfo1->SetFunction(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo1->SetThis(obj.GetTaggedValue()); + ecmaRuntimeCallInfo1->SetCallArg(0, obj.GetTaggedValue()); + + [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread, ecmaRuntimeCallInfo1.get()); + JSTaggedValue result = Array::From(ecmaRuntimeCallInfo1.get()); + TestHelper::TearDownFrame(thread, prev); + JSTaggedValue value(static_cast(result.GetRawData())); + ASSERT_TRUE(value.IsECMAObject()); + PropertyDescriptor descRes(thread); + JSHandle valueHandle(thread, value); + JSObject::GetOwnProperty(thread, valueHandle, key0, descRes); + ASSERT_EQ(descRes.GetValue().GetTaggedValue(), JSTaggedValue(1)); + JSObject::GetOwnProperty(thread, valueHandle, key1, descRes); + ASSERT_EQ(descRes.GetValue().GetTaggedValue(), JSTaggedValue(2)); + JSObject::GetOwnProperty(thread, valueHandle, key2, descRes); + ASSERT_EQ(descRes.GetValue().GetTaggedValue(), JSTaggedValue(3)); + JSObject::GetOwnProperty(thread, valueHandle, key3, descRes); + ASSERT_EQ(descRes.GetValue().GetTaggedValue(), JSTaggedValue(4)); + JSObject::GetOwnProperty(thread, valueHandle, key4, descRes); + ASSERT_EQ(descRes.GetValue().GetTaggedValue(), JSTaggedValue(5)); +} + +// 22.1.2.2 Array.isArray(arg) +TEST_F(BuiltinsArrayTest, IsArray) +{ + JSHandle lengthKeyHandle = thread->GlobalConstants()->GetHandledLengthString(); + JSArray *arr = JSArray::Cast(JSArray::ArrayCreate(thread, JSTaggedNumber(0)).GetTaggedValue().GetTaggedObject()); + EXPECT_TRUE(arr != nullptr); + JSHandle obj(thread, arr); + EXPECT_EQ(JSArray::GetProperty(thread, JSHandle(obj), lengthKeyHandle).GetValue()->GetInt(), 0); + + auto ecmaRuntimeCallInfo1 = TestHelper::CreateEcmaRuntimeCallInfo(thread, JSTaggedValue::Undefined(), 6); + ecmaRuntimeCallInfo1->SetFunction(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo1->SetThis(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo1->SetCallArg(0, obj.GetTaggedValue()); + + [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread, ecmaRuntimeCallInfo1.get()); + JSTaggedValue result = Array::IsArray(ecmaRuntimeCallInfo1.get()); + TestHelper::TearDownFrame(thread, prev); + ASSERT_EQ(result.GetRawData(), JSTaggedValue::True().GetRawData()); + + auto ecmaRuntimeCallInfo2 = TestHelper::CreateEcmaRuntimeCallInfo(thread, JSTaggedValue::Undefined(), 6); + ecmaRuntimeCallInfo2->SetFunction(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo2->SetThis(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo2->SetCallArg(0, JSTaggedValue(static_cast(1))); + + prev = TestHelper::SetupFrame(thread, ecmaRuntimeCallInfo2.get()); + result = Array::IsArray(ecmaRuntimeCallInfo2.get()); + TestHelper::TearDownFrame(thread, prev); + ASSERT_EQ(result.GetRawData(), JSTaggedValue::False().GetRawData()); +} + +TEST_F(BuiltinsArrayTest, Of) +{ + auto ecmaVM = thread->GetEcmaVM(); + JSHandle env = ecmaVM->GetGlobalEnv(); + JSHandle array(env->GetArrayFunction()); + JSHandle globalObject(thread, env->GetGlobalObject()); + + auto ecmaRuntimeCallInfo1 = TestHelper::CreateEcmaRuntimeCallInfo(thread, JSTaggedValue::Undefined(), 10); + ecmaRuntimeCallInfo1->SetFunction(array.GetTaggedValue()); + ecmaRuntimeCallInfo1->SetThis(globalObject.GetTaggedValue()); + ecmaRuntimeCallInfo1->SetCallArg(0, JSTaggedValue(static_cast(1))); + ecmaRuntimeCallInfo1->SetCallArg(1, JSTaggedValue(static_cast(3))); + ecmaRuntimeCallInfo1->SetCallArg(2, JSTaggedValue(static_cast(5))); + + [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread, ecmaRuntimeCallInfo1.get()); + JSTaggedValue result = Array::Of(ecmaRuntimeCallInfo1.get()); + TestHelper::TearDownFrame(thread, prev); + JSTaggedValue value(static_cast(result.GetRawData())); + ASSERT_TRUE(value.IsECMAObject()); + PropertyDescriptor descRes(thread); + JSHandle valueHandle(thread, value); + JSHandle key0(thread, JSTaggedValue(0)); + JSHandle key1(thread, JSTaggedValue(1)); + JSHandle key2(thread, JSTaggedValue(2)); + + JSObject::GetOwnProperty(thread, valueHandle, key0, descRes); + ASSERT_EQ(descRes.GetValue().GetTaggedValue(), JSTaggedValue(1)); + JSObject::GetOwnProperty(thread, valueHandle, key1, descRes); + ASSERT_EQ(descRes.GetValue().GetTaggedValue(), JSTaggedValue(3)); + JSObject::GetOwnProperty(thread, valueHandle, key2, descRes); + ASSERT_EQ(descRes.GetValue().GetTaggedValue(), JSTaggedValue(5)); +} + +TEST_F(BuiltinsArrayTest, Species) +{ + auto ecmaVM = thread->GetEcmaVM(); + JSHandle env = ecmaVM->GetGlobalEnv(); + JSHandle array(env->GetArrayFunction()); + JSHandle globalObject(thread, env->GetGlobalObject()); + + auto ecmaRuntimeCallInfo1 = TestHelper::CreateEcmaRuntimeCallInfo(thread, JSTaggedValue::Undefined(), 4); + ecmaRuntimeCallInfo1->SetFunction(array.GetTaggedValue()); + ecmaRuntimeCallInfo1->SetThis(globalObject.GetTaggedValue()); + + [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread, ecmaRuntimeCallInfo1.get()); + JSTaggedValue result = Array::Species(ecmaRuntimeCallInfo1.get()); + TestHelper::TearDownFrame(thread, prev); + ASSERT_TRUE(result.IsECMAObject()); +} + +TEST_F(BuiltinsArrayTest, Concat) +{ + JSHandle lengthKeyHandle = thread->GlobalConstants()->GetHandledLengthString(); + JSArray *arr = JSArray::Cast(JSArray::ArrayCreate(thread, JSTaggedNumber(0)).GetTaggedValue().GetTaggedObject()); + EXPECT_TRUE(arr != nullptr); + JSHandle obj(thread, arr); + JSHandle key0(thread, JSTaggedValue(0)); + PropertyDescriptor desc0(thread, JSHandle(thread, JSTaggedValue(1)), true, true, true); + JSArray::DefineOwnProperty(thread, obj, key0, desc0); + JSHandle key1(thread, JSTaggedValue(1)); + PropertyDescriptor desc1(thread, JSHandle(thread, JSTaggedValue(2)), true, true, true); + JSArray::DefineOwnProperty(thread, obj, key1, desc1); + JSHandle key2(thread, JSTaggedValue(2)); + PropertyDescriptor desc2(thread, JSHandle(thread, JSTaggedValue(3)), true, true, true); + JSArray::DefineOwnProperty(thread, obj, key2, desc2); + + JSArray *arr1 = JSArray::ArrayCreate(thread, JSTaggedNumber(0)).GetObject(); + EXPECT_TRUE(arr1 != nullptr); + JSHandle obj1(thread, arr1); + JSHandle key4(thread, JSTaggedValue(0)); + PropertyDescriptor desc4(thread, JSHandle(thread, JSTaggedValue(4)), true, true, true); + JSArray::DefineOwnProperty(thread, obj1, key4, desc4); + JSHandle key5(thread, JSTaggedValue(1)); + PropertyDescriptor desc5(thread, JSHandle(thread, JSTaggedValue(5)), true, true, true); + JSArray::DefineOwnProperty(thread, obj1, key5, desc5); + JSHandle key6(thread, JSTaggedValue(2)); + PropertyDescriptor desc6(thread, JSHandle(thread, JSTaggedValue(6)), true, true, true); + JSArray::DefineOwnProperty(thread, obj1, key6, desc6); + + auto ecmaRuntimeCallInfo1 = TestHelper::CreateEcmaRuntimeCallInfo(thread, JSTaggedValue::Undefined(), 6); + ecmaRuntimeCallInfo1->SetFunction(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo1->SetThis(obj.GetTaggedValue()); + ecmaRuntimeCallInfo1->SetCallArg(0, obj1.GetTaggedValue()); + + [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread, ecmaRuntimeCallInfo1.get()); + JSTaggedValue result = Array::Concat(ecmaRuntimeCallInfo1.get()); + TestHelper::TearDownFrame(thread, prev); + JSTaggedValue value(static_cast(result.GetRawData())); + ASSERT_TRUE(value.IsECMAObject()); + + PropertyDescriptor descRes(thread); + JSHandle valueHandle(thread, value); + JSHandle key7(thread, JSTaggedValue(5)); + EXPECT_EQ( + JSArray::GetProperty(thread, JSHandle(valueHandle), lengthKeyHandle).GetValue()->GetInt(), 6); + JSObject::GetOwnProperty(thread, valueHandle, key7, descRes); + ASSERT_EQ(descRes.GetValue().GetTaggedValue(), JSTaggedValue(6)); +} + +// 22.1.3.3 new Array(1,2,3,4,5).CopyWithin(0,3,5) +TEST_F(BuiltinsArrayTest, CopyWithin) +{ + JSHandle lengthKeyHandle = thread->GlobalConstants()->GetHandledLengthString(); + JSArray *arr = JSArray::Cast(JSArray::ArrayCreate(thread, JSTaggedNumber(0)).GetTaggedValue().GetTaggedObject()); + EXPECT_TRUE(arr != nullptr); + JSHandle obj(thread, arr); + EXPECT_EQ(JSArray::GetProperty(thread, JSHandle(obj), lengthKeyHandle).GetValue()->GetInt(), 0); + + JSHandle key0(thread, JSTaggedValue(0)); + PropertyDescriptor desc0(thread, JSHandle(thread, JSTaggedValue(1)), true, true, true); + JSArray::DefineOwnProperty(thread, obj, key0, desc0); + JSHandle key1(thread, JSTaggedValue(1)); + PropertyDescriptor desc1(thread, JSHandle(thread, JSTaggedValue(2)), true, true, true); + JSArray::DefineOwnProperty(thread, obj, key1, desc1); + JSHandle key2(thread, JSTaggedValue(2)); + PropertyDescriptor desc2(thread, JSHandle(thread, JSTaggedValue(3)), true, true, true); + JSArray::DefineOwnProperty(thread, obj, key2, desc2); + JSHandle key3(thread, JSTaggedValue(3)); + PropertyDescriptor desc3(thread, JSHandle(thread, JSTaggedValue(4)), true, true, true); + JSArray::DefineOwnProperty(thread, obj, key3, desc3); + JSHandle key4(thread, JSTaggedValue(4)); + PropertyDescriptor desc4(thread, JSHandle(thread, JSTaggedValue(5)), true, true, true); + JSArray::DefineOwnProperty(thread, obj, key4, desc4); + + auto ecmaRuntimeCallInfo1 = TestHelper::CreateEcmaRuntimeCallInfo(thread, JSTaggedValue::Undefined(), 10); + ecmaRuntimeCallInfo1->SetFunction(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo1->SetThis(obj.GetTaggedValue()); + ecmaRuntimeCallInfo1->SetCallArg(0, JSTaggedValue(static_cast(0))); + ecmaRuntimeCallInfo1->SetCallArg(1, JSTaggedValue(static_cast(3))); + ecmaRuntimeCallInfo1->SetCallArg(2, JSTaggedValue(static_cast(5))); + + [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread, ecmaRuntimeCallInfo1.get()); + JSTaggedValue result = Array::CopyWithin(ecmaRuntimeCallInfo1.get()); + TestHelper::TearDownFrame(thread, prev); + JSTaggedValue value(static_cast(result.GetRawData())); + ASSERT_TRUE(value.IsECMAObject()); + PropertyDescriptor descRes(thread); + JSHandle valueHandle(thread, value); + JSObject::GetOwnProperty(thread, valueHandle, key0, descRes); + ASSERT_EQ(descRes.GetValue().GetTaggedValue(), JSTaggedValue(4)); + JSObject::GetOwnProperty(thread, valueHandle, key1, descRes); + ASSERT_EQ(descRes.GetValue().GetTaggedValue(), JSTaggedValue(5)); + JSObject::GetOwnProperty(thread, valueHandle, key2, descRes); + ASSERT_EQ(descRes.GetValue().GetTaggedValue(), JSTaggedValue(3)); + JSObject::GetOwnProperty(thread, valueHandle, key3, descRes); + ASSERT_EQ(descRes.GetValue().GetTaggedValue(), JSTaggedValue(4)); + JSObject::GetOwnProperty(thread, valueHandle, key4, descRes); + ASSERT_EQ(descRes.GetValue().GetTaggedValue(), JSTaggedValue(5)); +} + +TEST_F(BuiltinsArrayTest, Every) +{ + ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); + auto ecmaVM = thread->GetEcmaVM(); + JSHandle env = ecmaVM->GetGlobalEnv(); + + JSHandle lengthKeyHandle = thread->GlobalConstants()->GetHandledLengthString(); + JSArray *arr = JSArray::Cast(JSArray::ArrayCreate(thread, JSTaggedNumber(0)).GetTaggedValue().GetTaggedObject()); + EXPECT_TRUE(arr != nullptr); + JSHandle obj(thread, arr); + EXPECT_EQ(JSArray::GetProperty(thread, JSHandle(obj), lengthKeyHandle).GetValue()->GetInt(), 0); + + JSHandle key0(thread, JSTaggedValue(0)); + PropertyDescriptor desc0(thread, JSHandle(thread, JSTaggedValue(100)), true, true, true); + JSArray::DefineOwnProperty(thread, obj, key0, desc0); + + JSHandle key1(thread, JSTaggedValue(1)); + PropertyDescriptor desc1(thread, JSHandle(thread, JSTaggedValue(200)), true, true, true); + JSArray::DefineOwnProperty(thread, obj, key1, desc1); + JSHandle key2(thread, JSTaggedValue(2)); + PropertyDescriptor desc2(thread, JSHandle(thread, JSTaggedValue(300)), true, true, true); + JSArray::DefineOwnProperty(thread, obj, key2, desc2); + + JSHandle jsArray = factory->NewJSArray(); + JSHandle func = factory->NewJSFunction(env, reinterpret_cast(TestClass::TestEveryFunc)); + + auto ecmaRuntimeCallInfo1 = TestHelper::CreateEcmaRuntimeCallInfo(thread, JSTaggedValue::Undefined(), 8); + ecmaRuntimeCallInfo1->SetFunction(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo1->SetThis(obj.GetTaggedValue()); + ecmaRuntimeCallInfo1->SetCallArg(0, func.GetTaggedValue()); + ecmaRuntimeCallInfo1->SetCallArg(1, jsArray.GetTaggedValue()); + + [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread, ecmaRuntimeCallInfo1.get()); + JSTaggedValue result2 = Array::Every(ecmaRuntimeCallInfo1.get()); + TestHelper::TearDownFrame(thread, prev); + + ASSERT_EQ(result2.GetRawData(), JSTaggedValue::True().GetRawData()); +} + +TEST_F(BuiltinsArrayTest, Map) +{ + auto ecmaVM = thread->GetEcmaVM(); + JSHandle env = ecmaVM->GetGlobalEnv(); + ObjectFactory *factory = ecmaVM->GetFactory(); + + JSHandle lengthKeyHandle = thread->GlobalConstants()->GetHandledLengthString(); + JSArray *arr = JSArray::Cast(JSArray::ArrayCreate(thread, JSTaggedNumber(0)).GetTaggedValue().GetTaggedObject()); + EXPECT_TRUE(arr != nullptr); + JSHandle obj(thread, arr); + EXPECT_EQ(JSArray::GetProperty(thread, JSHandle(obj), lengthKeyHandle).GetValue()->GetInt(), 0); + JSHandle key0(thread, JSTaggedValue(0)); + PropertyDescriptor desc0(thread, JSHandle(thread, JSTaggedValue(50)), true, true, true); + JSArray::DefineOwnProperty(thread, obj, key0, desc0); + JSHandle key1(thread, JSTaggedValue(1)); + PropertyDescriptor desc1(thread, JSHandle(thread, JSTaggedValue(200)), true, true, true); + JSArray::DefineOwnProperty(thread, obj, key1, desc1); + JSHandle key2(thread, JSTaggedValue(2)); + PropertyDescriptor desc2(thread, JSHandle(thread, JSTaggedValue(3)), true, true, true); + JSArray::DefineOwnProperty(thread, obj, key2, desc2); + JSHandle jsArray(JSArray::ArrayCreate(thread, JSTaggedNumber(0))); + JSHandle func = factory->NewJSFunction(env, reinterpret_cast(TestClass::TestMapFunc)); + + auto ecmaRuntimeCallInfo1 = TestHelper::CreateEcmaRuntimeCallInfo(thread, JSTaggedValue::Undefined(), 8); + ecmaRuntimeCallInfo1->SetFunction(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo1->SetThis(obj.GetTaggedValue()); + ecmaRuntimeCallInfo1->SetCallArg(0, func.GetTaggedValue()); + ecmaRuntimeCallInfo1->SetCallArg(1, jsArray.GetTaggedValue()); + + [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread, ecmaRuntimeCallInfo1.get()); + JSTaggedValue result = Array::Map(ecmaRuntimeCallInfo1.get()); + TestHelper::TearDownFrame(thread, prev); + JSTaggedValue value(static_cast(result.GetRawData())); + ASSERT_TRUE(value.IsECMAObject()); + + PropertyDescriptor descRes(thread); + JSHandle valueHandle(thread, value); + EXPECT_EQ( + JSArray::GetProperty(thread, JSHandle(valueHandle), lengthKeyHandle).GetValue()->GetInt(), 3); + JSObject::GetOwnProperty(thread, valueHandle, key0, descRes); + + ASSERT_EQ(descRes.GetValue()->GetInt(), 100); + JSObject::GetOwnProperty(thread, valueHandle, key1, descRes); + ASSERT_EQ(descRes.GetValue()->GetInt(), 400); + JSObject::GetOwnProperty(thread, valueHandle, key2, descRes); + ASSERT_EQ(descRes.GetValue()->GetInt(), 6); +} + +TEST_F(BuiltinsArrayTest, Reverse) +{ + JSHandle lengthKeyHandle = thread->GlobalConstants()->GetHandledLengthString(); + JSArray *arr = JSArray::Cast(JSArray::ArrayCreate(thread, JSTaggedNumber(0)).GetTaggedValue().GetTaggedObject()); + EXPECT_TRUE(arr != nullptr); + JSHandle obj(thread, arr); + EXPECT_EQ(JSArray::GetProperty(thread, JSHandle(obj), lengthKeyHandle).GetValue()->GetInt(), 0); + JSHandle key0(thread, JSTaggedValue(0)); + PropertyDescriptor desc0(thread, JSHandle(thread, JSTaggedValue(50)), true, true, true); + JSArray::DefineOwnProperty(thread, obj, key0, desc0); + JSHandle key1(thread, JSTaggedValue(1)); + PropertyDescriptor desc1(thread, JSHandle(thread, JSTaggedValue(200)), true, true, true); + JSArray::DefineOwnProperty(thread, obj, key1, desc1); + JSHandle key2(thread, JSTaggedValue(2)); + PropertyDescriptor desc2(thread, JSHandle(thread, JSTaggedValue(3)), true, true, true); + JSArray::DefineOwnProperty(thread, obj, key2, desc2); + + auto ecmaRuntimeCallInfo1 = TestHelper::CreateEcmaRuntimeCallInfo(thread, JSTaggedValue::Undefined(), 4); + ecmaRuntimeCallInfo1->SetFunction(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo1->SetThis(obj.GetTaggedValue()); + + [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread, ecmaRuntimeCallInfo1.get()); + JSTaggedValue result = Array::Reverse(ecmaRuntimeCallInfo1.get()); + TestHelper::TearDownFrame(thread, prev); + JSTaggedValue value(static_cast(result.GetRawData())); + ASSERT_TRUE(value.IsECMAObject()); + + PropertyDescriptor descRes(thread); + JSHandle valueHandle(thread, value); + EXPECT_EQ( + JSArray::GetProperty(thread, JSHandle(valueHandle), lengthKeyHandle).GetValue()->GetInt(), 3); + JSObject::GetOwnProperty(thread, valueHandle, key0, descRes); + ASSERT_EQ(descRes.GetValue().GetTaggedValue(), JSTaggedValue(3)); + JSObject::GetOwnProperty(thread, valueHandle, key1, descRes); + ASSERT_EQ(descRes.GetValue().GetTaggedValue(), JSTaggedValue(200)); + JSObject::GetOwnProperty(thread, valueHandle, key2, descRes); + ASSERT_EQ(descRes.GetValue().GetTaggedValue(), JSTaggedValue(50)); + EXPECT_EQ(JSArray::GetProperty(thread, JSHandle(obj), lengthKeyHandle).GetValue()->GetInt(), 3); + JSObject::GetOwnProperty(thread, obj, key0, descRes); + ASSERT_EQ(descRes.GetValue().GetTaggedValue(), JSTaggedValue(3)); + JSObject::GetOwnProperty(thread, obj, key1, descRes); + ASSERT_EQ(descRes.GetValue().GetTaggedValue(), JSTaggedValue(200)); + JSObject::GetOwnProperty(thread, obj, key2, descRes); + ASSERT_EQ(descRes.GetValue().GetTaggedValue(), JSTaggedValue(50)); +} + +TEST_F(BuiltinsArrayTest, Slice) +{ + JSHandle lengthKeyHandle = thread->GlobalConstants()->GetHandledLengthString(); + JSArray *arr = JSArray::Cast(JSArray::ArrayCreate(thread, JSTaggedNumber(0)).GetTaggedValue().GetTaggedObject()); + EXPECT_TRUE(arr != nullptr); + JSHandle obj(thread, arr); + EXPECT_EQ(JSArray::GetProperty(thread, JSHandle(obj), lengthKeyHandle).GetValue()->GetInt(), 0); + JSHandle key0(thread, JSTaggedValue(0)); + PropertyDescriptor desc0(thread, JSHandle(thread, JSTaggedValue(1)), true, true, true); + JSArray::DefineOwnProperty(thread, obj, key0, desc0); + JSHandle key1(thread, JSTaggedValue(1)); + PropertyDescriptor desc1(thread, JSHandle(thread, JSTaggedValue(2)), true, true, true); + JSArray::DefineOwnProperty(thread, obj, key1, desc1); + JSHandle key2(thread, JSTaggedValue(2)); + PropertyDescriptor desc2(thread, JSHandle(thread, JSTaggedValue(3)), true, true, true); + JSArray::DefineOwnProperty(thread, obj, key2, desc2); + JSHandle key3(thread, JSTaggedValue(3)); + PropertyDescriptor desc3(thread, JSHandle(thread, JSTaggedValue(4)), true, true, true); + JSArray::DefineOwnProperty(thread, obj, key3, desc3); + JSHandle key4(thread, JSTaggedValue(4)); + PropertyDescriptor desc4(thread, JSHandle(thread, JSTaggedValue(3)), true, true, true); + JSArray::DefineOwnProperty(thread, obj, key4, desc4); + + auto ecmaRuntimeCallInfo1 = TestHelper::CreateEcmaRuntimeCallInfo(thread, JSTaggedValue::Undefined(), 8); + ecmaRuntimeCallInfo1->SetFunction(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo1->SetThis(obj.GetTaggedValue()); + ecmaRuntimeCallInfo1->SetCallArg(0, JSTaggedValue(static_cast(1))); + ecmaRuntimeCallInfo1->SetCallArg(1, JSTaggedValue(static_cast(4))); + + [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread, ecmaRuntimeCallInfo1.get()); + JSTaggedValue result = Array::Slice(ecmaRuntimeCallInfo1.get()); + TestHelper::TearDownFrame(thread, prev); + JSTaggedValue value(static_cast(result.GetRawData())); + ASSERT_TRUE(value.IsECMAObject()); + + PropertyDescriptor descRes(thread); + JSHandle valueHandle(thread, value); + EXPECT_EQ( + JSArray::GetProperty(thread, JSHandle(valueHandle), lengthKeyHandle).GetValue()->GetInt(), 3); + JSObject::GetOwnProperty(thread, valueHandle, key0, descRes); + ASSERT_EQ(descRes.GetValue().GetTaggedValue(), JSTaggedValue(2)); + JSObject::GetOwnProperty(thread, valueHandle, key1, descRes); + ASSERT_EQ(descRes.GetValue().GetTaggedValue(), JSTaggedValue(3)); + JSObject::GetOwnProperty(thread, valueHandle, key2, descRes); + ASSERT_EQ(descRes.GetValue().GetTaggedValue(), JSTaggedValue(4)); +} + +TEST_F(BuiltinsArrayTest, Splice) +{ + JSHandle lengthKeyHandle = thread->GlobalConstants()->GetHandledLengthString(); + JSArray *arr = JSArray::Cast(JSArray::ArrayCreate(thread, JSTaggedNumber(0)).GetTaggedValue().GetTaggedObject()); + EXPECT_TRUE(arr != nullptr); + JSHandle obj(thread, arr); + EXPECT_EQ(JSArray::GetProperty(thread, JSHandle(obj), lengthKeyHandle).GetValue()->GetInt(), 0); + JSHandle key0(thread, JSTaggedValue(0)); + PropertyDescriptor desc0(thread, JSHandle(thread, JSTaggedValue(1)), true, true, true); + JSArray::DefineOwnProperty(thread, obj, key0, desc0); + JSHandle key1(thread, JSTaggedValue(1)); + PropertyDescriptor desc1(thread, JSHandle(thread, JSTaggedValue(2)), true, true, true); + JSArray::DefineOwnProperty(thread, obj, key1, desc1); + JSHandle key2(thread, JSTaggedValue(2)); + PropertyDescriptor desc2(thread, JSHandle(thread, JSTaggedValue(3)), true, true, true); + JSArray::DefineOwnProperty(thread, obj, key2, desc2); + JSHandle key3(thread, JSTaggedValue(3)); + PropertyDescriptor desc3(thread, JSHandle(thread, JSTaggedValue(4)), true, true, true); + JSArray::DefineOwnProperty(thread, obj, key3, desc3); + JSHandle key4(thread, JSTaggedValue(4)); + PropertyDescriptor desc4(thread, JSHandle(thread, JSTaggedValue(5)), true, true, true); + JSArray::DefineOwnProperty(thread, obj, key4, desc4); + + auto ecmaRuntimeCallInfo1 = TestHelper::CreateEcmaRuntimeCallInfo(thread, JSTaggedValue::Undefined(), 10); + ecmaRuntimeCallInfo1->SetFunction(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo1->SetThis(obj.GetTaggedValue()); + ecmaRuntimeCallInfo1->SetCallArg(0, JSTaggedValue(static_cast(1))); + ecmaRuntimeCallInfo1->SetCallArg(1, JSTaggedValue(static_cast(2))); + ecmaRuntimeCallInfo1->SetCallArg(2, JSTaggedValue(static_cast(100))); + + [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread, ecmaRuntimeCallInfo1.get()); + JSTaggedValue result = Array::Splice(ecmaRuntimeCallInfo1.get()); + TestHelper::TearDownFrame(thread, prev); + JSTaggedValue value(static_cast(result.GetRawData())); + ASSERT_TRUE(value.IsECMAObject()); + EXPECT_EQ(JSArray::GetProperty(thread, JSHandle(obj), lengthKeyHandle).GetValue()->GetInt(), 4); + + PropertyDescriptor descRes(thread); + JSHandle valueHandle(thread, value); + EXPECT_EQ( + JSArray::GetProperty(thread, JSHandle(valueHandle), lengthKeyHandle).GetValue()->GetInt(), 2); + JSObject::GetOwnProperty(thread, valueHandle, key0, descRes); + ASSERT_EQ(descRes.GetValue().GetTaggedValue(), JSTaggedValue(2)); +} + +// ES2019 22.1.3.10 Builtin Array.flat() +TEST_F(BuiltinsArrayTest, Flat) +{ + ASSERT_NE(thread, nullptr); + JSHandle length_key_handle = thread->GlobalConstants()->GetHandledLengthString(); + + JSArray *arr1 = JSArray::ArrayCreate(thread, JSTaggedNumber(0)).GetObject(); + JSArray *arr2 = JSArray::ArrayCreate(thread, JSTaggedNumber(0)).GetObject(); + JSArray *arr3 = JSArray::ArrayCreate(thread, JSTaggedNumber(0)).GetObject(); + + EXPECT_TRUE(arr1 != nullptr); + EXPECT_TRUE(arr2 != nullptr); + EXPECT_TRUE(arr3 != nullptr); + + JSHandle obj1(thread, arr1); + JSHandle obj2(thread, arr2); + JSHandle obj3(thread, arr3); + + EXPECT_EQ(JSArray::GetProperty(thread, JSHandle(obj1), length_key_handle).GetValue()->GetInt(), 0); + EXPECT_EQ(JSArray::GetProperty(thread, JSHandle(obj2), length_key_handle).GetValue()->GetInt(), 0); + EXPECT_EQ(JSArray::GetProperty(thread, JSHandle(obj3), length_key_handle).GetValue()->GetInt(), 0); + + JSHandle key0(thread, JSTaggedValue(0)); + JSHandle key1(thread, JSTaggedValue(1)); + JSHandle key2(thread, JSTaggedValue(2)); + + // arr1 = [0, 1, arr2] + PropertyDescriptor desc0(thread, JSHandle(thread, JSTaggedValue(0)), true, true, true); + PropertyDescriptor desc1(thread, JSHandle(thread, JSTaggedValue(1)), true, true, true); + PropertyDescriptor desc_nested1(thread, JSHandle(thread, obj2.GetTaggedValue()), true, true, true); + JSArray::DefineOwnProperty(thread, obj1, key0, desc0); + JSArray::DefineOwnProperty(thread, obj1, key1, desc1); + JSArray::DefineOwnProperty(thread, obj1, key2, desc_nested1); + + // arr2 = [2, 3, arr3] + PropertyDescriptor desc2(thread, JSHandle(thread, JSTaggedValue(2)), true, true, true); + PropertyDescriptor desc3(thread, JSHandle(thread, JSTaggedValue(3)), true, true, true); + PropertyDescriptor desc_nested2(thread, JSHandle(thread, obj3.GetTaggedValue()), true, true, true); + JSArray::DefineOwnProperty(thread, obj2, key0, desc2); + JSArray::DefineOwnProperty(thread, obj2, key1, desc3); + JSArray::DefineOwnProperty(thread, obj2, key2, desc_nested2); + + // arr3 = [4, 5] + PropertyDescriptor desc4(thread, JSHandle(thread, JSTaggedValue(4)), true, true, true); + PropertyDescriptor desc5(thread, JSHandle(thread, JSTaggedValue(5)), true, true, true); + JSArray::DefineOwnProperty(thread, obj3, key0, desc4); + JSArray::DefineOwnProperty(thread, obj3, key1, desc5); + + // [0, 1, [2, 3, [4, 5]]].flat(2) = [0, 1, 2, 3, 4, 5] + auto ecmaRuntimeCallInfo1 = TestHelper::CreateEcmaRuntimeCallInfo(thread, JSTaggedValue::Undefined(), 6); + ecmaRuntimeCallInfo1->SetFunction(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo1->SetThis(obj1.GetTaggedValue()); + ecmaRuntimeCallInfo1->SetCallArg(0, JSTaggedValue(static_cast(2))); + + [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread, ecmaRuntimeCallInfo1.get()); + JSTaggedValue result = Array::Flat(ecmaRuntimeCallInfo1.get()); + TestHelper::TearDownFrame(thread, prev); + + PropertyDescriptor desc_res(thread); + + JSTaggedValue value(static_cast(result.GetRawData())); + JSHandle value_handle(thread, value); + + EXPECT_EQ( + JSArray::GetProperty(thread, JSHandle(value_handle), length_key_handle).GetValue()->GetInt(), 6); + + JSHandle key3(thread, JSTaggedValue(3)); + JSHandle key4(thread, JSTaggedValue(4)); + JSHandle key5(thread, JSTaggedValue(5)); + + JSObject::GetOwnProperty(thread, value_handle, key0, desc_res); + ASSERT_EQ(desc_res.GetValue().GetTaggedValue(), JSTaggedValue(0)); + JSObject::GetOwnProperty(thread, value_handle, key1, desc_res); + ASSERT_EQ(desc_res.GetValue().GetTaggedValue(), JSTaggedValue(1)); + JSObject::GetOwnProperty(thread, value_handle, key2, desc_res); + ASSERT_EQ(desc_res.GetValue().GetTaggedValue(), JSTaggedValue(2)); + JSObject::GetOwnProperty(thread, value_handle, key3, desc_res); + ASSERT_EQ(desc_res.GetValue().GetTaggedValue(), JSTaggedValue(3)); + JSObject::GetOwnProperty(thread, value_handle, key4, desc_res); + ASSERT_EQ(desc_res.GetValue().GetTaggedValue(), JSTaggedValue(4)); + JSObject::GetOwnProperty(thread, value_handle, key5, desc_res); + ASSERT_EQ(desc_res.GetValue().GetTaggedValue(), JSTaggedValue(5)); + + // [0, 1, [2, 3, [4, 5]]].flat() = [0, 1, 2, 3, [4, 5]] + auto ecmaRuntimeCallInfo2 = TestHelper::CreateEcmaRuntimeCallInfo(thread, JSTaggedValue::Undefined(), 4); + ecmaRuntimeCallInfo2->SetFunction(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo2->SetThis(obj1.GetTaggedValue()); + + prev = TestHelper::SetupFrame(thread, ecmaRuntimeCallInfo2.get()); + result = Array::Flat(ecmaRuntimeCallInfo2.get()); + TestHelper::TearDownFrame(thread, prev); + + JSTaggedValue value2(static_cast(result.GetRawData())); + JSHandle value_handle2(thread, value2); + + EXPECT_EQ( + JSArray::GetProperty(thread, JSHandle(value_handle2), length_key_handle).GetValue()->GetInt(), + 5); + + JSObject::GetOwnProperty(thread, value_handle2, key0, desc_res); + ASSERT_EQ(desc_res.GetValue().GetTaggedValue(), JSTaggedValue(0)); + JSObject::GetOwnProperty(thread, value_handle2, key1, desc_res); + ASSERT_EQ(desc_res.GetValue().GetTaggedValue(), JSTaggedValue(1)); + JSObject::GetOwnProperty(thread, value_handle2, key2, desc_res); + ASSERT_EQ(desc_res.GetValue().GetTaggedValue(), JSTaggedValue(2)); + JSObject::GetOwnProperty(thread, value_handle2, key3, desc_res); + ASSERT_EQ(desc_res.GetValue().GetTaggedValue(), JSTaggedValue(3)); + + JSObject::GetOwnProperty(thread, value_handle2, key4, desc_res); + ASSERT_TRUE(desc_res.GetValue().GetTaggedValue().IsECMAObject()); + + JSTaggedValue inner_arr_value(desc_res.GetValue().GetTaggedValue()); + JSHandle inner_arr_handle(thread, inner_arr_value); + + EXPECT_EQ( + JSArray::GetProperty(thread, JSHandle(inner_arr_handle), length_key_handle).GetValue()->GetInt(), + 2); + + JSObject::GetOwnProperty(thread, inner_arr_handle, key0, desc_res); + ASSERT_EQ(desc_res.GetValue().GetTaggedValue(), JSTaggedValue(4)); + JSObject::GetOwnProperty(thread, inner_arr_handle, key1, desc_res); + ASSERT_EQ(desc_res.GetValue().GetTaggedValue(), JSTaggedValue(5)); +} + +// ES2019 22.1.3.11 Builtin Array.flatMap() +TEST_F(BuiltinsArrayTest, FlatMap) +{ + // [[1], [2], [4]].flatMap(x => [x*2]) = [2, 4, 8] + ASSERT_NE(thread, nullptr); + auto ecma_vm = thread->GetEcmaVM(); + JSHandle env = ecma_vm->GetGlobalEnv(); + ObjectFactory *factory_ = ecma_vm->GetFactory(); + + JSHandle length_key_handle = thread->GlobalConstants()->GetHandledLengthString(); + JSArray *arr = JSArray::ArrayCreate(thread, JSTaggedNumber(0)).GetObject(); + JSArray *arr1 = JSArray::ArrayCreate(thread, JSTaggedNumber(0)).GetObject(); + JSArray *arr2 = JSArray::ArrayCreate(thread, JSTaggedNumber(0)).GetObject(); + JSArray *arr3 = JSArray::ArrayCreate(thread, JSTaggedNumber(0)).GetObject(); + + EXPECT_TRUE(arr != nullptr); + EXPECT_TRUE(arr1 != nullptr); + EXPECT_TRUE(arr2 != nullptr); + EXPECT_TRUE(arr3 != nullptr); + + JSHandle obj(thread, arr); + JSHandle obj1(thread, arr1); + JSHandle obj2(thread, arr2); + JSHandle obj3(thread, arr3); + + EXPECT_EQ(JSArray::GetProperty(thread, JSHandle(obj), length_key_handle).GetValue()->GetInt(), 0); + EXPECT_EQ(JSArray::GetProperty(thread, JSHandle(obj1), length_key_handle).GetValue()->GetInt(), 0); + EXPECT_EQ(JSArray::GetProperty(thread, JSHandle(obj2), length_key_handle).GetValue()->GetInt(), 0); + EXPECT_EQ(JSArray::GetProperty(thread, JSHandle(obj3), length_key_handle).GetValue()->GetInt(), 0); + + // create [1], [2], [4] elements + JSHandle key0(thread, JSTaggedNumber(0)); + JSHandle key1(thread, JSTaggedNumber(1)); + JSHandle key2(thread, JSTaggedNumber(2)); + PropertyDescriptor desc1(thread, JSHandle(thread, JSTaggedValue(1)), true, true, true); + PropertyDescriptor desc2(thread, JSHandle(thread, JSTaggedValue(2)), true, true, true); + PropertyDescriptor desc4(thread, JSHandle(thread, JSTaggedValue(4)), true, true, true); + JSObject::DefineOwnProperty(thread, obj1, key0, desc1); + JSObject::DefineOwnProperty(thread, obj2, key0, desc2); + JSObject::DefineOwnProperty(thread, obj3, key0, desc4); + + // arr = [[1], [2], [4]] + PropertyDescriptor obj_desc1(thread, JSHandle(thread, obj1.GetTaggedValue()), true, true, true); + PropertyDescriptor obj_desc2(thread, JSHandle(thread, obj2.GetTaggedValue()), true, true, true); + PropertyDescriptor obj_desc4(thread, JSHandle(thread, obj3.GetTaggedValue()), true, true, true); + JSObject::DefineOwnProperty(thread, obj, key0, obj_desc1); + JSObject::DefineOwnProperty(thread, obj, key1, obj_desc2); + JSObject::DefineOwnProperty(thread, obj, key2, obj_desc4); + + EXPECT_EQ(JSArray::GetProperty(thread, JSHandle(obj1), length_key_handle).GetValue()->GetInt(), 1); + EXPECT_EQ(JSArray::GetProperty(thread, JSHandle(obj2), length_key_handle).GetValue()->GetInt(), 1); + EXPECT_EQ(JSArray::GetProperty(thread, JSHandle(obj3), length_key_handle).GetValue()->GetInt(), 1); + EXPECT_EQ(JSArray::GetProperty(thread, JSHandle(obj), length_key_handle).GetValue()->GetInt(), 3); + + JSHandle cb = factory_->NewJSFunction(env, reinterpret_cast(TestClass::TestFlatMapFunc)); + auto ecmaRuntimeCallInfo1 = TestHelper::CreateEcmaRuntimeCallInfo(thread, JSTaggedValue::Undefined(), 6); + ecmaRuntimeCallInfo1->SetFunction(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo1->SetThis(obj.GetTaggedValue()); + ecmaRuntimeCallInfo1->SetCallArg(0, cb.GetTaggedValue()); + + [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread, ecmaRuntimeCallInfo1.get()); + JSTaggedValue result = Array::FlatMap(ecmaRuntimeCallInfo1.get()); + TestHelper::TearDownFrame(thread, prev); + + PropertyDescriptor res_desc(thread); + + JSTaggedValue value(static_cast(result.GetRawData())); + JSHandle value_handle(thread, value); + + EXPECT_EQ( + JSArray::GetProperty(thread, JSHandle(value_handle), length_key_handle).GetValue()->GetInt(), 3); + + JSObject::GetOwnProperty(thread, value_handle, key0, res_desc); + ASSERT_EQ(res_desc.GetValue().GetTaggedValue(), JSTaggedValue(2)); + JSObject::GetOwnProperty(thread, value_handle, key1, res_desc); + ASSERT_EQ(res_desc.GetValue().GetTaggedValue(), JSTaggedValue(4)); + JSObject::GetOwnProperty(thread, value_handle, key2, res_desc); + ASSERT_EQ(res_desc.GetValue().GetTaggedValue(), JSTaggedValue(8)); +} + +// 22.1.3.6 new Array(1,2,3,4,5).Fill(0,1,3) +TEST_F(BuiltinsArrayTest, Fill) +{ + JSHandle lengthKeyHandle = thread->GlobalConstants()->GetHandledLengthString(); + JSArray *arr = JSArray::Cast(JSArray::ArrayCreate(thread, JSTaggedNumber(0)).GetTaggedValue().GetTaggedObject()); + EXPECT_TRUE(arr != nullptr); + JSHandle obj(thread, arr); + EXPECT_EQ(JSArray::GetProperty(thread, JSHandle(obj), lengthKeyHandle).GetValue()->GetInt(), 0); + + JSHandle key0(thread, JSTaggedValue(0)); + PropertyDescriptor desc0(thread, JSHandle(thread, JSTaggedValue(1)), true, true, true); + JSArray::DefineOwnProperty(thread, obj, key0, desc0); + JSHandle key1(thread, JSTaggedValue(1)); + PropertyDescriptor desc1(thread, JSHandle(thread, JSTaggedValue(2)), true, true, true); + JSArray::DefineOwnProperty(thread, obj, key1, desc1); + JSHandle key2(thread, JSTaggedValue(2)); + PropertyDescriptor desc2(thread, JSHandle(thread, JSTaggedValue(3)), true, true, true); + JSArray::DefineOwnProperty(thread, obj, key2, desc2); + JSHandle key3(thread, JSTaggedValue(3)); + PropertyDescriptor desc3(thread, JSHandle(thread, JSTaggedValue(4)), true, true, true); + JSArray::DefineOwnProperty(thread, obj, key3, desc3); + JSHandle key4(thread, JSTaggedValue(4)); + PropertyDescriptor desc4(thread, JSHandle(thread, JSTaggedValue(5)), true, true, true); + JSArray::DefineOwnProperty(thread, obj, key4, desc4); + + auto ecmaRuntimeCallInfo1 = TestHelper::CreateEcmaRuntimeCallInfo(thread, JSTaggedValue::Undefined(), 10); + ecmaRuntimeCallInfo1->SetFunction(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo1->SetThis(obj.GetTaggedValue()); + ecmaRuntimeCallInfo1->SetCallArg(0, JSTaggedValue(static_cast(0))); + ecmaRuntimeCallInfo1->SetCallArg(1, JSTaggedValue(static_cast(1))); + ecmaRuntimeCallInfo1->SetCallArg(2, JSTaggedValue(static_cast(3))); + + [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread, ecmaRuntimeCallInfo1.get()); + JSTaggedValue result = Array::Fill(ecmaRuntimeCallInfo1.get()); + TestHelper::TearDownFrame(thread, prev); + JSTaggedValue value(static_cast(result.GetRawData())); + ASSERT_TRUE(value.IsECMAObject()); + PropertyDescriptor descRes(thread); + JSHandle valueHandle(thread, value); + JSObject::GetOwnProperty(thread, valueHandle, key0, descRes); + ASSERT_EQ(descRes.GetValue().GetTaggedValue(), JSTaggedValue(1)); + JSObject::GetOwnProperty(thread, valueHandle, key1, descRes); + ASSERT_EQ(descRes.GetValue().GetTaggedValue(), JSTaggedValue(0)); + JSObject::GetOwnProperty(thread, valueHandle, key2, descRes); + ASSERT_EQ(descRes.GetValue().GetTaggedValue(), JSTaggedValue(0)); + JSObject::GetOwnProperty(thread, valueHandle, key3, descRes); + ASSERT_EQ(descRes.GetValue().GetTaggedValue(), JSTaggedValue(4)); + JSObject::GetOwnProperty(thread, valueHandle, key4, descRes); + ASSERT_EQ(descRes.GetValue().GetTaggedValue(), JSTaggedValue(5)); +} + +TEST_F(BuiltinsArrayTest, Find) +{ + auto ecmaVM = thread->GetEcmaVM(); + JSHandle env = ecmaVM->GetGlobalEnv(); + ObjectFactory *factory = ecmaVM->GetFactory(); + + JSHandle lengthKeyHandle = thread->GlobalConstants()->GetHandledLengthString(); + JSArray *arr = JSArray::Cast(JSArray::ArrayCreate(thread, JSTaggedNumber(0)).GetTaggedValue().GetTaggedObject()); + EXPECT_TRUE(arr != nullptr); + JSHandle obj(thread, arr); + EXPECT_EQ(JSArray::GetProperty(thread, JSHandle(obj), lengthKeyHandle).GetValue()->GetInt(), 0); + JSHandle key0(thread, JSTaggedValue(0)); + PropertyDescriptor desc0(thread, JSHandle(thread, JSTaggedValue(1)), true, true, true); + JSArray::DefineOwnProperty(thread, obj, key0, desc0); + JSHandle key1(thread, JSTaggedValue(1)); + PropertyDescriptor desc1(thread, JSHandle(thread, JSTaggedValue(102)), true, true, true); + JSArray::DefineOwnProperty(thread, obj, key1, desc1); + JSHandle key2(thread, JSTaggedValue(2)); + PropertyDescriptor desc2(thread, JSHandle(thread, JSTaggedValue(3)), true, true, true); + JSArray::DefineOwnProperty(thread, obj, key2, desc2); + JSHandle jsArray(JSArray::ArrayCreate(thread, JSTaggedNumber(0))); + JSHandle func = factory->NewJSFunction(env, reinterpret_cast(TestClass::TestFindFunc)); + + auto ecmaRuntimeCallInfo1 = TestHelper::CreateEcmaRuntimeCallInfo(thread, JSTaggedValue::Undefined(), 8); + ecmaRuntimeCallInfo1->SetFunction(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo1->SetThis(obj.GetTaggedValue()); + ecmaRuntimeCallInfo1->SetCallArg(0, func.GetTaggedValue()); + ecmaRuntimeCallInfo1->SetCallArg(1, jsArray.GetTaggedValue()); + + [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread, ecmaRuntimeCallInfo1.get()); + JSTaggedValue result2 = Array::Find(ecmaRuntimeCallInfo1.get()); + TestHelper::TearDownFrame(thread, prev); + + EXPECT_EQ(result2.GetRawData(), JSTaggedValue(102).GetRawData()); +} + +TEST_F(BuiltinsArrayTest, FindIndex) +{ + auto ecmaVM = thread->GetEcmaVM(); + JSHandle env = ecmaVM->GetGlobalEnv(); + ObjectFactory *factory = ecmaVM->GetFactory(); + + JSHandle lengthKeyHandle = thread->GlobalConstants()->GetHandledLengthString(); + JSArray *arr = JSArray::Cast(JSArray::ArrayCreate(thread, JSTaggedNumber(0)).GetTaggedValue().GetTaggedObject()); + EXPECT_TRUE(arr != nullptr); + JSHandle obj(thread, arr); + EXPECT_EQ(JSArray::GetProperty(thread, JSHandle(obj), lengthKeyHandle).GetValue()->GetInt(), 0); + + JSHandle key0(thread, JSTaggedValue(0)); + PropertyDescriptor desc0(thread, JSHandle(thread, JSTaggedValue(1)), true, true, true); + JSArray::DefineOwnProperty(thread, obj, key0, desc0); + + JSHandle key1(thread, JSTaggedValue(1)); + PropertyDescriptor desc1(thread, JSHandle(thread, JSTaggedValue(2)), true, true, true); + JSArray::DefineOwnProperty(thread, obj, key1, desc1); + JSHandle key2(thread, JSTaggedValue(2)); + PropertyDescriptor desc2(thread, JSHandle(thread, JSTaggedValue(30)), true, true, true); + JSArray::DefineOwnProperty(thread, obj, key2, desc2); + + JSHandle jsArray(JSArray::ArrayCreate(thread, JSTaggedNumber(0))); + JSHandle func = factory->NewJSFunction(env, reinterpret_cast(TestClass::TestFindIndexFunc)); + + auto ecmaRuntimeCallInfo1 = TestHelper::CreateEcmaRuntimeCallInfo(thread, JSTaggedValue::Undefined(), 8); + ecmaRuntimeCallInfo1->SetFunction(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo1->SetThis(obj.GetTaggedValue()); + ecmaRuntimeCallInfo1->SetCallArg(0, func.GetTaggedValue()); + ecmaRuntimeCallInfo1->SetCallArg(1, jsArray.GetTaggedValue()); + + [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread, ecmaRuntimeCallInfo1.get()); + JSTaggedValue result2 = Array::FindIndex(ecmaRuntimeCallInfo1.get()); + TestHelper::TearDownFrame(thread, prev); + + EXPECT_EQ(result2.GetRawData(), JSTaggedValue(static_cast(2)).GetRawData()); +} + +TEST_F(BuiltinsArrayTest, ForEach) +{ + auto ecmaVM = thread->GetEcmaVM(); + JSHandle env = ecmaVM->GetGlobalEnv(); + ObjectFactory *factory = ecmaVM->GetFactory(); + + JSHandle lengthKeyHandle = thread->GlobalConstants()->GetHandledLengthString(); + JSArray *arr = JSArray::Cast(JSArray::ArrayCreate(thread, JSTaggedNumber(0)).GetTaggedValue().GetTaggedObject()); + EXPECT_TRUE(arr != nullptr); + JSHandle obj(thread, arr); + EXPECT_EQ(JSArray::GetProperty(thread, JSHandle(obj), lengthKeyHandle).GetValue()->GetInt(), 0); + + JSHandle key0(thread, JSTaggedValue(0)); + PropertyDescriptor desc0(thread, JSHandle(thread, JSTaggedValue(1)), true, true, true); + JSArray::DefineOwnProperty(thread, obj, key0, desc0); + JSHandle key1(thread, JSTaggedValue(1)); + PropertyDescriptor desc1(thread, JSHandle(thread, JSTaggedValue(2)), true, true, true); + JSArray::DefineOwnProperty(thread, obj, key1, desc1); + JSHandle key2(thread, JSTaggedValue(2)); + PropertyDescriptor desc2(thread, JSHandle(thread, JSTaggedValue(3)), true, true, true); + JSArray::DefineOwnProperty(thread, obj, key2, desc2); + + JSHandle jsArray(JSArray::ArrayCreate(thread, JSTaggedNumber(0))); + JSHandle func = factory->NewJSFunction(env, reinterpret_cast(TestClass::TestForEachFunc)); + + auto ecmaRuntimeCallInfo1 = TestHelper::CreateEcmaRuntimeCallInfo(thread, JSTaggedValue::Undefined(), 8); + ecmaRuntimeCallInfo1->SetFunction(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo1->SetThis(obj.GetTaggedValue()); + ecmaRuntimeCallInfo1->SetCallArg(0, func.GetTaggedValue()); + ecmaRuntimeCallInfo1->SetCallArg(1, jsArray.GetTaggedValue()); + + [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread, ecmaRuntimeCallInfo1.get()); + JSTaggedValue result2 = Array::ForEach(ecmaRuntimeCallInfo1.get()); + TestHelper::TearDownFrame(thread, prev); + EXPECT_EQ(result2.GetRawData(), JSTaggedValue::VALUE_UNDEFINED); + EXPECT_EQ(jsArray->GetArrayLength(), 3); +} + +// ES2021 23.1.3.13 new Array(1,2,3,4,3).includes(searchElement [ , fromIndex ]) +TEST_F(BuiltinsArrayTest, Includes1) +{ + ASSERT_NE(thread, nullptr); + + JSHandle length_key_handle = thread->GlobalConstants()->GetHandledLengthString(); + JSArray *arr = JSArray::Cast(JSArray::ArrayCreate(thread, JSTaggedNumber(0)).GetTaggedValue().GetHeapObject()); + EXPECT_TRUE(arr != nullptr); + JSHandle obj(thread, arr); + EXPECT_EQ(JSArray::GetProperty(thread, JSHandle(obj), length_key_handle).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).includes(1,0) + auto ecmaRuntimeCallInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread, JSTaggedValue::Undefined(), 8); + ecmaRuntimeCallInfo->SetFunction(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetThis(obj.GetTaggedValue()); + ecmaRuntimeCallInfo->SetCallArg(0, JSTaggedValue(static_cast(1))); + ecmaRuntimeCallInfo->SetCallArg(1, JSTaggedValue(static_cast(0))); + + [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread, ecmaRuntimeCallInfo.get()); + JSTaggedValue result = Array::Includes(ecmaRuntimeCallInfo.get()); + TestHelper::TearDownFrame(thread, prev); + ASSERT_EQ(result.GetRawData(), JSTaggedValue(true).GetRawData()); +} + +// ES2021 23.1.3.13 new Array(1,2,3,4,3).includes(searchElement [ , fromIndex ]) +TEST_F(BuiltinsArrayTest, Includes2) +{ + ASSERT_NE(thread, nullptr); + + JSHandle length_key_handle = thread->GlobalConstants()->GetHandledLengthString(); + JSArray *arr = JSArray::Cast(JSArray::ArrayCreate(thread, JSTaggedNumber(0)).GetTaggedValue().GetHeapObject()); + EXPECT_TRUE(arr != nullptr); + JSHandle obj(thread, arr); + EXPECT_EQ(JSArray::GetProperty(thread, JSHandle(obj), length_key_handle).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).includes(1,3) + auto ecmaRuntimeCallInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread, JSTaggedValue::Undefined(), 8); + ecmaRuntimeCallInfo->SetFunction(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetThis(obj.GetTaggedValue()); + ecmaRuntimeCallInfo->SetCallArg(0, JSTaggedValue(static_cast(1))); + ecmaRuntimeCallInfo->SetCallArg(1, JSTaggedValue(static_cast(3))); + + [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread, ecmaRuntimeCallInfo.get()); + JSTaggedValue result = Array::Includes(ecmaRuntimeCallInfo.get()); + TestHelper::TearDownFrame(thread, prev); + ASSERT_EQ(result.GetRawData(), JSTaggedValue(false).GetRawData()); +} + +// ES2021 23.1.3.13 new Array(1,2,3,4,3).includes(searchElement [ , fromIndex ]) +TEST_F(BuiltinsArrayTest, Includes3) +{ + ASSERT_NE(thread, nullptr); + + JSHandle length_key_handle = thread->GlobalConstants()->GetHandledLengthString(); + JSArray *arr = JSArray::Cast(JSArray::ArrayCreate(thread, JSTaggedNumber(0)).GetTaggedValue().GetHeapObject()); + EXPECT_TRUE(arr != nullptr); + JSHandle obj(thread, arr); + EXPECT_EQ(JSArray::GetProperty(thread, JSHandle(obj), length_key_handle).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).includes(5,0) + auto ecmaRuntimeCallInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread, JSTaggedValue::Undefined(), 8); + ecmaRuntimeCallInfo->SetFunction(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetThis(obj.GetTaggedValue()); + ecmaRuntimeCallInfo->SetCallArg(0, JSTaggedValue(static_cast(5))); + ecmaRuntimeCallInfo->SetCallArg(1, JSTaggedValue(static_cast(0))); + + [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread, ecmaRuntimeCallInfo.get()); + JSTaggedValue result = Array::Includes(ecmaRuntimeCallInfo.get()); + TestHelper::TearDownFrame(thread, prev); + ASSERT_EQ(result.GetRawData(), JSTaggedValue(false).GetRawData()); +} + +// ES2021 23.1.3.13 new Array(1,2,3,4,3).includes(searchElement [ , fromIndex ]) +TEST_F(BuiltinsArrayTest, Includes4) +{ + ASSERT_NE(thread, nullptr); + + JSHandle length_key_handle = thread->GlobalConstants()->GetHandledLengthString(); + JSArray *arr = JSArray::Cast(JSArray::ArrayCreate(thread, JSTaggedNumber(0)).GetTaggedValue().GetHeapObject()); + EXPECT_TRUE(arr != nullptr); + JSHandle obj(thread, arr); + EXPECT_EQ(JSArray::GetProperty(thread, JSHandle(obj), length_key_handle).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).includes(1) + auto ecmaRuntimeCallInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread, JSTaggedValue::Undefined(), 6); + ecmaRuntimeCallInfo->SetFunction(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetThis(obj.GetTaggedValue()); + ecmaRuntimeCallInfo->SetCallArg(0, JSTaggedValue(static_cast(1))); + + [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread, ecmaRuntimeCallInfo.get()); + JSTaggedValue result = Array::Includes(ecmaRuntimeCallInfo.get()); + TestHelper::TearDownFrame(thread, prev); + ASSERT_EQ(result.GetRawData(), JSTaggedValue(true).GetRawData()); +} + +// ES2021 23.1.3.13 new Array(1,2,3,4,3).includes(searchElement [ , fromIndex ]) +TEST_F(BuiltinsArrayTest, Includes5) +{ + ASSERT_NE(thread, nullptr); + + JSHandle length_key_handle = thread->GlobalConstants()->GetHandledLengthString(); + JSArray *arr = JSArray::Cast(JSArray::ArrayCreate(thread, JSTaggedNumber(0)).GetTaggedValue().GetHeapObject()); + EXPECT_TRUE(arr != nullptr); + JSHandle obj(thread, arr); + EXPECT_EQ(JSArray::GetProperty(thread, JSHandle(obj), length_key_handle).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).includes(5) + auto ecmaRuntimeCallInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread, JSTaggedValue::Undefined(), 6); + ecmaRuntimeCallInfo->SetFunction(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetThis(obj.GetTaggedValue()); + ecmaRuntimeCallInfo->SetCallArg(0, JSTaggedValue(static_cast(5))); + + [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread, ecmaRuntimeCallInfo.get()); + JSTaggedValue result = Array::Includes(ecmaRuntimeCallInfo.get()); + TestHelper::TearDownFrame(thread, prev); + ASSERT_EQ(result.GetRawData(), JSTaggedValue(false).GetRawData()); +} + +// 22.1.3.11 new Array(1,2,3,4,3).IndexOf(searchElement [ , fromIndex ]) +TEST_F(BuiltinsArrayTest, IndexOf) +{ + JSHandle lengthKeyHandle = thread->GlobalConstants()->GetHandledLengthString(); + JSArray *arr = JSArray::Cast(JSArray::ArrayCreate(thread, JSTaggedNumber(0)).GetTaggedValue().GetTaggedObject()); + EXPECT_TRUE(arr != nullptr); + JSHandle obj(thread, arr); + EXPECT_EQ(JSArray::GetProperty(thread, JSHandle(obj), lengthKeyHandle).GetValue()->GetInt(), 0); + + JSHandle key0(thread, JSTaggedValue(0)); + PropertyDescriptor desc0(thread, JSHandle(thread, JSTaggedValue(1)), true, true, true); + JSArray::DefineOwnProperty(thread, obj, key0, desc0); + JSHandle key1(thread, JSTaggedValue(1)); + PropertyDescriptor desc1(thread, JSHandle(thread, JSTaggedValue(2)), true, true, true); + JSArray::DefineOwnProperty(thread, obj, key1, desc1); + JSHandle key2(thread, JSTaggedValue(2)); + PropertyDescriptor desc2(thread, JSHandle(thread, JSTaggedValue(3)), true, true, true); + JSArray::DefineOwnProperty(thread, obj, key2, desc2); + JSHandle key3(thread, JSTaggedValue(3)); + PropertyDescriptor desc3(thread, JSHandle(thread, JSTaggedValue(4)), true, true, true); + JSArray::DefineOwnProperty(thread, obj, key3, desc3); + JSHandle key4(thread, JSTaggedValue(4)); + PropertyDescriptor desc4(thread, JSHandle(thread, JSTaggedValue(3)), true, true, true); + JSArray::DefineOwnProperty(thread, obj, key4, desc4); + + auto ecmaRuntimeCallInfo1 = TestHelper::CreateEcmaRuntimeCallInfo(thread, JSTaggedValue::Undefined(), 8); + ecmaRuntimeCallInfo1->SetFunction(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo1->SetThis(obj.GetTaggedValue()); + ecmaRuntimeCallInfo1->SetCallArg(0, JSTaggedValue(static_cast(3))); + ecmaRuntimeCallInfo1->SetCallArg(1, JSTaggedValue(static_cast(0))); + + [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread, ecmaRuntimeCallInfo1.get()); + JSTaggedValue result = Array::IndexOf(ecmaRuntimeCallInfo1.get()); + TestHelper::TearDownFrame(thread, prev); + ASSERT_EQ(result.GetRawData(), JSTaggedValue(static_cast(2)).GetRawData()); + + auto ecmaRuntimeCallInfo2 = TestHelper::CreateEcmaRuntimeCallInfo(thread, JSTaggedValue::Undefined(), 8); + ecmaRuntimeCallInfo2->SetFunction(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo2->SetThis(obj.GetTaggedValue()); + ecmaRuntimeCallInfo2->SetCallArg(0, JSTaggedValue(static_cast(3))); + ecmaRuntimeCallInfo2->SetCallArg(1, JSTaggedValue(static_cast(3))); + + prev = TestHelper::SetupFrame(thread, ecmaRuntimeCallInfo2.get()); + result = Array::IndexOf(ecmaRuntimeCallInfo2.get()); + TestHelper::TearDownFrame(thread, prev); + ASSERT_EQ(result.GetRawData(), JSTaggedValue(static_cast(4)).GetRawData()); + + auto ecmaRuntimeCallInfo3 = TestHelper::CreateEcmaRuntimeCallInfo(thread, JSTaggedValue::Undefined(), 8); + ecmaRuntimeCallInfo3->SetFunction(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo3->SetThis(obj.GetTaggedValue()); + ecmaRuntimeCallInfo3->SetCallArg(0, JSTaggedValue(static_cast(5))); + ecmaRuntimeCallInfo3->SetCallArg(1, JSTaggedValue(static_cast(0))); + + prev = TestHelper::SetupFrame(thread, ecmaRuntimeCallInfo3.get()); + result = Array::IndexOf(ecmaRuntimeCallInfo3.get()); + TestHelper::TearDownFrame(thread, prev); + ASSERT_EQ(result.GetRawData(), JSTaggedValue(-1).GetRawData()); + + auto ecmaRuntimeCallInfo4 = TestHelper::CreateEcmaRuntimeCallInfo(thread, JSTaggedValue::Undefined(), 6); + ecmaRuntimeCallInfo4->SetFunction(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo4->SetThis(obj.GetTaggedValue()); + ecmaRuntimeCallInfo4->SetCallArg(0, JSTaggedValue(static_cast(3))); + + prev = TestHelper::SetupFrame(thread, ecmaRuntimeCallInfo4.get()); + result = Array::IndexOf(ecmaRuntimeCallInfo4.get()); + TestHelper::TearDownFrame(thread, prev); + ASSERT_EQ(result.GetRawData(), JSTaggedValue(static_cast(2)).GetRawData()); +} + +// 22.1.3.14 new Array(1,2,3,4,3).LastIndexOf(searchElement [ , fromIndex ]) +TEST_F(BuiltinsArrayTest, LastIndexOf) +{ + JSHandle lengthKeyHandle = thread->GlobalConstants()->GetHandledLengthString(); + JSArray *arr = JSArray::Cast(JSArray::ArrayCreate(thread, JSTaggedNumber(0)).GetTaggedValue().GetTaggedObject()); + EXPECT_TRUE(arr != nullptr); + JSHandle obj(thread, arr); + EXPECT_EQ(JSArray::GetProperty(thread, JSHandle(obj), lengthKeyHandle).GetValue()->GetInt(), 0); + + JSHandle key0(thread, JSTaggedValue(0)); + PropertyDescriptor desc0(thread, JSHandle(thread, JSTaggedValue(1)), true, true, true); + JSArray::DefineOwnProperty(thread, obj, key0, desc0); + JSHandle key1(thread, JSTaggedValue(1)); + PropertyDescriptor desc1(thread, JSHandle(thread, JSTaggedValue(2)), true, true, true); + JSArray::DefineOwnProperty(thread, obj, key1, desc1); + JSHandle key2(thread, JSTaggedValue(2)); + PropertyDescriptor desc2(thread, JSHandle(thread, JSTaggedValue(3)), true, true, true); + JSArray::DefineOwnProperty(thread, obj, key2, desc2); + JSHandle key3(thread, JSTaggedValue(3)); + PropertyDescriptor desc3(thread, JSHandle(thread, JSTaggedValue(4)), true, true, true); + JSArray::DefineOwnProperty(thread, obj, key3, desc3); + JSHandle key4(thread, JSTaggedValue(4)); + PropertyDescriptor desc4(thread, JSHandle(thread, JSTaggedValue(3)), true, true, true); + JSArray::DefineOwnProperty(thread, obj, key4, desc4); + + // new Array(1,2,3,4,3).LastIndexOf(3,4) + auto ecmaRuntimeCallInfo1 = TestHelper::CreateEcmaRuntimeCallInfo(thread, JSTaggedValue::Undefined(), 8); + ecmaRuntimeCallInfo1->SetFunction(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo1->SetThis(obj.GetTaggedValue()); + ecmaRuntimeCallInfo1->SetCallArg(0, JSTaggedValue(static_cast(3))); + ecmaRuntimeCallInfo1->SetCallArg(1, JSTaggedValue(static_cast(4))); + + [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread, ecmaRuntimeCallInfo1.get()); + JSTaggedValue result = Array::LastIndexOf(ecmaRuntimeCallInfo1.get()); + TestHelper::TearDownFrame(thread, prev); + ASSERT_EQ(result.GetRawData(), JSTaggedValue(static_cast(4)).GetRawData()); + + // new Array(1,2,3,4,3).LastIndexOf(3,3) + auto ecmaRuntimeCallInfo2 = TestHelper::CreateEcmaRuntimeCallInfo(thread, JSTaggedValue::Undefined(), 8); + ecmaRuntimeCallInfo2->SetFunction(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo2->SetThis(obj.GetTaggedValue()); + ecmaRuntimeCallInfo2->SetCallArg(0, JSTaggedValue(static_cast(3))); + ecmaRuntimeCallInfo2->SetCallArg(1, JSTaggedValue(static_cast(3))); + + prev = TestHelper::SetupFrame(thread, ecmaRuntimeCallInfo2.get()); + result = Array::LastIndexOf(ecmaRuntimeCallInfo2.get()); + TestHelper::TearDownFrame(thread, prev); + ASSERT_EQ(result.GetRawData(), JSTaggedValue(static_cast(2)).GetRawData()); + + // new Array(1,2,3,4,3).LastIndexOf(5,4) + auto ecmaRuntimeCallInfo3 = TestHelper::CreateEcmaRuntimeCallInfo(thread, JSTaggedValue::Undefined(), 8); + ecmaRuntimeCallInfo3->SetFunction(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo3->SetThis(obj.GetTaggedValue()); + ecmaRuntimeCallInfo3->SetCallArg(0, JSTaggedValue(static_cast(5))); + ecmaRuntimeCallInfo3->SetCallArg(1, JSTaggedValue(static_cast(4))); + + prev = TestHelper::SetupFrame(thread, ecmaRuntimeCallInfo3.get()); + result = Array::LastIndexOf(ecmaRuntimeCallInfo3.get()); + TestHelper::TearDownFrame(thread, prev); + ASSERT_EQ(result.GetRawData(), JSTaggedValue(-1).GetRawData()); + + // new Array(1,2,3,4,3).LastIndexOf(3) + auto ecmaRuntimeCallInfo4 = TestHelper::CreateEcmaRuntimeCallInfo(thread, JSTaggedValue::Undefined(), 6); + ecmaRuntimeCallInfo4->SetFunction(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo4->SetThis(obj.GetTaggedValue()); + ecmaRuntimeCallInfo4->SetCallArg(0, JSTaggedValue(static_cast(3))); + + prev = TestHelper::SetupFrame(thread, ecmaRuntimeCallInfo4.get()); + result = Array::LastIndexOf(ecmaRuntimeCallInfo4.get()); + TestHelper::TearDownFrame(thread, prev); + ASSERT_EQ(result.GetRawData(), JSTaggedValue(static_cast(4)).GetRawData()); +} + +// 22.1.3.11 new Array().Pop() +TEST_F(BuiltinsArrayTest, Pop) +{ + JSHandle lengthKeyHandle = thread->GlobalConstants()->GetHandledLengthString(); + JSArray *arr = JSArray::Cast(JSArray::ArrayCreate(thread, JSTaggedNumber(0)).GetTaggedValue().GetTaggedObject()); + EXPECT_TRUE(arr != nullptr); + JSHandle obj(thread, arr); + EXPECT_EQ(JSArray::GetProperty(thread, JSHandle(obj), lengthKeyHandle).GetValue()->GetInt(), 0); + + auto ecmaRuntimeCallInfo1 = TestHelper::CreateEcmaRuntimeCallInfo(thread, JSTaggedValue::Undefined(), 4); + ecmaRuntimeCallInfo1->SetFunction(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo1->SetThis(obj.GetTaggedValue()); + + [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread, ecmaRuntimeCallInfo1.get()); + JSTaggedValue result = Array::Pop(ecmaRuntimeCallInfo1.get()); + TestHelper::TearDownFrame(thread, prev); + ASSERT_EQ(result.GetRawData(), JSTaggedValue::VALUE_UNDEFINED); + ASSERT_EQ(result.GetRawData(), JSTaggedValue::VALUE_UNDEFINED); + + JSHandle key0(thread, JSTaggedValue(0)); + PropertyDescriptor desc0(thread, JSHandle(thread, JSTaggedValue(1)), true, true, true); + JSArray::DefineOwnProperty(thread, obj, key0, desc0); + JSHandle key1(thread, JSTaggedValue(1)); + PropertyDescriptor desc1(thread, JSHandle(thread, JSTaggedValue(2)), true, true, true); + JSArray::DefineOwnProperty(thread, obj, key1, desc1); + JSHandle key2(thread, JSTaggedValue(2)); + PropertyDescriptor desc2(thread, JSHandle(thread, JSTaggedValue(3)), true, true, true); + JSArray::DefineOwnProperty(thread, obj, key2, desc2); + + auto ecmaRuntimeCallInfo2 = TestHelper::CreateEcmaRuntimeCallInfo(thread, JSTaggedValue::Undefined(), 4); + ecmaRuntimeCallInfo2->SetFunction(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo2->SetThis(obj.GetTaggedValue()); + + prev = TestHelper::SetupFrame(thread, ecmaRuntimeCallInfo2.get()); + result = Array::Pop(ecmaRuntimeCallInfo2.get()); + TestHelper::TearDownFrame(thread, prev); + ASSERT_EQ(result.GetRawData(), JSTaggedValue(3).GetRawData()); +} + +// 22.1.3.11 new Array(1,2,3).Push(...items) +TEST_F(BuiltinsArrayTest, Push) +{ + JSHandle lengthKeyHandle = thread->GlobalConstants()->GetHandledLengthString(); + JSArray *arr = JSArray::Cast(JSArray::ArrayCreate(thread, JSTaggedNumber(0)).GetTaggedValue().GetTaggedObject()); + EXPECT_TRUE(arr != nullptr); + JSHandle obj(thread, arr); + EXPECT_EQ(JSArray::GetProperty(thread, JSHandle(obj), lengthKeyHandle).GetValue()->GetInt(), 0); + + JSHandle key0(thread, JSTaggedValue(0)); + PropertyDescriptor desc0(thread, JSHandle(thread, JSTaggedValue(1)), true, true, true); + JSArray::DefineOwnProperty(thread, obj, key0, desc0); + JSHandle key1(thread, JSTaggedValue(1)); + PropertyDescriptor desc1(thread, JSHandle(thread, JSTaggedValue(2)), true, true, true); + JSArray::DefineOwnProperty(thread, obj, key1, desc1); + JSHandle key2(thread, JSTaggedValue(2)); + PropertyDescriptor desc2(thread, JSHandle(thread, JSTaggedValue(3)), true, true, true); + JSArray::DefineOwnProperty(thread, obj, key2, desc2); + + auto ecmaRuntimeCallInfo1 = TestHelper::CreateEcmaRuntimeCallInfo(thread, JSTaggedValue::Undefined(), 8); + ecmaRuntimeCallInfo1->SetFunction(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo1->SetThis(obj.GetTaggedValue()); + ecmaRuntimeCallInfo1->SetCallArg(0, JSTaggedValue(static_cast(4))); + ecmaRuntimeCallInfo1->SetCallArg(1, JSTaggedValue(static_cast(5))); + + [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread, ecmaRuntimeCallInfo1.get()); + JSTaggedValue result = Array::Push(ecmaRuntimeCallInfo1.get()); + TestHelper::TearDownFrame(thread, prev); + ASSERT_EQ(result.GetNumber(), 5); + + EXPECT_EQ(JSArray::GetProperty(thread, JSHandle(obj), lengthKeyHandle).GetValue()->GetInt(), 5); + JSHandle key3(thread, JSTaggedValue(3)); + EXPECT_EQ(JSArray::GetProperty(thread, JSHandle(obj), key3).GetValue()->GetInt(), 4); + JSHandle key4(thread, JSTaggedValue(4)); + EXPECT_EQ(JSArray::GetProperty(thread, JSHandle(obj), key4).GetValue()->GetInt(), 5); +} + +TEST_F(BuiltinsArrayTest, Reduce) +{ + auto ecmaVM = thread->GetEcmaVM(); + JSHandle env = ecmaVM->GetGlobalEnv(); + ObjectFactory *factory = ecmaVM->GetFactory(); + + JSHandle lengthKeyHandle = thread->GlobalConstants()->GetHandledLengthString(); + JSArray *arr = JSArray::Cast(JSArray::ArrayCreate(thread, JSTaggedNumber(0)).GetTaggedValue().GetTaggedObject()); + EXPECT_TRUE(arr != nullptr); + JSHandle obj(thread, arr); + EXPECT_EQ(JSArray::GetProperty(thread, JSHandle(obj), lengthKeyHandle).GetValue()->GetInt(), 0); + JSHandle key0(thread, JSTaggedValue(0)); + PropertyDescriptor desc0(thread, JSHandle(thread, JSTaggedValue(1)), true, true, true); + JSArray::DefineOwnProperty(thread, obj, key0, desc0); + JSHandle key1(thread, JSTaggedValue(1)); + PropertyDescriptor desc1(thread, JSHandle(thread, JSTaggedValue(2)), true, true, true); + JSArray::DefineOwnProperty(thread, obj, key1, desc1); + JSHandle key2(thread, JSTaggedValue(2)); + PropertyDescriptor desc2(thread, JSHandle(thread, JSTaggedValue(3)), true, true, true); + JSArray::DefineOwnProperty(thread, obj, key2, desc2); + + JSHandle func = factory->NewJSFunction(env, reinterpret_cast(TestClass::TestReduceFunc)); + + auto ecmaRuntimeCallInfo1 = TestHelper::CreateEcmaRuntimeCallInfo(thread, JSTaggedValue::Undefined(), 8); + ecmaRuntimeCallInfo1->SetFunction(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo1->SetThis(obj.GetTaggedValue()); + ecmaRuntimeCallInfo1->SetCallArg(0, func.GetTaggedValue()); + ecmaRuntimeCallInfo1->SetCallArg(1, JSTaggedValue(static_cast(10))); + + [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread, ecmaRuntimeCallInfo1.get()); + JSTaggedValue result = Array::Reduce(ecmaRuntimeCallInfo1.get()); + TestHelper::TearDownFrame(thread, prev); + ASSERT_EQ(result.GetRawData(), JSTaggedValue(16).GetRawData()); +} + +TEST_F(BuiltinsArrayTest, ReduceRight) +{ + auto ecmaVM = thread->GetEcmaVM(); + JSHandle env = ecmaVM->GetGlobalEnv(); + ObjectFactory *factory = ecmaVM->GetFactory(); + + JSHandle lengthKeyHandle = thread->GlobalConstants()->GetHandledLengthString(); + JSArray *arr = JSArray::Cast(JSArray::ArrayCreate(thread, JSTaggedNumber(0)).GetTaggedValue().GetTaggedObject()); + EXPECT_TRUE(arr != nullptr); + JSHandle obj(thread, arr); + EXPECT_EQ(JSArray::GetProperty(thread, JSHandle(obj), lengthKeyHandle).GetValue()->GetInt(), 0); + JSHandle key0(thread, JSTaggedValue(0)); + PropertyDescriptor desc0(thread, JSHandle(thread, JSTaggedValue(1)), true, true, true); + JSArray::DefineOwnProperty(thread, obj, key0, desc0); + JSHandle key1(thread, JSTaggedValue(1)); + PropertyDescriptor desc1(thread, JSHandle(thread, JSTaggedValue(2)), true, true, true); + JSArray::DefineOwnProperty(thread, obj, key1, desc1); + JSHandle key2(thread, JSTaggedValue(2)); + PropertyDescriptor desc2(thread, JSHandle(thread, JSTaggedValue(3)), true, true, true); + JSArray::DefineOwnProperty(thread, obj, key2, desc2); + + JSHandle func = factory->NewJSFunction(env, reinterpret_cast(TestClass::TestReduceRightFunc)); + + auto ecmaRuntimeCallInfo1 = TestHelper::CreateEcmaRuntimeCallInfo(thread, JSTaggedValue::Undefined(), 8); + ecmaRuntimeCallInfo1->SetFunction(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo1->SetThis(obj.GetTaggedValue()); + ecmaRuntimeCallInfo1->SetCallArg(0, func.GetTaggedValue()); + ecmaRuntimeCallInfo1->SetCallArg(1, JSTaggedValue(static_cast(10))); + + [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread, ecmaRuntimeCallInfo1.get()); + JSTaggedValue result = Array::ReduceRight(ecmaRuntimeCallInfo1.get()); + TestHelper::TearDownFrame(thread, prev); + ASSERT_EQ(result.GetRawData(), JSTaggedValue(16).GetRawData()); +} + +TEST_F(BuiltinsArrayTest, Shift) +{ + JSHandle lengthKeyHandle = thread->GlobalConstants()->GetHandledLengthString(); + JSArray *arr = JSArray::Cast(JSArray::ArrayCreate(thread, JSTaggedNumber(0)).GetTaggedValue().GetTaggedObject()); + EXPECT_TRUE(arr != nullptr); + JSHandle obj(thread, arr); + EXPECT_EQ(JSArray::GetProperty(thread, JSHandle(obj), lengthKeyHandle).GetValue()->GetInt(), 0); + + JSHandle key0(thread, JSTaggedValue(0)); + PropertyDescriptor desc0(thread, JSHandle(thread, JSTaggedValue(1)), true, true, true); + JSArray::DefineOwnProperty(thread, obj, key0, desc0); + JSHandle key1(thread, JSTaggedValue(1)); + PropertyDescriptor desc1(thread, JSHandle(thread, JSTaggedValue(2)), true, true, true); + JSArray::DefineOwnProperty(thread, obj, key1, desc1); + JSHandle key2(thread, JSTaggedValue(2)); + PropertyDescriptor desc2(thread, JSHandle(thread, JSTaggedValue(3)), true, true, true); + JSArray::DefineOwnProperty(thread, obj, key2, desc2); + + auto ecmaRuntimeCallInfo1 = TestHelper::CreateEcmaRuntimeCallInfo(thread, JSTaggedValue::Undefined(), 4); + ecmaRuntimeCallInfo1->SetFunction(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo1->SetThis(obj.GetTaggedValue()); + + [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread, ecmaRuntimeCallInfo1.get()); + JSTaggedValue result = Array::Shift(ecmaRuntimeCallInfo1.get()); + TestHelper::TearDownFrame(thread, prev); + ASSERT_EQ(result.GetRawData(), JSTaggedValue(1).GetRawData()); +} + +TEST_F(BuiltinsArrayTest, Some) +{ + auto ecmaVM = thread->GetEcmaVM(); + JSHandle env = ecmaVM->GetGlobalEnv(); + ObjectFactory *factory = ecmaVM->GetFactory(); + + JSHandle lengthKeyHandle = thread->GlobalConstants()->GetHandledLengthString(); + JSArray *arr = JSArray::Cast(JSArray::ArrayCreate(thread, JSTaggedNumber(0)).GetTaggedValue().GetTaggedObject()); + EXPECT_TRUE(arr != nullptr); + JSHandle obj(thread, arr); + EXPECT_EQ(JSArray::GetProperty(thread, JSHandle(obj), lengthKeyHandle).GetValue()->GetInt(), 0); + + JSHandle key0(thread, JSTaggedValue(0)); + PropertyDescriptor desc0(thread, JSHandle(thread, JSTaggedValue(1)), true, true, true); + JSArray::DefineOwnProperty(thread, obj, key0, desc0); + JSHandle key1(thread, JSTaggedValue(1)); + PropertyDescriptor desc1(thread, JSHandle(thread, JSTaggedValue(20)), true, true, true); + JSArray::DefineOwnProperty(thread, obj, key1, desc1); + JSHandle key2(thread, JSTaggedValue(2)); + PropertyDescriptor desc2(thread, JSHandle(thread, JSTaggedValue(3)), true, true, true); + JSArray::DefineOwnProperty(thread, obj, key2, desc2); + + JSHandle jsArray(JSArray::ArrayCreate(thread, JSTaggedNumber(0))); + JSHandle func = factory->NewJSFunction(env, reinterpret_cast(TestClass::TestSomeFunc)); + + auto ecmaRuntimeCallInfo1 = TestHelper::CreateEcmaRuntimeCallInfo(thread, JSTaggedValue::Undefined(), 8); + ecmaRuntimeCallInfo1->SetFunction(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo1->SetThis(obj.GetTaggedValue()); + ecmaRuntimeCallInfo1->SetCallArg(0, func.GetTaggedValue()); + ecmaRuntimeCallInfo1->SetCallArg(1, jsArray.GetTaggedValue()); + + [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread, ecmaRuntimeCallInfo1.get()); + JSTaggedValue result2 = Array::Some(ecmaRuntimeCallInfo1.get()); + TestHelper::TearDownFrame(thread, prev); + ASSERT_EQ(result2.GetRawData(), JSTaggedValue::True().GetRawData()); +} + +TEST_F(BuiltinsArrayTest, Sort) +{ + JSHandle lengthKeyHandle = thread->GlobalConstants()->GetHandledLengthString(); + JSArray *arr = JSArray::Cast(JSArray::ArrayCreate(thread, JSTaggedNumber(0)).GetTaggedValue().GetTaggedObject()); + EXPECT_TRUE(arr != nullptr); + JSHandle obj(thread, arr); + EXPECT_EQ(JSArray::GetProperty(thread, JSHandle(obj), lengthKeyHandle).GetValue()->GetInt(), 0); + + JSHandle key0(thread, JSTaggedValue(0)); + PropertyDescriptor desc0(thread, JSHandle(thread, JSTaggedValue(3)), true, true, true); + JSArray::DefineOwnProperty(thread, obj, key0, desc0); + JSHandle key1(thread, JSTaggedValue(1)); + PropertyDescriptor desc1(thread, JSHandle(thread, JSTaggedValue(2)), true, true, true); + JSArray::DefineOwnProperty(thread, obj, key1, desc1); + JSHandle key2(thread, JSTaggedValue(2)); + PropertyDescriptor desc2(thread, JSHandle(thread, JSTaggedValue(1)), true, true, true); + JSArray::DefineOwnProperty(thread, obj, key2, desc2); + + auto ecmaRuntimeCallInfo1 = TestHelper::CreateEcmaRuntimeCallInfo(thread, JSTaggedValue::Undefined(), 4); + ecmaRuntimeCallInfo1->SetFunction(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo1->SetThis(obj.GetTaggedValue()); + + [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread, ecmaRuntimeCallInfo1.get()); + JSTaggedValue result2 = Array::Sort(ecmaRuntimeCallInfo1.get()); + TestHelper::TearDownFrame(thread, prev); + + EXPECT_TRUE(result2.IsECMAObject()); + JSHandle resultArr = + JSHandle(thread, JSTaggedValue(static_cast(result2.GetRawData()))); + EXPECT_EQ(JSArray::GetProperty(thread, resultArr, key0).GetValue()->GetInt(), 1); + EXPECT_EQ(JSArray::GetProperty(thread, resultArr, key1).GetValue()->GetInt(), 2); + EXPECT_EQ(JSArray::GetProperty(thread, resultArr, key2).GetValue()->GetInt(), 3); +} + +TEST_F(BuiltinsArrayTest, Unshift) +{ + JSHandle lengthKeyHandle = thread->GlobalConstants()->GetHandledLengthString(); + JSArray *arr = JSArray::Cast(JSArray::ArrayCreate(thread, JSTaggedNumber(0)).GetTaggedValue().GetTaggedObject()); + EXPECT_TRUE(arr != nullptr); + JSHandle obj(thread, arr); + EXPECT_EQ(JSArray::GetProperty(thread, JSHandle(obj), lengthKeyHandle).GetValue()->GetInt(), 0); + + JSHandle key0(thread, JSTaggedValue(0)); + PropertyDescriptor desc0(thread, JSHandle(thread, JSTaggedValue(1)), true, true, true); + JSArray::DefineOwnProperty(thread, obj, key0, desc0); + JSHandle key1(thread, JSTaggedValue(1)); + PropertyDescriptor desc1(thread, JSHandle(thread, JSTaggedValue(2)), true, true, true); + JSArray::DefineOwnProperty(thread, obj, key1, desc1); + JSHandle key2(thread, JSTaggedValue(2)); + PropertyDescriptor desc2(thread, JSHandle(thread, JSTaggedValue(3)), true, true, true); + JSArray::DefineOwnProperty(thread, obj, key2, desc2); + + auto ecmaRuntimeCallInfo1 = TestHelper::CreateEcmaRuntimeCallInfo(thread, JSTaggedValue::Undefined(), 8); + ecmaRuntimeCallInfo1->SetFunction(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo1->SetThis(obj.GetTaggedValue()); + ecmaRuntimeCallInfo1->SetCallArg(0, JSTaggedValue(static_cast(4))); + ecmaRuntimeCallInfo1->SetCallArg(1, JSTaggedValue(static_cast(5))); + + [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread, ecmaRuntimeCallInfo1.get()); + JSTaggedValue result = Array::Unshift(ecmaRuntimeCallInfo1.get()); + TestHelper::TearDownFrame(thread, prev); + + ASSERT_EQ(result.GetRawData(), JSTaggedValue(static_cast(5)).GetRawData()); + + EXPECT_EQ(JSArray::GetProperty(thread, JSHandle(obj), lengthKeyHandle).GetValue()->GetInt(), 5); + JSHandle key3(thread, JSTaggedValue(0)); + EXPECT_EQ(JSArray::GetProperty(thread, JSHandle(obj), key3).GetValue()->GetInt(), 4); + JSHandle key4(thread, JSTaggedValue(1)); + EXPECT_EQ(JSArray::GetProperty(thread, JSHandle(obj), key4).GetValue()->GetInt(), 5); +} + +TEST_F(BuiltinsArrayTest, Join) +{ + JSHandle lengthKeyHandle = thread->GlobalConstants()->GetHandledLengthString(); + JSArray *arr = JSArray::Cast(JSArray::ArrayCreate(thread, JSTaggedNumber(0)).GetTaggedValue().GetTaggedObject()); + EXPECT_TRUE(arr != nullptr); + JSHandle obj(thread, arr); + EXPECT_EQ(JSArray::GetProperty(thread, obj, lengthKeyHandle).GetValue()->GetInt(), 0); + + JSHandle key0(thread, JSTaggedValue(0)); + PropertyDescriptor desc0(thread, JSHandle(thread, JSTaggedValue(2))); + JSArray::DefineOwnProperty(thread, JSHandle(obj), key0, desc0); + JSHandle key1(thread, JSTaggedValue(1)); + PropertyDescriptor desc1(thread, JSHandle(thread, JSTaggedValue(3))); + JSArray::DefineOwnProperty(thread, JSHandle(obj), key1, desc1); + JSHandle key2(thread, JSTaggedValue(2)); + PropertyDescriptor desc2(thread, JSHandle(thread, JSTaggedValue(4))); + JSArray::DefineOwnProperty(thread, JSHandle(obj), key2, desc2); + + JSHandle str = thread->GetEcmaVM()->GetFactory()->NewFromCanBeCompressString("2,3,4"); + auto ecmaRuntimeCallInfo1 = TestHelper::CreateEcmaRuntimeCallInfo(thread, JSTaggedValue::Undefined(), 4); + ecmaRuntimeCallInfo1->SetFunction(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo1->SetThis(obj.GetTaggedValue()); + + [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread, ecmaRuntimeCallInfo1.get()); + JSTaggedValue result = Array::Join(ecmaRuntimeCallInfo1.get()); + TestHelper::TearDownFrame(thread, prev); + JSHandle resultHandle(thread, reinterpret_cast(result.GetRawData())); + [[maybe_unused]] auto *res = EcmaString::Cast(resultHandle.GetTaggedValue().GetTaggedObject()); + + ASSERT_EQ(res->Compare(*str), 0); +} + +TEST_F(BuiltinsArrayTest, ToString) +{ + JSHandle lengthKeyHandle = thread->GlobalConstants()->GetHandledLengthString(); + JSArray *arr = JSArray::Cast(JSArray::ArrayCreate(thread, JSTaggedNumber(0)).GetTaggedValue().GetTaggedObject()); + EXPECT_TRUE(arr != nullptr); + JSHandle obj(thread, arr); + EXPECT_EQ(JSArray::GetProperty(thread, obj, lengthKeyHandle).GetValue()->GetInt(), 0); + + JSHandle key0(thread, JSTaggedValue(0)); + PropertyDescriptor desc0(thread, JSHandle(thread, JSTaggedValue(2))); + JSArray::DefineOwnProperty(thread, JSHandle(obj), key0, desc0); + JSHandle key1(thread, JSTaggedValue(1)); + PropertyDescriptor desc1(thread, JSHandle(thread, JSTaggedValue(3))); + JSArray::DefineOwnProperty(thread, JSHandle(obj), key1, desc1); + JSHandle key2(thread, JSTaggedValue(2)); + PropertyDescriptor desc2(thread, JSHandle(thread, JSTaggedValue(4))); + JSArray::DefineOwnProperty(thread, JSHandle(obj), key2, desc2); + + JSHandle str = thread->GetEcmaVM()->GetFactory()->NewFromCanBeCompressString("2,3,4"); + auto ecmaRuntimeCallInfo1 = TestHelper::CreateEcmaRuntimeCallInfo(thread, JSTaggedValue::Undefined(), 4); + ecmaRuntimeCallInfo1->SetFunction(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo1->SetThis(obj.GetTaggedValue()); + + [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread, ecmaRuntimeCallInfo1.get()); + JSTaggedValue result = Array::Join(ecmaRuntimeCallInfo1.get()); + TestHelper::TearDownFrame(thread, prev); + JSHandle resultHandle(thread, reinterpret_cast(result.GetRawData())); + [[maybe_unused]] auto *res = EcmaString::Cast(resultHandle.GetTaggedValue().GetTaggedObject()); + + ASSERT_EQ(res->Compare(*str), 0); +} +} // namespace panda::test diff --git a/tests/runtime/builtins/builtins_arraybuffer_test.cpp b/tests/runtime/builtins/builtins_arraybuffer_test.cpp new file mode 100644 index 000000000..3e6aa4132 --- /dev/null +++ b/tests/runtime/builtins/builtins_arraybuffer_test.cpp @@ -0,0 +1,127 @@ +/* + * Copyright (c) 2021-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 "plugins/ecmascript/runtime/builtins/builtins_arraybuffer.h" +#include "plugins/ecmascript/runtime/ecma_runtime_call_info.h" +#include "plugins/ecmascript/runtime/ecma_vm.h" +#include "plugins/ecmascript/runtime/global_env.h" + +#include "plugins/ecmascript/runtime/js_arraybuffer.h" +#include "plugins/ecmascript/runtime/js_handle.h" +#include "plugins/ecmascript/runtime/js_tagged_value.h" +#include "plugins/ecmascript/tests/runtime/common/test_helper.h" + +using namespace panda::ecmascript; +using namespace panda::ecmascript::builtins; + +namespace panda::test { +class BuiltinsArrayBufferTest : public testing::Test { +public: + static void SetUpTestCase() + { + GTEST_LOG_(INFO) << "SetUpTestCase"; + } + + static void TearDownTestCase() + { + GTEST_LOG_(INFO) << "TearDownCase"; + } + + void SetUp() override + { + TestHelper::CreateEcmaVMWithScope(instance, thread, scope); + } + + void TearDown() override + { + TestHelper::DestroyEcmaVMWithScope(instance, scope); + } + + PandaVM *instance {nullptr}; + EcmaHandleScope *scope {nullptr}; + JSThread *thread {nullptr}; +}; + +JSTaggedValue CreateBuiltinsArrayBuffer(JSThread *thread, int32_t length) +{ + JSHandle env = thread->GetEcmaVM()->GetGlobalEnv(); + JSHandle arrayBuffer(thread, env->GetArrayBufferFunction().GetTaggedValue()); + JSHandle globalObject(thread, env->GetGlobalObject()); + // 6 : test case + auto ecmaRuntimeCallInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread, arrayBuffer.GetTaggedValue(), 6); + ecmaRuntimeCallInfo->SetFunction(arrayBuffer.GetTaggedValue()); + ecmaRuntimeCallInfo->SetThis(globalObject.GetTaggedValue()); + ecmaRuntimeCallInfo->SetCallArg(0, JSTaggedValue(length)); + + [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread, ecmaRuntimeCallInfo.get()); + JSTaggedValue result = BuiltinsArrayBuffer::ArrayBufferConstructor(ecmaRuntimeCallInfo.get()); + return result; +} + +// new ArrayBuffer(8) +TEST_F(BuiltinsArrayBufferTest, Constructor1) +{ + JSHandle env = thread->GetEcmaVM()->GetGlobalEnv(); + JSHandle arrayBuffer(thread, env->GetArrayBufferFunction().GetTaggedValue()); + JSHandle globalObject(thread, env->GetGlobalObject()); + auto ecmaRuntimeCallInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread, arrayBuffer.GetTaggedValue(), 6); + ecmaRuntimeCallInfo->SetFunction(arrayBuffer.GetTaggedValue()); + ecmaRuntimeCallInfo->SetThis(globalObject.GetTaggedValue()); + ecmaRuntimeCallInfo->SetCallArg(0, JSTaggedValue(static_cast(8))); + + [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread, ecmaRuntimeCallInfo.get()); + JSTaggedValue result = BuiltinsArrayBuffer::ArrayBufferConstructor(ecmaRuntimeCallInfo.get()); + ASSERT_TRUE(result.IsECMAObject()); +} + +// (new ArrayBuffer(5)).byteLength +TEST_F(BuiltinsArrayBufferTest, byteLength1) +{ + JSTaggedValue tagged = CreateBuiltinsArrayBuffer(thread, 5); + JSHandle arrBuf(thread, JSArrayBuffer::Cast(reinterpret_cast(tagged.GetRawData()))); + auto ecmaRuntimeCallInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread, JSTaggedValue::Undefined(), 4); + ecmaRuntimeCallInfo->SetFunction(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetThis(arrBuf.GetTaggedValue()); + + [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread, ecmaRuntimeCallInfo.get()); + JSTaggedValue result = BuiltinsArrayBuffer::GetByteLength(ecmaRuntimeCallInfo.get()); + ASSERT_EQ(result.GetRawData(), JSTaggedValue(5).GetRawData()); +} + +// (new ArrayBuffer(10)).slice(1, 5).bytelength +TEST_F(BuiltinsArrayBufferTest, slice1) +{ + JSTaggedValue tagged = CreateBuiltinsArrayBuffer(thread, 10); + JSHandle arrBuf(thread, JSArrayBuffer::Cast(reinterpret_cast(tagged.GetRawData()))); + auto ecmaRuntimeCallInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread, JSTaggedValue::Undefined(), 8); + ecmaRuntimeCallInfo->SetFunction(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetThis(arrBuf.GetTaggedValue()); + ecmaRuntimeCallInfo->SetCallArg(0, JSTaggedValue(static_cast(1))); + ecmaRuntimeCallInfo->SetCallArg(1, JSTaggedValue(static_cast(5))); + + [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread, ecmaRuntimeCallInfo.get()); + JSTaggedValue result1 = BuiltinsArrayBuffer::Slice(ecmaRuntimeCallInfo.get()); + TestHelper::TearDownFrame(thread, prev); + JSHandle arrBuf1(thread, + JSArrayBuffer::Cast(reinterpret_cast(result1.GetRawData()))); + auto ecmaRuntimeCallInfo1 = TestHelper::CreateEcmaRuntimeCallInfo(thread, JSTaggedValue::Undefined(), 4); + ecmaRuntimeCallInfo1->SetFunction(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo1->SetThis(arrBuf1.GetTaggedValue()); + prev = TestHelper::SetupFrame(thread, ecmaRuntimeCallInfo1.get()); + JSTaggedValue result2 = BuiltinsArrayBuffer::GetByteLength(ecmaRuntimeCallInfo1.get()); + + ASSERT_EQ(result2.GetRawData(), JSTaggedValue(4).GetRawData()); +} +} // namespace panda::test diff --git a/tests/runtime/builtins/builtins_boolean_test.cpp b/tests/runtime/builtins/builtins_boolean_test.cpp new file mode 100644 index 000000000..a8f70f1b2 --- /dev/null +++ b/tests/runtime/builtins/builtins_boolean_test.cpp @@ -0,0 +1,191 @@ +/* + * Copyright (c) 2021-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 "plugins/ecmascript/runtime/builtins/builtins_boolean.h" +#include "plugins/ecmascript/runtime/ecma_runtime_call_info.h" +#include "plugins/ecmascript/runtime/ecma_string.h" +#include "plugins/ecmascript/runtime/ecma_vm.h" +#include "plugins/ecmascript/runtime/global_env.h" +#include "plugins/ecmascript/runtime/js_primitive_ref.h" +#include "plugins/ecmascript/runtime/js_tagged_value-inl.h" +#include "plugins/ecmascript/runtime/js_thread.h" +#include "plugins/ecmascript/runtime/object_factory.h" +#include "plugins/ecmascript/tests/runtime/common/test_helper.h" + +using namespace panda::ecmascript; +using namespace panda::ecmascript::builtins; +using BuiltinsBase = panda::ecmascript::base::BuiltinsBase; + +namespace panda::test { +class BuiltinsBooleanTest : public testing::Test { +public: + static void SetUpTestCase() + { + GTEST_LOG_(INFO) << "SetUpTestCase"; + } + + static void TearDownTestCase() + { + GTEST_LOG_(INFO) << "TearDownCase"; + } + + void SetUp() override + { + TestHelper::CreateEcmaVMWithScope(instance, thread, scope); + } + + void TearDown() override + { + TestHelper::DestroyEcmaVMWithScope(instance, scope); + } + + PandaVM *instance {nullptr}; + EcmaHandleScope *scope {nullptr}; + JSThread *thread {nullptr}; +}; + +// new Boolean(123) +TEST_F(BuiltinsBooleanTest, BooleanConstructor) +{ + JSHandle env = thread->GetEcmaVM()->GetGlobalEnv(); + + JSHandle boolean(env->GetBooleanFunction()); + JSHandle globalObject(thread, env->GetGlobalObject()); + + auto ecmaRuntimeCallInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread, JSTaggedValue(*boolean), 6); + ecmaRuntimeCallInfo->SetFunction(boolean.GetTaggedValue()); + ecmaRuntimeCallInfo->SetThis(globalObject.GetTaggedValue()); + ecmaRuntimeCallInfo->SetCallArg(0, JSTaggedValue(static_cast(123))); + + [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread, ecmaRuntimeCallInfo.get()); + JSTaggedValue result = BuiltinsBoolean::BooleanConstructor(ecmaRuntimeCallInfo.get()); + + ASSERT_TRUE(result.IsECMAObject()); + ASSERT_EQ(JSPrimitiveRef::Cast(result.GetTaggedObject())->GetValue().IsTrue(), 1); +} + +// new Boolean(undefined) +TEST_F(BuiltinsBooleanTest, BooleanConstructor1) +{ + JSHandle env = thread->GetEcmaVM()->GetGlobalEnv(); + + JSHandle boolean(env->GetBooleanFunction()); + JSHandle globalObject(thread, env->GetGlobalObject()); + + auto ecmaRuntimeCallInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread, JSTaggedValue(*boolean), 6); + ecmaRuntimeCallInfo->SetFunction(boolean.GetTaggedValue()); + ecmaRuntimeCallInfo->SetThis(globalObject.GetTaggedValue()); + ecmaRuntimeCallInfo->SetCallArg(0, JSTaggedValue::Undefined()); + + [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread, ecmaRuntimeCallInfo.get()); + JSTaggedValue result = BuiltinsBoolean::BooleanConstructor(ecmaRuntimeCallInfo.get()); + + ASSERT_TRUE(result.IsECMAObject()); + ASSERT_EQ(JSPrimitiveRef::Cast(result.GetTaggedObject())->GetValue().IsFalse(), 1); +} + +// Boolean("helloworld") +TEST_F(BuiltinsBooleanTest, BooleanConstructor2) +{ + JSHandle env = thread->GetEcmaVM()->GetGlobalEnv(); + + JSHandle boolean(env->GetBooleanFunction()); + JSHandle globalObject(thread, env->GetGlobalObject()); + JSHandle str = thread->GetEcmaVM()->GetFactory()->NewFromCanBeCompressString("helloworld"); + + auto ecmaRuntimeCallInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread, JSTaggedValue::Undefined(), 6); + ecmaRuntimeCallInfo->SetFunction(boolean.GetTaggedValue()); + ecmaRuntimeCallInfo->SetThis(globalObject.GetTaggedValue()); + ecmaRuntimeCallInfo->SetCallArg(0, str.GetTaggedValue()); + + [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread, ecmaRuntimeCallInfo.get()); + JSTaggedValue result = BuiltinsBoolean::BooleanConstructor(ecmaRuntimeCallInfo.get()); + + JSTaggedValue ruler = BuiltinsBase::GetTaggedBoolean(true); + ASSERT_EQ(result.GetRawData(), ruler.GetRawData()); +} + +// false.toString() +TEST_F(BuiltinsBooleanTest, BooleanPrototypeToString) +{ + auto ecmaRuntimeCallInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread, JSTaggedValue::Undefined(), 4); + ecmaRuntimeCallInfo->SetFunction(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetThis(JSTaggedValue::False()); + + [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread, ecmaRuntimeCallInfo.get()); + JSTaggedValue result = BuiltinsBoolean::BooleanPrototypeToString(ecmaRuntimeCallInfo.get()); + ASSERT_TRUE(result.IsString()); + JSHandle res(thread, reinterpret_cast(result.GetRawData())); + auto ruler = thread->GetEcmaVM()->GetFactory()->NewFromCanBeCompressString("false"); + ASSERT_EQ(res->Compare(*ruler), 0); +} + +// (new Boolean(true)).toString() +TEST_F(BuiltinsBooleanTest, BooleanPrototypeToString1) +{ + auto ecmaVM = thread->GetEcmaVM(); + JSHandle env = ecmaVM->GetGlobalEnv(); + + JSHandle booleanObject(env->GetBooleanFunction()); + JSHandle value(thread, JSTaggedValue::True()); + JSHandle boolean = thread->GetEcmaVM()->GetFactory()->NewJSPrimitiveRef(booleanObject, value); + + auto ecmaRuntimeCallInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread, JSTaggedValue::Undefined(), 4); + ecmaRuntimeCallInfo->SetFunction(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetThis(boolean.GetTaggedValue()); + + [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread, ecmaRuntimeCallInfo.get()); + JSTaggedValue result = BuiltinsBoolean::BooleanPrototypeToString(ecmaRuntimeCallInfo.get()); + ASSERT_TRUE(result.IsString()); + JSHandle res(thread, reinterpret_cast(result.GetRawData())); + auto ruler = thread->GetEcmaVM()->GetFactory()->NewFromCanBeCompressString("true"); + ASSERT_EQ(res->Compare(*ruler), 0); +} + +// true.valueOf() +TEST_F(BuiltinsBooleanTest, BooleanPrototypeValueOf) +{ + auto ecmaRuntimeCallInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread, JSTaggedValue::Undefined(), 4); + ecmaRuntimeCallInfo->SetFunction(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetThis(JSTaggedValue::True()); + + [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread, ecmaRuntimeCallInfo.get()); + JSTaggedValue result = BuiltinsBoolean::BooleanPrototypeValueOf(ecmaRuntimeCallInfo.get()); + + JSTaggedValue ruler = BuiltinsBase::GetTaggedBoolean(true); + ASSERT_EQ(result.GetRawData(), ruler.GetRawData()); +} + +// (new Boolean(false)).valueOf() +TEST_F(BuiltinsBooleanTest, BooleanPrototypeValueOf1) +{ + auto ecmaVM = thread->GetEcmaVM(); + JSHandle env = ecmaVM->GetGlobalEnv(); + + JSHandle booleanObject(env->GetBooleanFunction()); + JSHandle value(thread, JSTaggedValue::False()); + JSHandle boolean = thread->GetEcmaVM()->GetFactory()->NewJSPrimitiveRef(booleanObject, value); + + auto ecmaRuntimeCallInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread, JSTaggedValue::Undefined(), 4); + ecmaRuntimeCallInfo->SetFunction(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetThis(boolean.GetTaggedValue()); + + [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread, ecmaRuntimeCallInfo.get()); + JSTaggedValue result = BuiltinsBoolean::BooleanPrototypeValueOf(ecmaRuntimeCallInfo.get()); + + JSTaggedValue ruler = BuiltinsBase::GetTaggedBoolean(false); + ASSERT_EQ(result.GetRawData(), ruler.GetRawData()); +} +} // namespace panda::test diff --git a/tests/runtime/builtins/builtins_dataview_test.cpp b/tests/runtime/builtins/builtins_dataview_test.cpp new file mode 100644 index 000000000..0f036ccc3 --- /dev/null +++ b/tests/runtime/builtins/builtins_dataview_test.cpp @@ -0,0 +1,466 @@ +/* + * Copyright (c) 2021-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 "plugins/ecmascript/runtime/builtins/builtins_dataview.h" +#include "plugins/ecmascript/runtime/builtins/builtins_arraybuffer.h" +#include "plugins/ecmascript/runtime/ecma_runtime_call_info.h" +#include "plugins/ecmascript/runtime/ecma_vm.h" +#include "plugins/ecmascript/runtime/global_env.h" +#include "plugins/ecmascript/runtime/js_arraybuffer.h" +#include "plugins/ecmascript/runtime/js_dataview.h" +#include "plugins/ecmascript/runtime/js_handle.h" +#include "plugins/ecmascript/runtime/js_tagged_value.h" +#include "plugins/ecmascript/tests/runtime/common/test_helper.h" + +using namespace panda::ecmascript; +using namespace panda::ecmascript::builtins; + +namespace panda::test { +using DataViewType = ecmascript::DataViewType; +class BuiltinsDataViewTest : public testing::Test { +public: + static void SetUpTestCase() + { + GTEST_LOG_(INFO) << "SetUpTestCase"; + } + + static void TearDownTestCase() + { + GTEST_LOG_(INFO) << "TearDownCase"; + } + + void SetUp() override + { + TestHelper::CreateEcmaVMWithScope(instance, thread, scope); + } + + void TearDown() override + { + TestHelper::DestroyEcmaVMWithScope(instance, scope); + } + + PandaVM *instance {nullptr}; + EcmaHandleScope *scope {nullptr}; + JSThread *thread {nullptr}; +}; + +JSTaggedValue CreateBuiltinsArrayBuffer(JSThread *thread, int32_t length) +{ + JSHandle env = thread->GetEcmaVM()->GetGlobalEnv(); + JSHandle arrayBuffer(thread, env->GetArrayBufferFunction().GetTaggedValue()); + JSHandle globalObject(thread, env->GetGlobalObject()); + // 6 : test case + auto ecmaRuntimeCallInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread, JSTaggedValue(*arrayBuffer), 6); + ecmaRuntimeCallInfo->SetFunction(arrayBuffer.GetTaggedValue()); + ecmaRuntimeCallInfo->SetThis(globalObject.GetTaggedValue()); + ecmaRuntimeCallInfo->SetCallArg(0, JSTaggedValue(length)); + + [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread, ecmaRuntimeCallInfo.get()); + JSTaggedValue result = BuiltinsArrayBuffer::ArrayBufferConstructor(ecmaRuntimeCallInfo.get()); + return result; +} + +JSTaggedValue CreateBuiltinsDataView(JSThread *thread, int32_t length, int32_t byte_offset) +{ + JSHandle env = thread->GetEcmaVM()->GetGlobalEnv(); + JSHandle dataView(thread, env->GetDataViewFunction().GetTaggedValue()); + JSHandle globalObject(thread, env->GetGlobalObject()); + JSTaggedValue tagged = CreateBuiltinsArrayBuffer(thread, length); + JSHandle arrBuf(thread, JSArrayBuffer::Cast(reinterpret_cast(tagged.GetRawData()))); + // 8 : test case + auto ecmaRuntimeCallInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread, JSTaggedValue(*dataView), 8); + ecmaRuntimeCallInfo->SetFunction(dataView.GetTaggedValue()); + ecmaRuntimeCallInfo->SetThis(globalObject.GetTaggedValue()); + ecmaRuntimeCallInfo->SetCallArg(0, arrBuf.GetTaggedValue()); + ecmaRuntimeCallInfo->SetCallArg(1, JSTaggedValue(byte_offset)); + + [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread, ecmaRuntimeCallInfo.get()); + JSTaggedValue result = BuiltinsDataView::DataViewConstructor(ecmaRuntimeCallInfo.get()); + return result; +} + +void SetUint8(JSThread *thread, const JSHandle &view, int32_t offset, JSTaggedValue value) +{ + auto ecmaRuntimeCallInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread, JSTaggedValue::Undefined(), 8); + ecmaRuntimeCallInfo->SetFunction(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetThis(view.GetTaggedValue()); + ecmaRuntimeCallInfo->SetCallArg(0, JSTaggedValue(offset)); + ecmaRuntimeCallInfo->SetCallArg(1, value); + + [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread, ecmaRuntimeCallInfo.get()); + BuiltinsDataView::SetUint8(ecmaRuntimeCallInfo.get()); +} + +// new DataView(new ArrayBuffer(10), 1) +TEST_F(BuiltinsDataViewTest, Constructor) +{ + JSHandle env = thread->GetEcmaVM()->GetGlobalEnv(); + JSHandle dataView(thread, env->GetDataViewFunction().GetTaggedValue()); + JSHandle globalObject(thread, env->GetGlobalObject()); + JSTaggedValue tagged = CreateBuiltinsArrayBuffer(thread, 10); + JSHandle arrBuf(thread, JSArrayBuffer::Cast(reinterpret_cast(tagged.GetRawData()))); + auto ecmaRuntimeCallInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread, JSTaggedValue(*dataView), 8); + ecmaRuntimeCallInfo->SetFunction(dataView.GetTaggedValue()); + ecmaRuntimeCallInfo->SetThis(globalObject.GetTaggedValue()); + ecmaRuntimeCallInfo->SetCallArg(0, arrBuf.GetTaggedValue()); + ecmaRuntimeCallInfo->SetCallArg(1, JSTaggedValue(1)); + + [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread, ecmaRuntimeCallInfo.get()); + JSTaggedValue result = BuiltinsDataView::DataViewConstructor(ecmaRuntimeCallInfo.get()); + ASSERT_TRUE(result.IsECMAObject()); +} + +// new DataView(new ArrayBuffer(10), 1).byteOffset +TEST_F(BuiltinsDataViewTest, byteOffset) +{ + JSTaggedValue tagged = CreateBuiltinsDataView(thread, 10, 1); + JSHandle view(thread, JSDataView::Cast(reinterpret_cast(tagged.GetRawData()))); + auto ecmaRuntimeCallInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread, JSTaggedValue::Undefined(), 4); + ecmaRuntimeCallInfo->SetFunction(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetThis(view.GetTaggedValue()); + + [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread, ecmaRuntimeCallInfo.get()); + JSTaggedValue result = BuiltinsDataView::GetOffset(ecmaRuntimeCallInfo.get()); + ASSERT_EQ(result.GetRawData(), JSTaggedValue(1).GetRawData()); +} + +// new DataView(new ArrayBuffer(10), 2).byteLength +TEST_F(BuiltinsDataViewTest, byteLength) +{ + JSTaggedValue tagged = CreateBuiltinsDataView(thread, 10, 2); + JSHandle view(thread, JSDataView::Cast(reinterpret_cast(tagged.GetRawData()))); + auto ecmaRuntimeCallInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread, JSTaggedValue::Undefined(), 4); + ecmaRuntimeCallInfo->SetFunction(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetThis(view.GetTaggedValue()); + + [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread, ecmaRuntimeCallInfo.get()); + JSTaggedValue result = BuiltinsDataView::GetByteLength(ecmaRuntimeCallInfo.get()); + ASSERT_EQ(result.GetRawData(), JSTaggedValue(8).GetRawData()); +} + +// new DataView(new ArrayBuffer(10), 1).buffer +TEST_F(BuiltinsDataViewTest, buffer) +{ + JSTaggedValue tagged = CreateBuiltinsDataView(thread, 10, 1); + JSHandle view(thread, JSDataView::Cast(reinterpret_cast(tagged.GetRawData()))); + auto ecmaRuntimeCallInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread, JSTaggedValue::Undefined(), 4); + ecmaRuntimeCallInfo->SetFunction(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetThis(view.GetTaggedValue()); + + [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread, ecmaRuntimeCallInfo.get()); + JSTaggedValue result = BuiltinsDataView::GetBuffer(ecmaRuntimeCallInfo.get()); + ASSERT_EQ(result.IsArrayBuffer(), true); +} + +// new DataView(new ArrayBuffer(8), 0).SetUint16/GetUint16 +TEST_F(BuiltinsDataViewTest, getUint16) +{ + JSTaggedValue tagged = CreateBuiltinsDataView(thread, 8, 0); + JSHandle view(thread, JSDataView::Cast(reinterpret_cast(tagged.GetRawData()))); + auto ecmaRuntimeCallInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread, JSTaggedValue::Undefined(), 10); + ecmaRuntimeCallInfo->SetFunction(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetThis(view.GetTaggedValue()); + ecmaRuntimeCallInfo->SetCallArg(0, JSTaggedValue(0)); + ecmaRuntimeCallInfo->SetCallArg(1, JSTaggedValue(-1870724872)); + ecmaRuntimeCallInfo->SetCallArg(2, JSTaggedValue::False()); + + [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread, ecmaRuntimeCallInfo.get()); + JSTaggedValue result = BuiltinsDataView::SetUint16(ecmaRuntimeCallInfo.get()); + ASSERT_EQ(result.GetRawData(), JSTaggedValue::VALUE_UNDEFINED); + + auto ecmaRuntimeCallInfo1 = TestHelper::CreateEcmaRuntimeCallInfo(thread, JSTaggedValue::Undefined(), 8); + ecmaRuntimeCallInfo1->SetFunction(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo1->SetThis(view.GetTaggedValue()); + ecmaRuntimeCallInfo1->SetCallArg(0, JSTaggedValue(0)); + ecmaRuntimeCallInfo1->SetCallArg(1, JSTaggedValue::True()); + + JSTaggedValue result1 = BuiltinsDataView::GetUint16(ecmaRuntimeCallInfo1.get()); + ASSERT_EQ(result1.GetRawData(), JSTaggedValue(63488).GetRawData()); +} + +// new DataView(new ArrayBuffer(8), 0).SetInt16/GetInt16 +TEST_F(BuiltinsDataViewTest, getInt16) +{ + JSTaggedValue tagged = CreateBuiltinsDataView(thread, 8, 0); + JSHandle view(thread, JSDataView::Cast(reinterpret_cast(tagged.GetRawData()))); + auto ecmaRuntimeCallInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread, JSTaggedValue::Undefined(), 10); + ecmaRuntimeCallInfo->SetFunction(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetThis(view.GetTaggedValue()); + ecmaRuntimeCallInfo->SetCallArg(0, JSTaggedValue(0)); + ecmaRuntimeCallInfo->SetCallArg(1, JSTaggedValue(-1870724872)); + ecmaRuntimeCallInfo->SetCallArg(2, JSTaggedValue::False()); + + [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread, ecmaRuntimeCallInfo.get()); + JSTaggedValue result = BuiltinsDataView::SetInt16(ecmaRuntimeCallInfo.get()); + ASSERT_EQ(result.GetRawData(), JSTaggedValue::VALUE_UNDEFINED); + + auto ecmaRuntimeCallInfo1 = TestHelper::CreateEcmaRuntimeCallInfo(thread, JSTaggedValue::Undefined(), 8); + ecmaRuntimeCallInfo1->SetFunction(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo1->SetThis(view.GetTaggedValue()); + ecmaRuntimeCallInfo1->SetCallArg(0, JSTaggedValue(0)); + ecmaRuntimeCallInfo1->SetCallArg(1, JSTaggedValue::True()); + + JSTaggedValue result1 = BuiltinsDataView::GetInt16(ecmaRuntimeCallInfo1.get()); + ASSERT_EQ(result1.GetRawData(), JSTaggedValue(-2048).GetRawData()); +} + +// new DataView(new ArrayBuffer(8), 0).SetUint8/GetUint32 +TEST_F(BuiltinsDataViewTest, GetUint32) +{ + JSTaggedValue tagged = CreateBuiltinsDataView(thread, 8, 0); + JSHandle view(thread, JSDataView::Cast(reinterpret_cast(tagged.GetRawData()))); + SetUint8(thread, view, 0, JSTaggedValue(127)); + SetUint8(thread, view, 1, JSTaggedValue(255)); + SetUint8(thread, view, 2, JSTaggedValue(255)); + SetUint8(thread, view, 3, JSTaggedValue(255)); + + auto ecmaRuntimeCallInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread, JSTaggedValue::Undefined(), 8); + ecmaRuntimeCallInfo->SetFunction(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetThis(view.GetTaggedValue()); + ecmaRuntimeCallInfo->SetCallArg(0, JSTaggedValue(0)); + ecmaRuntimeCallInfo->SetCallArg(1, JSTaggedValue::False()); + + [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread, ecmaRuntimeCallInfo.get()); + JSTaggedValue result = BuiltinsDataView::GetUint32(ecmaRuntimeCallInfo.get()); + ASSERT_EQ(result.GetRawData(), JSTaggedValue(2147483647).GetRawData()); +} + +// new DataView(new ArrayBuffer(8), 0).SetUint8/GetInt32 +TEST_F(BuiltinsDataViewTest, GetInt32) +{ + JSTaggedValue tagged = CreateBuiltinsDataView(thread, 8, 0); + JSHandle view(thread, JSDataView::Cast(reinterpret_cast(tagged.GetRawData()))); + SetUint8(thread, view, 0, JSTaggedValue(127)); + SetUint8(thread, view, 1, JSTaggedValue(255)); + SetUint8(thread, view, 2, JSTaggedValue(255)); + SetUint8(thread, view, 3, JSTaggedValue(255)); + + auto ecmaRuntimeCallInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread, JSTaggedValue::Undefined(), 8); + ecmaRuntimeCallInfo->SetFunction(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetThis(view.GetTaggedValue()); + ecmaRuntimeCallInfo->SetCallArg(0, JSTaggedValue(0)); + ecmaRuntimeCallInfo->SetCallArg(1, JSTaggedValue::False()); + + [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread, ecmaRuntimeCallInfo.get()); + JSTaggedValue result = BuiltinsDataView::GetInt32(ecmaRuntimeCallInfo.get()); + ASSERT_EQ(result.GetRawData(), JSTaggedValue(2147483647).GetRawData()); +} + +// new DataView(new ArrayBuffer(8), 0).SetUint8/GetInt8 +TEST_F(BuiltinsDataViewTest, GetInt8) +{ + JSTaggedValue tagged = CreateBuiltinsDataView(thread, 8, 0); + JSHandle view(thread, JSDataView::Cast(reinterpret_cast(tagged.GetRawData()))); + SetUint8(thread, view, 0, JSTaggedValue(255)); + + auto ecmaRuntimeCallInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread, JSTaggedValue::Undefined(), 6); + ecmaRuntimeCallInfo->SetFunction(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetThis(view.GetTaggedValue()); + ecmaRuntimeCallInfo->SetCallArg(0, JSTaggedValue(0)); + + [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread, ecmaRuntimeCallInfo.get()); + JSTaggedValue result = BuiltinsDataView::GetInt8(ecmaRuntimeCallInfo.get()); + ASSERT_EQ(result.GetRawData(), JSTaggedValue(-1).GetRawData()); +} + +// new DataView(new ArrayBuffer(8), 0).SetUint8/GetUint8 +TEST_F(BuiltinsDataViewTest, GetUint8) +{ + JSTaggedValue tagged = CreateBuiltinsDataView(thread, 8, 0); + JSHandle view(thread, JSDataView::Cast(reinterpret_cast(tagged.GetRawData()))); + SetUint8(thread, view, 0, JSTaggedValue(127)); + + auto ecmaRuntimeCallInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread, JSTaggedValue::Undefined(), 6); + ecmaRuntimeCallInfo->SetFunction(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetThis(view.GetTaggedValue()); + ecmaRuntimeCallInfo->SetCallArg(0, JSTaggedValue(0)); + + [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread, ecmaRuntimeCallInfo.get()); + JSTaggedValue result = BuiltinsDataView::GetUint8(ecmaRuntimeCallInfo.get()); + ASSERT_EQ(result.GetRawData(), JSTaggedValue(127).GetRawData()); +} + +// new DataView(new ArrayBuffer(8), 4).SetUint8/GetFloat32 +TEST_F(BuiltinsDataViewTest, GetFloat32) +{ + JSTaggedValue tagged = CreateBuiltinsDataView(thread, 8, 0); + JSHandle view(thread, JSDataView::Cast(reinterpret_cast(tagged.GetRawData()))); + SetUint8(thread, view, 4, JSTaggedValue(75)); + SetUint8(thread, view, 5, JSTaggedValue(75)); + SetUint8(thread, view, 6, JSTaggedValue(75)); + SetUint8(thread, view, 7, JSTaggedValue(75)); + + auto ecmaRuntimeCallInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread, JSTaggedValue::Undefined(), 8); + ecmaRuntimeCallInfo->SetFunction(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetThis(view.GetTaggedValue()); + ecmaRuntimeCallInfo->SetCallArg(0, JSTaggedValue(4)); + ecmaRuntimeCallInfo->SetCallArg(1, JSTaggedValue::False()); + + [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread, ecmaRuntimeCallInfo.get()); + JSTaggedValue result = BuiltinsDataView::GetFloat32(ecmaRuntimeCallInfo.get()); + ASSERT_EQ(result.GetRawData(), JSTaggedValue(static_cast(13323083)).GetRawData()); +} + +// new DataView(new ArrayBuffer(12), 4).SetUint8/GetFloat64 +TEST_F(BuiltinsDataViewTest, GetFloat64) +{ + JSTaggedValue tagged = CreateBuiltinsDataView(thread, 12, 0); + JSHandle view(thread, JSDataView::Cast(reinterpret_cast(tagged.GetRawData()))); + SetUint8(thread, view, 4, JSTaggedValue(67)); + SetUint8(thread, view, 5, JSTaggedValue(67)); + SetUint8(thread, view, 6, JSTaggedValue(68)); + SetUint8(thread, view, 7, JSTaggedValue(68)); + SetUint8(thread, view, 8, JSTaggedValue(67)); + SetUint8(thread, view, 9, JSTaggedValue(67)); + SetUint8(thread, view, 10, JSTaggedValue(68)); + SetUint8(thread, view, 11, JSTaggedValue(68)); + + auto ecmaRuntimeCallInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread, JSTaggedValue::Undefined(), 8); + ecmaRuntimeCallInfo->SetFunction(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetThis(view.GetTaggedValue()); + ecmaRuntimeCallInfo->SetCallArg(0, JSTaggedValue(4)); + ecmaRuntimeCallInfo->SetCallArg(1, JSTaggedValue::False()); + + [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread, ecmaRuntimeCallInfo.get()); + JSTaggedValue result = BuiltinsDataView::GetFloat64(ecmaRuntimeCallInfo.get()); + ASSERT_EQ(result.GetRawData(), JSTaggedValue(static_cast(10846169068898440)).GetRawData()); +} + +// new DataView(new ArrayBuffer(8), 0).SetUint32/GetUint32 +TEST_F(BuiltinsDataViewTest, SetUint32) +{ + JSTaggedValue tagged = CreateBuiltinsDataView(thread, 8, 0); + JSHandle view(thread, JSDataView::Cast(reinterpret_cast(tagged.GetRawData()))); + auto ecmaRuntimeCallInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread, JSTaggedValue::Undefined(), 10); + ecmaRuntimeCallInfo->SetFunction(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetThis(view.GetTaggedValue()); + ecmaRuntimeCallInfo->SetCallArg(0, JSTaggedValue(0)); + ecmaRuntimeCallInfo->SetCallArg(1, JSTaggedValue(0x907f00f8)); + ecmaRuntimeCallInfo->SetCallArg(2, JSTaggedValue::True()); + + [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread, ecmaRuntimeCallInfo.get()); + JSTaggedValue result = BuiltinsDataView::SetUint32(ecmaRuntimeCallInfo.get()); + ASSERT_EQ(result.GetRawData(), JSTaggedValue::VALUE_UNDEFINED); + + auto ecmaRuntimeCallInfo1 = TestHelper::CreateEcmaRuntimeCallInfo(thread, JSTaggedValue::Undefined(), 8); + ecmaRuntimeCallInfo1->SetFunction(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo1->SetThis(view.GetTaggedValue()); + ecmaRuntimeCallInfo1->SetCallArg(0, JSTaggedValue(0)); + ecmaRuntimeCallInfo1->SetCallArg(1, JSTaggedValue::False()); + + JSTaggedValue result1 = BuiltinsDataView::GetUint32(ecmaRuntimeCallInfo1.get()); + ASSERT_EQ(result1.GetRawData(), JSTaggedValue(static_cast(0xf8007f90)).GetRawData()); +} + +// new DataView(new ArrayBuffer(8), 0).SetInt32/GetInt32 +TEST_F(BuiltinsDataViewTest, SetInt32) +{ + JSTaggedValue tagged = CreateBuiltinsDataView(thread, 8, 0); + JSHandle view(thread, JSDataView::Cast(reinterpret_cast(tagged.GetRawData()))); + auto ecmaRuntimeCallInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread, JSTaggedValue::Undefined(), 10); + ecmaRuntimeCallInfo->SetFunction(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetThis(view.GetTaggedValue()); + ecmaRuntimeCallInfo->SetCallArg(0, JSTaggedValue(0)); + ecmaRuntimeCallInfo->SetCallArg(1, JSTaggedValue(-1870724872)); + ecmaRuntimeCallInfo->SetCallArg(2, JSTaggedValue::True()); + + [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread, ecmaRuntimeCallInfo.get()); + JSTaggedValue result = BuiltinsDataView::SetInt32(ecmaRuntimeCallInfo.get()); + ASSERT_EQ(result.GetRawData(), JSTaggedValue::VALUE_UNDEFINED); + + auto ecmaRuntimeCallInfo1 = TestHelper::CreateEcmaRuntimeCallInfo(thread, JSTaggedValue::Undefined(), 8); + ecmaRuntimeCallInfo1->SetFunction(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo1->SetThis(view.GetTaggedValue()); + ecmaRuntimeCallInfo1->SetCallArg(0, JSTaggedValue(0)); + ecmaRuntimeCallInfo1->SetCallArg(1, JSTaggedValue::False()); + + JSTaggedValue result1 = BuiltinsDataView::GetInt32(ecmaRuntimeCallInfo1.get()); + ASSERT_EQ(result1.GetRawData(), JSTaggedValue(-134185072).GetRawData()); +} + +// new DataView(new ArrayBuffer(8), 0).SetInt8/GetUint8 +TEST_F(BuiltinsDataViewTest, SetInt8) +{ + JSTaggedValue tagged = CreateBuiltinsDataView(thread, 8, 0); + JSHandle view(thread, JSDataView::Cast(reinterpret_cast(tagged.GetRawData()))); + auto ecmaRuntimeCallInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread, JSTaggedValue::Undefined(), 8); + ecmaRuntimeCallInfo->SetFunction(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetThis(view.GetTaggedValue()); + ecmaRuntimeCallInfo->SetCallArg(0, JSTaggedValue(0)); + ecmaRuntimeCallInfo->SetCallArg(1, JSTaggedValue(-1)); + [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread, ecmaRuntimeCallInfo.get()); + JSTaggedValue result = BuiltinsDataView::SetInt8(ecmaRuntimeCallInfo.get()); + ASSERT_EQ(result.GetRawData(), JSTaggedValue::VALUE_UNDEFINED); + + auto ecmaRuntimeCallInfo1 = TestHelper::CreateEcmaRuntimeCallInfo(thread, JSTaggedValue::Undefined(), 6); + ecmaRuntimeCallInfo1->SetFunction(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo1->SetThis(view.GetTaggedValue()); + ecmaRuntimeCallInfo1->SetCallArg(0, JSTaggedValue(0)); + JSTaggedValue result1 = BuiltinsDataView::GetUint8(ecmaRuntimeCallInfo1.get()); + ASSERT_EQ(result1.GetRawData(), JSTaggedValue(255).GetRawData()); +} + +// new DataView(new ArrayBuffer(4), 0).SetFloat32/GetFloat32 +TEST_F(BuiltinsDataViewTest, SetFloat32) +{ + JSTaggedValue tagged = CreateBuiltinsDataView(thread, 4, 0); + JSHandle view(thread, JSDataView::Cast(reinterpret_cast(tagged.GetRawData()))); + auto ecmaRuntimeCallInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread, JSTaggedValue::Undefined(), 10); + ecmaRuntimeCallInfo->SetFunction(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetThis(view.GetTaggedValue()); + ecmaRuntimeCallInfo->SetCallArg(0, JSTaggedValue(0)); + ecmaRuntimeCallInfo->SetCallArg(1, JSTaggedValue(42)); + ecmaRuntimeCallInfo->SetCallArg(2, JSTaggedValue::True()); + + [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread, ecmaRuntimeCallInfo.get()); + JSTaggedValue result = BuiltinsDataView::SetFloat32(ecmaRuntimeCallInfo.get()); + ASSERT_EQ(result.GetRawData(), JSTaggedValue::VALUE_UNDEFINED); + + auto ecmaRuntimeCallInfo1 = TestHelper::CreateEcmaRuntimeCallInfo(thread, JSTaggedValue::Undefined(), 8); + ecmaRuntimeCallInfo1->SetFunction(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo1->SetThis(view.GetTaggedValue()); + ecmaRuntimeCallInfo1->SetCallArg(0, JSTaggedValue(0)); + ecmaRuntimeCallInfo1->SetCallArg(1, JSTaggedValue::False()); + + JSTaggedValue result1 = BuiltinsDataView::GetFloat32(ecmaRuntimeCallInfo1.get()); + ASSERT_EQ(result1.GetRawData(), JSTaggedValue(static_cast(1.4441781973331565e-41)).GetRawData()); +} + +// new DataView(new ArrayBuffer(8), 0).SetFloat64/GetFloat64 +TEST_F(BuiltinsDataViewTest, SetFloat64) +{ + JSTaggedValue tagged = CreateBuiltinsDataView(thread, 8, 0); + JSHandle view(thread, JSDataView::Cast(reinterpret_cast(tagged.GetRawData()))); + auto ecmaRuntimeCallInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread, JSTaggedValue::Undefined(), 10); + ecmaRuntimeCallInfo->SetFunction(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetThis(view.GetTaggedValue()); + ecmaRuntimeCallInfo->SetCallArg(0, JSTaggedValue(0)); + ecmaRuntimeCallInfo->SetCallArg(1, JSTaggedValue(42)); + ecmaRuntimeCallInfo->SetCallArg(2, JSTaggedValue::True()); + + [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread, ecmaRuntimeCallInfo.get()); + JSTaggedValue result = BuiltinsDataView::SetFloat64(ecmaRuntimeCallInfo.get()); + ASSERT_EQ(result.GetRawData(), JSTaggedValue::VALUE_UNDEFINED); + + auto ecmaRuntimeCallInfo1 = TestHelper::CreateEcmaRuntimeCallInfo(thread, JSTaggedValue::Undefined(), 8); + ecmaRuntimeCallInfo1->SetFunction(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo1->SetThis(view.GetTaggedValue()); + ecmaRuntimeCallInfo1->SetCallArg(0, JSTaggedValue(0)); + ecmaRuntimeCallInfo1->SetCallArg(1, JSTaggedValue::False()); + + JSTaggedValue result1 = BuiltinsDataView::GetFloat64(ecmaRuntimeCallInfo1.get()); + ASSERT_EQ(result1.GetRawData(), JSTaggedValue(static_cast(8.759e-320)).GetRawData()); +} +} // namespace panda::test diff --git a/tests/runtime/builtins/builtins_date_test.cpp b/tests/runtime/builtins/builtins_date_test.cpp new file mode 100644 index 000000000..3e44cdca5 --- /dev/null +++ b/tests/runtime/builtins/builtins_date_test.cpp @@ -0,0 +1,960 @@ +/* + * Copyright (c) 2021-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 "plugins/ecmascript/runtime/base/string_helper.h" +#include "plugins/ecmascript/runtime/builtins/builtins_date.h" +#include "plugins/ecmascript/runtime/ecma_vm.h" +#include "plugins/ecmascript/runtime/global_env.h" +#include "plugins/ecmascript/runtime/js_function.h" +#include "plugins/ecmascript/runtime/js_thread.h" +#include "plugins/ecmascript/tests/runtime/common/test_helper.h" +#include "utils/bit_utils.h" + +using namespace panda::ecmascript; +using namespace panda::ecmascript::builtins; +namespace panda::test { +const char NEG = '-'; +const char PLUS = '+'; +const int STR_LENGTH_OTHERS = 2; +const int MINUTE_PER_HOUR = 60; +const int CHINA_BEFORE_1901_MIN = 485; +const int CHINA_AFTER_1901_MIN = 480; +const int64_t CHINA_BEFORE_1900_MS = -2177481943000; +class BuiltinsDateTest : public testing::Test { +public: + static void SetUpTestCase() + { + GTEST_LOG_(INFO) << "SetUpTestCase"; + } + + static void TearDownTestCase() + { + GTEST_LOG_(INFO) << "TearDownCase"; + } + + void SetUp() override + { + TestHelper::CreateEcmaVMWithScope(instance, thread, scope); + } + + void TearDown() override + { + TestHelper::DestroyEcmaVMWithScope(instance, scope); + } + + PandaVM *instance {nullptr}; + EcmaHandleScope *scope {nullptr}; + JSThread *thread {nullptr}; +}; + +JSHandle JSDateCreateTest(JSThread *thread) +{ + EcmaVM *ecmaVM = thread->GetEcmaVM(); + ObjectFactory *factory = ecmaVM->GetFactory(); + JSHandle globalEnv = ecmaVM->GetGlobalEnv(); + JSHandle dateFunction = globalEnv->GetDateFunction(); + JSHandle dateObject = + JSHandle::Cast(factory->NewJSObjectByConstructor(JSHandle(dateFunction), dateFunction)); + return dateObject; +} + +static std::unique_ptr CreateAndSetRuntimeCallInfo(JSThread *thread, array_size_t argvLength, + JSTaggedValue this_value) +{ + auto ecmaRuntimeCallInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread, JSTaggedValue::Undefined(), argvLength); + ecmaRuntimeCallInfo->SetFunction(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetThis(this_value); + return ecmaRuntimeCallInfo; +} + +TEST_F(BuiltinsDateTest, SetGetDate) +{ + JSHandle jsDate = JSDateCreateTest(thread); + + auto ecmaRuntimeCallInfo = CreateAndSetRuntimeCallInfo(thread, 6, jsDate.GetTaggedValue()); + ecmaRuntimeCallInfo->SetCallArg(0, JSTaggedValue(static_cast(2))); + + [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread, ecmaRuntimeCallInfo.get()); + [[maybe_unused]] JSTaggedValue result1 = BuiltinsDate::SetDate(ecmaRuntimeCallInfo.get()); + JSTaggedValue result2 = BuiltinsDate::GetDate(ecmaRuntimeCallInfo.get()); + ASSERT_EQ(result2.GetRawData(), JSTaggedValue(static_cast(2)).GetRawData()); +} + +TEST_F(BuiltinsDateTest, SetGetUTCDate) +{ + JSHandle jsDate = JSDateCreateTest(thread); + + auto ecmaRuntimeCallInfo = CreateAndSetRuntimeCallInfo(thread, 6, jsDate.GetTaggedValue()); + ecmaRuntimeCallInfo->SetCallArg(0, JSTaggedValue(static_cast(2))); + + [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread, ecmaRuntimeCallInfo.get()); + [[maybe_unused]] JSTaggedValue result3 = BuiltinsDate::SetUTCDate(ecmaRuntimeCallInfo.get()); + JSTaggedValue result4 = BuiltinsDate::GetUTCDate(ecmaRuntimeCallInfo.get()); + ASSERT_EQ(result4.GetRawData(), JSTaggedValue(static_cast(2)).GetRawData()); +} + +TEST_F(BuiltinsDateTest, SetGetMinusUTCDate) +{ + JSHandle jsDate = JSDateCreateTest(thread); + + auto ecmaRuntimeCallInfo = CreateAndSetRuntimeCallInfo(thread, 6, jsDate.GetTaggedValue()); + ecmaRuntimeCallInfo->SetCallArg(0, JSTaggedValue(static_cast(-2))); + + [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread, ecmaRuntimeCallInfo.get()); + [[maybe_unused]] JSTaggedValue result3 = BuiltinsDate::SetUTCDate(ecmaRuntimeCallInfo.get()); + JSTaggedValue result4 = BuiltinsDate::GetUTCDate(ecmaRuntimeCallInfo.get()); + ASSERT_EQ(result4.GetRawData(), JSTaggedValue(static_cast(29)).GetRawData()); +} + +TEST_F(BuiltinsDateTest, SetGetFullYear) +{ + JSHandle jsDate = JSDateCreateTest(thread); + + auto ecmaRuntimeCallInfo = CreateAndSetRuntimeCallInfo(thread, 10, jsDate.GetTaggedValue()); + // 2018 : test case + ecmaRuntimeCallInfo->SetCallArg(0, JSTaggedValue(static_cast(2018))); + // 10 : test case + ecmaRuntimeCallInfo->SetCallArg(1, JSTaggedValue(static_cast(10))); + // 2, 6 : test case + ecmaRuntimeCallInfo->SetCallArg(2, JSTaggedValue(static_cast(6))); + + [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread, ecmaRuntimeCallInfo.get()); + BuiltinsDate::SetFullYear(ecmaRuntimeCallInfo.get()); + JSTaggedValue result1 = BuiltinsDate::GetFullYear(ecmaRuntimeCallInfo.get()); + // 2018 : test case + ASSERT_EQ(result1.GetRawData(), JSTaggedValue(static_cast(2018)).GetRawData()); + + JSTaggedValue result2 = BuiltinsDate::GetMonth(ecmaRuntimeCallInfo.get()); + // 10 : test case + ASSERT_EQ(result2.GetRawData(), JSTaggedValue(static_cast(10)).GetRawData()); + + JSTaggedValue result3 = BuiltinsDate::GetDate(ecmaRuntimeCallInfo.get()); + // 6 : test case + ASSERT_EQ(result3.GetRawData(), JSTaggedValue(static_cast(6)).GetRawData()); +} + +TEST_F(BuiltinsDateTest, SetGetUTCFullYear) +{ + JSHandle jsDate = JSDateCreateTest(thread); + + auto ecmaRuntimeCallInfo = CreateAndSetRuntimeCallInfo(thread, 10, jsDate.GetTaggedValue()); + // 2018 : test case + ecmaRuntimeCallInfo->SetCallArg(0, JSTaggedValue(static_cast(2018))); + // 10 : test case + ecmaRuntimeCallInfo->SetCallArg(1, JSTaggedValue(static_cast(10))); + // 2, 6 : test case + ecmaRuntimeCallInfo->SetCallArg(2, JSTaggedValue(static_cast(6))); + + [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread, ecmaRuntimeCallInfo.get()); + BuiltinsDate::SetUTCFullYear(ecmaRuntimeCallInfo.get()); + JSTaggedValue result4 = BuiltinsDate::GetUTCFullYear(ecmaRuntimeCallInfo.get()); + // 2018 : test case + ASSERT_EQ(result4.GetRawData(), JSTaggedValue(static_cast(2018)).GetRawData()); + + JSTaggedValue result5 = BuiltinsDate::GetUTCMonth(ecmaRuntimeCallInfo.get()); + // 10 : test case + ASSERT_EQ(result5.GetRawData(), JSTaggedValue(static_cast(10)).GetRawData()); + + JSTaggedValue result6 = BuiltinsDate::GetUTCDate(ecmaRuntimeCallInfo.get()); + // 6 : test case + ASSERT_EQ(result6.GetRawData(), JSTaggedValue(static_cast(6)).GetRawData()); +} + +TEST_F(BuiltinsDateTest, SetGetMinusFullYear) +{ + JSHandle jsDate = JSDateCreateTest(thread); + auto ecmaRuntimeCallInfo = CreateAndSetRuntimeCallInfo(thread, 10, jsDate.GetTaggedValue()); + ecmaRuntimeCallInfo->SetCallArg(0, JSTaggedValue(static_cast(-2018))); + ecmaRuntimeCallInfo->SetCallArg(1, JSTaggedValue(static_cast(-10))); + ecmaRuntimeCallInfo->SetCallArg(2, JSTaggedValue(static_cast(-6))); + + [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread, ecmaRuntimeCallInfo.get()); + BuiltinsDate::SetFullYear(ecmaRuntimeCallInfo.get()); + JSTaggedValue result1 = BuiltinsDate::GetFullYear(ecmaRuntimeCallInfo.get()); + ASSERT_EQ(result1.GetRawData(), JSTaggedValue(static_cast(-2019)).GetRawData()); + + JSTaggedValue result2 = BuiltinsDate::GetMonth(ecmaRuntimeCallInfo.get()); + ASSERT_EQ(result2.GetRawData(), JSTaggedValue(static_cast(1)).GetRawData()); + + JSTaggedValue result3 = BuiltinsDate::GetDate(ecmaRuntimeCallInfo.get()); + ASSERT_EQ(result3.GetRawData(), JSTaggedValue(static_cast(22)).GetRawData()); +} + +TEST_F(BuiltinsDateTest, SetGetMinusUTCFullYear) +{ + JSHandle jsDate = JSDateCreateTest(thread); + + auto ecmaRuntimeCallInfo = CreateAndSetRuntimeCallInfo(thread, 10, jsDate.GetTaggedValue()); + ecmaRuntimeCallInfo->SetCallArg(0, JSTaggedValue(static_cast(-2018))); + ecmaRuntimeCallInfo->SetCallArg(1, JSTaggedValue(static_cast(-10))); + ecmaRuntimeCallInfo->SetCallArg(2, JSTaggedValue(static_cast(-6))); + + [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread, ecmaRuntimeCallInfo.get()); + BuiltinsDate::SetUTCFullYear(ecmaRuntimeCallInfo.get()); + JSTaggedValue result4 = BuiltinsDate::GetUTCFullYear(ecmaRuntimeCallInfo.get()); + ASSERT_EQ(result4.GetRawData(), JSTaggedValue(static_cast(-2019)).GetRawData()); + + JSTaggedValue result5 = BuiltinsDate::GetUTCMonth(ecmaRuntimeCallInfo.get()); + ASSERT_EQ(result5.GetRawData(), JSTaggedValue(static_cast(1)).GetRawData()); + + JSTaggedValue result6 = BuiltinsDate::GetUTCDate(ecmaRuntimeCallInfo.get()); + ASSERT_EQ(result6.GetRawData(), JSTaggedValue(static_cast(22)).GetRawData()); +} + +TEST_F(BuiltinsDateTest, SetGetHours) +{ + JSHandle jsDate = JSDateCreateTest(thread); + + auto ecmaRuntimeCallInfo = CreateAndSetRuntimeCallInfo(thread, 12, jsDate.GetTaggedValue()); + ecmaRuntimeCallInfo->SetCallArg(0, JSTaggedValue(static_cast(18))); + ecmaRuntimeCallInfo->SetCallArg(1, JSTaggedValue(static_cast(10))); + ecmaRuntimeCallInfo->SetCallArg(2, JSTaggedValue(static_cast(6))); + ecmaRuntimeCallInfo->SetCallArg(3, JSTaggedValue(static_cast(111))); + + [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread, ecmaRuntimeCallInfo.get()); + BuiltinsDate::SetHours(ecmaRuntimeCallInfo.get()); + JSTaggedValue result1 = BuiltinsDate::GetHours(ecmaRuntimeCallInfo.get()); + ASSERT_EQ(result1.GetRawData(), JSTaggedValue(static_cast(18)).GetRawData()); + + JSTaggedValue result2 = BuiltinsDate::GetMinutes(ecmaRuntimeCallInfo.get()); + ASSERT_EQ(result2.GetRawData(), JSTaggedValue(static_cast(10)).GetRawData()); + + JSTaggedValue result3 = BuiltinsDate::GetSeconds(ecmaRuntimeCallInfo.get()); + ASSERT_EQ(result3.GetRawData(), JSTaggedValue(static_cast(6)).GetRawData()); + + JSTaggedValue result4 = BuiltinsDate::GetMilliseconds(ecmaRuntimeCallInfo.get()); + ASSERT_EQ(result4.GetRawData(), JSTaggedValue(static_cast(111)).GetRawData()); +} + +TEST_F(BuiltinsDateTest, SetGetUTCHours) +{ + JSHandle jsDate = JSDateCreateTest(thread); + + auto ecmaRuntimeCallInfo = CreateAndSetRuntimeCallInfo(thread, 12, jsDate.GetTaggedValue()); + ecmaRuntimeCallInfo->SetCallArg(0, JSTaggedValue(static_cast(18))); + ecmaRuntimeCallInfo->SetCallArg(1, JSTaggedValue(static_cast(10))); + ecmaRuntimeCallInfo->SetCallArg(2, JSTaggedValue(static_cast(6))); + ecmaRuntimeCallInfo->SetCallArg(3, JSTaggedValue(static_cast(111))); + + [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread, ecmaRuntimeCallInfo.get()); + BuiltinsDate::SetUTCHours(ecmaRuntimeCallInfo.get()); + JSTaggedValue result5 = BuiltinsDate::GetUTCHours(ecmaRuntimeCallInfo.get()); + ASSERT_EQ(result5.GetRawData(), JSTaggedValue(static_cast(18)).GetRawData()); + + JSTaggedValue result6 = BuiltinsDate::GetUTCMinutes(ecmaRuntimeCallInfo.get()); + ASSERT_EQ(result6.GetRawData(), JSTaggedValue(static_cast(10)).GetRawData()); + + JSTaggedValue result7 = BuiltinsDate::GetUTCSeconds(ecmaRuntimeCallInfo.get()); + ASSERT_EQ(result7.GetRawData(), JSTaggedValue(static_cast(6)).GetRawData()); + + JSTaggedValue result8 = BuiltinsDate::GetUTCMilliseconds(ecmaRuntimeCallInfo.get()); + ASSERT_EQ(result8.GetRawData(), JSTaggedValue(static_cast(111)).GetRawData()); +} + +TEST_F(BuiltinsDateTest, SetGetMinusHours) +{ + JSHandle jsDate = JSDateCreateTest(thread); + + auto ecmaRuntimeCallInfo = CreateAndSetRuntimeCallInfo(thread, 12, jsDate.GetTaggedValue()); + ecmaRuntimeCallInfo->SetCallArg(0, JSTaggedValue(static_cast(-18))); + ecmaRuntimeCallInfo->SetCallArg(1, JSTaggedValue(static_cast(-10))); + ecmaRuntimeCallInfo->SetCallArg(2, JSTaggedValue(static_cast(-6))); + ecmaRuntimeCallInfo->SetCallArg(3, JSTaggedValue(static_cast(-111))); + + [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread, ecmaRuntimeCallInfo.get()); + BuiltinsDate::SetHours(ecmaRuntimeCallInfo.get()); + JSTaggedValue result1 = BuiltinsDate::GetHours(ecmaRuntimeCallInfo.get()); + ASSERT_EQ(result1.GetRawData(), JSTaggedValue(static_cast(5)).GetRawData()); + + JSTaggedValue result2 = BuiltinsDate::GetMinutes(ecmaRuntimeCallInfo.get()); + ASSERT_EQ(result2.GetRawData(), JSTaggedValue(static_cast(49)).GetRawData()); + + JSTaggedValue result3 = BuiltinsDate::GetSeconds(ecmaRuntimeCallInfo.get()); + ASSERT_EQ(result3.GetRawData(), JSTaggedValue(static_cast(53)).GetRawData()); + + JSTaggedValue result4 = BuiltinsDate::GetMilliseconds(ecmaRuntimeCallInfo.get()); + ASSERT_EQ(result4.GetRawData(), JSTaggedValue(static_cast(889)).GetRawData()); +} + +TEST_F(BuiltinsDateTest, SetGetMinusUTCHours) +{ + JSHandle jsDate = JSDateCreateTest(thread); + + auto ecmaRuntimeCallInfo = CreateAndSetRuntimeCallInfo(thread, 12, jsDate.GetTaggedValue()); + ecmaRuntimeCallInfo->SetCallArg(0, JSTaggedValue(static_cast(-18))); + ecmaRuntimeCallInfo->SetCallArg(1, JSTaggedValue(static_cast(-10))); + ecmaRuntimeCallInfo->SetCallArg(2, JSTaggedValue(static_cast(-6))); + ecmaRuntimeCallInfo->SetCallArg(3, JSTaggedValue(static_cast(-111))); + + [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread, ecmaRuntimeCallInfo.get()); + BuiltinsDate::SetUTCHours(ecmaRuntimeCallInfo.get()); + JSTaggedValue result5 = BuiltinsDate::GetUTCHours(ecmaRuntimeCallInfo.get()); + ASSERT_EQ(result5.GetRawData(), JSTaggedValue(static_cast(5)).GetRawData()); + + JSTaggedValue result6 = BuiltinsDate::GetUTCMinutes(ecmaRuntimeCallInfo.get()); + ASSERT_EQ(result6.GetRawData(), JSTaggedValue(static_cast(49)).GetRawData()); + + JSTaggedValue result7 = BuiltinsDate::GetUTCSeconds(ecmaRuntimeCallInfo.get()); + ASSERT_EQ(result7.GetRawData(), JSTaggedValue(static_cast(53)).GetRawData()); + + JSTaggedValue result8 = BuiltinsDate::GetUTCMilliseconds(ecmaRuntimeCallInfo.get()); + ASSERT_EQ(result8.GetRawData(), JSTaggedValue(static_cast(889)).GetRawData()); +} + +TEST_F(BuiltinsDateTest, SetGetMilliseconds) +{ + JSHandle jsDate = JSDateCreateTest(thread); + + auto ecmaRuntimeCallInfo = CreateAndSetRuntimeCallInfo(thread, 6, jsDate.GetTaggedValue()); + ecmaRuntimeCallInfo->SetCallArg(0, JSTaggedValue(static_cast(100))); + + [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread, ecmaRuntimeCallInfo.get()); + JSTaggedValue result1 = BuiltinsDate::SetMilliseconds(ecmaRuntimeCallInfo.get()); + ASSERT_EQ(result1.GetRawData(), JSTaggedValue(static_cast(100)).GetRawData()); + + JSTaggedValue result2 = BuiltinsDate::GetMilliseconds(ecmaRuntimeCallInfo.get()); + ASSERT_EQ(result2.GetRawData(), JSTaggedValue(static_cast(100)).GetRawData()); +} + +TEST_F(BuiltinsDateTest, SetGetUTCMilliseconds) +{ + JSHandle jsDate = JSDateCreateTest(thread); + + auto ecmaRuntimeCallInfo = CreateAndSetRuntimeCallInfo(thread, 6, jsDate.GetTaggedValue()); + ecmaRuntimeCallInfo->SetCallArg(0, JSTaggedValue(static_cast(100))); + + [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread, ecmaRuntimeCallInfo.get()); + JSTaggedValue result3 = BuiltinsDate::SetUTCMilliseconds(ecmaRuntimeCallInfo.get()); + ASSERT_EQ(result3.GetRawData(), JSTaggedValue(static_cast(100)).GetRawData()); + + JSTaggedValue result4 = BuiltinsDate::GetUTCMilliseconds(ecmaRuntimeCallInfo.get()); + ASSERT_EQ(result4.GetRawData(), JSTaggedValue(static_cast(100)).GetRawData()); +} + +TEST_F(BuiltinsDateTest, SetGetMinutes) +{ + JSHandle jsDate = JSDateCreateTest(thread); + + auto ecmaRuntimeCallInfo = CreateAndSetRuntimeCallInfo(thread, 10, jsDate.GetTaggedValue()); + ecmaRuntimeCallInfo->SetCallArg(0, JSTaggedValue(static_cast(10))); + ecmaRuntimeCallInfo->SetCallArg(1, JSTaggedValue(static_cast(6))); + ecmaRuntimeCallInfo->SetCallArg(2, JSTaggedValue(static_cast(111))); + + [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread, ecmaRuntimeCallInfo.get()); + BuiltinsDate::SetMinutes(ecmaRuntimeCallInfo.get()); + JSTaggedValue result1 = BuiltinsDate::GetMinutes(ecmaRuntimeCallInfo.get()); + ASSERT_EQ(result1.GetRawData(), JSTaggedValue(static_cast(10)).GetRawData()); + + JSTaggedValue result2 = BuiltinsDate::GetSeconds(ecmaRuntimeCallInfo.get()); + ASSERT_EQ(result2.GetRawData(), JSTaggedValue(static_cast(6)).GetRawData()); + + JSTaggedValue result3 = BuiltinsDate::GetMilliseconds(ecmaRuntimeCallInfo.get()); + ASSERT_EQ(result3.GetRawData(), JSTaggedValue(static_cast(111)).GetRawData()); +} + +TEST_F(BuiltinsDateTest, SetGetUTCMinutes) +{ + JSHandle jsDate = JSDateCreateTest(thread); + + auto ecmaRuntimeCallInfo = CreateAndSetRuntimeCallInfo(thread, 10, jsDate.GetTaggedValue()); + ecmaRuntimeCallInfo->SetCallArg(0, JSTaggedValue(static_cast(10))); + ecmaRuntimeCallInfo->SetCallArg(1, JSTaggedValue(static_cast(6))); + ecmaRuntimeCallInfo->SetCallArg(2, JSTaggedValue(static_cast(111))); + + [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread, ecmaRuntimeCallInfo.get()); + BuiltinsDate::SetUTCMinutes(ecmaRuntimeCallInfo.get()); + JSTaggedValue result4 = BuiltinsDate::GetUTCMinutes(ecmaRuntimeCallInfo.get()); + ASSERT_EQ(result4.GetRawData(), JSTaggedValue(static_cast(10)).GetRawData()); + + JSTaggedValue result5 = BuiltinsDate::GetUTCSeconds(ecmaRuntimeCallInfo.get()); + ASSERT_EQ(result5.GetRawData(), JSTaggedValue(static_cast(6)).GetRawData()); + + JSTaggedValue result6 = BuiltinsDate::GetUTCMilliseconds(ecmaRuntimeCallInfo.get()); + ASSERT_EQ(result6.GetRawData(), JSTaggedValue(static_cast(111)).GetRawData()); +} + +TEST_F(BuiltinsDateTest, SetGetMonth) +{ + JSHandle jsDate = JSDateCreateTest(thread); + + auto ecmaRuntimeCallInfo = CreateAndSetRuntimeCallInfo(thread, 8, jsDate.GetTaggedValue()); + ecmaRuntimeCallInfo->SetCallArg(0, JSTaggedValue(static_cast(8))); + ecmaRuntimeCallInfo->SetCallArg(1, JSTaggedValue(static_cast(3))); + + [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread, ecmaRuntimeCallInfo.get()); + BuiltinsDate::SetMonth(ecmaRuntimeCallInfo.get()); + JSTaggedValue result1 = BuiltinsDate::GetMonth(ecmaRuntimeCallInfo.get()); + ASSERT_EQ(result1.GetRawData(), JSTaggedValue(static_cast(8)).GetRawData()); + + JSTaggedValue result2 = BuiltinsDate::GetDate(ecmaRuntimeCallInfo.get()); + ASSERT_EQ(result2.GetRawData(), JSTaggedValue(static_cast(3)).GetRawData()); +} + +TEST_F(BuiltinsDateTest, SetGetUTCMonth) +{ + JSHandle jsDate = JSDateCreateTest(thread); + + auto ecmaRuntimeCallInfo = CreateAndSetRuntimeCallInfo(thread, 8, jsDate.GetTaggedValue()); + ecmaRuntimeCallInfo->SetCallArg(0, JSTaggedValue(static_cast(8))); + ecmaRuntimeCallInfo->SetCallArg(1, JSTaggedValue(static_cast(3))); + + [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread, ecmaRuntimeCallInfo.get()); + BuiltinsDate::SetUTCMonth(ecmaRuntimeCallInfo.get()); + JSTaggedValue result3 = BuiltinsDate::GetUTCMonth(ecmaRuntimeCallInfo.get()); + ASSERT_EQ(result3.GetRawData(), JSTaggedValue(static_cast(8)).GetRawData()); + + JSTaggedValue result4 = BuiltinsDate::GetUTCDate(ecmaRuntimeCallInfo.get()); + ASSERT_EQ(result4.GetRawData(), JSTaggedValue(static_cast(3)).GetRawData()); +} + +TEST_F(BuiltinsDateTest, SetGetSeconds) +{ + JSHandle jsDate = JSDateCreateTest(thread); + + auto ecmaRuntimeCallInfo = CreateAndSetRuntimeCallInfo(thread, 8, jsDate.GetTaggedValue()); + ecmaRuntimeCallInfo->SetCallArg(0, JSTaggedValue(static_cast(59))); + ecmaRuntimeCallInfo->SetCallArg(1, JSTaggedValue(static_cast(123))); + + [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread, ecmaRuntimeCallInfo.get()); + BuiltinsDate::SetSeconds(ecmaRuntimeCallInfo.get()); + JSTaggedValue result1 = BuiltinsDate::GetSeconds(ecmaRuntimeCallInfo.get()); + ASSERT_EQ(result1.GetRawData(), JSTaggedValue(static_cast(59)).GetRawData()); + + JSTaggedValue result2 = BuiltinsDate::GetMilliseconds(ecmaRuntimeCallInfo.get()); + ASSERT_EQ(result2.GetRawData(), JSTaggedValue(static_cast(123)).GetRawData()); +} + +TEST_F(BuiltinsDateTest, SetGetUTCSeconds) +{ + JSHandle jsDate = JSDateCreateTest(thread); + + auto ecmaRuntimeCallInfo = CreateAndSetRuntimeCallInfo(thread, 8, jsDate.GetTaggedValue()); + ecmaRuntimeCallInfo->SetCallArg(0, JSTaggedValue(static_cast(59))); + ecmaRuntimeCallInfo->SetCallArg(1, JSTaggedValue(static_cast(123))); + + [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread, ecmaRuntimeCallInfo.get()); + BuiltinsDate::SetUTCSeconds(ecmaRuntimeCallInfo.get()); + JSTaggedValue result3 = BuiltinsDate::GetUTCSeconds(ecmaRuntimeCallInfo.get()); + ASSERT_EQ(result3.GetRawData(), JSTaggedValue(static_cast(59)).GetRawData()); + + JSTaggedValue result4 = BuiltinsDate::GetUTCMilliseconds(ecmaRuntimeCallInfo.get()); + ASSERT_EQ(result4.GetRawData(), JSTaggedValue(static_cast(123)).GetRawData()); +} + +TEST_F(BuiltinsDateTest, SetGetTime) +{ + JSHandle jsDate = JSDateCreateTest(thread); + + auto ecmaRuntimeCallInfo = CreateAndSetRuntimeCallInfo(thread, 6, jsDate.GetTaggedValue()); + ecmaRuntimeCallInfo->SetCallArg(0, JSTaggedValue(static_cast(2))); + + [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread, ecmaRuntimeCallInfo.get()); + JSTaggedValue result1 = BuiltinsDate::SetTime(ecmaRuntimeCallInfo.get()); + ASSERT_EQ(result1.GetRawData(), JSTaggedValue(static_cast(2)).GetRawData()); + + JSTaggedValue result2 = BuiltinsDate::GetTime(ecmaRuntimeCallInfo.get()); + ASSERT_EQ(result2.GetRawData(), JSTaggedValue(static_cast(2)).GetRawData()); +} + +TEST_F(BuiltinsDateTest, UTC) +{ + auto ecmaRuntimeCallInfo = CreateAndSetRuntimeCallInfo(thread, 12, JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetCallArg(0, JSTaggedValue(2020.982)); + ecmaRuntimeCallInfo->SetCallArg(1, JSTaggedValue(10.23)); + ecmaRuntimeCallInfo->SetCallArg(2, JSTaggedValue(4.32)); + ecmaRuntimeCallInfo->SetCallArg(3, JSTaggedValue(11.32)); + + [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread, ecmaRuntimeCallInfo.get()); + JSTaggedValue result1 = BuiltinsDate::UTC(ecmaRuntimeCallInfo.get()); + TestHelper::TearDownFrame(thread, prev); + ASSERT_EQ(result1.GetRawData(), JSTaggedValue(static_cast(1604487600000)).GetRawData()); + + auto ecmaRuntimeCallInfo1 = CreateAndSetRuntimeCallInfo(thread, 18, JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo1->SetCallArg(0, JSTaggedValue(2020.982)); + ecmaRuntimeCallInfo1->SetCallArg(1, JSTaggedValue(10.23)); + ecmaRuntimeCallInfo1->SetCallArg(2, JSTaggedValue(4.32)); + ecmaRuntimeCallInfo1->SetCallArg(3, JSTaggedValue(11.32)); + ecmaRuntimeCallInfo1->SetCallArg(4, JSTaggedValue(45.1)); + ecmaRuntimeCallInfo1->SetCallArg(5, JSTaggedValue(34.321)); + ecmaRuntimeCallInfo1->SetCallArg(6, JSTaggedValue(static_cast(231))); + + prev = TestHelper::SetupFrame(thread, ecmaRuntimeCallInfo1.get()); + result1 = BuiltinsDate::UTC(ecmaRuntimeCallInfo1.get()); + TestHelper::TearDownFrame(thread, prev); + ASSERT_EQ(result1.GetRawData(), JSTaggedValue(static_cast(1604490334231)).GetRawData()); + + auto ecmaRuntimeCallInfo2 = CreateAndSetRuntimeCallInfo(thread, 10, JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo2->SetCallArg(0, JSTaggedValue(10.23)); + ecmaRuntimeCallInfo2->SetCallArg(1, JSTaggedValue(4.32)); + ecmaRuntimeCallInfo2->SetCallArg(2, JSTaggedValue(11.32)); + + prev = TestHelper::SetupFrame(thread, ecmaRuntimeCallInfo2.get()); + result1 = BuiltinsDate::UTC(ecmaRuntimeCallInfo2.get()); + TestHelper::TearDownFrame(thread, prev); + ASSERT_EQ(result1.GetRawData(), JSTaggedValue(static_cast(-1882224000000)).GetRawData()); + + auto ecmaRuntimeCallInfo3 = CreateAndSetRuntimeCallInfo(thread, 6, JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo3->SetCallArg(0, JSTaggedValue(1994.982)); + + prev = TestHelper::SetupFrame(thread, ecmaRuntimeCallInfo3.get()); + result1 = BuiltinsDate::UTC(ecmaRuntimeCallInfo3.get()); + TestHelper::TearDownFrame(thread, prev); + ASSERT_EQ(result1.GetRawData(), JSTaggedValue(static_cast(757382400000)).GetRawData()); + + auto ecmaRuntimeCallInfo4 = CreateAndSetRuntimeCallInfo(thread, 6, JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo4->SetCallArg(0, JSTaggedValue(19999944.982)); + + prev = TestHelper::SetupFrame(thread, ecmaRuntimeCallInfo4.get()); + result1 = BuiltinsDate::UTC(ecmaRuntimeCallInfo4.get()); + TestHelper::TearDownFrame(thread, prev); + ASSERT_EQ(result1.GetRawData(), JSTaggedValue(static_cast(base::NAN_VALUE)).GetRawData()); +} + +void SetAllYearAndHours(JSThread *thread, const JSHandle &jsDate) +{ + auto ecmaRuntimeCallInfo = CreateAndSetRuntimeCallInfo(thread, 10, jsDate.GetTaggedValue()); + // 2018 : test case + ecmaRuntimeCallInfo->SetCallArg(0, JSTaggedValue(static_cast(2018))); + // 10 : test case + ecmaRuntimeCallInfo->SetCallArg(1, JSTaggedValue(static_cast(10))); + // 2, 6 : test case + ecmaRuntimeCallInfo->SetCallArg(2, JSTaggedValue(static_cast(6))); + + [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread, ecmaRuntimeCallInfo.get()); + BuiltinsDate::SetFullYear(ecmaRuntimeCallInfo.get()); + TestHelper::TearDownFrame(thread, prev); + + auto ecmaRuntimeCallInfo1 = CreateAndSetRuntimeCallInfo(thread, 12, jsDate.GetTaggedValue()); + // 18 : test case + ecmaRuntimeCallInfo1->SetCallArg(0, JSTaggedValue(static_cast(18))); + // 10 : test case + ecmaRuntimeCallInfo1->SetCallArg(1, JSTaggedValue(static_cast(10))); + // 2, 6 : test case + ecmaRuntimeCallInfo1->SetCallArg(2, JSTaggedValue(static_cast(6))); + // 3, 111 : test case + ecmaRuntimeCallInfo1->SetCallArg(3, JSTaggedValue(static_cast(111))); + + prev = TestHelper::SetupFrame(thread, ecmaRuntimeCallInfo1.get()); + BuiltinsDate::SetHours(ecmaRuntimeCallInfo1.get()); + TestHelper::TearDownFrame(thread, prev); +} + +void SetAll1(JSThread *thread, const JSHandle &jsDate) +{ + auto ecmaRuntimeCallInfo = CreateAndSetRuntimeCallInfo(thread, 10, jsDate.GetTaggedValue()); + // 1900 : test case + ecmaRuntimeCallInfo->SetCallArg(0, JSTaggedValue(static_cast(1900))); + // 11 : test case + ecmaRuntimeCallInfo->SetCallArg(1, JSTaggedValue(static_cast(11))); + // 2, 31 : test case + ecmaRuntimeCallInfo->SetCallArg(2, JSTaggedValue(static_cast(31))); + + [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread, ecmaRuntimeCallInfo.get()); + BuiltinsDate::SetFullYear(ecmaRuntimeCallInfo.get()); + TestHelper::TearDownFrame(thread, prev); + + auto ecmaRuntimeCallInfo1 = CreateAndSetRuntimeCallInfo(thread, 12, jsDate.GetTaggedValue()); + // 23 : test case + ecmaRuntimeCallInfo1->SetCallArg(0, JSTaggedValue(static_cast(23))); + // 54 : test case + ecmaRuntimeCallInfo1->SetCallArg(1, JSTaggedValue(static_cast(54))); + // 2, 16 : test case + ecmaRuntimeCallInfo1->SetCallArg(2, JSTaggedValue(static_cast(16))); + // 3, 888 : test case + ecmaRuntimeCallInfo1->SetCallArg(3, JSTaggedValue(static_cast(888))); + + prev = TestHelper::SetupFrame(thread, ecmaRuntimeCallInfo1.get()); + BuiltinsDate::SetHours(ecmaRuntimeCallInfo1.get()); + TestHelper::TearDownFrame(thread, prev); +} + +void SetAll2(JSThread *thread, const JSHandle &jsDate) +{ + auto ecmaRuntimeCallInfo = CreateAndSetRuntimeCallInfo(thread, 10, jsDate.GetTaggedValue()); + ecmaRuntimeCallInfo->SetCallArg(0, JSTaggedValue(static_cast(1901))); // 1901 : test case + ecmaRuntimeCallInfo->SetCallArg(1, JSTaggedValue(static_cast(0))); + ecmaRuntimeCallInfo->SetCallArg(2, JSTaggedValue(static_cast(1))); // 2 : test case + + [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread, ecmaRuntimeCallInfo.get()); + BuiltinsDate::SetFullYear(ecmaRuntimeCallInfo.get()); + TestHelper::TearDownFrame(thread, prev); + + // 12 : test case + auto ecmaRuntimeCallInfo1 = CreateAndSetRuntimeCallInfo(thread, 12, jsDate.GetTaggedValue()); + ecmaRuntimeCallInfo1->SetCallArg(0, JSTaggedValue(static_cast(0))); + ecmaRuntimeCallInfo1->SetCallArg(1, JSTaggedValue(static_cast(3))); // 3 : test case + ecmaRuntimeCallInfo1->SetCallArg(2, JSTaggedValue(static_cast(21))); // 2, 21 : test case + ecmaRuntimeCallInfo1->SetCallArg(3, JSTaggedValue(static_cast(129))); // 3, 129 : test case + + prev = TestHelper::SetupFrame(thread, ecmaRuntimeCallInfo1.get()); + BuiltinsDate::SetHours(ecmaRuntimeCallInfo1.get()); + TestHelper::TearDownFrame(thread, prev); +} + +TEST_F(BuiltinsDateTest, parse) +{ + JSHandle str = + thread->GetEcmaVM()->GetFactory()->NewFromCanBeCompressString("2020-11-19T12:18:18.132Z"); + auto ecmaRuntimeCallInfo = CreateAndSetRuntimeCallInfo(thread, 6, JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetCallArg(0, str.GetTaggedValue()); + + [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread, ecmaRuntimeCallInfo.get()); + JSTaggedValue result1 = BuiltinsDate::Parse(ecmaRuntimeCallInfo.get()); + ASSERT_EQ(result1.GetRawData(), JSTaggedValue(static_cast(1605788298132)).GetRawData()); + + JSHandle str1 = thread->GetEcmaVM()->GetFactory()->NewFromCanBeCompressString("2020-11-19Z"); + auto ecmaRuntimeCallInfo1 = CreateAndSetRuntimeCallInfo(thread, 6, JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo1->SetCallArg(0, str1.GetTaggedValue()); + + prev = TestHelper::SetupFrame(thread, ecmaRuntimeCallInfo1.get()); + result1 = BuiltinsDate::Parse(ecmaRuntimeCallInfo1.get()); + TestHelper::TearDownFrame(thread, prev); + ASSERT_EQ(result1.GetRawData(), JSTaggedValue(static_cast(1605744000000)).GetRawData()); + + JSHandle str2 = + thread->GetEcmaVM()->GetFactory()->NewFromCanBeCompressString("2020-11T12:18:17.231+08:00"); + auto ecmaRuntimeCallInfo2 = CreateAndSetRuntimeCallInfo(thread, 6, JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo2->SetCallArg(0, str2.GetTaggedValue()); + + prev = TestHelper::SetupFrame(thread, ecmaRuntimeCallInfo2.get()); + result1 = BuiltinsDate::Parse(ecmaRuntimeCallInfo2.get()); + TestHelper::TearDownFrame(thread, prev); + ASSERT_EQ(result1.GetRawData(), JSTaggedValue(static_cast(1604204297231)).GetRawData()); + + JSHandle str3 = + thread->GetEcmaVM()->GetFactory()->NewFromCanBeCompressString("Thu Nov 19 2020 20:18:18 GMT+0800"); + auto ecmaRuntimeCallInfo3 = CreateAndSetRuntimeCallInfo(thread, 6, JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo3->SetCallArg(0, str3.GetTaggedValue()); + + prev = TestHelper::SetupFrame(thread, ecmaRuntimeCallInfo3.get()); + result1 = BuiltinsDate::Parse(ecmaRuntimeCallInfo3.get()); + TestHelper::TearDownFrame(thread, prev); + ASSERT_EQ(result1.GetRawData(), JSTaggedValue(static_cast(1605788298000)).GetRawData()); + + JSHandle str4 = + thread->GetEcmaVM()->GetFactory()->NewFromCanBeCompressString("Thu 03 Jun 2093 04:18 GMT"); + auto ecmaRuntimeCallInfo4 = CreateAndSetRuntimeCallInfo(thread, 6, JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo4->SetCallArg(0, str4.GetTaggedValue()); + + prev = TestHelper::SetupFrame(thread, ecmaRuntimeCallInfo4.get()); + result1 = BuiltinsDate::Parse(ecmaRuntimeCallInfo4.get()); + TestHelper::TearDownFrame(thread, prev); + ASSERT_EQ(result1.GetRawData(), JSTaggedValue(static_cast(3894841080000)).GetRawData()); + + auto ecmaRuntimeCallInfo5 = CreateAndSetRuntimeCallInfo(thread, 6, JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo5->SetCallArg(0, JSTaggedValue::Null()); + + prev = TestHelper::SetupFrame(thread, ecmaRuntimeCallInfo5.get()); + result1 = BuiltinsDate::Parse(ecmaRuntimeCallInfo5.get()); + TestHelper::TearDownFrame(thread, prev); + ASSERT_EQ(result1.GetRawData(), JSTaggedValue(static_cast(base::NAN_VALUE)).GetRawData()); +} + +TEST_F(BuiltinsDateTest, ToDateString) +{ + JSHandle expect_value = + thread->GetEcmaVM()->GetFactory()->NewFromCanBeCompressString("Tue Nov 06 2018"); + JSHandle jsDate = JSDateCreateTest(thread); + SetAllYearAndHours(thread, jsDate); + + auto ecmaRuntimeCallInfo = CreateAndSetRuntimeCallInfo(thread, 4, jsDate.GetTaggedValue()); + + [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread, ecmaRuntimeCallInfo.get()); + JSTaggedValue result = BuiltinsDate::ToDateString(ecmaRuntimeCallInfo.get()); + TestHelper::TearDownFrame(thread, prev); + + ASSERT_TRUE(result.IsString()); + ASSERT_TRUE(EcmaString::StringsAreEqual(reinterpret_cast(result.GetRawData()), *expect_value)); +} + +TEST_F(BuiltinsDateTest, ToISOString) +{ + JSHandle expect_value = + thread->GetEcmaVM()->GetFactory()->NewFromCanBeCompressString("2020-11-19T12:18:18.132Z"); + JSHandle jsDate = JSDateCreateTest(thread); + JSDate::Cast(jsDate.GetTaggedValue().GetTaggedObject())->SetTimeValue(thread, JSTaggedValue(1605788298132.0)); + auto ecmaRuntimeCallInfo = CreateAndSetRuntimeCallInfo(thread, 4, jsDate.GetTaggedValue()); + + [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread, ecmaRuntimeCallInfo.get()); + JSTaggedValue result1 = BuiltinsDate::ToISOString(ecmaRuntimeCallInfo.get()); + TestHelper::TearDownFrame(thread, prev); + ASSERT_TRUE(result1.IsString()); + ASSERT_TRUE(EcmaString::StringsAreEqual(reinterpret_cast(result1.GetRawData()), *expect_value)); +} + +TEST_F(BuiltinsDateTest, ToISOStringMinus) +{ + JSHandle expect_value = + thread->GetEcmaVM()->GetFactory()->NewFromCanBeCompressString("1831-12-02T21:47:18.382Z"); + JSHandle jsDate = JSDateCreateTest(thread); + JSDate::Cast(jsDate.GetTaggedValue().GetTaggedObject())->SetTimeValue(thread, JSTaggedValue(-4357419161618.0)); + + auto ecmaRuntimeCallInfo = CreateAndSetRuntimeCallInfo(thread, 4, jsDate.GetTaggedValue()); + + [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread, ecmaRuntimeCallInfo.get()); + JSTaggedValue result1 = BuiltinsDate::ToISOString(ecmaRuntimeCallInfo.get()); + TestHelper::TearDownFrame(thread, prev); + ASSERT_TRUE(result1.IsString()); + ASSERT_TRUE(EcmaString::StringsAreEqual(reinterpret_cast(result1.GetRawData()), *expect_value)); +} + +// test toJSON and toPrimitive +TEST_F(BuiltinsDateTest, ToJSON) +{ + JSHandle expect_value = + thread->GetEcmaVM()->GetFactory()->NewFromCanBeCompressString("2020-11-19T12:18:18.132Z"); + JSHandle jsDate = JSDateCreateTest(thread); + jsDate->SetTimeValue(thread, JSTaggedValue(1605788298132.0)); + auto ecmaRuntimeCallInfo = CreateAndSetRuntimeCallInfo(thread, 4, jsDate.GetTaggedValue()); + + [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread, ecmaRuntimeCallInfo.get()); + JSTaggedValue result1 = BuiltinsDate::ToJSON(ecmaRuntimeCallInfo.get()); + TestHelper::TearDownFrame(thread, prev); + ASSERT_TRUE(result1.IsString()); + ASSERT_TRUE(EcmaString::StringsAreEqual(reinterpret_cast(result1.GetRawData()), *expect_value)); +} + +TEST_F(BuiltinsDateTest, ToJSONMinus) +{ + JSHandle expect_value = + thread->GetEcmaVM()->GetFactory()->NewFromCanBeCompressString("1831-12-02T21:47:18.382Z"); + JSHandle jsDate = JSDateCreateTest(thread); + jsDate->SetTimeValue(thread, JSTaggedValue(-4357419161618.0)); + auto ecmaRuntimeCallInfo = CreateAndSetRuntimeCallInfo(thread, 4, jsDate.GetTaggedValue()); + + [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread, ecmaRuntimeCallInfo.get()); + JSTaggedValue result1 = BuiltinsDate::ToJSON(ecmaRuntimeCallInfo.get()); + ASSERT_TRUE(result1.IsString()); + ASSERT_TRUE(EcmaString::StringsAreEqual(reinterpret_cast(result1.GetRawData()), *expect_value)); +} + +static CString GetLocalTime(JSHandle jsDate, int64_t localMin) +{ + CString localTime = ""; + localMin = JSDate::GetLocalOffsetFromOS(localMin, true); + if (static_cast(JSDate::Cast(jsDate.GetTaggedValue().GetTaggedObject())->GetTimeValue().GetDouble()) < + CHINA_BEFORE_1900_MS && + localMin == CHINA_AFTER_1901_MIN) { + localMin = CHINA_BEFORE_1901_MIN; + } + if (localMin >= 0) { + localTime += PLUS; + } else if (localMin < 0) { + localTime += NEG; + localMin = -localMin; + } + localTime = localTime + JSDate::StrToTargetLength(ToCString(localMin / MINUTE_PER_HOUR), STR_LENGTH_OTHERS); + return localTime + JSDate::StrToTargetLength(ToCString(localMin % MINUTE_PER_HOUR), STR_LENGTH_OTHERS); +} + +TEST_F(BuiltinsDateTest, ToString) +{ + int localMin = 0; + CString localTime; + + JSHandle jsDate = JSDateCreateTest(thread); + auto ecmaRuntimeCallInfo = CreateAndSetRuntimeCallInfo(thread, 4, jsDate.GetTaggedValue()); + + [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread, ecmaRuntimeCallInfo.get()); + + SetAllYearAndHours(thread, jsDate); + localTime = GetLocalTime(jsDate, localMin); + JSTaggedValue result1 = BuiltinsDate::ToString(ecmaRuntimeCallInfo.get()); + ASSERT_TRUE(result1.IsString()); + TestHelper::TearDownFrame(thread, prev); + JSHandle result1_val(thread, reinterpret_cast(result1.GetRawData())); + CString str = "Tue Nov 06 2018 18:10:06 GMT" + localTime; + JSHandle str_handle = thread->GetEcmaVM()->GetFactory()->NewFromCanBeCompressString(str); + ASSERT_TRUE(EcmaString::StringsAreEqual(*result1_val, *str_handle)); + + JSHandle js_date1 = JSDateCreateTest(thread); + auto ecmaRuntimeCallInfo1 = CreateAndSetRuntimeCallInfo(thread, 4, js_date1.GetTaggedValue()); + prev = TestHelper::SetupFrame(thread, ecmaRuntimeCallInfo1.get()); + + SetAll1(thread, js_date1); + localTime = GetLocalTime(js_date1, localMin); + JSTaggedValue result2 = BuiltinsDate::ToString(ecmaRuntimeCallInfo1.get()); + ASSERT_TRUE(result2.IsString()); + TestHelper::TearDownFrame(thread, prev); + JSHandle result2_val(thread, reinterpret_cast(result2.GetRawData())); + str = "Mon Dec 31 1900 23:54:16 GMT" + localTime; + str_handle = thread->GetEcmaVM()->GetFactory()->NewFromCanBeCompressString(str); + ASSERT_TRUE(EcmaString::StringsAreEqual(*result2_val, *str_handle)); + + JSHandle js_date2 = JSDateCreateTest(thread); + auto ecmaRuntimeCallInfo2 = CreateAndSetRuntimeCallInfo(thread, 4, js_date2.GetTaggedValue()); + prev = TestHelper::SetupFrame(thread, ecmaRuntimeCallInfo2.get()); + + SetAll2(thread, js_date2); + localTime = GetLocalTime(jsDate, localMin); + JSTaggedValue result3 = BuiltinsDate::ToString(ecmaRuntimeCallInfo2.get()); + ASSERT_TRUE(result3.IsString()); + TestHelper::TearDownFrame(thread, prev); + JSHandle result3_val(thread, reinterpret_cast(result3.GetRawData())); + str = "Tue Jan 01 1901 00:03:21 GMT" + localTime; + str_handle = thread->GetEcmaVM()->GetFactory()->NewFromCanBeCompressString(str); + ASSERT_TRUE(EcmaString::StringsAreEqual(*result3_val, *str_handle)); +} + +TEST_F(BuiltinsDateTest, ToTimeString) +{ + int localMin = 0; + CString localTime; + + JSHandle jsDate = JSDateCreateTest(thread); + auto ecmaRuntimeCallInfo = CreateAndSetRuntimeCallInfo(thread, 4, jsDate.GetTaggedValue()); + + [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread, ecmaRuntimeCallInfo.get()); + + SetAllYearAndHours(thread, jsDate); + localTime = GetLocalTime(jsDate, localMin); + JSTaggedValue result1 = BuiltinsDate::ToTimeString(ecmaRuntimeCallInfo.get()); + ASSERT_TRUE(result1.IsString()); + JSHandle result1_val(thread, reinterpret_cast(result1.GetRawData())); + CString str = "18:10:06 GMT" + localTime; + JSHandle str_handle = thread->GetEcmaVM()->GetFactory()->NewFromCanBeCompressString(str); + ASSERT_TRUE(EcmaString::StringsAreEqual(*result1_val, *str_handle)); + + JSHandle js_date1 = JSDateCreateTest(thread); + auto ecmaRuntimeCallInfo1 = CreateAndSetRuntimeCallInfo(thread, 4, js_date1.GetTaggedValue()); + prev = TestHelper::SetupFrame(thread, ecmaRuntimeCallInfo1.get()); + SetAll1(thread, js_date1); + localTime = GetLocalTime(js_date1, localMin); + JSTaggedValue result2 = BuiltinsDate::ToTimeString(ecmaRuntimeCallInfo1.get()); + ASSERT_TRUE(result2.IsString()); + JSHandle result2_val(thread, reinterpret_cast(result2.GetRawData())); + str = "23:54:16 GMT" + localTime; + str_handle = thread->GetEcmaVM()->GetFactory()->NewFromCanBeCompressString(str); + ASSERT_TRUE(EcmaString::StringsAreEqual(*result2_val, *str_handle)); + JSHandle js_date2 = JSDateCreateTest(thread); + auto ecmaRuntimeCallInfo2 = CreateAndSetRuntimeCallInfo(thread, 4, js_date2.GetTaggedValue()); + prev = TestHelper::SetupFrame(thread, ecmaRuntimeCallInfo2.get()); + SetAll2(thread, js_date2); + localTime = GetLocalTime(jsDate, localMin); + JSTaggedValue result3 = BuiltinsDate::ToTimeString(ecmaRuntimeCallInfo2.get()); + ASSERT_TRUE(result3.IsString()); + JSHandle result3_val(thread, reinterpret_cast(result3.GetRawData())); + str = "00:03:21 GMT" + localTime; + str_handle = thread->GetEcmaVM()->GetFactory()->NewFromCanBeCompressString(str); + ASSERT_TRUE(EcmaString::StringsAreEqual(*result3_val, *str_handle)); +} + +TEST_F(BuiltinsDateTest, ToUTCString) +{ + JSHandle expect_value = + thread->GetEcmaVM()->GetFactory()->NewFromCanBeCompressString("Thu, 19 Nov 2020 12:18:18 GMT"); + JSHandle jsDate = JSDateCreateTest(thread); + JSDate::Cast(jsDate.GetTaggedValue().GetTaggedObject())->SetTimeValue(thread, JSTaggedValue(1605788298132.0)); + auto ecmaRuntimeCallInfo = CreateAndSetRuntimeCallInfo(thread, 4, jsDate.GetTaggedValue()); + + [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread, ecmaRuntimeCallInfo.get()); + JSTaggedValue result1 = BuiltinsDate::ToUTCString(ecmaRuntimeCallInfo.get()); + ASSERT_TRUE(result1.IsString()); + ASSERT_TRUE(EcmaString::StringsAreEqual(reinterpret_cast(result1.GetRawData()), *expect_value)); +} + +TEST_F(BuiltinsDateTest, ToUTCStringMinus) +{ + JSHandle expect_value = + thread->GetEcmaVM()->GetFactory()->NewFromCanBeCompressString("Fri, 02 Dec 1831 21:47:18 GMT"); + JSHandle jsDate = JSDateCreateTest(thread); + JSDate::Cast(jsDate.GetTaggedValue().GetTaggedObject())->SetTimeValue(thread, JSTaggedValue(-4357419161618.0)); + auto ecmaRuntimeCallInfo = CreateAndSetRuntimeCallInfo(thread, 4, jsDate.GetTaggedValue()); + + [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread, ecmaRuntimeCallInfo.get()); + JSTaggedValue result1 = BuiltinsDate::ToUTCString(ecmaRuntimeCallInfo.get()); + ASSERT_TRUE(result1.IsString()); + ASSERT_TRUE(EcmaString::StringsAreEqual(reinterpret_cast(result1.GetRawData()), *expect_value)); +} + +TEST_F(BuiltinsDateTest, ValueOf) +{ + JSHandle jsDate = JSDateCreateTest(thread); + JSDate::Cast(jsDate.GetTaggedValue().GetTaggedObject())->SetTimeValue(thread, JSTaggedValue(1605788298132.0)); + auto ecmaRuntimeCallInfo = CreateAndSetRuntimeCallInfo(thread, 4, jsDate.GetTaggedValue()); + + [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread, ecmaRuntimeCallInfo.get()); + JSTaggedValue result1 = BuiltinsDate::ValueOf(ecmaRuntimeCallInfo.get()); + ASSERT_EQ(result1.GetRawData(), JSTaggedValue(static_cast(1605788298132)).GetRawData()); +} + +TEST_F(BuiltinsDateTest, ValueOfMinus) +{ + JSHandle jsDate = JSDateCreateTest(thread); + JSDate::Cast(jsDate.GetTaggedValue().GetTaggedObject())->SetTimeValue(thread, JSTaggedValue(-4357419161618.0)); + auto ecmaRuntimeCallInfo = CreateAndSetRuntimeCallInfo(thread, 4, jsDate.GetTaggedValue()); + + [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread, ecmaRuntimeCallInfo.get()); + JSTaggedValue result1 = BuiltinsDate::ValueOf(ecmaRuntimeCallInfo.get()); + ASSERT_EQ(result1.GetRawData(), JSTaggedValue(static_cast(-4357419161618)).GetRawData()); +} + +TEST_F(BuiltinsDateTest, DateConstructor) +{ + // case1: test new target is undefined. + JSHandle jsDate = JSDateCreateTest(thread); + JSHandle globalEnv = thread->GetEcmaVM()->GetGlobalEnv(); + JSHandle date_func(globalEnv->GetDateFunction()); + auto ecmaRuntimeCallInfo1 = CreateAndSetRuntimeCallInfo(thread, 4, JSTaggedValue::Undefined()); + + [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread, ecmaRuntimeCallInfo1.get()); + JSTaggedValue result1 = BuiltinsDate::DateConstructor(ecmaRuntimeCallInfo1.get()); + TestHelper::TearDownFrame(thread, prev); + ASSERT_TRUE(result1.IsString()); + + // case2: length == 0 + auto ecmaRuntimeCallInfo2 = TestHelper::CreateEcmaRuntimeCallInfo(thread, jsDate.GetTaggedValue(), 4); + ecmaRuntimeCallInfo2->SetFunction(date_func.GetTaggedValue()); + ecmaRuntimeCallInfo2->SetThis(jsDate.GetTaggedValue()); + + prev = TestHelper::SetupFrame(thread, ecmaRuntimeCallInfo2.get()); + JSTaggedValue result2 = BuiltinsDate::DateConstructor(ecmaRuntimeCallInfo2.get()); + TestHelper::TearDownFrame(thread, prev); + ASSERT_TRUE(result2.IsObject()); + + // case3: length == 1 + auto ecmaRuntimeCallInfo3 = TestHelper::CreateEcmaRuntimeCallInfo(thread, jsDate.GetTaggedValue(), 6); + ecmaRuntimeCallInfo3->SetFunction(date_func.GetTaggedValue()); + ecmaRuntimeCallInfo3->SetThis(jsDate.GetTaggedValue()); + ecmaRuntimeCallInfo3->SetCallArg(0, JSTaggedValue(static_cast(2018))); + + prev = TestHelper::SetupFrame(thread, ecmaRuntimeCallInfo3.get()); + JSTaggedValue result3 = BuiltinsDate::DateConstructor(ecmaRuntimeCallInfo3.get()); + TestHelper::TearDownFrame(thread, prev); + ASSERT_TRUE(result3.IsObject()); + + BuiltinsDate::SetFullYear(ecmaRuntimeCallInfo3.get()); + JSTaggedValue result4 = BuiltinsDate::GetFullYear(ecmaRuntimeCallInfo3.get()); + ASSERT_EQ(result4.GetRawData(), JSTaggedValue(static_cast(2018)).GetRawData()); + + // case3: length > 1 + auto ecmaRuntimeCallInfo4 = TestHelper::CreateEcmaRuntimeCallInfo(thread, jsDate.GetTaggedValue(), 8); + ecmaRuntimeCallInfo4->SetFunction(date_func.GetTaggedValue()); + ecmaRuntimeCallInfo4->SetThis(jsDate.GetTaggedValue()); + ecmaRuntimeCallInfo4->SetCallArg(0, JSTaggedValue(static_cast(2018))); + ecmaRuntimeCallInfo4->SetCallArg(1, JSTaggedValue(static_cast(10))); + + prev = TestHelper::SetupFrame(thread, ecmaRuntimeCallInfo4.get()); + JSTaggedValue result5 = BuiltinsDate::DateConstructor(ecmaRuntimeCallInfo4.get()); + ASSERT_TRUE(result5.IsObject()); + + SetAllYearAndHours(thread, jsDate); + BuiltinsDate::SetFullYear(ecmaRuntimeCallInfo4.get()); + JSTaggedValue result6 = BuiltinsDate::GetFullYear(ecmaRuntimeCallInfo4.get()); + ASSERT_EQ(result6.GetRawData(), JSTaggedValue(static_cast(2018)).GetRawData()); + JSTaggedValue result7 = BuiltinsDate::GetMonth(ecmaRuntimeCallInfo4.get()); + ASSERT_EQ(result7.GetRawData(), JSTaggedValue(static_cast(10)).GetRawData()); +} +} // namespace panda::test diff --git a/tests/runtime/builtins/builtins_errors_test.cpp b/tests/runtime/builtins/builtins_errors_test.cpp new file mode 100644 index 000000000..6630318b5 --- /dev/null +++ b/tests/runtime/builtins/builtins_errors_test.cpp @@ -0,0 +1,1069 @@ +/* + * Copyright (c) 2021-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 "plugins/ecmascript/runtime/builtins/builtins_errors.h" + +#include "plugins/ecmascript/runtime/base/error_helper.h" +#include "plugins/ecmascript/runtime/ecma_string.h" +#include "plugins/ecmascript/runtime/ecma_vm.h" +#include "plugins/ecmascript/runtime/global_env.h" + +#include "plugins/ecmascript/runtime/js_handle.h" +#include "plugins/ecmascript/runtime/js_hclass.h" +#include "plugins/ecmascript/runtime/js_object-inl.h" +#include "plugins/ecmascript/runtime/js_tagged_value-inl.h" + +#include "plugins/ecmascript/runtime/js_thread.h" +#include "plugins/ecmascript/runtime/object_factory.h" +#include "plugins/ecmascript/tests/runtime/common/test_helper.h" +#include "utils/bit_utils.h" + +using namespace panda::ecmascript; +using namespace panda::ecmascript::builtins; + +namespace panda::test { +using Error = ecmascript::builtins::BuiltinsError; +using RangeError = builtins::BuiltinsRangeError; +using ReferenceError = builtins::BuiltinsReferenceError; +using TypeError = builtins::BuiltinsTypeError; +using URIError = builtins::BuiltinsURIError; +using EvalError = builtins::BuiltinsEvalError; +using SyntaxError = builtins::BuiltinsSyntaxError; +using JSType = ecmascript::JSType; + +class BuiltinsErrorsTest : public testing::Test { +public: + static void SetUpTestCase() + { + GTEST_LOG_(INFO) << "BuiltinsErrorsTest SetUpTestCase"; + } + + static void TearDownTestCase() + { + GTEST_LOG_(INFO) << "BuiltinsErrorsTest TearDownCase"; + } + + void SetUp() override + { + TestHelper::CreateEcmaVMWithScope(instance, thread, scope); + } + + void TearDown() override + { + TestHelper::DestroyEcmaVMWithScope(instance, scope); + } + + PandaVM *instance {nullptr}; + EcmaHandleScope *scope {nullptr}; + JSThread *thread {nullptr}; +}; + +/* + * @tc.name: GetJSErrorObject + * @tc.desc: get JSError Object + * @tc.type: FUNC + */ +TEST_F(BuiltinsErrorsTest, GetJSErrorObject) +{ + /** + * @tc.steps: step1. Create JSError object + */ + ObjectFactory *factory = EcmaVM::Cast(instance)->GetFactory(); + + JSHandle handleObj = factory->GetJSError(ErrorType::TYPE_ERROR); + JSHandle msgKey(factory->NewFromCanBeCompressString("message")); + JSHandle nameKey = thread->GlobalConstants()->GetHandledNameString(); + + /** + * @tc.steps: step2. obtain JSError object prototype chain name property and message property + */ + JSHandle msgValue( + JSObject::GetProperty(thread, JSHandle(handleObj), msgKey).GetValue()); + EXPECT_EQ(reinterpret_cast(msgValue->GetRawData()) + ->Compare(reinterpret_cast( + ecmascript::JSTaggedValue(*factory->NewFromCanBeCompressString("")).GetRawData())), + 0); + JSHandle nameValue( + JSObject::GetProperty(thread, JSHandle(handleObj), nameKey).GetValue()); + ASSERT_EQ(reinterpret_cast( + ecmascript::JSTaggedValue(*factory->NewFromCanBeCompressString("TypeError")).GetRawData()) + ->Compare(reinterpret_cast(nameValue->GetRawData())), + 0); +} + +/* + * @tc.name: GetJSErrorWithMessage + * @tc.desc: Obtains the TypeError object. + * @tc.type: FUNC + */ +TEST_F(BuiltinsErrorsTest, GetJSErrorWithMessage) +{ + ObjectFactory *factory = EcmaVM::Cast(instance)->GetFactory(); + + JSHandle handleObj = factory->GetJSError(ErrorType::TYPE_ERROR, "I am type error"); + JSHandle msgKey(factory->NewFromCanBeCompressString("message")); + JSHandle nameKey = thread->GlobalConstants()->GetHandledNameString(); + JSHandle msgValue( + JSObject::GetProperty(thread, JSHandle(handleObj), msgKey).GetValue()); + ASSERT_EQ(reinterpret_cast( + ecmascript::JSTaggedValue(*factory->NewFromCanBeCompressString("I am type error")).GetRawData()) + ->Compare(reinterpret_cast(msgValue->GetRawData())), + 0); + JSHandle nameValue( + JSObject::GetProperty(thread, JSHandle(handleObj), nameKey).GetValue()); + ASSERT_EQ(reinterpret_cast( + ecmascript::JSTaggedValue(*factory->NewFromCanBeCompressString("TypeError")).GetRawData()) + ->Compare(reinterpret_cast(nameValue->GetRawData())), + 0); +} + +/* + * @tc.name: ErrorNoParameterConstructor + * @tc.desc: new Error() + * @tc.type: FUNC + */ +TEST_F(BuiltinsErrorsTest, ErrorNoParameterConstructor) +{ + ObjectFactory *factory = EcmaVM::Cast(instance)->GetFactory(); + JSHandle env = EcmaVM::Cast(instance)->GetGlobalEnv(); + + JSHandle error(env->GetErrorFunction()); + + auto ecmaRuntimeCallInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread, JSTaggedValue(*error), 4); + ecmaRuntimeCallInfo->SetFunction(error.GetTaggedValue()); + ecmaRuntimeCallInfo->SetThis(JSTaggedValue(*error)); + + [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread, ecmaRuntimeCallInfo.get()); + JSTaggedValue result = Error::ErrorConstructor(ecmaRuntimeCallInfo.get()); + + EXPECT_TRUE(result.IsECMAObject()); + + JSHandle errorObject(thread, reinterpret_cast(result.GetRawData())); + JSHandle msgKey(factory->NewFromCanBeCompressString("message")); + JSHandle nameKey = thread->GlobalConstants()->GetHandledNameString(); + + JSHandle msgValue(JSObject::GetProperty(thread, errorObject, msgKey).GetValue()); + ASSERT_EQ( + reinterpret_cast(ecmascript::JSTaggedValue(*factory->NewFromCanBeCompressString("")).GetRawData()) + ->Compare(reinterpret_cast(msgValue->GetRawData())), + 0); + + JSHandle nameValue(JSObject::GetProperty(thread, errorObject, nameKey).GetValue()); + ASSERT_EQ(reinterpret_cast( + ecmascript::JSTaggedValue(*factory->NewFromCanBeCompressString("Error")).GetRawData()) + ->Compare(reinterpret_cast(nameValue->GetRawData())), + 0); +} + +/* + * @tc.name: ErrorParameterConstructor + * @tc.desc: new Error("Hello Error!") + * @tc.type: FUNC + */ +TEST_F(BuiltinsErrorsTest, ErrorParameterConstructor) +{ + ObjectFactory *factory = EcmaVM::Cast(instance)->GetFactory(); + JSHandle env = EcmaVM::Cast(instance)->GetGlobalEnv(); + + JSHandle error(env->GetErrorFunction()); + JSHandle paramMsg(factory->NewFromCanBeCompressString("Hello Error!")); + + auto ecmaRuntimeCallInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread, JSTaggedValue(*error), 6); + ecmaRuntimeCallInfo->SetFunction(error.GetTaggedValue()); + ecmaRuntimeCallInfo->SetThis(JSTaggedValue(*error)); + ecmaRuntimeCallInfo->SetCallArg(0, paramMsg.GetTaggedValue()); + + [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread, ecmaRuntimeCallInfo.get()); + JSTaggedValue result = Error::ErrorConstructor(ecmaRuntimeCallInfo.get()); + + EXPECT_TRUE(result.IsECMAObject()); + + JSHandle errorObject(thread, reinterpret_cast(result.GetRawData())); + JSHandle msgKey(factory->NewFromCanBeCompressString("message")); + JSHandle nameKey = thread->GlobalConstants()->GetHandledNameString(); + JSHandle msgValue(JSObject::GetProperty(thread, errorObject, msgKey).GetValue()); + + ASSERT_EQ(reinterpret_cast( + ecmascript::JSTaggedValue(*factory->NewFromCanBeCompressString("Hello Error!")).GetRawData()) + ->Compare(reinterpret_cast(msgValue->GetRawData())), + 0); + + JSHandle nameValue(JSObject::GetProperty(thread, errorObject, nameKey).GetValue()); + ASSERT_EQ(reinterpret_cast( + ecmascript::JSTaggedValue(*factory->NewFromCanBeCompressString("Error")).GetRawData()) + ->Compare(reinterpret_cast(nameValue->GetRawData())), + 0); +} + +/* + * @tc.name: ErrorNoParameterToString + * @tc.desc: new Error().toString() + * @tc.type: FUNC + */ +TEST_F(BuiltinsErrorsTest, ErrorNoParameterToString) +{ + ObjectFactory *factory = EcmaVM::Cast(instance)->GetFactory(); + JSHandle env = EcmaVM::Cast(instance)->GetGlobalEnv(); + + JSHandle errorObject = env->GetErrorFunction(); + JSHandle error = factory->NewJSObjectByConstructor(JSHandle(errorObject), errorObject); + + auto ecmaRuntimeCallInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread, JSTaggedValue::Undefined(), 4); + ecmaRuntimeCallInfo->SetFunction(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetThis(JSTaggedValue(*error)); + + [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread, ecmaRuntimeCallInfo.get()); + JSTaggedValue result = Error::ToString(ecmaRuntimeCallInfo.get()); + + JSHandle resultHandle(thread, reinterpret_cast(result.GetRawData())); + EXPECT_TRUE(result.IsString()); + EXPECT_EQ(reinterpret_cast( + ecmascript::JSTaggedValue(*factory->NewFromCanBeCompressString("Error")).GetRawData()) + ->Compare(reinterpret_cast(*resultHandle)), + 0); +} + +/* + * @tc.name: ErrorToString + * @tc.desc: new Error("This is Error!").toString() + * @tc.type: FUNC + */ +TEST_F(BuiltinsErrorsTest, ErrorToString) +{ + ObjectFactory *factory = EcmaVM::Cast(instance)->GetFactory(); + JSHandle env = EcmaVM::Cast(instance)->GetGlobalEnv(); + + JSHandle errorObject = env->GetErrorFunction(); + JSHandle error = factory->NewJSObjectByConstructor(JSHandle(errorObject), errorObject); + + JSHandle handleMsgKey(factory->NewFromCanBeCompressString("message")); + JSObject::SetProperty( + thread, JSHandle(error), handleMsgKey, + JSHandle(thread, factory->NewFromCanBeCompressString("This is Error!").GetTaggedValue())); + + auto ecmaRuntimeCallInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread, JSTaggedValue::Undefined(), 4); + ecmaRuntimeCallInfo->SetFunction(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetThis(JSTaggedValue(*error)); + + [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread, ecmaRuntimeCallInfo.get()); + JSTaggedValue result = Error::ToString(ecmaRuntimeCallInfo.get()); + + JSHandle resultHandle(thread, reinterpret_cast(result.GetRawData())); + EXPECT_TRUE(result.IsString()); + EXPECT_EQ(reinterpret_cast( + ecmascript::JSTaggedValue(*factory->NewFromCanBeCompressString("Error: This is Error!")).GetRawData()) + ->Compare(*resultHandle), + 0); +} + +/* + * @tc.name: RangeErrorNoParameterConstructor + * @tc.desc: new RangeError() + * @tc.type: FUNC + */ +TEST_F(BuiltinsErrorsTest, RangeErrorNoParameterConstructor) +{ + ObjectFactory *factory = EcmaVM::Cast(instance)->GetFactory(); + JSHandle env = EcmaVM::Cast(instance)->GetGlobalEnv(); + + JSHandle error(env->GetRangeErrorFunction()); + + auto ecmaRuntimeCallInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread, JSTaggedValue::Undefined(), 4); + ecmaRuntimeCallInfo->SetFunction(error.GetTaggedValue()); + ecmaRuntimeCallInfo->SetThis(JSTaggedValue(*error)); + + [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread, ecmaRuntimeCallInfo.get()); + JSTaggedValue result = RangeError::RangeErrorConstructor(ecmaRuntimeCallInfo.get()); + + EXPECT_TRUE(result.IsECMAObject()); + + JSHandle errorObject(thread, reinterpret_cast(result.GetRawData())); + JSHandle msgKey(factory->NewFromCanBeCompressString("message")); + JSHandle nameKey = thread->GlobalConstants()->GetHandledNameString(); + + JSHandle msgValue(JSObject::GetProperty(thread, errorObject, msgKey).GetValue()); + ASSERT_EQ( + reinterpret_cast(ecmascript::JSTaggedValue(*factory->NewFromCanBeCompressString("")).GetRawData()) + ->Compare(reinterpret_cast(JSTaggedValue(msgValue.GetTaggedValue()).GetRawData())), + 0); + JSHandle nameValue(JSObject::GetProperty(thread, errorObject, nameKey).GetValue()); + ASSERT_EQ(reinterpret_cast( + ecmascript::JSTaggedValue(*factory->NewFromCanBeCompressString("RangeError")).GetRawData()) + ->Compare(reinterpret_cast(JSTaggedValue(nameValue.GetTaggedValue()).GetRawData())), + 0); +} + +/* + * @tc.name: RangeErrorParameterConstructor + * @tc.desc: new RangeError("Hello RangeError!") + * @tc.type: FUNC + */ +TEST_F(BuiltinsErrorsTest, RangeErrorParameterConstructor) +{ + ObjectFactory *factory = EcmaVM::Cast(instance)->GetFactory(); + JSHandle env = EcmaVM::Cast(instance)->GetGlobalEnv(); + + JSHandle error(env->GetRangeErrorFunction()); + JSHandle paramMsg(factory->NewFromCanBeCompressString("Hello RangeError!")); + + auto ecmaRuntimeCallInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread, JSTaggedValue(*error), 6); + ecmaRuntimeCallInfo->SetFunction(error.GetTaggedValue()); + ecmaRuntimeCallInfo->SetThis(JSTaggedValue(*error)); + ecmaRuntimeCallInfo->SetCallArg(0, paramMsg.GetTaggedValue()); + + [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread, ecmaRuntimeCallInfo.get()); + JSTaggedValue result = RangeError::RangeErrorConstructor(ecmaRuntimeCallInfo.get()); + + EXPECT_TRUE(result.IsECMAObject()); + + JSHandle errorObject(thread, reinterpret_cast(result.GetRawData())); + JSHandle msgKey(factory->NewFromCanBeCompressString("message")); + JSHandle nameKey = thread->GlobalConstants()->GetHandledNameString(); + + JSHandle msgValue(JSObject::GetProperty(thread, errorObject, msgKey).GetValue()); + ASSERT_EQ(reinterpret_cast( + ecmascript::JSTaggedValue(*factory->NewFromCanBeCompressString("Hello RangeError!")).GetRawData()) + ->Compare(reinterpret_cast(msgValue->GetRawData())), + 0); + + JSHandle nameValue(JSObject::GetProperty(thread, errorObject, nameKey).GetValue()); + ASSERT_EQ(reinterpret_cast( + ecmascript::JSTaggedValue(*factory->NewFromCanBeCompressString("RangeError")).GetRawData()) + ->Compare(reinterpret_cast(nameValue->GetRawData())), + 0); +} + +/* + * @tc.name: RangeErrorNoParameterToString + * @tc.desc: new RangeError().toString() + * @tc.type: FUNC + */ +TEST_F(BuiltinsErrorsTest, RangeErrorNoParameterToString) +{ + ObjectFactory *factory = EcmaVM::Cast(instance)->GetFactory(); + JSHandle env = EcmaVM::Cast(instance)->GetGlobalEnv(); + + JSHandle errorObject = env->GetRangeErrorFunction(); + JSHandle error = factory->NewJSObjectByConstructor(JSHandle(errorObject), errorObject); + + auto ecmaRuntimeCallInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread, JSTaggedValue::Undefined(), 4); + ecmaRuntimeCallInfo->SetFunction(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetThis(JSTaggedValue(*error)); + + [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread, ecmaRuntimeCallInfo.get()); + JSTaggedValue result = RangeError::ToString(ecmaRuntimeCallInfo.get()); + JSHandle resultHandle(thread, result); + + EXPECT_TRUE(result.IsString()); + + EXPECT_EQ(reinterpret_cast( + ecmascript::JSTaggedValue(*factory->NewFromCanBeCompressString("RangeError")).GetRawData()) + ->Compare(reinterpret_cast(resultHandle->GetRawData())), + 0); +} + +/* + * @tc.name: RangeErrorToString + * @tc.desc: new RangeError("This is RangeError!").toString() + * @tc.type: FUNC + */ +TEST_F(BuiltinsErrorsTest, RangeErrorToString) +{ + ObjectFactory *factory = EcmaVM::Cast(instance)->GetFactory(); + JSHandle env = EcmaVM::Cast(instance)->GetGlobalEnv(); + + JSHandle errorObject = env->GetRangeErrorFunction(); + JSHandle error = factory->NewJSObjectByConstructor(JSHandle(errorObject), errorObject); + + JSHandle handleMsgKey(factory->NewFromCanBeCompressString("message")); + JSObject::SetProperty(thread, JSHandle(error), handleMsgKey, + JSHandle(factory->NewFromCanBeCompressString("This is RangeError!"))); + + auto ecmaRuntimeCallInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread, JSTaggedValue::Undefined(), 4); + ecmaRuntimeCallInfo->SetFunction(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetThis(JSTaggedValue(*error)); + + [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread, ecmaRuntimeCallInfo.get()); + JSTaggedValue result = RangeError::ToString(ecmaRuntimeCallInfo.get()); + + JSHandle resultHandle(thread, reinterpret_cast(result.GetRawData())); + EXPECT_TRUE(result.IsString()); + EXPECT_EQ(factory->NewFromCanBeCompressString("RangeError: This is RangeError!")->Compare(*resultHandle), 0); +} + +// new ReferenceError() +/* + * @tc.name: ReferenceErrorNoParameterConstructor + * @tc.desc: new ReferenceError() + * @tc.type: FUNC + */ +TEST_F(BuiltinsErrorsTest, ReferenceErrorNoParameterConstructor) +{ + ObjectFactory *factory = EcmaVM::Cast(instance)->GetFactory(); + JSHandle env = EcmaVM::Cast(instance)->GetGlobalEnv(); + + JSHandle error(env->GetReferenceErrorFunction()); + + auto ecmaRuntimeCallInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread, JSTaggedValue(*error), 4); + ecmaRuntimeCallInfo->SetFunction(error.GetTaggedValue()); + ecmaRuntimeCallInfo->SetThis(JSTaggedValue(*error)); + + [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread, ecmaRuntimeCallInfo.get()); + JSTaggedValue result = ReferenceError::ReferenceErrorConstructor(ecmaRuntimeCallInfo.get()); + EXPECT_TRUE(result.IsECMAObject()); + + JSHandle errorObject(thread, reinterpret_cast(result.GetRawData())); + JSHandle msgKey(factory->NewFromCanBeCompressString("message")); + JSHandle nameKey = thread->GlobalConstants()->GetHandledNameString(); + + JSHandle msgValue(JSObject::GetProperty(thread, errorObject, msgKey).GetValue()); + ASSERT_EQ( + reinterpret_cast(ecmascript::JSTaggedValue(*factory->NewFromCanBeCompressString("")).GetRawData()) + ->Compare(reinterpret_cast(msgValue->GetRawData())), + 0); + + JSHandle nameValue(JSObject::GetProperty(thread, errorObject, nameKey).GetValue()); + ASSERT_EQ(reinterpret_cast( + ecmascript::JSTaggedValue(*factory->NewFromCanBeCompressString("ReferenceError")).GetRawData()) + ->Compare(reinterpret_cast(nameValue->GetRawData())), + 0); +} + +/* + * @tc.name: ReferenceErrorParameterConstructor + * @tc.desc: new ReferenceError("Hello RangeError!") + * @tc.type: FUNC + */ +TEST_F(BuiltinsErrorsTest, ReferenceErrorParameterConstructor) +{ + ObjectFactory *factory = EcmaVM::Cast(instance)->GetFactory(); + JSHandle env = EcmaVM::Cast(instance)->GetGlobalEnv(); + + JSHandle error(env->GetReferenceErrorFunction()); + JSHandle paramMsg(factory->NewFromCanBeCompressString("Hello ReferenceError!")); + + auto ecmaRuntimeCallInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread, JSTaggedValue(*error), 6); + ecmaRuntimeCallInfo->SetFunction(error.GetTaggedValue()); + ecmaRuntimeCallInfo->SetThis(JSTaggedValue(*error)); + ecmaRuntimeCallInfo->SetCallArg(0, paramMsg.GetTaggedValue()); + + [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread, ecmaRuntimeCallInfo.get()); + JSTaggedValue result = ReferenceError::ReferenceErrorConstructor(ecmaRuntimeCallInfo.get()); + EXPECT_TRUE(result.IsECMAObject()); + + JSHandle errorObject(thread, reinterpret_cast(result.GetRawData())); + JSHandle msgKey(factory->NewFromCanBeCompressString("message")); + JSHandle nameKey = thread->GlobalConstants()->GetHandledNameString(); + JSHandle msgValue(JSObject::GetProperty(thread, errorObject, msgKey).GetValue()); + ASSERT_EQ(reinterpret_cast( + ecmascript::JSTaggedValue(*factory->NewFromCanBeCompressString("Hello ReferenceError!")).GetRawData()) + ->Compare(reinterpret_cast(msgValue->GetRawData())), + 0); + + JSHandle nameValue(JSObject::GetProperty(thread, errorObject, nameKey).GetValue()); + ASSERT_EQ(reinterpret_cast( + ecmascript::JSTaggedValue(*factory->NewFromCanBeCompressString("ReferenceError")).GetRawData()) + ->Compare(reinterpret_cast(nameValue->GetRawData())), + 0); +} + +/* + * @tc.name: ReferenceErrorNoParameterToString + * @tc.desc: new ReferenceError().toString() + * @tc.type: FUNC + */ +TEST_F(BuiltinsErrorsTest, ReferenceErrorNoParameterToString) +{ + ObjectFactory *factory = EcmaVM::Cast(instance)->GetFactory(); + JSHandle env = EcmaVM::Cast(instance)->GetGlobalEnv(); + + JSHandle errorObject = env->GetReferenceErrorFunction(); + JSHandle error = factory->NewJSObjectByConstructor(JSHandle(errorObject), errorObject); + + auto ecmaRuntimeCallInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread, JSTaggedValue::Undefined(), 4); + ecmaRuntimeCallInfo->SetFunction(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetThis(JSTaggedValue(*error)); + + [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread, ecmaRuntimeCallInfo.get()); + JSTaggedValue result = ReferenceError::ToString(ecmaRuntimeCallInfo.get()); + JSHandle resultHandle(thread, reinterpret_cast(result.GetRawData())); + EXPECT_TRUE(result.IsString()); + EXPECT_EQ(reinterpret_cast( + ecmascript::JSTaggedValue(*factory->NewFromCanBeCompressString("ReferenceError")).GetRawData()) + ->Compare(*resultHandle), + 0); +} + +/* + * @tc.name: ReferenceErrorToString + * @tc.desc: new ReferenceError("This is ReferenceError!").toString() + * @tc.type: FUNC + */ +TEST_F(BuiltinsErrorsTest, ReferenceErrorToString) +{ + ObjectFactory *factory = EcmaVM::Cast(instance)->GetFactory(); + JSHandle env = EcmaVM::Cast(instance)->GetGlobalEnv(); + + JSHandle errorObject = env->GetReferenceErrorFunction(); + JSHandle error = factory->NewJSObjectByConstructor(JSHandle(errorObject), errorObject); + + JSHandle handleMsgKey(factory->NewFromCanBeCompressString("message")); + JSObject::SetProperty(thread, JSHandle(error), handleMsgKey, + JSHandle(factory->NewFromCanBeCompressString("This is ReferenceError!"))); + + auto ecmaRuntimeCallInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread, JSTaggedValue::Undefined(), 4); + ecmaRuntimeCallInfo->SetFunction(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetThis(JSTaggedValue(*error)); + + [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread, ecmaRuntimeCallInfo.get()); + JSTaggedValue result = ReferenceError::ToString(ecmaRuntimeCallInfo.get()); + JSHandle resultHandle(thread, reinterpret_cast(result.GetRawData())); + EXPECT_TRUE(result.IsString()); + EXPECT_EQ(factory->NewFromCanBeCompressString("ReferenceError: This is ReferenceError!")->Compare(*resultHandle), + 0); +} + +/* + * @tc.name: TypeErrorNoParameterConstructor + * @tc.desc: new TypeError() + * @tc.type: FUNC + */ +TEST_F(BuiltinsErrorsTest, TypeErrorNoParameterConstructor) +{ + ObjectFactory *factory = EcmaVM::Cast(instance)->GetFactory(); + JSHandle env = EcmaVM::Cast(instance)->GetGlobalEnv(); + + JSHandle error(env->GetTypeErrorFunction()); + + auto ecmaRuntimeCallInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread, JSTaggedValue(*error), 4); + ecmaRuntimeCallInfo->SetFunction(error.GetTaggedValue()); + ecmaRuntimeCallInfo->SetThis(JSTaggedValue(*error)); + + [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread, ecmaRuntimeCallInfo.get()); + JSTaggedValue result = TypeError::TypeErrorConstructor(ecmaRuntimeCallInfo.get()); + EXPECT_TRUE(result.IsECMAObject()); + + JSHandle errorObject(thread, reinterpret_cast(result.GetRawData())); + JSHandle msgKey(factory->NewFromCanBeCompressString("message")); + JSHandle nameKey = thread->GlobalConstants()->GetHandledNameString(); + + JSHandle msgValue(JSObject::GetProperty(thread, errorObject, msgKey).GetValue()); + ASSERT_EQ( + reinterpret_cast(ecmascript::JSTaggedValue(*factory->NewFromCanBeCompressString("")).GetRawData()) + ->Compare(reinterpret_cast(msgValue->GetRawData())), + 0); + + JSHandle nameValue(JSObject::GetProperty(thread, errorObject, nameKey).GetValue()); + EXPECT_EQ(reinterpret_cast(JSTaggedValue(nameValue.GetTaggedValue()).GetRawData()) + ->Compare(reinterpret_cast(JSTaggedValue(nameValue.GetTaggedValue()).GetRawData())), + 0); +} + +/* + * @tc.name: TypeErrorParameterConstructor + * @tc.desc: new TypeError("Hello RangeError!") + * @tc.type: FUNC + */ +TEST_F(BuiltinsErrorsTest, TypeErrorParameterConstructor) +{ + ObjectFactory *factory = EcmaVM::Cast(instance)->GetFactory(); + JSHandle env = EcmaVM::Cast(instance)->GetGlobalEnv(); + + JSHandle error(env->GetTypeErrorFunction()); + JSHandle paramMsg(factory->NewFromCanBeCompressString("Hello TypeError!")); + + auto ecmaRuntimeCallInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread, JSTaggedValue(*error), 6); + ecmaRuntimeCallInfo->SetFunction(error.GetTaggedValue()); + ecmaRuntimeCallInfo->SetThis(JSTaggedValue(*error)); + ecmaRuntimeCallInfo->SetCallArg(0, paramMsg.GetTaggedValue()); + + [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread, ecmaRuntimeCallInfo.get()); + JSTaggedValue result = TypeError::TypeErrorConstructor(ecmaRuntimeCallInfo.get()); + EXPECT_TRUE(result.IsECMAObject()); + + JSHandle errorObject(thread, reinterpret_cast(result.GetRawData())); + JSHandle msgKey(factory->NewFromCanBeCompressString("message")); + JSHandle nameKey = thread->GlobalConstants()->GetHandledNameString(); + + JSHandle msgValue(JSObject::GetProperty(thread, errorObject, msgKey).GetValue()); + ASSERT_EQ(reinterpret_cast( + ecmascript::JSTaggedValue(*factory->NewFromCanBeCompressString("Hello TypeError!")).GetRawData()) + ->Compare(reinterpret_cast(msgValue->GetRawData())), + 0); + + JSHandle nameValue(JSObject::GetProperty(thread, errorObject, nameKey).GetValue()); + ASSERT_EQ(reinterpret_cast( + ecmascript::JSTaggedValue(*factory->NewFromCanBeCompressString("TypeError")).GetRawData()) + ->Compare(reinterpret_cast(nameValue->GetRawData())), + 0); +} + +/* + * @tc.name: TypeErrorNoParameterToString + * @tc.desc: new TypeError().toString() + * @tc.type: FUNC + */ +TEST_F(BuiltinsErrorsTest, TypeErrorNoParameterToString) +{ + ObjectFactory *factory = EcmaVM::Cast(instance)->GetFactory(); + JSHandle env = EcmaVM::Cast(instance)->GetGlobalEnv(); + + JSHandle errorObject = env->GetTypeErrorFunction(); + JSHandle error = factory->NewJSObjectByConstructor(JSHandle(errorObject), errorObject); + + auto ecmaRuntimeCallInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread, JSTaggedValue::Undefined(), 4); + ecmaRuntimeCallInfo->SetFunction(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetThis(JSTaggedValue(*error)); + + [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread, ecmaRuntimeCallInfo.get()); + JSTaggedValue result = TypeError::ToString(ecmaRuntimeCallInfo.get()); + JSHandle resultHandle(thread, reinterpret_cast(result.GetRawData())); + EXPECT_TRUE(result.IsString()); + EXPECT_EQ(reinterpret_cast( + ecmascript::JSTaggedValue(*factory->NewFromCanBeCompressString("TypeError")).GetRawData()) + ->Compare(*resultHandle), + 0); +} + +/* + * @tc.name: TypeErrorToString + * @tc.desc: new TypeError("This is TypeError!").toString() + * @tc.type: FUNC + */ +TEST_F(BuiltinsErrorsTest, TypeErrorToString) +{ + ObjectFactory *factory = EcmaVM::Cast(instance)->GetFactory(); + JSHandle env = EcmaVM::Cast(instance)->GetGlobalEnv(); + + JSHandle errorObject = env->GetTypeErrorFunction(); + JSHandle error = factory->NewJSObjectByConstructor(JSHandle(errorObject), errorObject); + + JSHandle value(factory->NewFromCanBeCompressString("This is TypeError!")); + JSHandle handleMsgKey(factory->NewFromCanBeCompressString("message")); + JSObject::SetProperty(thread, JSHandle(error), handleMsgKey, value); + + auto ecmaRuntimeCallInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread, JSTaggedValue::Undefined(), 4); + ecmaRuntimeCallInfo->SetFunction(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetThis(JSTaggedValue(*error)); + + [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread, ecmaRuntimeCallInfo.get()); + JSTaggedValue result = TypeError::ToString(ecmaRuntimeCallInfo.get()); + JSHandle resultHandle(thread, reinterpret_cast(result.GetRawData())); + EXPECT_TRUE(result.IsString()); + EXPECT_EQ(factory->NewFromCanBeCompressString("TypeError: This is TypeError!")->Compare(*resultHandle), 0); +} + +/* + * @tc.name: URIErrorNoParameterConstructor + * @tc.desc: new URIError() + * @tc.type: FUNC + */ +TEST_F(BuiltinsErrorsTest, URIErrorNoParameterConstructor) +{ + ObjectFactory *factory = EcmaVM::Cast(instance)->GetFactory(); + JSHandle env = EcmaVM::Cast(instance)->GetGlobalEnv(); + + JSHandle error(env->GetURIErrorFunction()); + + auto ecmaRuntimeCallInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread, JSTaggedValue(*error), 4); + ecmaRuntimeCallInfo->SetFunction(error.GetTaggedValue()); + ecmaRuntimeCallInfo->SetThis(JSTaggedValue(*error)); + + [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread, ecmaRuntimeCallInfo.get()); + JSTaggedValue result = URIError::URIErrorConstructor(ecmaRuntimeCallInfo.get()); + EXPECT_TRUE(result.IsECMAObject()); + + JSHandle errorObject(thread, reinterpret_cast(result.GetRawData())); + JSHandle msgKey(factory->NewFromCanBeCompressString("message")); + JSHandle nameKey = thread->GlobalConstants()->GetHandledNameString(); + + JSHandle msgValue(JSObject::GetProperty(thread, errorObject, msgKey).GetValue()); + ASSERT_EQ( + reinterpret_cast(ecmascript::JSTaggedValue(*factory->NewFromCanBeCompressString("")).GetRawData()) + ->Compare(reinterpret_cast(msgValue->GetRawData())), + 0); + + JSHandle nameValue(JSObject::GetProperty(thread, errorObject, nameKey).GetValue()); + ASSERT_EQ(reinterpret_cast( + ecmascript::JSTaggedValue(*factory->NewFromCanBeCompressString("URIError")).GetRawData()) + ->Compare(reinterpret_cast(nameValue->GetRawData())), + 0); +} + +/* + * @tc.name: URIErrorParameterConstructor + * @tc.desc: new URIError("Hello RangeError!") + * @tc.type: FUNC + */ +TEST_F(BuiltinsErrorsTest, URIErrorParameterConstructor) +{ + ObjectFactory *factory = EcmaVM::Cast(instance)->GetFactory(); + JSHandle env = EcmaVM::Cast(instance)->GetGlobalEnv(); + + JSHandle error(env->GetURIErrorFunction()); + JSHandle paramMsg(factory->NewFromCanBeCompressString("Hello URIError!")); + + auto ecmaRuntimeCallInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread, JSTaggedValue(*error), 6); + ecmaRuntimeCallInfo->SetFunction(error.GetTaggedValue()); + ecmaRuntimeCallInfo->SetThis(JSTaggedValue(*error)); + ecmaRuntimeCallInfo->SetCallArg(0, paramMsg.GetTaggedValue()); + + [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread, ecmaRuntimeCallInfo.get()); + JSTaggedValue result = URIError::URIErrorConstructor(ecmaRuntimeCallInfo.get()); + EXPECT_TRUE(result.IsECMAObject()); + + JSHandle errorObject(thread, reinterpret_cast(result.GetRawData())); + JSHandle msgKey(factory->NewFromCanBeCompressString("message")); + JSHandle nameKey = thread->GlobalConstants()->GetHandledNameString(); + + JSHandle msgValue(JSObject::GetProperty(thread, errorObject, msgKey).GetValue()); + ASSERT_EQ(reinterpret_cast( + ecmascript::JSTaggedValue(*factory->NewFromCanBeCompressString("Hello URIError!")).GetRawData()) + ->Compare(reinterpret_cast(msgValue->GetRawData())), + 0); + + JSHandle nameValue(JSObject::GetProperty(thread, errorObject, nameKey).GetValue()); + ASSERT_EQ(reinterpret_cast( + ecmascript::JSTaggedValue(*factory->NewFromCanBeCompressString("URIError")).GetRawData()) + ->Compare(reinterpret_cast(nameValue->GetRawData())), + 0); +} + +/* + * @tc.name: URIErrorNoParameterToString + * @tc.desc: new URIError().toString() + * @tc.type: FUNC + */ +TEST_F(BuiltinsErrorsTest, URIErrorNoParameterToString) +{ + ObjectFactory *factory = EcmaVM::Cast(instance)->GetFactory(); + JSHandle env = EcmaVM::Cast(instance)->GetGlobalEnv(); + + JSHandle errorObject = env->GetURIErrorFunction(); + JSHandle error = factory->NewJSObjectByConstructor(JSHandle(errorObject), errorObject); + + auto ecmaRuntimeCallInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread, JSTaggedValue::Undefined(), 4); + ecmaRuntimeCallInfo->SetFunction(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetThis(JSTaggedValue(*error)); + + [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread, ecmaRuntimeCallInfo.get()); + JSTaggedValue result = URIError::ToString(ecmaRuntimeCallInfo.get()); + JSHandle resultHandle(thread, reinterpret_cast(result.GetRawData())); + EXPECT_TRUE(result.IsString()); + + EXPECT_EQ(reinterpret_cast( + ecmascript::JSTaggedValue(*factory->NewFromCanBeCompressString("URIError")).GetRawData()) + ->Compare(*resultHandle), + 0); +} + +/* + * @tc.name: URIErrorToString + * @tc.desc: new URIError("This is URIError!").toString() + * @tc.type: FUNC + */ +TEST_F(BuiltinsErrorsTest, URIErrorToString) +{ + ObjectFactory *factory = EcmaVM::Cast(instance)->GetFactory(); + JSHandle env = EcmaVM::Cast(instance)->GetGlobalEnv(); + + JSHandle errorObject = env->GetURIErrorFunction(); + JSHandle error = factory->NewJSObjectByConstructor(JSHandle(errorObject), errorObject); + + JSHandle handleMsgKey(factory->NewFromCanBeCompressString("message")); + JSObject::SetProperty( + thread, JSHandle(error), handleMsgKey, + JSHandle(thread, factory->NewFromCanBeCompressString("This is URIError!").GetTaggedValue())); + + auto ecmaRuntimeCallInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread, JSTaggedValue::Undefined(), 4); + ecmaRuntimeCallInfo->SetFunction(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetThis(JSTaggedValue(*error)); + + [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread, ecmaRuntimeCallInfo.get()); + JSTaggedValue result = URIError::ToString(ecmaRuntimeCallInfo.get()); + JSHandle resultHandle(thread, reinterpret_cast(result.GetRawData())); + EXPECT_TRUE(result.IsString()); + + EXPECT_EQ( + reinterpret_cast( + ecmascript::JSTaggedValue(*factory->NewFromCanBeCompressString("URIError: This is URIError!")).GetRawData()) + ->Compare(*resultHandle), + 0); +} + +/* + * @tc.name: SyntaxErrorNoParameterConstructor + * @tc.desc: new SyntaxError() + * @tc.type: FUNC + */ +TEST_F(BuiltinsErrorsTest, SyntaxErrorNoParameterConstructor) +{ + ObjectFactory *factory = EcmaVM::Cast(instance)->GetFactory(); + JSHandle env = EcmaVM::Cast(instance)->GetGlobalEnv(); + + JSHandle error(env->GetSyntaxErrorFunction()); + + auto ecmaRuntimeCallInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread, JSTaggedValue(*error), 4); + ecmaRuntimeCallInfo->SetFunction(error.GetTaggedValue()); + ecmaRuntimeCallInfo->SetThis(JSTaggedValue(*error)); + + [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread, ecmaRuntimeCallInfo.get()); + JSTaggedValue result = SyntaxError::SyntaxErrorConstructor(ecmaRuntimeCallInfo.get()); + EXPECT_TRUE(result.IsECMAObject()); + + JSHandle errorObject(thread, reinterpret_cast(result.GetRawData())); + JSHandle msgKey(factory->NewFromCanBeCompressString("message")); + JSHandle nameKey = thread->GlobalConstants()->GetHandledNameString(); + + JSHandle msgValue(JSObject::GetProperty(thread, errorObject, msgKey).GetValue()); + ASSERT_EQ( + reinterpret_cast(ecmascript::JSTaggedValue(*factory->NewFromCanBeCompressString("")).GetRawData()) + ->Compare(reinterpret_cast(msgValue->GetRawData())), + 0); + + JSHandle nameValue(JSObject::GetProperty(thread, errorObject, nameKey).GetValue()); + ASSERT_EQ(reinterpret_cast( + ecmascript::JSTaggedValue(*factory->NewFromCanBeCompressString("SyntaxError")).GetRawData()) + ->Compare(reinterpret_cast(nameValue->GetRawData())), + 0); +} + +/* + * @tc.name: SyntaxErrorParameterConstructor + * @tc.desc: new SyntaxError("Hello RangeError!") + * @tc.type: FUNC + */ +TEST_F(BuiltinsErrorsTest, SyntaxErrorParameterConstructor) +{ + ObjectFactory *factory = EcmaVM::Cast(instance)->GetFactory(); + JSHandle env = EcmaVM::Cast(instance)->GetGlobalEnv(); + + JSHandle error(env->GetSyntaxErrorFunction()); + JSHandle paramMsg(factory->NewFromCanBeCompressString("Hello SyntaxError!")); + + auto ecmaRuntimeCallInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread, JSTaggedValue(*error), 6); + ecmaRuntimeCallInfo->SetFunction(error.GetTaggedValue()); + ecmaRuntimeCallInfo->SetThis(JSTaggedValue(*error)); + ecmaRuntimeCallInfo->SetCallArg(0, paramMsg.GetTaggedValue()); + + [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread, ecmaRuntimeCallInfo.get()); + JSTaggedValue result = SyntaxError::SyntaxErrorConstructor(ecmaRuntimeCallInfo.get()); + EXPECT_TRUE(result.IsECMAObject()); + + JSHandle errorObject(thread, reinterpret_cast(result.GetRawData())); + JSHandle msgKey(factory->NewFromCanBeCompressString("message")); + JSHandle nameKey = thread->GlobalConstants()->GetHandledNameString(); + + JSHandle msgValue(JSObject::GetProperty(thread, errorObject, msgKey).GetValue()); + ASSERT_EQ(reinterpret_cast( + ecmascript::JSTaggedValue(*factory->NewFromCanBeCompressString("Hello SyntaxError!")).GetRawData()) + ->Compare(reinterpret_cast(msgValue->GetRawData())), + 0); + + JSHandle nameValue(JSObject::GetProperty(thread, errorObject, nameKey).GetValue()); + ASSERT_EQ(reinterpret_cast( + ecmascript::JSTaggedValue(*factory->NewFromCanBeCompressString("SyntaxError")).GetRawData()) + ->Compare(reinterpret_cast(nameValue->GetRawData())), + 0); +} + +/* + * @tc.name: SyntaxErrorNoParameterToString + * @tc.desc: new SyntaxError().toString() + * @tc.type: FUNC + */ +TEST_F(BuiltinsErrorsTest, SyntaxErrorNoParameterToString) +{ + ObjectFactory *factory = EcmaVM::Cast(instance)->GetFactory(); + JSHandle env = EcmaVM::Cast(instance)->GetGlobalEnv(); + + JSHandle errorObject = env->GetSyntaxErrorFunction(); + JSHandle error = factory->NewJSObjectByConstructor(JSHandle(errorObject), errorObject); + + auto ecmaRuntimeCallInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread, JSTaggedValue::Undefined(), 4); + ecmaRuntimeCallInfo->SetFunction(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetThis(JSTaggedValue(*error)); + + [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread, ecmaRuntimeCallInfo.get()); + JSTaggedValue result = SyntaxError::ToString(ecmaRuntimeCallInfo.get()); + JSHandle resultHandle(thread, reinterpret_cast(result.GetRawData())); + EXPECT_TRUE(result.IsString()); + + EXPECT_EQ(reinterpret_cast( + ecmascript::JSTaggedValue(*factory->NewFromCanBeCompressString("SyntaxError")).GetRawData()) + ->Compare(*resultHandle), + 0); +} + +/* + * @tc.name: SyntaxErrorToString + * @tc.desc: new SyntaxError("This is SyntaxError!").toString() + * @tc.type: FUNC + */ +TEST_F(BuiltinsErrorsTest, SyntaxErrorToString) +{ + ObjectFactory *factory = EcmaVM::Cast(instance)->GetFactory(); + JSHandle env = EcmaVM::Cast(instance)->GetGlobalEnv(); + + JSHandle errorObject = env->GetSyntaxErrorFunction(); + JSHandle error = factory->NewJSObjectByConstructor(JSHandle(errorObject), errorObject); + + JSHandle handleMsgKey(factory->NewFromCanBeCompressString("message")); + JSObject::SetProperty(thread, JSHandle(error), handleMsgKey, + JSHandle(factory->NewFromCanBeCompressString("This is SyntaxError!"))); + + auto ecmaRuntimeCallInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread, JSTaggedValue::Undefined(), 4); + ecmaRuntimeCallInfo->SetFunction(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetThis(JSTaggedValue(*error)); + + [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread, ecmaRuntimeCallInfo.get()); + JSTaggedValue result = SyntaxError::ToString(ecmaRuntimeCallInfo.get()); + JSHandle resultHandle(thread, reinterpret_cast(result.GetRawData())); + EXPECT_TRUE(result.IsString()); + + EXPECT_EQ(factory->NewFromCanBeCompressString("SyntaxError: This is SyntaxError!")->Compare(*resultHandle), 0); +} + +/* + * @tc.name: EvalErrorNoParameterConstructor + * @tc.desc: new EvalError() + * @tc.type: FUNC + */ +TEST_F(BuiltinsErrorsTest, EvalErrorNoParameterConstructor) +{ + ObjectFactory *factory = EcmaVM::Cast(instance)->GetFactory(); + JSHandle env = EcmaVM::Cast(instance)->GetGlobalEnv(); + + JSHandle error(env->GetEvalErrorFunction()); + + auto ecmaRuntimeCallInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread, JSTaggedValue(*error), 4); + ecmaRuntimeCallInfo->SetFunction(error.GetTaggedValue()); + ecmaRuntimeCallInfo->SetThis(JSTaggedValue(*error)); + + [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread, ecmaRuntimeCallInfo.get()); + JSTaggedValue result = EvalError::EvalErrorConstructor(ecmaRuntimeCallInfo.get()); + EXPECT_TRUE(result.IsECMAObject()); + + JSHandle errorObject(thread, reinterpret_cast(result.GetRawData())); + JSHandle msgKey(factory->NewFromCanBeCompressString("message")); + JSHandle nameKey = thread->GlobalConstants()->GetHandledNameString(); + + JSHandle msgValue(JSObject::GetProperty(thread, errorObject, msgKey).GetValue()); + ASSERT_EQ( + reinterpret_cast(ecmascript::JSTaggedValue(*factory->NewFromCanBeCompressString("")).GetRawData()) + ->Compare(reinterpret_cast(msgValue->GetRawData())), + 0); + + JSHandle nameValue(JSObject::GetProperty(thread, errorObject, nameKey).GetValue()); + ASSERT_EQ(reinterpret_cast( + ecmascript::JSTaggedValue(*factory->NewFromCanBeCompressString("EvalError")).GetRawData()) + ->Compare(reinterpret_cast(nameValue->GetRawData())), + 0); +} + +/* + * @tc.name: EvalErrorParameterConstructor + * @tc.desc: new EvalError("Hello RangeError!") + * @tc.type: FUNC + */ +TEST_F(BuiltinsErrorsTest, EvalErrorParameterConstructor) +{ + ObjectFactory *factory = EcmaVM::Cast(instance)->GetFactory(); + JSHandle env = EcmaVM::Cast(instance)->GetGlobalEnv(); + + JSHandle error(env->GetEvalErrorFunction()); + JSHandle paramMsg(factory->NewFromCanBeCompressString("Hello EvalError!")); + + auto ecmaRuntimeCallInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread, JSTaggedValue(*error), 6); + ecmaRuntimeCallInfo->SetFunction(error.GetTaggedValue()); + ecmaRuntimeCallInfo->SetThis(JSTaggedValue(*error)); + ecmaRuntimeCallInfo->SetCallArg(0, paramMsg.GetTaggedValue()); + + [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread, ecmaRuntimeCallInfo.get()); + JSTaggedValue result = EvalError::EvalErrorConstructor(ecmaRuntimeCallInfo.get()); + EXPECT_TRUE(result.IsECMAObject()); + + JSHandle errorObject(thread, reinterpret_cast(result.GetRawData())); + JSHandle msgKey(factory->NewFromCanBeCompressString("message")); + JSHandle nameKey = thread->GlobalConstants()->GetHandledNameString(); + + JSHandle msgValue(JSObject::GetProperty(thread, errorObject, msgKey).GetValue()); + ASSERT_EQ(reinterpret_cast( + ecmascript::JSTaggedValue(*factory->NewFromCanBeCompressString("Hello EvalError!")).GetRawData()) + ->Compare(reinterpret_cast(msgValue->GetRawData())), + 0); + + JSHandle nameValue(JSObject::GetProperty(thread, errorObject, nameKey).GetValue()); + ASSERT_EQ(reinterpret_cast( + ecmascript::JSTaggedValue(*factory->NewFromCanBeCompressString("EvalError")).GetRawData()) + ->Compare(reinterpret_cast(nameValue->GetRawData())), + 0); +} + +/* + * @tc.name: EvalErrorNoParameterToString + * @tc.desc: new EvalError().toString() + * @tc.type: FUNC + */ +TEST_F(BuiltinsErrorsTest, EvalErrorNoParameterToString) +{ + ObjectFactory *factory = EcmaVM::Cast(instance)->GetFactory(); + JSHandle env = EcmaVM::Cast(instance)->GetGlobalEnv(); + JSHandle errorObject = env->GetEvalErrorFunction(); + JSHandle error = factory->NewJSObjectByConstructor(JSHandle(errorObject), errorObject); + + auto ecmaRuntimeCallInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread, JSTaggedValue::Undefined(), 4); + ecmaRuntimeCallInfo->SetFunction(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetThis(JSTaggedValue(*error)); + + [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread, ecmaRuntimeCallInfo.get()); + JSTaggedValue result = EvalError::ToString(ecmaRuntimeCallInfo.get()); + JSHandle resultHandle(thread, reinterpret_cast(result.GetRawData())); + EXPECT_TRUE(result.IsString()); + EXPECT_EQ(reinterpret_cast( + ecmascript::JSTaggedValue(*factory->NewFromCanBeCompressString("EvalError")).GetRawData()) + ->Compare(*resultHandle), + 0); +} + +/* + * @tc.name: EvalErrorToString + * @tc.desc: new EvalError("This is EvalError!").toString() + * @tc.type: FUNC + */ +TEST_F(BuiltinsErrorsTest, EvalErrorToString) +{ + ObjectFactory *factory = EcmaVM::Cast(instance)->GetFactory(); + JSHandle env = EcmaVM::Cast(instance)->GetGlobalEnv(); + + JSHandle errorObject = env->GetEvalErrorFunction(); + JSHandle error = factory->NewJSObjectByConstructor(JSHandle(errorObject), errorObject); + + JSHandle handleMsgKey(factory->NewFromCanBeCompressString("message")); + JSObject::SetProperty( + thread, JSHandle(error), handleMsgKey, + JSHandle(thread, factory->NewFromCanBeCompressString("This is EvalError!").GetTaggedValue())); + + auto ecmaRuntimeCallInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread, JSTaggedValue::Undefined(), 4); + ecmaRuntimeCallInfo->SetFunction(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetThis(JSTaggedValue(*error)); + + [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread, ecmaRuntimeCallInfo.get()); + JSTaggedValue result = EvalError::ToString(ecmaRuntimeCallInfo.get()); + JSHandle resultHandle(thread, reinterpret_cast(result.GetRawData())); + EXPECT_TRUE(result.IsString()); + EXPECT_EQ(factory->NewFromCanBeCompressString("EvalError: This is EvalError!")->Compare(*resultHandle), 0); +} +} // namespace panda::test diff --git a/tests/runtime/builtins/builtins_function_test.cpp b/tests/runtime/builtins/builtins_function_test.cpp new file mode 100644 index 000000000..14a9bfc87 --- /dev/null +++ b/tests/runtime/builtins/builtins_function_test.cpp @@ -0,0 +1,427 @@ +/* + * Copyright (c) 2021-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 "plugins/ecmascript/runtime/builtins/builtins_function.h" +#include "plugins/ecmascript/runtime/builtins/builtins_boolean.h" + +#include "plugins/ecmascript/runtime/ecma_runtime_call_info.h" +#include "plugins/ecmascript/runtime/ecma_string.h" +#include "plugins/ecmascript/runtime/ecma_vm.h" +#include "plugins/ecmascript/runtime/global_env.h" +#include "plugins/ecmascript/runtime/js_array.h" +#include "plugins/ecmascript/runtime/js_function.h" +#include "plugins/ecmascript/runtime/js_object-inl.h" + +#include "plugins/ecmascript/runtime/object_factory.h" +#include "plugins/ecmascript/runtime/tagged_array-inl.h" +#include "plugins/ecmascript/tests/runtime/common/test_helper.h" + +using namespace panda::ecmascript; +using namespace panda::ecmascript::builtins; +using BuiltinsBase = panda::ecmascript::base::BuiltinsBase; +using JSArray = panda::ecmascript::JSArray; + +namespace panda::test { +class BuiltinsFunctionTest : public testing::Test { +public: + static void SetUpTestCase() + { + GTEST_LOG_(INFO) << "SetUpTestCase"; + } + + static void TearDownTestCase() + { + GTEST_LOG_(INFO) << "TearDownCase"; + } + + void SetUp() override + { + TestHelper::CreateEcmaVMWithScope(instance, thread, scope); + } + + void TearDown() override + { + TestHelper::DestroyEcmaVMWithScope(instance, scope); + } + + PandaVM *instance {nullptr}; + EcmaHandleScope *scope {nullptr}; + JSThread *thread {nullptr}; +}; + +// native function for test apply and call +JSTaggedValue TestFunctionApplyAndCall(EcmaRuntimeCallInfo *argv) +{ + JSThread *thread = argv->GetThread(); + ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); + + int result = 0; + for (uint32_t index = 0; index < argv->GetArgsNumber(); ++index) { + result += BuiltinsBase::GetCallArg(argv, index)->GetInt(); + } + JSHandle thisValue(BuiltinsBase::GetThis(argv)); + + JSTaggedValue testA = + JSObject::GetProperty(thread, thisValue, + JSHandle(factory->NewFromCanBeCompressString("test_builtins_function_a"))) + .GetValue() + .GetTaggedValue(); + JSTaggedValue testB = + JSObject::GetProperty(thread, thisValue, + JSHandle(factory->NewFromCanBeCompressString("test_builtins_function_b"))) + .GetValue() + .GetTaggedValue(); + + result = result + testA.GetInt() + testB.GetInt(); + return BuiltinsBase::GetTaggedInt(result); +} + +// func.apply(thisArg) +TEST_F(BuiltinsFunctionTest, FunctionPrototypeApply) +{ + JSHandle env = thread->GetEcmaVM()->GetGlobalEnv(); + + ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); + // ecma 19.2.3.1: func + JSHandle func = factory->NewJSFunction(env, reinterpret_cast(TestFunctionApplyAndCall)); + + // ecma 19.2.3.1: thisArg + JSHandle thisArg(thread, env->GetGlobalObject()); + JSObject::SetProperty(thread, JSHandle(thisArg), + JSHandle(factory->NewFromCanBeCompressString("test_builtins_function_a")), + JSHandle(thread, JSTaggedValue(1))); + JSObject::SetProperty(thread, JSHandle(thisArg), + JSHandle(factory->NewFromCanBeCompressString("test_builtins_function_b")), + JSHandle(thread, JSTaggedValue(2))); + + auto ecmaRuntimeCallInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread, JSTaggedValue::Undefined(), 6); + ecmaRuntimeCallInfo->SetFunction(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetThis(func.GetTaggedValue()); + ecmaRuntimeCallInfo->SetCallArg(0, (thisArg.GetTaggedValue())); + + [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread, ecmaRuntimeCallInfo.get()); + JSTaggedValue result = BuiltinsFunction::FunctionPrototypeApply(ecmaRuntimeCallInfo.get()); + + ASSERT_EQ(result.GetRawData(), JSTaggedValue(3).GetRawData()); + + JSObject::DeleteProperty(thread, (thisArg), + JSHandle(factory->NewFromCanBeCompressString("test_builtins_function_a"))); + JSObject::DeleteProperty(thread, (thisArg), + JSHandle(factory->NewFromCanBeCompressString("test_builtins_function_b"))); +} + +// func.apply(thisArg, argArray) +TEST_F(BuiltinsFunctionTest, FunctionPrototypeApply1) +{ + JSHandle env = thread->GetEcmaVM()->GetGlobalEnv(); + + ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); + + // ecma 19.2.3.1: func + JSHandle func = factory->NewJSFunction(env, reinterpret_cast(TestFunctionApplyAndCall)); + + // ecma 19.2.3.1: thisArg + JSHandle thisArg(thread, env->GetGlobalObject()); + JSObject::SetProperty(thread, JSHandle(thisArg), + JSHandle(factory->NewFromCanBeCompressString("test_builtins_function_a")), + JSHandle(thread, JSTaggedValue(10))); + JSObject::SetProperty(thread, JSHandle(thisArg), + JSHandle(factory->NewFromCanBeCompressString("test_builtins_function_b")), + JSHandle(thread, JSTaggedValue(20))); + + // ecma 19.2.3.1: argArray + JSHandle array(JSArray::ArrayCreate(thread, JSTaggedNumber(2))); + PropertyDescriptor desc(thread, JSHandle(thread, JSTaggedValue(30))); + JSArray::DefineOwnProperty(thread, array, JSHandle(thread, JSTaggedValue(0)), desc); + + PropertyDescriptor desc1(thread, JSHandle(thread, JSTaggedValue(40))); + JSArray::DefineOwnProperty(thread, array, JSHandle(thread, JSTaggedValue(1)), desc1); + + auto ecmaRuntimeCallInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread, JSTaggedValue::Undefined(), 8); + ecmaRuntimeCallInfo->SetFunction(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetThis(func.GetTaggedValue()); + ecmaRuntimeCallInfo->SetCallArg(0, (thisArg.GetTaggedValue())); + ecmaRuntimeCallInfo->SetCallArg(1, array.GetTaggedValue()); + + [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread, ecmaRuntimeCallInfo.get()); + JSTaggedValue result = BuiltinsFunction::FunctionPrototypeApply(ecmaRuntimeCallInfo.get()); + + ASSERT_EQ(result.GetRawData(), JSTaggedValue(100).GetRawData()); + + JSObject::DeleteProperty(thread, (thisArg), + JSHandle(factory->NewFromCanBeCompressString("test_builtins_function_a"))); + JSObject::DeleteProperty(thread, (thisArg), + JSHandle(factory->NewFromCanBeCompressString("test_builtins_function_b"))); +} + +// target.bind(thisArg) +TEST_F(BuiltinsFunctionTest, FunctionPrototypeBind) +{ + JSHandle env = thread->GetEcmaVM()->GetGlobalEnv(); + + ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); + + JSHandle target = factory->NewJSFunction(env); + JSFunction::SetFunctionName(thread, JSHandle(target), + JSHandle(factory->NewFromCanBeCompressString("target")), + JSHandle(thread, JSTaggedValue::Undefined())); + JSFunction::SetFunctionLength(thread, target, JSTaggedValue(2)); + + JSHandle thisArg(thread, env->GetGlobalObject()); + + auto ecmaRuntimeCallInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread, JSTaggedValue::Undefined(), 6); + ecmaRuntimeCallInfo->SetFunction(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetThis(target.GetTaggedValue()); + ecmaRuntimeCallInfo->SetCallArg(0, (thisArg.GetTaggedValue())); + + [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread, ecmaRuntimeCallInfo.get()); + JSTaggedValue result = BuiltinsFunction::FunctionPrototypeBind(ecmaRuntimeCallInfo.get()); + + ASSERT_TRUE(result.IsECMAObject()); + + JSHandle resultFunc(thread, reinterpret_cast(result.GetRawData())); + // test BoundTarget + ASSERT_EQ(resultFunc->GetBoundTarget(), target.GetTaggedValue()); + // test BoundThis + ASSERT_EQ(resultFunc->GetBoundThis(), thisArg.GetTaggedValue()); + // test BoundArguments + JSHandle array(thread, resultFunc->GetBoundArguments()); + ASSERT_EQ(array->GetLength(), 0); + // test name property + auto globalConst = thread->GlobalConstants(); + JSHandle nameKey = globalConst->GetHandledNameString(); + JSHandle resultFuncHandle(thread, *resultFunc); + JSHandle resultName(JSObject::GetProperty(thread, resultFuncHandle, nameKey).GetValue()); + JSHandle boundTarget = factory->NewFromCanBeCompressString("bound target"); + ASSERT_EQ(resultName->Compare(*boundTarget), 0); + // test length property + JSHandle lengthKey = globalConst->GetHandledLengthString(); + JSHandle resultLength(JSObject::GetProperty(thread, resultFuncHandle, lengthKey).GetValue()); + ASSERT_EQ(JSTaggedValue::ToNumber(thread, resultLength).GetNumber(), 2.0); +} + +// target.bind(thisArg, 123, "helloworld") +TEST_F(BuiltinsFunctionTest, FunctionPrototypeBind1) +{ + JSHandle env = thread->GetEcmaVM()->GetGlobalEnv(); + + ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); + + JSHandle target = factory->NewJSFunction(env); + JSFunction::SetFunctionName(thread, JSHandle(target), + JSHandle(factory->NewFromCanBeCompressString("target1")), + JSHandle(thread, JSTaggedValue::Undefined())); + JSFunction::SetFunctionLength(thread, target, JSTaggedValue(5)); + + JSHandle thisArg(thread, env->GetGlobalObject()); + JSHandle str = factory->NewFromCanBeCompressString("helloworld"); + + auto ecmaRuntimeCallInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread, JSTaggedValue::Undefined(), 10); + ecmaRuntimeCallInfo->SetFunction(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetThis(target.GetTaggedValue()); + ecmaRuntimeCallInfo->SetCallArg(0, thisArg.GetTaggedValue()); + ecmaRuntimeCallInfo->SetCallArg(1, JSTaggedValue(static_cast(123))); + ecmaRuntimeCallInfo->SetCallArg(2, str.GetTaggedValue()); + + [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread, ecmaRuntimeCallInfo.get()); + JSTaggedValue result = BuiltinsFunction::FunctionPrototypeBind(ecmaRuntimeCallInfo.get()); + + ASSERT_TRUE(result.IsECMAObject()); + + JSHandle resultFunc(thread, reinterpret_cast(result.GetRawData())); + // test BoundTarget + ASSERT_EQ(resultFunc->GetBoundTarget(), target.GetTaggedValue()); + // test BoundThis + ASSERT_EQ(resultFunc->GetBoundThis(), thisArg.GetTaggedValue()); + // test BoundArguments + JSHandle array(thread, resultFunc->GetBoundArguments()); + ASSERT_EQ(array->GetLength(), 2); + JSTaggedValue elem = array->Get(0); + JSTaggedValue elem1 = array->Get(1); + ASSERT_EQ(elem.GetRawData(), JSTaggedValue(123).GetRawData()); + + ASSERT_EQ(elem1.GetRawData(), str.GetTaggedType()); + ASSERT_TRUE(elem1.IsString()); + // test name property + auto globalConst = thread->GlobalConstants(); + JSHandle nameKey = globalConst->GetHandledNameString(); + JSHandle resultFuncHandle(thread, *resultFunc); + JSHandle resultName(JSObject::GetProperty(thread, resultFuncHandle, nameKey).GetValue()); + JSHandle rulerName = factory->NewFromCanBeCompressString("bound target1"); + ASSERT_EQ(resultName->Compare(*rulerName), 0); + // test length property + JSHandle lengthKey = globalConst->GetHandledLengthString(); + JSHandle resultLength(JSObject::GetProperty(thread, resultFuncHandle, lengthKey).GetValue()); + // target.length is 5, (...args) length is 2 + ASSERT_EQ(JSTaggedValue::ToNumber(thread, resultLength).GetNumber(), 3.0); +} + +// target.bind(thisArg, 123, "helloworld") set target_name = EmptyString() +TEST_F(BuiltinsFunctionTest, FunctionPrototypeBind2) +{ + JSHandle env = thread->GetEcmaVM()->GetGlobalEnv(); + + ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); + + JSHandle target = factory->NewJSFunction(env); + PropertyDescriptor nameDesc(thread, JSHandle(thread, JSTaggedValue(123)), false, false, true); + JSTaggedValue::DefinePropertyOrThrow(thread, JSHandle(target), + thread->GlobalConstants()->GetHandledNameString(), nameDesc); + JSFunction::SetFunctionLength(thread, target, JSTaggedValue(5)); + + JSHandle thisArg(thread, env->GetGlobalObject()); + JSHandle str = factory->NewFromCanBeCompressString("helloworld"); + + auto ecmaRuntimeCallInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread, JSTaggedValue::Undefined(), 10); + ecmaRuntimeCallInfo->SetFunction(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetThis(target.GetTaggedValue()); + ecmaRuntimeCallInfo->SetCallArg(0, (thisArg.GetTaggedValue())); + ecmaRuntimeCallInfo->SetCallArg(1, JSTaggedValue(static_cast(123))); + ecmaRuntimeCallInfo->SetCallArg(2, str.GetTaggedValue()); + + [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread, ecmaRuntimeCallInfo.get()); + JSTaggedValue result = BuiltinsFunction::FunctionPrototypeBind(ecmaRuntimeCallInfo.get()); + + ASSERT_TRUE(result.IsECMAObject()); + + JSHandle resultFunc(thread, reinterpret_cast(result.GetRawData())); + // test BoundTarget + ASSERT_EQ(resultFunc->GetBoundTarget(), target.GetTaggedValue()); + // test BoundThis + ASSERT_EQ(resultFunc->GetBoundThis(), thisArg.GetTaggedValue()); + // test BoundArguments + JSHandle array(thread, resultFunc->GetBoundArguments()); + ASSERT_EQ(array->GetLength(), 2); + JSTaggedValue elem = array->Get(0); + JSTaggedValue elem1 = array->Get(1); + ASSERT_EQ(elem.GetRawData(), JSTaggedValue(123).GetRawData()); + + ASSERT_EQ(elem1.GetRawData(), str.GetTaggedType()); + ASSERT_TRUE(elem1.IsString()); + // test name property + auto globalConst = thread->GlobalConstants(); + JSHandle nameKey = globalConst->GetHandledNameString(); + JSHandle resultFuncHandle(resultFunc); + JSHandle resultName(JSObject::GetProperty(thread, resultFuncHandle, nameKey).GetValue()); + JSHandle rulerName = factory->NewFromCanBeCompressString("bound "); + ASSERT_EQ(resultName->Compare(*rulerName), 0); + // test length property + JSHandle lengthKey = globalConst->GetHandledLengthString(); + JSHandle resultLength(JSObject::GetProperty(thread, resultFuncHandle, lengthKey).GetValue()); + // target.length is 5, (...args) length is 2 + ASSERT_EQ(JSTaggedValue::ToNumber(thread, resultLength).GetNumber(), 3.0); +} + +// func.call(thisArg) +TEST_F(BuiltinsFunctionTest, FunctionPrototypeCall) +{ + JSHandle env = thread->GetEcmaVM()->GetGlobalEnv(); + + ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); + + // ecma 19.2.3.3: func + JSHandle func = factory->NewJSFunction(env, reinterpret_cast(TestFunctionApplyAndCall)); + + // ecma 19.2.3.3: thisArg + JSHandle thisArg(thread, env->GetGlobalObject()); + JSObject::SetProperty(thread, JSHandle(thisArg), + JSHandle(factory->NewFromCanBeCompressString("test_builtins_function_a")), + JSHandle(thread, JSTaggedValue(1))); + JSObject::SetProperty(thread, JSHandle(thisArg), + JSHandle(factory->NewFromCanBeCompressString("test_builtins_function_b")), + JSHandle(thread, JSTaggedValue(2))); + + auto ecmaRuntimeCallInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread, JSTaggedValue::Undefined(), 6); + ecmaRuntimeCallInfo->SetFunction(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetThis(func.GetTaggedValue()); + ecmaRuntimeCallInfo->SetCallArg(0, (thisArg.GetTaggedValue())); + + [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread, ecmaRuntimeCallInfo.get()); + JSTaggedValue result = BuiltinsFunction::FunctionPrototypeCall(ecmaRuntimeCallInfo.get()); + + ASSERT_EQ(result.GetRawData(), JSTaggedValue(3).GetRawData()); + + JSObject::DeleteProperty(thread, (thisArg), + JSHandle(factory->NewFromCanBeCompressString("test_builtins_function_a"))); + JSObject::DeleteProperty(thread, (thisArg), + JSHandle(factory->NewFromCanBeCompressString("test_builtins_function_b"))); +} + +// func.call(thisArg, 123, 456, 789) +TEST_F(BuiltinsFunctionTest, FunctionPrototypeCall1) +{ + JSHandle env = thread->GetEcmaVM()->GetGlobalEnv(); + + ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); + + // ecma 19.2.3.3: func + JSHandle func = factory->NewJSFunction(env, reinterpret_cast(TestFunctionApplyAndCall)); + + // ecma 19.2.3.3: thisArg + JSHandle thisArg(thread, env->GetGlobalObject()); + JSObject::SetProperty(thread, JSHandle(thisArg), + JSHandle(factory->NewFromCanBeCompressString("test_builtins_function_a")), + JSHandle(thread, JSTaggedValue(1))); + JSObject::SetProperty(thread, JSHandle(thisArg), + JSHandle(factory->NewFromCanBeCompressString("test_builtins_function_b")), + JSHandle(thread, JSTaggedValue(2))); + + // func thisArg ...args + auto ecmaRuntimeCallInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread, JSTaggedValue::Undefined(), 12); + ecmaRuntimeCallInfo->SetFunction(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetThis(func.GetTaggedValue()); + ecmaRuntimeCallInfo->SetCallArg(0, (thisArg.GetTaggedValue())); + ecmaRuntimeCallInfo->SetCallArg(1, JSTaggedValue(static_cast(123))); + ecmaRuntimeCallInfo->SetCallArg(2, JSTaggedValue(static_cast(456))); + ecmaRuntimeCallInfo->SetCallArg(3, JSTaggedValue(static_cast(789))); + + [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread, ecmaRuntimeCallInfo.get()); + JSTaggedValue result = BuiltinsFunction::FunctionPrototypeCall(ecmaRuntimeCallInfo.get()); + + ASSERT_EQ(result.GetRawData(), JSTaggedValue(1371).GetRawData()); + + JSObject::DeleteProperty(thread, (thisArg), + JSHandle(factory->NewFromCanBeCompressString("test_builtins_function_a"))); + JSObject::DeleteProperty(thread, (thisArg), + JSHandle(factory->NewFromCanBeCompressString("test_builtins_function_b"))); +} + +TEST_F(BuiltinsFunctionTest, FunctionPrototypeHasInstance) +{ + JSHandle env = thread->GetEcmaVM()->GetGlobalEnv(); + + JSHandle booleanCtor(env->GetBooleanFunction()); + + auto ecmaRuntimeCallInfo1 = TestHelper::CreateEcmaRuntimeCallInfo(thread, JSTaggedValue(*booleanCtor), 6); + ecmaRuntimeCallInfo1->SetFunction(booleanCtor.GetTaggedValue()); + ecmaRuntimeCallInfo1->SetThis(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo1->SetCallArg(0, JSTaggedValue(static_cast(123))); + + [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread, ecmaRuntimeCallInfo1.get()); + JSTaggedValue result = BuiltinsBoolean::BooleanConstructor(ecmaRuntimeCallInfo1.get()); + TestHelper::TearDownFrame(thread, prev); + + JSHandle booleanInstance(thread, result); + + auto ecmaRuntimeCallInfo2 = TestHelper::CreateEcmaRuntimeCallInfo(thread, JSTaggedValue::Undefined(), 6); + ecmaRuntimeCallInfo2->SetFunction(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo2->SetThis(booleanCtor.GetTaggedValue()); + ecmaRuntimeCallInfo2->SetCallArg(0, booleanInstance.GetTaggedValue()); + + prev = TestHelper::SetupFrame(thread, ecmaRuntimeCallInfo2.get()); + EXPECT_TRUE(BuiltinsFunction::FunctionPrototypeHasInstance(ecmaRuntimeCallInfo2.get()).GetRawData()); + TestHelper::TearDownFrame(thread, prev); +} +} // namespace panda::test diff --git a/tests/runtime/builtins/builtins_iterator_test.cpp b/tests/runtime/builtins/builtins_iterator_test.cpp new file mode 100644 index 000000000..3c9340be1 --- /dev/null +++ b/tests/runtime/builtins/builtins_iterator_test.cpp @@ -0,0 +1,56 @@ +/* + * Copyright (c) 2021-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 "plugins/ecmascript/runtime/builtins/builtins_iterator.h" + +#include "plugins/ecmascript/runtime/ecma_runtime_call_info.h" +#include "plugins/ecmascript/runtime/global_env.h" +#include "plugins/ecmascript/runtime/js_object-inl.h" +#include "plugins/ecmascript/runtime/js_thread.h" + +#include "plugins/ecmascript/runtime/object_factory.h" +#include "plugins/ecmascript/tests/runtime/common/test_helper.h" + +using namespace panda::ecmascript; +using namespace panda::ecmascript::builtins; + +namespace panda::test { +class BuiltinsIteratorTest : public testing::Test { +public: + static void SetUpTestCase() + { + GTEST_LOG_(INFO) << "SetUpTestCase"; + } + + static void TearDownTestCase() + { + GTEST_LOG_(INFO) << "TearDownCase"; + } + + void SetUp() override + { + TestHelper::CreateEcmaVMWithScope(instance, thread, scope); + } + + void TearDown() override + { + TestHelper::DestroyEcmaVMWithScope(instance, scope); + } + + PandaVM *instance {nullptr}; + EcmaHandleScope *scope {nullptr}; + JSThread *thread {nullptr}; +}; +} // namespace panda::test diff --git a/tests/runtime/builtins/builtins_json_test.cpp b/tests/runtime/builtins/builtins_json_test.cpp new file mode 100644 index 000000000..11b595602 --- /dev/null +++ b/tests/runtime/builtins/builtins_json_test.cpp @@ -0,0 +1,408 @@ +/* + * Copyright (c) 2021-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 +#include + +#include "algorithm" +#include "plugins/ecmascript/runtime/base/builtins_base.h" +#include "plugins/ecmascript/runtime/builtins/builtins_errors.h" +#include "plugins/ecmascript/runtime/builtins/builtins_json.h" +#include "plugins/ecmascript/runtime/ecma_runtime_call_info.h" +#include "plugins/ecmascript/runtime/ecma_string-inl.h" +#include "plugins/ecmascript/runtime/ecma_vm.h" +#include "plugins/ecmascript/runtime/global_env.h" +#include "plugins/ecmascript/runtime/js_array.h" +#include "plugins/ecmascript/runtime/js_function.h" +#include "plugins/ecmascript/runtime/js_handle.h" +#include "plugins/ecmascript/runtime/js_invoker.h" +#include "plugins/ecmascript/runtime/js_object-inl.h" +#include "plugins/ecmascript/runtime/js_primitive_ref.h" +#include "plugins/ecmascript/runtime/js_tagged_value-inl.h" +#include "plugins/ecmascript/runtime/js_tagged_value.h" +#include "plugins/ecmascript/runtime/js_thread.h" +#include "plugins/ecmascript/runtime/object_factory.h" +#include "plugins/ecmascript/tests/runtime/common/test_helper.h" + +using namespace panda::ecmascript; +using namespace panda::ecmascript::builtins; + +namespace panda::test { +class BuiltinsJsonTest : public testing::Test { +public: + static void SetUpTestCase() + { + GTEST_LOG_(INFO) << "SetUpTestCase"; + } + + static void TearDownTestCase() + { + GTEST_LOG_(INFO) << "TearDownCase"; + } + + void SetUp() override + { + TestHelper::CreateEcmaVMWithScope(instance, thread, scope); + } + + void TearDown() override + { + TestHelper::DestroyEcmaVMWithScope(instance, scope); + } + + PandaVM *instance {nullptr}; + EcmaHandleScope *scope {nullptr}; + JSThread *thread {nullptr}; + + class TestClass : public base::BuiltinsBase { + public: + static JSTaggedValue TestForParse(EcmaRuntimeCallInfo *argv) + { + uint32_t argc = argv->GetArgsNumber(); + if (argc > 0) { + } + JSTaggedValue key = GetCallArg(argv, 0).GetTaggedValue(); + if (key.IsUndefined()) { + return JSTaggedValue::Undefined(); + } + JSTaggedValue value = GetCallArg(argv, 1).GetTaggedValue(); + if (value.IsUndefined()) { + return JSTaggedValue::Undefined(); + } + + return JSTaggedValue(value); + } + + static JSTaggedValue TestForParse1(EcmaRuntimeCallInfo *argv) + { + uint32_t argc = argv->GetArgsNumber(); + if (argc > 0) { + } + return JSTaggedValue::Undefined(); + } + + static JSTaggedValue TestForStringfy(EcmaRuntimeCallInfo *argv) + { + uint32_t argc = argv->GetArgsNumber(); + if (argc > 0) { + JSTaggedValue key = GetCallArg(argv, 0).GetTaggedValue(); + if (key.IsUndefined()) { + return JSTaggedValue::Undefined(); + } + JSTaggedValue value = GetCallArg(argv, 1).GetTaggedValue(); + if (value.IsUndefined()) { + return JSTaggedValue::Undefined(); + } + return JSTaggedValue(value); + } + + return JSTaggedValue::Undefined(); + } + }; +}; + +JSTaggedValue CreateBuiltinJSObject1(JSThread *thread, const CString keyCStr) +{ + EcmaVM *ecmaVM = thread->GetEcmaVM(); + JSHandle globalEnv = ecmaVM->GetGlobalEnv(); + ObjectFactory *factory = ecmaVM->GetFactory(); + JSHandle objectFunc(globalEnv->GetObjectFunction()); + + JSHandle jsobject(factory->NewJSObjectByConstructor(JSHandle(objectFunc), objectFunc)); + EXPECT_TRUE(*jsobject != nullptr); + + JSHandle key(factory->NewFromCanBeCompressString(&keyCStr[0])); + JSHandle value(thread, JSTaggedValue(1)); + JSObject::SetProperty(thread, JSHandle(jsobject), key, value); + + CString str2 = "y"; + JSHandle key2(factory->NewFromCanBeCompressString(str2)); + JSHandle value2(thread, JSTaggedValue(2.5)); // 2.5 : test case + JSObject::SetProperty(thread, JSHandle(jsobject), key2, value2); + + CString str3 = "z"; + JSHandle key3(factory->NewFromCanBeCompressString(str3)); + JSHandle value3(factory->NewFromCanBeCompressString("abc")); + JSObject::SetProperty(thread, JSHandle(jsobject), key3, value3); + + return jsobject.GetTaggedValue(); +} +// Math.abs(-10) + +TEST_F(BuiltinsJsonTest, Parse10) +{ + ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); + + JSHandle msg(factory->NewFromCanBeCompressString( + "\t\r \n{\t\r \n \"property\"\t\r \n:\t\r \n{\t\r \n}\t\r \n,\t\r \n \"prop2\"\t\r \n:\t\r \n [\t\r \ntrue\t\r " + "\n,\t\r \nnull\t\r \n,123.456\t\r \n] \t\r \n}\t\r \n")); + JSHandle str(JSTaggedValue::ToString(thread, msg)); + + auto ecmaRuntimeCallInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread, JSTaggedValue::Undefined(), 6); + ecmaRuntimeCallInfo->SetFunction(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetThis(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetCallArg(0, str.GetTaggedValue()); + + [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread, ecmaRuntimeCallInfo.get()); + JSTaggedValue result = BuiltinsJson::Parse(ecmaRuntimeCallInfo.get()); + ASSERT_TRUE(result.IsECMAObject()); +} + +TEST_F(BuiltinsJsonTest, Parse21) +{ + ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); + JSHandle env = thread->GetEcmaVM()->GetGlobalEnv(); + + JSHandle msg(factory->NewFromCanBeCompressString("[100,2.5,\"abc\"]")); + + JSHandle handleFunc = factory->NewJSFunction(env, reinterpret_cast(TestClass::TestForParse)); + JSHandle str(JSTaggedValue::ToString(thread, msg)); + + auto ecmaRuntimeCallInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread, JSTaggedValue::Undefined(), 8); + ecmaRuntimeCallInfo->SetFunction(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetThis(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetCallArg(0, str.GetTaggedValue()); + ecmaRuntimeCallInfo->SetCallArg(1, handleFunc.GetTaggedValue()); + + [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread, ecmaRuntimeCallInfo.get()); + JSTaggedValue result = BuiltinsJson::Parse(ecmaRuntimeCallInfo.get()); + ASSERT_TRUE(result.IsECMAObject()); +} + +TEST_F(BuiltinsJsonTest, Parse) +{ + ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); + JSHandle lengthKeyHandle = thread->GlobalConstants()->GetHandledLengthString(); + + JSHandle msg(factory->NewFromCanBeCompressString("[100,2.5,\"abc\"]")); + JSHandle str(JSTaggedValue::ToString(thread, msg)); + + auto ecmaRuntimeCallInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread, JSTaggedValue::Undefined(), 6); + ecmaRuntimeCallInfo->SetFunction(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetThis(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetCallArg(0, str.GetTaggedValue()); + + [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread, ecmaRuntimeCallInfo.get()); + JSTaggedValue result = BuiltinsJson::Parse(ecmaRuntimeCallInfo.get()); + JSTaggedValue value(static_cast(result.GetRawData())); + ASSERT_TRUE(value.IsECMAObject()); + JSHandle valueHandle(thread, value); + JSHandle lenResult = + JSObject::GetProperty(thread, JSHandle(valueHandle), lengthKeyHandle).GetValue(); + uint32_t length = JSTaggedValue::ToLength(thread, lenResult).ToUint32(); + EXPECT_EQ(length, 3); +} + +TEST_F(BuiltinsJsonTest, Parse2) +{ + ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); + JSHandle msg(factory->NewFromCanBeCompressString("{\"epf\":100,\"key1\":200}")); + JSHandle str(JSTaggedValue::ToString(thread, msg)); + + auto ecmaRuntimeCallInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread, JSTaggedValue::Undefined(), 6); + ecmaRuntimeCallInfo->SetFunction(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetThis(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetCallArg(0, str.GetTaggedValue()); + + [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread, ecmaRuntimeCallInfo.get()); + JSTaggedValue result = BuiltinsJson::Parse(ecmaRuntimeCallInfo.get()); + JSTaggedValue value(static_cast(result.GetRawData())); + ASSERT_TRUE(value.IsECMAObject()); + JSHandle valueHandle(thread, value); + + JSHandle nameList(JSObject::EnumerableOwnNames(thread, valueHandle)); + JSHandle nameResult = JSArray::CreateArrayFromList(thread, nameList); + + JSHandle handleKey(nameResult); + JSHandle lengthKey(factory->NewFromCanBeCompressString("length")); + JSHandle lenResult = JSObject::GetProperty(thread, handleKey, lengthKey).GetValue(); + uint32_t length = JSTaggedValue::ToLength(thread, lenResult).ToUint32(); + ASSERT_EQ(length, 2); +} + +TEST_F(BuiltinsJsonTest, Stringify11) +{ + ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); + JSHandle env = thread->GetEcmaVM()->GetGlobalEnv(); + JSHandle obj = JSHandle(thread, CreateBuiltinJSObject1(thread, "x")); + JSHandle handleFunc = + factory->NewJSFunction(env, reinterpret_cast(TestClass::TestForStringfy)); + + auto ecmaRuntimeCallInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread, JSTaggedValue::Undefined(), 8); + ecmaRuntimeCallInfo->SetFunction(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetThis(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetCallArg(0, obj.GetTaggedValue()); + ecmaRuntimeCallInfo->SetCallArg(1, handleFunc.GetTaggedValue()); + + [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread, ecmaRuntimeCallInfo.get()); + JSTaggedValue result = BuiltinsJson::Stringify(ecmaRuntimeCallInfo.get()); + ASSERT_TRUE(result.IsString()); +} + +TEST_F(BuiltinsJsonTest, Stringify12) +{ + ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); + JSHandle obj = JSHandle(thread, CreateBuiltinJSObject1(thread, "x")); + JSHandle env = thread->GetEcmaVM()->GetGlobalEnv(); + JSHandle handleFunc = + factory->NewJSFunction(env, reinterpret_cast(TestClass::TestForStringfy)); + + auto ecmaRuntimeCallInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread, JSTaggedValue::Undefined(), 10); + ecmaRuntimeCallInfo->SetFunction(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetThis(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetCallArg(0, obj.GetTaggedValue()); + ecmaRuntimeCallInfo->SetCallArg(1, handleFunc.GetTaggedValue()); + ecmaRuntimeCallInfo->SetCallArg(2, JSTaggedValue(static_cast(10))); + + [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread, ecmaRuntimeCallInfo.get()); + JSTaggedValue result = BuiltinsJson::Stringify(ecmaRuntimeCallInfo.get()); + ASSERT_TRUE(result.IsString()); +} + +TEST_F(BuiltinsJsonTest, Stringify13) +{ + ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); + JSHandle obj = JSHandle(thread, CreateBuiltinJSObject1(thread, "x")); + JSHandle env = thread->GetEcmaVM()->GetGlobalEnv(); + JSHandle handleFunc = + factory->NewJSFunction(env, reinterpret_cast(TestClass::TestForStringfy)); + JSHandle msg(factory->NewFromCanBeCompressString("tttt")); + JSHandle str(JSTaggedValue::ToString(thread, msg)); + + auto ecmaRuntimeCallInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread, JSTaggedValue::Undefined(), 10); + ecmaRuntimeCallInfo->SetFunction(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetThis(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetCallArg(0, obj.GetTaggedValue()); + ecmaRuntimeCallInfo->SetCallArg(1, handleFunc.GetTaggedValue()); + ecmaRuntimeCallInfo->SetCallArg(2, str.GetTaggedValue()); + + [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread, ecmaRuntimeCallInfo.get()); + JSTaggedValue result = BuiltinsJson::Stringify(ecmaRuntimeCallInfo.get()); + ASSERT_TRUE(result.IsString()); +} + +TEST_F(BuiltinsJsonTest, Stringify14) +{ + ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); + JSHandle obj = JSHandle(thread, CreateBuiltinJSObject1(thread, "x")); + JSArray *arr = JSArray::Cast(JSArray::ArrayCreate(thread, JSTaggedNumber(0)).GetTaggedValue().GetTaggedObject()); + + JSHandle obj1(thread, arr); + JSHandle key0(thread, JSTaggedValue(0)); + JSHandle value0(factory->NewFromCanBeCompressString("x")); + JSObject::SetProperty(thread, JSHandle(obj), key0, value0); + JSHandle key1(thread, JSTaggedValue(1)); + JSHandle value1(factory->NewFromCanBeCompressString("z")); + JSObject::SetProperty(thread, JSHandle(obj), key1, value1); + + JSHandle msg(factory->NewFromCanBeCompressString("tttt")); + JSHandle str(JSTaggedValue::ToString(thread, msg)); + + auto ecmaRuntimeCallInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread, JSTaggedValue::Undefined(), 10); + ecmaRuntimeCallInfo->SetFunction(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetThis(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetCallArg(0, obj.GetTaggedValue()); + ecmaRuntimeCallInfo->SetCallArg(1, obj1.GetTaggedValue()); + ecmaRuntimeCallInfo->SetCallArg(2, str.GetTaggedValue()); + + [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread, ecmaRuntimeCallInfo.get()); + JSTaggedValue result = BuiltinsJson::Stringify(ecmaRuntimeCallInfo.get()); + ASSERT_TRUE(result.IsString()); +} + +TEST_F(BuiltinsJsonTest, Stringify) +{ + JSHandle obj = JSHandle(thread, CreateBuiltinJSObject1(thread, "x")); + auto ecmaRuntimeCallInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread, JSTaggedValue::Undefined(), 6); + ecmaRuntimeCallInfo->SetFunction(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetThis(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetCallArg(0, obj.GetTaggedValue()); + + [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread, ecmaRuntimeCallInfo.get()); + JSTaggedValue result = BuiltinsJson::Stringify(ecmaRuntimeCallInfo.get()); + ASSERT_TRUE(result.IsString()); +} + +TEST_F(BuiltinsJsonTest, Stringify1) +{ + auto ecmaVM = thread->GetEcmaVM(); + ObjectFactory *factory = ecmaVM->GetFactory(); + JSHandle env = ecmaVM->GetGlobalEnv(); + + JSArray *arr = JSArray::Cast(JSArray::ArrayCreate(thread, JSTaggedNumber(0)).GetTaggedValue().GetTaggedObject()); + + EXPECT_TRUE(arr != nullptr); + JSHandle obj(thread, arr); + JSHandle key0(thread, JSTaggedValue(0)); + + JSHandle value(factory->NewFromCanBeCompressString("def")); + JSObject::SetProperty(thread, JSHandle(obj), key0, value); + + JSHandle key1(thread, JSTaggedValue(1)); + PropertyDescriptor desc1(thread, JSHandle(thread, JSTaggedValue(200)), true, true, true); + JSArray::DefineOwnProperty(thread, obj, key1, desc1); + + JSHandle key2(thread, JSTaggedValue(2)); + JSHandle value2(factory->NewFromCanBeCompressString("abc")); + JSObject::SetProperty(thread, JSHandle(obj), key2, value2); + + JSHandle handleFunc = + factory->NewJSFunction(env, reinterpret_cast(TestClass::TestForStringfy)); + JSHandle msg(factory->NewFromCanBeCompressString("tttt")); + JSHandle str(JSTaggedValue::ToString(thread, msg)); + + auto ecmaRuntimeCallInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread, JSTaggedValue::Undefined(), 10); + ecmaRuntimeCallInfo->SetFunction(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetThis(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetCallArg(0, obj.GetTaggedValue()); + ecmaRuntimeCallInfo->SetCallArg(1, handleFunc.GetTaggedValue()); + ecmaRuntimeCallInfo->SetCallArg(2, str.GetTaggedValue()); + + [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread, ecmaRuntimeCallInfo.get()); + JSTaggedValue result = BuiltinsJson::Stringify(ecmaRuntimeCallInfo.get()); + ASSERT_TRUE(result.IsString()); +} + +TEST_F(BuiltinsJsonTest, Stringify2) +{ + auto ecmaVM = thread->GetEcmaVM(); + ObjectFactory *factory = ecmaVM->GetFactory(); + + JSArray *arr = JSArray::Cast(JSArray::ArrayCreate(thread, JSTaggedNumber(0)).GetTaggedValue().GetTaggedObject()); + EXPECT_TRUE(arr != nullptr); + JSHandle obj(thread, arr); + + JSHandle key0(thread, JSTaggedValue(0)); + PropertyDescriptor desc0(thread, JSHandle(thread, JSTaggedValue(1)), true, true, true); + JSArray::DefineOwnProperty(thread, obj, key0, desc0); + JSHandle key1(thread, JSTaggedValue(1)); + // 2.5 : test case + PropertyDescriptor desc1(thread, JSHandle(thread, JSTaggedValue(2.5)), true, true, true); + JSArray::DefineOwnProperty(thread, obj, key1, desc1); + // 2 : test case + JSHandle key2(thread, JSTaggedValue(2)); + JSHandle value2(factory->NewFromCanBeCompressString("abc")); + JSObject::SetProperty(thread, JSHandle(obj), key2, value2); + + auto ecmaRuntimeCallInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread, JSTaggedValue::Undefined(), 6); + ecmaRuntimeCallInfo->SetFunction(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetThis(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetCallArg(0, obj.GetTaggedValue()); + + [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread, ecmaRuntimeCallInfo.get()); + JSTaggedValue result = BuiltinsJson::Stringify(ecmaRuntimeCallInfo.get()); + ASSERT_TRUE(result.IsString()); +} +} // namespace panda::test diff --git a/tests/runtime/builtins/builtins_map_test.cpp b/tests/runtime/builtins/builtins_map_test.cpp new file mode 100644 index 000000000..28f0aa85a --- /dev/null +++ b/tests/runtime/builtins/builtins_map_test.cpp @@ -0,0 +1,359 @@ +/* + * Copyright (c) 2021-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 "plugins/ecmascript/runtime/base/builtins_base.h" +#include "plugins/ecmascript/runtime/builtins/builtins_map.h" +#include "plugins/ecmascript/runtime/ecma_runtime_call_info.h" +#include "plugins/ecmascript/runtime/ecma_string.h" +#include "plugins/ecmascript/runtime/ecma_vm.h" +#include "plugins/ecmascript/runtime/global_env.h" +#include "plugins/ecmascript/runtime/js_array.h" +#include "plugins/ecmascript/runtime/js_handle.h" +#include "plugins/ecmascript/runtime/js_hclass.h" +#include "plugins/ecmascript/runtime/js_map.h" +#include "plugins/ecmascript/runtime/js_map_iterator.h" +#include "plugins/ecmascript/runtime/js_object-inl.h" +#include "plugins/ecmascript/runtime/js_tagged_value.h" +#include "plugins/ecmascript/runtime/js_thread.h" +#include "plugins/ecmascript/runtime/object_factory.h" +#include "plugins/ecmascript/runtime/tagged_array-inl.h" +#include "plugins/ecmascript/tests/runtime/common/test_helper.h" +#include "utils/bit_utils.h" + +using namespace panda::ecmascript; +using namespace panda::ecmascript::builtins; + +namespace panda::test { +using BuiltinsMap = ecmascript::builtins::BuiltinsMap; +using JSMap = ecmascript::JSMap; + +class BuiltinsMapTest : public testing::Test { +public: + static void SetUpTestCase() + { + GTEST_LOG_(INFO) << "SetUpTestCase"; + } + + static void TearDownTestCase() + { + GTEST_LOG_(INFO) << "TearDownCase"; + } + + void SetUp() override + { + TestHelper::CreateEcmaVMWithScope(instance, thread, scope); + } + + void TearDown() override + { + TestHelper::DestroyEcmaVMWithScope(instance, scope); + } + + PandaVM *instance {nullptr}; + EcmaHandleScope *scope {nullptr}; + JSThread *thread {nullptr}; + + class TestClass : public base::BuiltinsBase { + public: + static JSTaggedValue TestFunc(EcmaRuntimeCallInfo *argv) + { + int num = GetCallArg(argv, 0)->GetInt(); + JSArray *jsArray = JSArray::Cast(GetThis(argv)->GetTaggedObject()); + int length = jsArray->GetArrayLength() + num; + jsArray->SetArrayLength(argv->GetThread(), length); + return JSTaggedValue::Undefined(); + } + }; +}; + +JSMap *CreateBuiltinsMap(JSThread *thread) +{ + JSHandle env = thread->GetEcmaVM()->GetGlobalEnv(); + JSHandle newTarget(env->GetBuiltinsMapFunction()); + // 4 : test case + auto ecmaRuntimeCallInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread, JSTaggedValue(*newTarget), 4); + ecmaRuntimeCallInfo->SetFunction(newTarget.GetTaggedValue()); + ecmaRuntimeCallInfo->SetThis(JSTaggedValue::Undefined()); + + [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread, ecmaRuntimeCallInfo.get()); + JSTaggedValue result = BuiltinsMap::MapConstructor(ecmaRuntimeCallInfo.get()); + + EXPECT_TRUE(result.IsECMAObject()); + JSMap *jsMap = JSMap::Cast(reinterpret_cast(result.GetRawData())); + return jsMap; +} +// new Map("abrupt").toString() +TEST_F(BuiltinsMapTest, CreateAndGetSize) +{ + ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); + JSHandle env = thread->GetEcmaVM()->GetGlobalEnv(); + JSHandle newTarget(env->GetBuiltinsMapFunction()); + JSHandle map(thread, CreateBuiltinsMap(thread)); + + auto ecmaRuntimeCallInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread, JSTaggedValue::Undefined(), 6); + ecmaRuntimeCallInfo->SetFunction(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetThis(map.GetTaggedValue()); + ecmaRuntimeCallInfo->SetCallArg(0, JSTaggedValue::Undefined()); + + { + [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread, ecmaRuntimeCallInfo.get()); + JSTaggedValue result = BuiltinsMap::GetSize(ecmaRuntimeCallInfo.get()); + + EXPECT_EQ(result.GetRawData(), JSTaggedValue(0).GetRawData()); + } + JSHandle array(factory->NewTaggedArray(5)); + for (int i = 0; i < 5; i++) { + JSHandle internal_array(factory->NewTaggedArray(2)); + internal_array->Set(thread, 0, JSTaggedValue(i)); + internal_array->Set(thread, 1, JSTaggedValue(i)); + auto arr = JSArray::CreateArrayFromList(thread, internal_array); + array->Set(thread, i, arr); + } + JSHandle values = JSArray::CreateArrayFromList(thread, array); + auto ecmaRuntimeCallInfo1 = TestHelper::CreateEcmaRuntimeCallInfo(thread, JSTaggedValue::Undefined(), 6); + ecmaRuntimeCallInfo1->SetFunction(newTarget.GetTaggedValue()); + ecmaRuntimeCallInfo1->SetThis(map.GetTaggedValue()); + ecmaRuntimeCallInfo1->SetCallArg(0, values.GetTaggedValue()); + ecmaRuntimeCallInfo1->SetNewTarget(newTarget.GetTaggedValue()); + { + [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread, ecmaRuntimeCallInfo1.get()); + JSTaggedValue result1 = BuiltinsMap::MapConstructor(ecmaRuntimeCallInfo1.get()); + + EXPECT_EQ(JSMap::Cast(reinterpret_cast(result1.GetRawData()))->GetSize(), 5); + } +} + +TEST_F(BuiltinsMapTest, SetAndHas) +{ + ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); + // create jsMap + JSHandle map(thread, CreateBuiltinsMap(thread)); + JSHandle key(factory->NewFromCanBeCompressString("key")); + + auto ecmaRuntimeCallInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread, JSTaggedValue::Undefined(), 8); + ecmaRuntimeCallInfo->SetFunction(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetThis(map.GetTaggedValue()); + ecmaRuntimeCallInfo->SetCallArg(0, key.GetTaggedValue()); + ecmaRuntimeCallInfo->SetCallArg(1, JSTaggedValue(static_cast(1))); + + JSMap *jsMap; + { + [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread, ecmaRuntimeCallInfo.get()); + JSTaggedValue result1 = BuiltinsMap::Has(ecmaRuntimeCallInfo.get()); + + EXPECT_EQ(result1.GetRawData(), JSTaggedValue::False().GetRawData()); + + // test Set() + JSTaggedValue result2 = BuiltinsMap::Set(ecmaRuntimeCallInfo.get()); + + EXPECT_TRUE(result2.IsECMAObject()); + jsMap = JSMap::Cast(reinterpret_cast(result2.GetRawData())); + EXPECT_EQ(jsMap->GetSize(), 1); + } + + // test Has() + auto ecmaRuntimeCallInfo1 = TestHelper::CreateEcmaRuntimeCallInfo(thread, JSTaggedValue::Undefined(), 8); + ecmaRuntimeCallInfo1->SetFunction(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo1->SetThis(JSTaggedValue(jsMap)); + ecmaRuntimeCallInfo1->SetCallArg(0, key.GetTaggedValue()); + ecmaRuntimeCallInfo1->SetCallArg(1, JSTaggedValue(static_cast(1))); + { + [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread, ecmaRuntimeCallInfo1.get()); + JSTaggedValue result3 = BuiltinsMap::Has(ecmaRuntimeCallInfo1.get()); + + EXPECT_EQ(result3.GetRawData(), JSTaggedValue::True().GetRawData()); + } +} + +TEST_F(BuiltinsMapTest, ForEach) +{ + // generate a map has 5 entries{key1:0,key2:1,key3:2,key4:3,key5:4} + ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); + JSHandle env = thread->GetEcmaVM()->GetGlobalEnv(); + JSHandle map(thread, CreateBuiltinsMap(thread)); + char keyArray[] = "key0"; + for (int i = 0; i < 5; i++) { + keyArray[3] = '1' + i; + JSHandle key(factory->NewFromCanBeCompressString(keyArray)); + auto ecmaRuntimeCallInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread, JSTaggedValue::Undefined(), 8); + ecmaRuntimeCallInfo->SetFunction(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetThis(map.GetTaggedValue()); + ecmaRuntimeCallInfo->SetCallArg(0, key.GetTaggedValue()); + ecmaRuntimeCallInfo->SetCallArg(1, JSTaggedValue(static_cast(i))); + + [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread, ecmaRuntimeCallInfo.get()); + JSTaggedValue result1 = BuiltinsMap::Set(ecmaRuntimeCallInfo.get()); + EXPECT_TRUE(result1.IsECMAObject()); + JSMap *jsMap = JSMap::Cast(reinterpret_cast(result1.GetRawData())); + EXPECT_EQ(jsMap->GetSize(), i + 1); + } + // test foreach; + JSHandle jsArray(JSArray::ArrayCreate(thread, JSTaggedNumber(0))); + JSHandle func = factory->NewJSFunction(env, reinterpret_cast(TestClass::TestFunc)); + + auto ecmaRuntimeCallInfo1 = TestHelper::CreateEcmaRuntimeCallInfo(thread, JSTaggedValue::Undefined(), 8); + ecmaRuntimeCallInfo1->SetFunction(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo1->SetThis(map.GetTaggedValue()); + ecmaRuntimeCallInfo1->SetCallArg(0, func.GetTaggedValue()); + ecmaRuntimeCallInfo1->SetCallArg(1, jsArray.GetTaggedValue()); + + [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread, ecmaRuntimeCallInfo1.get()); + JSTaggedValue result2 = BuiltinsMap::ForEach(ecmaRuntimeCallInfo1.get()); + + EXPECT_EQ(result2.GetRawData(), JSTaggedValue::VALUE_UNDEFINED); + EXPECT_EQ(jsArray->GetArrayLength(), 10); +} + +TEST_F(BuiltinsMapTest, DeleteAndRemove) +{ + ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); + // create jsMap + JSHandle map(thread, CreateBuiltinsMap(thread)); + + // add 40 keys + char keyArray[] = "key0"; + for (int i = 0; i < 40; i++) { + keyArray[3] = '1' + i; + JSHandle key(thread, factory->NewFromCanBeCompressString(keyArray).GetTaggedValue()); + auto ecmaRuntimeCallInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread, JSTaggedValue::Undefined(), 8); + ecmaRuntimeCallInfo->SetFunction(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetThis(map.GetTaggedValue()); + ecmaRuntimeCallInfo->SetCallArg(0, key.GetTaggedValue()); + ecmaRuntimeCallInfo->SetCallArg(1, JSTaggedValue(static_cast(i))); + + [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread, ecmaRuntimeCallInfo.get()); + JSTaggedValue result1 = BuiltinsMap::Set(ecmaRuntimeCallInfo.get()); + + EXPECT_TRUE(result1.IsECMAObject()); + JSMap *jsMap = JSMap::Cast(reinterpret_cast(result1.GetRawData())); + EXPECT_EQ(jsMap->GetSize(), i + 1); + } + // whether jsMap has delete key + keyArray[3] = '1' + 8; + JSHandle deleteKey(factory->NewFromCanBeCompressString(keyArray)); + + auto ecmaRuntimeCallInfo1 = TestHelper::CreateEcmaRuntimeCallInfo(thread, JSTaggedValue::Undefined(), 6); + ecmaRuntimeCallInfo1->SetFunction(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo1->SetThis(map.GetTaggedValue()); + ecmaRuntimeCallInfo1->SetCallArg(0, deleteKey.GetTaggedValue()); + + [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread, ecmaRuntimeCallInfo1.get()); + JSTaggedValue result2 = BuiltinsMap::Has(ecmaRuntimeCallInfo1.get()); + + EXPECT_EQ(result2.GetRawData(), JSTaggedValue::True().GetRawData()); + + // delete + JSTaggedValue result3 = BuiltinsMap::Delete(ecmaRuntimeCallInfo1.get()); + + EXPECT_EQ(result3.GetRawData(), JSTaggedValue::True().GetRawData()); + + // check deleteKey is deleted + JSTaggedValue result4 = BuiltinsMap::Has(ecmaRuntimeCallInfo1.get()); + + EXPECT_EQ(result4.GetRawData(), JSTaggedValue::False().GetRawData()); + JSTaggedValue result5 = BuiltinsMap::GetSize(ecmaRuntimeCallInfo1.get()); + + EXPECT_EQ(result5.GetRawData(), JSTaggedValue(39).GetRawData()); + + // clear + JSTaggedValue result6 = BuiltinsMap::Clear(ecmaRuntimeCallInfo1.get()); + EXPECT_EQ(result6.GetRawData(), JSTaggedValue::VALUE_UNDEFINED); + EXPECT_EQ(map->GetSize(), 0); +} + +TEST_F(BuiltinsMapTest, Species) +{ + ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); + JSHandle env = thread->GetEcmaVM()->GetGlobalEnv(); + JSHandle map(thread, CreateBuiltinsMap(thread)); + + // test species + JSHandle speciesSymbol = env->GetSpeciesSymbol(); + EXPECT_TRUE(!speciesSymbol.GetTaggedValue().IsUndefined()); + + JSHandle newTarget(env->GetBuiltinsMapFunction()); + + JSTaggedValue value = + JSObject::GetProperty(thread, JSHandle(newTarget), speciesSymbol).GetValue().GetTaggedValue(); + JSHandle valueHandle(thread, value); + EXPECT_EQ(value, newTarget.GetTaggedValue()); + + // to string tag + JSHandle toStringTagSymbol = env->GetToStringTagSymbol(); + JSHandle stringTag(JSObject::GetProperty(thread, map, toStringTagSymbol).GetValue()); + JSHandle str = factory->NewFromCanBeCompressString("Map"); + EXPECT_TRUE(!stringTag.GetTaggedValue().IsUndefined()); + EXPECT_TRUE(EcmaString::StringsAreEqual(*str, *stringTag)); + + JSHandle constructor = JSHandle::Cast(JSTaggedValue::ToObject(thread, valueHandle)); + EXPECT_EQ(JSHandle(map)->GetPrototype(thread), constructor->GetFunctionPrototype()); + + JSHandle key1(factory->NewFromCanBeCompressString("set")); + JSTaggedValue value1 = JSObject::GetProperty(thread, map, key1).GetValue().GetTaggedValue(); + EXPECT_FALSE(value1.IsUndefined()); + + JSHandle key2(factory->NewFromCanBeCompressString("has")); + JSTaggedValue value2 = JSObject::GetProperty(thread, map, key1).GetValue().GetTaggedValue(); + EXPECT_FALSE(value2.IsUndefined()); + + JSHandle key3(factory->NewFromCanBeCompressString("clear")); + JSTaggedValue value3 = JSObject::GetProperty(thread, map, key1).GetValue().GetTaggedValue(); + EXPECT_FALSE(value3.IsUndefined()); + + JSHandle key4(factory->NewFromCanBeCompressString("size")); + JSTaggedValue value4 = JSObject::GetProperty(thread, map, key1).GetValue().GetTaggedValue(); + EXPECT_FALSE(value4.IsUndefined()); + + JSHandle key5(factory->NewFromCanBeCompressString("delete")); + JSTaggedValue value5 = JSObject::GetProperty(thread, map, key1).GetValue().GetTaggedValue(); + EXPECT_FALSE(value5.IsUndefined()); + + JSHandle key6(factory->NewFromCanBeCompressString("forEach")); + JSTaggedValue value6 = JSObject::GetProperty(thread, map, key1).GetValue().GetTaggedValue(); + EXPECT_FALSE(value6.IsUndefined()); + + JSHandle key7(factory->NewFromCanBeCompressString("get")); + JSTaggedValue value7 = JSObject::GetProperty(thread, map, key1).GetValue().GetTaggedValue(); + EXPECT_FALSE(value7.IsUndefined()); +} + +TEST_F(BuiltinsMapTest, GetIterator) +{ + JSHandle map(thread, CreateBuiltinsMap(thread)); + auto ecmaRuntimeCallInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread, JSTaggedValue::Undefined(), 4); + ecmaRuntimeCallInfo->SetFunction(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetThis(map.GetTaggedValue()); + [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread, ecmaRuntimeCallInfo.get()); + + // test Values() + JSTaggedValue result = BuiltinsMap::Values(ecmaRuntimeCallInfo.get()); + JSHandle iter(thread, result); + EXPECT_TRUE(iter->IsJSMapIterator()); + EXPECT_EQ(IterationKind::VALUE, IterationKind(iter->GetIterationKind().GetInt())); + EXPECT_EQ(JSMap::Cast(map.GetTaggedValue().GetTaggedObject())->GetLinkedMap(), iter->GetIteratedMap()); + + // test Keys() + JSTaggedValue result1 = BuiltinsMap::Keys(ecmaRuntimeCallInfo.get()); + JSHandle iter1(thread, result1); + EXPECT_TRUE(iter1->IsJSMapIterator()); + EXPECT_EQ(IterationKind::KEY, IterationKind(iter1->GetIterationKind().GetInt())); + + // test entries() + JSTaggedValue result2 = BuiltinsMap::Entries(ecmaRuntimeCallInfo.get()); + JSHandle iter2(thread, result2); + EXPECT_TRUE(iter2->IsJSMapIterator()); + EXPECT_EQ(IterationKind::KEY_AND_VALUE, IterationKind(iter2->GetIterationKind().GetInt())); +} +} // namespace panda::test diff --git a/tests/runtime/builtins/builtins_math_test.cpp b/tests/runtime/builtins/builtins_math_test.cpp new file mode 100644 index 000000000..3c5ed834d --- /dev/null +++ b/tests/runtime/builtins/builtins_math_test.cpp @@ -0,0 +1,3924 @@ +/* + * Copyright (c) 2021-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 "plugins/ecmascript/runtime/base/number_helper.h" +#include "plugins/ecmascript/runtime/builtins/builtins_math.h" +#include "plugins/ecmascript/runtime/ecma_runtime_call_info.h" +#include "plugins/ecmascript/runtime/ecma_vm.h" +#include "plugins/ecmascript/runtime/js_tagged_value-inl.h" +#include "plugins/ecmascript/runtime/js_tagged_value.h" +#include "plugins/ecmascript/runtime/js_thread.h" +#include "plugins/ecmascript/runtime/object_factory.h" +#include "plugins/ecmascript/tests/runtime/common/test_helper.h" + +using namespace panda::ecmascript; +using namespace panda::ecmascript::builtins; +using namespace panda::ecmascript::base; + +namespace panda::test { +class BuiltinsMathTest : public testing::Test { +public: + // Workaround: Avoid thread local leak [F/runtime: cannot create thread specific key for __cxa_get_globals()] + static void SetUpTestCase() + { + TestHelper::CreateEcmaVMWithScope(instance_, thread_, scope_); + } + + static void TearDownTestCase() + { + TestHelper::DestroyEcmaVMWithScope(instance_, scope_); + } + + static PandaVM *instance_; + static EcmaHandleScope *scope_; + static JSThread *thread_; +}; +PandaVM *BuiltinsMathTest::instance_ = nullptr; +EcmaHandleScope *BuiltinsMathTest::scope_ = nullptr; +JSThread *BuiltinsMathTest::thread_ = nullptr; + +// Math.abs(-10) +TEST_F(BuiltinsMathTest, Abs) +{ + auto ecmaRuntimeCallInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread_, JSTaggedValue::Undefined(), 6); + ecmaRuntimeCallInfo->SetFunction(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetThis(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetCallArg(0, JSTaggedValue(static_cast(-10))); + + [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread_, ecmaRuntimeCallInfo.get()); + JSTaggedValue result = BuiltinsMath::Abs(ecmaRuntimeCallInfo.get()); + TestHelper::TearDownFrame(thread_, prev); + JSTaggedValue expect = BuiltinsBase::GetTaggedInt(10); + ASSERT_EQ(result.GetRawData(), expect.GetRawData()); +} + +// Math.abs(10) +TEST_F(BuiltinsMathTest, Abs_1) +{ + auto ecmaRuntimeCallInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread_, JSTaggedValue::Undefined(), 6); + ecmaRuntimeCallInfo->SetFunction(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetThis(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetCallArg(0, JSTaggedValue(static_cast(10))); + + [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread_, ecmaRuntimeCallInfo.get()); + JSTaggedValue result = BuiltinsMath::Abs(ecmaRuntimeCallInfo.get()); + TestHelper::TearDownFrame(thread_, prev); + JSTaggedValue expect = BuiltinsBase::GetTaggedInt(10); + ASSERT_EQ(result.GetRawData(), expect.GetRawData()); +} + +// Math.abs(0) +TEST_F(BuiltinsMathTest, Abs_2) +{ + auto ecmaRuntimeCallInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread_, JSTaggedValue::Undefined(), 6); + ecmaRuntimeCallInfo->SetFunction(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetThis(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetCallArg(0, JSTaggedValue(static_cast(0))); + + [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread_, ecmaRuntimeCallInfo.get()); + JSTaggedValue result = BuiltinsMath::Abs(ecmaRuntimeCallInfo.get()); + TestHelper::TearDownFrame(thread_, prev); + JSTaggedValue expect = BuiltinsBase::GetTaggedInt(0); + ASSERT_EQ(result.GetRawData(), expect.GetRawData()); +} + +// Math.abs(null) +TEST_F(BuiltinsMathTest, Abs_3) +{ + auto ecmaRuntimeCallInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread_, JSTaggedValue::Undefined(), 6); + ecmaRuntimeCallInfo->SetFunction(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetThis(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetCallArg(0, JSTaggedValue::Null()); + + [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread_, ecmaRuntimeCallInfo.get()); + JSTaggedValue result = BuiltinsMath::Abs(ecmaRuntimeCallInfo.get()); + TestHelper::TearDownFrame(thread_, prev); + JSTaggedValue expect = BuiltinsBase::GetTaggedInt(0); + ASSERT_EQ(result.GetRawData(), expect.GetRawData()); +} + +// Math.abs("hello") +TEST_F(BuiltinsMathTest, Abs_4) +{ + JSHandle test = thread_->GetEcmaVM()->GetFactory()->NewFromCanBeCompressString("helloworld"); + + auto ecmaRuntimeCallInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread_, JSTaggedValue::Undefined(), 6); + ecmaRuntimeCallInfo->SetFunction(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetThis(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetCallArg(0, test.GetTaggedValue()); + + [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread_, ecmaRuntimeCallInfo.get()); + JSTaggedValue result = BuiltinsMath::Abs(ecmaRuntimeCallInfo.get()); + TestHelper::TearDownFrame(thread_, prev); + JSTaggedValue expect = BuiltinsBase::GetTaggedDouble(base::NAN_VALUE); + ASSERT_EQ(result.GetRawData(), expect.GetRawData()); +} + +// Math.abs(Number.MAX_VALUE + 1) +TEST_F(BuiltinsMathTest, Abs_5) +{ + const double testValue = base::MAX_VALUE + 1; + auto ecmaRuntimeCallInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread_, JSTaggedValue::Undefined(), 6); + ecmaRuntimeCallInfo->SetFunction(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetThis(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetCallArg(0, JSTaggedValue(testValue)); + + [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread_, ecmaRuntimeCallInfo.get()); + JSTaggedValue result = BuiltinsMath::Abs(ecmaRuntimeCallInfo.get()); + TestHelper::TearDownFrame(thread_, prev); + JSTaggedValue expect = BuiltinsBase::GetTaggedDouble(base::MAX_VALUE); + ASSERT_EQ(result.GetRawData(), expect.GetRawData()); +} + +// Math.abs(Number.MIN_VALUE) +TEST_F(BuiltinsMathTest, Abs_6) +{ + const double testValue = base::MIN_VALUE + 1; + auto ecmaRuntimeCallInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread_, JSTaggedValue::Undefined(), 6); + ecmaRuntimeCallInfo->SetFunction(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetThis(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetCallArg(0, JSTaggedValue(testValue)); + + [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread_, ecmaRuntimeCallInfo.get()); + JSTaggedValue result = BuiltinsMath::Abs(ecmaRuntimeCallInfo.get()); + TestHelper::TearDownFrame(thread_, prev); + JSTaggedValue expect = BuiltinsBase::GetTaggedDouble(1.0); + ASSERT_EQ(result.GetRawData(), expect.GetRawData()); +} + +// Math.abs(Number.POSITIVE_INFINITY + 1) +TEST_F(BuiltinsMathTest, Abs_7) +{ + const double testValue = base::POSITIVE_INFINITY + 1; + auto ecmaRuntimeCallInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread_, JSTaggedValue::Undefined(), 6); + ecmaRuntimeCallInfo->SetFunction(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetThis(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetCallArg(0, JSTaggedValue(testValue)); + + [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread_, ecmaRuntimeCallInfo.get()); + JSTaggedValue result = BuiltinsMath::Abs(ecmaRuntimeCallInfo.get()); + TestHelper::TearDownFrame(thread_, prev); + JSTaggedValue expect = BuiltinsBase::GetTaggedDouble(base::POSITIVE_INFINITY); + ASSERT_EQ(result.GetRawData(), expect.GetRawData()); +} + +// Math.abs(Number.NEGATIVE_INFINITY - 1) +TEST_F(BuiltinsMathTest, Abs_8) +{ + const double testValue = -base::POSITIVE_INFINITY - 1; + auto ecmaRuntimeCallInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread_, JSTaggedValue::Undefined(), 6); + ecmaRuntimeCallInfo->SetFunction(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetThis(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetCallArg(0, JSTaggedValue(testValue)); + + [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread_, ecmaRuntimeCallInfo.get()); + JSTaggedValue result = BuiltinsMath::Abs(ecmaRuntimeCallInfo.get()); + TestHelper::TearDownFrame(thread_, prev); + JSTaggedValue expect = BuiltinsBase::GetTaggedDouble(base::POSITIVE_INFINITY); + ASSERT_EQ(result.GetRawData(), expect.GetRawData()); +} + +// Math.abs(Number.NAN_VALUE) +TEST_F(BuiltinsMathTest, Abs_9) +{ + const double testValue = base::NAN_VALUE; + auto ecmaRuntimeCallInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread_, JSTaggedValue::Undefined(), 6); + ecmaRuntimeCallInfo->SetFunction(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetThis(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetCallArg(0, JSTaggedValue(testValue)); + + [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread_, ecmaRuntimeCallInfo.get()); + JSTaggedValue result = BuiltinsMath::Abs(ecmaRuntimeCallInfo.get()); + TestHelper::TearDownFrame(thread_, prev); + JSTaggedValue expect = BuiltinsBase::GetTaggedDouble(base::NAN_VALUE); + ASSERT_EQ(result.GetRawData(), expect.GetRawData()); +} + +// Math.abs(VALUE_UNDEFINED) +TEST_F(BuiltinsMathTest, Abs_10) +{ + auto ecmaRuntimeCallInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread_, JSTaggedValue::Undefined(), 6); + ecmaRuntimeCallInfo->SetFunction(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetThis(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetCallArg(0, JSTaggedValue::Undefined()); + + [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread_, ecmaRuntimeCallInfo.get()); + JSTaggedValue result = BuiltinsMath::Abs(ecmaRuntimeCallInfo.get()); + TestHelper::TearDownFrame(thread_, prev); + JSTaggedValue expect = BuiltinsBase::GetTaggedDouble(base::NAN_VALUE); + ASSERT_EQ(result.GetRawData(), expect.GetRawData()); +} + +// Math.abs(true) +TEST_F(BuiltinsMathTest, Abs_11) +{ + auto ecmaRuntimeCallInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread_, JSTaggedValue::Undefined(), 6); + ecmaRuntimeCallInfo->SetFunction(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetThis(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetCallArg(0, JSTaggedValue::True()); + + [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread_, ecmaRuntimeCallInfo.get()); + JSTaggedValue result = BuiltinsMath::Abs(ecmaRuntimeCallInfo.get()); + TestHelper::TearDownFrame(thread_, prev); + JSTaggedValue expect = BuiltinsBase::GetTaggedInt(1); + ASSERT_EQ(result.GetRawData(), expect.GetRawData()); +} + +// Math.abs(false) +TEST_F(BuiltinsMathTest, Abs_12) +{ + auto ecmaRuntimeCallInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread_, JSTaggedValue::Undefined(), 6); + ecmaRuntimeCallInfo->SetFunction(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetThis(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetCallArg(0, JSTaggedValue::False()); + + [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread_, ecmaRuntimeCallInfo.get()); + JSTaggedValue result = BuiltinsMath::Abs(ecmaRuntimeCallInfo.get()); + TestHelper::TearDownFrame(thread_, prev); + JSTaggedValue expect = BuiltinsBase::GetTaggedInt(0); + ASSERT_EQ(result.GetRawData(), expect.GetRawData()); +} + +// Math.abs(hole) +TEST_F(BuiltinsMathTest, Abs_13) +{ + auto ecmaRuntimeCallInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread_, JSTaggedValue::Undefined(), 6); + ecmaRuntimeCallInfo->SetFunction(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetThis(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetCallArg(0, JSTaggedValue::Hole()); + + [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread_, ecmaRuntimeCallInfo.get()); + JSTaggedValue result = BuiltinsMath::Abs(ecmaRuntimeCallInfo.get()); + TestHelper::TearDownFrame(thread_, prev); + JSTaggedValue expect = BuiltinsBase::GetTaggedDouble(base::NAN_VALUE); + ASSERT_EQ(result.GetRawData(), expect.GetRawData()); +} + +// Math.abs("100.12") +TEST_F(BuiltinsMathTest, Abs_14) +{ + JSHandle test = thread_->GetEcmaVM()->GetFactory()->NewFromCanBeCompressString("100.12"); + auto ecmaRuntimeCallInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread_, JSTaggedValue::Undefined(), 6); + ecmaRuntimeCallInfo->SetFunction(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetThis(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetCallArg(0, test.GetTaggedValue()); + + [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread_, ecmaRuntimeCallInfo.get()); + JSTaggedValue result = BuiltinsMath::Abs(ecmaRuntimeCallInfo.get()); + TestHelper::TearDownFrame(thread_, prev); + JSTaggedValue expect = BuiltinsBase::GetTaggedDouble(100.12); + ASSERT_EQ(result.GetRawData(), expect.GetRawData()); +} + +// Math.acos(-1) +TEST_F(BuiltinsMathTest, Acos) +{ + auto ecmaRuntimeCallInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread_, JSTaggedValue::Undefined(), 6); + ecmaRuntimeCallInfo->SetFunction(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetThis(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetCallArg(0, JSTaggedValue(static_cast(-1))); + + [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread_, ecmaRuntimeCallInfo.get()); + JSTaggedValue result = BuiltinsMath::Acos(ecmaRuntimeCallInfo.get()); + TestHelper::TearDownFrame(thread_, prev); + JSTaggedValue expect = BuiltinsBase::GetTaggedDouble(BuiltinsMath::PI); + ASSERT_EQ(result.GetRawData(), expect.GetRawData()); +} + +// Math.acos(1) +TEST_F(BuiltinsMathTest, Acos_1) +{ + auto ecmaRuntimeCallInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread_, JSTaggedValue::Undefined(), 6); + ecmaRuntimeCallInfo->SetFunction(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetThis(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetCallArg(0, JSTaggedValue(static_cast(1))); + + [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread_, ecmaRuntimeCallInfo.get()); + JSTaggedValue result = BuiltinsMath::Acos(ecmaRuntimeCallInfo.get()); + TestHelper::TearDownFrame(thread_, prev); + JSTaggedValue expect = BuiltinsBase::GetTaggedDouble(0); + ASSERT_EQ(result.GetRawData(), expect.GetRawData()); +} + +// Math.acos(-1.5) +TEST_F(BuiltinsMathTest, Acos_2) +{ + auto ecmaRuntimeCallInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread_, JSTaggedValue::Undefined(), 6); + ecmaRuntimeCallInfo->SetFunction(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetThis(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetCallArg(0, JSTaggedValue(-1.5)); + + [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread_, ecmaRuntimeCallInfo.get()); + JSTaggedValue result = BuiltinsMath::Acos(ecmaRuntimeCallInfo.get()); + TestHelper::TearDownFrame(thread_, prev); + JSTaggedValue expect = BuiltinsBase::GetTaggedDouble(base::NAN_VALUE); + ASSERT_EQ(result.GetRawData(), expect.GetRawData()); +} + +// Math.acos(null) +TEST_F(BuiltinsMathTest, Acos_3) +{ + auto ecmaRuntimeCallInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread_, JSTaggedValue::Undefined(), 6); + ecmaRuntimeCallInfo->SetFunction(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetThis(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetCallArg(0, JSTaggedValue::Null()); + + [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread_, ecmaRuntimeCallInfo.get()); + JSTaggedValue result = BuiltinsMath::Acos(ecmaRuntimeCallInfo.get()); + TestHelper::TearDownFrame(thread_, prev); + JSTaggedValue expect = BuiltinsBase::GetTaggedDouble(1.5707963267948966); + ASSERT_EQ(result.GetRawData(), expect.GetRawData()); +} + +// Math.acos(UNDEFINED) +TEST_F(BuiltinsMathTest, Acos_4) +{ + auto ecmaRuntimeCallInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread_, JSTaggedValue::Undefined(), 6); + ecmaRuntimeCallInfo->SetFunction(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetThis(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetCallArg(0, JSTaggedValue::Undefined()); + + [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread_, ecmaRuntimeCallInfo.get()); + JSTaggedValue result = BuiltinsMath::Acos(ecmaRuntimeCallInfo.get()); + TestHelper::TearDownFrame(thread_, prev); + JSTaggedValue expect = BuiltinsBase::GetTaggedDouble(base::NAN_VALUE); + ASSERT_EQ(result.GetRawData(), expect.GetRawData()); +} + +// Math.acos(true) +TEST_F(BuiltinsMathTest, Acos_5) +{ + auto ecmaRuntimeCallInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread_, JSTaggedValue::Undefined(), 6); + ecmaRuntimeCallInfo->SetFunction(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetThis(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetCallArg(0, JSTaggedValue::True()); + + [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread_, ecmaRuntimeCallInfo.get()); + JSTaggedValue result = BuiltinsMath::Acos(ecmaRuntimeCallInfo.get()); + TestHelper::TearDownFrame(thread_, prev); + JSTaggedValue expect = BuiltinsBase::GetTaggedDouble(0.0); + ASSERT_EQ(result.GetRawData(), expect.GetRawData()); +} + +// Math.acos(false) +TEST_F(BuiltinsMathTest, Acos_6) +{ + auto ecmaRuntimeCallInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread_, JSTaggedValue::Undefined(), 6); + ecmaRuntimeCallInfo->SetFunction(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetThis(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetCallArg(0, JSTaggedValue::False()); + + [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread_, ecmaRuntimeCallInfo.get()); + JSTaggedValue result = BuiltinsMath::Acos(ecmaRuntimeCallInfo.get()); + TestHelper::TearDownFrame(thread_, prev); + JSTaggedValue expect = BuiltinsBase::GetTaggedDouble(1.5707963267948966); + ASSERT_EQ(result.GetRawData(), expect.GetRawData()); +} + +// Math.acos("0.1") +TEST_F(BuiltinsMathTest, Acos_7) +{ + JSHandle test = thread_->GetEcmaVM()->GetFactory()->NewFromCanBeCompressString("0.1"); + auto ecmaRuntimeCallInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread_, JSTaggedValue::Undefined(), 6); + ecmaRuntimeCallInfo->SetFunction(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetThis(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetCallArg(0, test.GetTaggedValue()); + + [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread_, ecmaRuntimeCallInfo.get()); + JSTaggedValue result = BuiltinsMath::Acos(ecmaRuntimeCallInfo.get()); + TestHelper::TearDownFrame(thread_, prev); + JSTaggedValue expect = BuiltinsBase::GetTaggedDouble(1.4706289056333368); + ASSERT_EQ(result.GetRawData(), expect.GetRawData()); +} + +// Math.acos("") +TEST_F(BuiltinsMathTest, Acos_8) +{ + JSHandle test = thread_->GetEcmaVM()->GetFactory()->NewFromCanBeCompressString(""); + auto ecmaRuntimeCallInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread_, JSTaggedValue::Undefined(), 6); + ecmaRuntimeCallInfo->SetFunction(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetThis(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetCallArg(0, test.GetTaggedValue()); + + [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread_, ecmaRuntimeCallInfo.get()); + JSTaggedValue result = BuiltinsMath::Acos(ecmaRuntimeCallInfo.get()); + TestHelper::TearDownFrame(thread_, prev); + JSTaggedValue expect = BuiltinsBase::GetTaggedDouble(1.5707963267948966); + ASSERT_EQ(result.GetRawData(), expect.GetRawData()); +} + +// Math.acos(-NaN) +TEST_F(BuiltinsMathTest, Acos_9) +{ + auto ecmaRuntimeCallInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread_, JSTaggedValue::Undefined(), 6); + ecmaRuntimeCallInfo->SetFunction(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetThis(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetCallArg(0, JSTaggedValue(-base::NAN_VALUE)); + + [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread_, ecmaRuntimeCallInfo.get()); + JSTaggedValue result = BuiltinsMath::Acos(ecmaRuntimeCallInfo.get()); + TestHelper::TearDownFrame(thread_, prev); + JSTaggedValue expect = BuiltinsBase::GetTaggedDouble(base::NAN_VALUE); + ASSERT_EQ(result.GetRawData(), expect.GetRawData()); +} + +// Math.acosh(1.1) +TEST_F(BuiltinsMathTest, Acosh) +{ + auto ecmaRuntimeCallInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread_, JSTaggedValue::Undefined(), 6); + ecmaRuntimeCallInfo->SetFunction(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetThis(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetCallArg(0, JSTaggedValue(1.1)); + + [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread_, ecmaRuntimeCallInfo.get()); + JSTaggedValue result = BuiltinsMath::Acosh(ecmaRuntimeCallInfo.get()); + TestHelper::TearDownFrame(thread_, prev); + JSTaggedValue expect = BuiltinsBase::GetTaggedDouble(0.4435682543851154); + ASSERT_EQ(result.GetRawData(), expect.GetRawData()); +} + +// Math.acosh(0.5) +TEST_F(BuiltinsMathTest, Acosh_1) +{ + auto ecmaRuntimeCallInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread_, JSTaggedValue::Undefined(), 6); + ecmaRuntimeCallInfo->SetFunction(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetThis(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetCallArg(0, JSTaggedValue(0.5)); + + [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread_, ecmaRuntimeCallInfo.get()); + JSTaggedValue result = BuiltinsMath::Acosh(ecmaRuntimeCallInfo.get()); + TestHelper::TearDownFrame(thread_, prev); + JSTaggedValue expect = BuiltinsBase::GetTaggedDouble(base::NAN_VALUE); + ASSERT_EQ(result.GetRawData(), expect.GetRawData()); +} + +// Math.acosh(base::POSITIVE_INFINITY) +TEST_F(BuiltinsMathTest, Acosh_2) +{ + auto ecmaRuntimeCallInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread_, JSTaggedValue::Undefined(), 6); + ecmaRuntimeCallInfo->SetFunction(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetThis(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetCallArg(0, JSTaggedValue(base::POSITIVE_INFINITY)); + + [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread_, ecmaRuntimeCallInfo.get()); + JSTaggedValue result = BuiltinsMath::Acosh(ecmaRuntimeCallInfo.get()); + TestHelper::TearDownFrame(thread_, prev); + JSTaggedValue expect = BuiltinsBase::GetTaggedDouble(base::POSITIVE_INFINITY); + ASSERT_EQ(result.GetRawData(), expect.GetRawData()); +} + +// Math.acosh(null) +TEST_F(BuiltinsMathTest, Acosh_3) +{ + auto ecmaRuntimeCallInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread_, JSTaggedValue::Undefined(), 6); + ecmaRuntimeCallInfo->SetFunction(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetThis(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetCallArg(0, JSTaggedValue::Null()); + + [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread_, ecmaRuntimeCallInfo.get()); + JSTaggedValue result = BuiltinsMath::Acosh(ecmaRuntimeCallInfo.get()); + TestHelper::TearDownFrame(thread_, prev); + JSTaggedValue expect = BuiltinsBase::GetTaggedDouble(base::NAN_VALUE); + ASSERT_EQ(result.GetRawData(), expect.GetRawData()); +} + +// Math.acosh(VALUE_UNDEFINED) +TEST_F(BuiltinsMathTest, Acosh_4) +{ + auto ecmaRuntimeCallInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread_, JSTaggedValue::Undefined(), 6); + ecmaRuntimeCallInfo->SetFunction(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetThis(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetCallArg(0, JSTaggedValue::Undefined()); + + [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread_, ecmaRuntimeCallInfo.get()); + JSTaggedValue result = BuiltinsMath::Acosh(ecmaRuntimeCallInfo.get()); + TestHelper::TearDownFrame(thread_, prev); + JSTaggedValue expect = BuiltinsBase::GetTaggedDouble(base::NAN_VALUE); + ASSERT_EQ(result.GetRawData(), expect.GetRawData()); +} + +// Math.acosh(true) +TEST_F(BuiltinsMathTest, Acosh_5) +{ + auto ecmaRuntimeCallInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread_, JSTaggedValue::Undefined(), 6); + ecmaRuntimeCallInfo->SetFunction(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetThis(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetCallArg(0, JSTaggedValue::True()); + + [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread_, ecmaRuntimeCallInfo.get()); + JSTaggedValue result = BuiltinsMath::Acosh(ecmaRuntimeCallInfo.get()); + TestHelper::TearDownFrame(thread_, prev); + JSTaggedValue expect = BuiltinsBase::GetTaggedDouble(0.0); + ASSERT_EQ(result.GetRawData(), expect.GetRawData()); +} + +// Math.acosh(false) +TEST_F(BuiltinsMathTest, Acosh_6) +{ + auto ecmaRuntimeCallInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread_, JSTaggedValue::Undefined(), 6); + ecmaRuntimeCallInfo->SetFunction(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetThis(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetCallArg(0, JSTaggedValue::False()); + + [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread_, ecmaRuntimeCallInfo.get()); + JSTaggedValue result = BuiltinsMath::Acosh(ecmaRuntimeCallInfo.get()); + TestHelper::TearDownFrame(thread_, prev); + JSTaggedValue expect = BuiltinsBase::GetTaggedDouble(base::NAN_VALUE); + ASSERT_EQ(result.GetRawData(), expect.GetRawData()); +} + +// Math.acosh(hole) +TEST_F(BuiltinsMathTest, Acosh_7) +{ + auto ecmaRuntimeCallInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread_, JSTaggedValue::Undefined(), 6); + ecmaRuntimeCallInfo->SetFunction(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetThis(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetCallArg(0, JSTaggedValue::Hole()); + + [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread_, ecmaRuntimeCallInfo.get()); + JSTaggedValue result = BuiltinsMath::Acosh(ecmaRuntimeCallInfo.get()); + TestHelper::TearDownFrame(thread_, prev); + JSTaggedValue expect = BuiltinsBase::GetTaggedDouble(base::NAN_VALUE); + ASSERT_EQ(result.GetRawData(), expect.GetRawData()); +} + +// Math.acosh("1") +TEST_F(BuiltinsMathTest, Acosh_8) +{ + JSHandle test = thread_->GetEcmaVM()->GetFactory()->NewFromCanBeCompressString("1"); + auto ecmaRuntimeCallInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread_, JSTaggedValue::Undefined(), 6); + ecmaRuntimeCallInfo->SetFunction(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetThis(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetCallArg(0, test.GetTaggedValue()); + + [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread_, ecmaRuntimeCallInfo.get()); + JSTaggedValue result = BuiltinsMath::Acosh(ecmaRuntimeCallInfo.get()); + TestHelper::TearDownFrame(thread_, prev); + JSTaggedValue expect = BuiltinsBase::GetTaggedDouble(0); + ASSERT_EQ(result.GetRawData(), expect.GetRawData()); +} + +// Math.acosh("") +TEST_F(BuiltinsMathTest, Acosh_9) +{ + JSHandle test = thread_->GetEcmaVM()->GetFactory()->NewFromCanBeCompressString(""); + auto ecmaRuntimeCallInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread_, JSTaggedValue::Undefined(), 6); + ecmaRuntimeCallInfo->SetFunction(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetThis(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetCallArg(0, test.GetTaggedValue()); + + [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread_, ecmaRuntimeCallInfo.get()); + JSTaggedValue result = BuiltinsMath::Acosh(ecmaRuntimeCallInfo.get()); + TestHelper::TearDownFrame(thread_, prev); + JSTaggedValue expect = BuiltinsBase::GetTaggedDouble(base::NAN_VALUE); + ASSERT_EQ(result.GetRawData(), expect.GetRawData()); +} + +// Math.acosh(-NaN) +TEST_F(BuiltinsMathTest, Acosh_10) +{ + auto ecmaRuntimeCallInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread_, JSTaggedValue::Undefined(), 6); + ecmaRuntimeCallInfo->SetFunction(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetThis(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetCallArg(0, JSTaggedValue(-base::NAN_VALUE)); + + [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread_, ecmaRuntimeCallInfo.get()); + JSTaggedValue result = BuiltinsMath::Acosh(ecmaRuntimeCallInfo.get()); + TestHelper::TearDownFrame(thread_, prev); + JSTaggedValue expect = BuiltinsBase::GetTaggedDouble(base::NAN_VALUE); + ASSERT_EQ(result.GetRawData(), expect.GetRawData()); +} + +// Math.asin(-1) +TEST_F(BuiltinsMathTest, Asin) +{ + auto ecmaRuntimeCallInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread_, JSTaggedValue::Undefined(), 6); + ecmaRuntimeCallInfo->SetFunction(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetThis(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetCallArg(0, JSTaggedValue(static_cast(-1))); + + [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread_, ecmaRuntimeCallInfo.get()); + JSTaggedValue result = BuiltinsMath::Asin(ecmaRuntimeCallInfo.get()); + TestHelper::TearDownFrame(thread_, prev); + JSTaggedValue expect = BuiltinsBase::GetTaggedDouble(-1.5707963267948966); + ASSERT_EQ(result.GetRawData(), expect.GetRawData()); +} + +// Math.asin(1) +TEST_F(BuiltinsMathTest, Asin_1) +{ + auto ecmaRuntimeCallInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread_, JSTaggedValue::Undefined(), 6); + ecmaRuntimeCallInfo->SetFunction(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetThis(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetCallArg(0, JSTaggedValue(static_cast(1))); + + [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread_, ecmaRuntimeCallInfo.get()); + JSTaggedValue result = BuiltinsMath::Asin(ecmaRuntimeCallInfo.get()); + TestHelper::TearDownFrame(thread_, prev); + JSTaggedValue expect = BuiltinsBase::GetTaggedDouble(1.5707963267948966); + ASSERT_EQ(result.GetRawData(), expect.GetRawData()); +} + +// Math.asin(-NaN) +TEST_F(BuiltinsMathTest, Asin_2) +{ + auto ecmaRuntimeCallInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread_, JSTaggedValue::Undefined(), 6); + ecmaRuntimeCallInfo->SetFunction(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetThis(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetCallArg(0, JSTaggedValue(-base::NAN_VALUE)); + + [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread_, ecmaRuntimeCallInfo.get()); + JSTaggedValue result = BuiltinsMath::Asin(ecmaRuntimeCallInfo.get()); + TestHelper::TearDownFrame(thread_, prev); + JSTaggedValue expect = BuiltinsBase::GetTaggedDouble(base::NAN_VALUE); + ASSERT_EQ(result.GetRawData(), expect.GetRawData()); +} + +// Math.asin(null) +TEST_F(BuiltinsMathTest, Asin_3) +{ + auto ecmaRuntimeCallInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread_, JSTaggedValue::Undefined(), 6); + ecmaRuntimeCallInfo->SetFunction(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetThis(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetCallArg(0, JSTaggedValue::Null()); + + [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread_, ecmaRuntimeCallInfo.get()); + JSTaggedValue result = BuiltinsMath::Asin(ecmaRuntimeCallInfo.get()); + TestHelper::TearDownFrame(thread_, prev); + JSTaggedValue expect = BuiltinsBase::GetTaggedDouble(0); + ASSERT_EQ(result.GetRawData(), expect.GetRawData()); +} + +// Math.asin(UNDEFINED) +TEST_F(BuiltinsMathTest, Asin_4) +{ + auto ecmaRuntimeCallInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread_, JSTaggedValue::Undefined(), 6); + ecmaRuntimeCallInfo->SetFunction(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetThis(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetCallArg(0, JSTaggedValue::Undefined()); + + [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread_, ecmaRuntimeCallInfo.get()); + JSTaggedValue result = BuiltinsMath::Asin(ecmaRuntimeCallInfo.get()); + TestHelper::TearDownFrame(thread_, prev); + JSTaggedValue expect = BuiltinsBase::GetTaggedDouble(base::NAN_VALUE); + ASSERT_EQ(result.GetRawData(), expect.GetRawData()); +} + +// Math.asin(true) +TEST_F(BuiltinsMathTest, Asin_5) +{ + auto ecmaRuntimeCallInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread_, JSTaggedValue::Undefined(), 6); + ecmaRuntimeCallInfo->SetFunction(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetThis(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetCallArg(0, JSTaggedValue::True()); + + [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread_, ecmaRuntimeCallInfo.get()); + JSTaggedValue result = BuiltinsMath::Asin(ecmaRuntimeCallInfo.get()); + TestHelper::TearDownFrame(thread_, prev); + JSTaggedValue expect = BuiltinsBase::GetTaggedDouble(1.5707963267948966); + ASSERT_EQ(result.GetRawData(), expect.GetRawData()); +} + +// Math.asin(false) +TEST_F(BuiltinsMathTest, Asin_6) +{ + auto ecmaRuntimeCallInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread_, JSTaggedValue::Undefined(), 6); + ecmaRuntimeCallInfo->SetFunction(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetThis(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetCallArg(0, JSTaggedValue::False()); + + [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread_, ecmaRuntimeCallInfo.get()); + JSTaggedValue result = BuiltinsMath::Asin(ecmaRuntimeCallInfo.get()); + TestHelper::TearDownFrame(thread_, prev); + JSTaggedValue expect = BuiltinsBase::GetTaggedDouble(0); + ASSERT_EQ(result.GetRawData(), expect.GetRawData()); +} + +// Math.asin(""") +TEST_F(BuiltinsMathTest, Asin_7) +{ + JSHandle test = thread_->GetEcmaVM()->GetFactory()->NewFromCanBeCompressString(""); + auto ecmaRuntimeCallInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread_, JSTaggedValue::Undefined(), 6); + ecmaRuntimeCallInfo->SetFunction(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetThis(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetCallArg(0, test.GetTaggedValue()); + + [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread_, ecmaRuntimeCallInfo.get()); + JSTaggedValue result = BuiltinsMath::Asin(ecmaRuntimeCallInfo.get()); + TestHelper::TearDownFrame(thread_, prev); + JSTaggedValue expect = BuiltinsBase::GetTaggedDouble(0); + ASSERT_EQ(result.GetRawData(), expect.GetRawData()); +} + +// Math.asin("1") +TEST_F(BuiltinsMathTest, Asin_8) +{ + JSHandle test = thread_->GetEcmaVM()->GetFactory()->NewFromCanBeCompressString("1"); + auto ecmaRuntimeCallInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread_, JSTaggedValue::Undefined(), 6); + ecmaRuntimeCallInfo->SetFunction(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetThis(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetCallArg(0, test.GetTaggedValue()); + + [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread_, ecmaRuntimeCallInfo.get()); + JSTaggedValue result = BuiltinsMath::Asin(ecmaRuntimeCallInfo.get()); + TestHelper::TearDownFrame(thread_, prev); + JSTaggedValue expect = BuiltinsBase::GetTaggedDouble(1.5707963267948966); + ASSERT_EQ(result.GetRawData(), expect.GetRawData()); +} + +// Math.asinh(-1) +TEST_F(BuiltinsMathTest, Asinh) +{ + auto ecmaRuntimeCallInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread_, JSTaggedValue::Undefined(), 6); + ecmaRuntimeCallInfo->SetFunction(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetThis(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetCallArg(0, JSTaggedValue(static_cast(-1))); + + [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread_, ecmaRuntimeCallInfo.get()); + JSTaggedValue result = BuiltinsMath::Asinh(ecmaRuntimeCallInfo.get()); + TestHelper::TearDownFrame(thread_, prev); + JSTaggedValue expect = BuiltinsBase::GetTaggedDouble(-0.881373587019543); + ASSERT_EQ(result.GetRawData(), expect.GetRawData()); +} + +// Math.asinh(1) +TEST_F(BuiltinsMathTest, Asinh_1) +{ + auto ecmaRuntimeCallInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread_, JSTaggedValue::Undefined(), 6); + ecmaRuntimeCallInfo->SetFunction(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetThis(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetCallArg(0, JSTaggedValue(static_cast(1))); + + [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread_, ecmaRuntimeCallInfo.get()); + JSTaggedValue result = BuiltinsMath::Asinh(ecmaRuntimeCallInfo.get()); + TestHelper::TearDownFrame(thread_, prev); + TestHelper::TearDownFrame(thread_, prev); + JSTaggedValue expect = BuiltinsBase::GetTaggedDouble(0.881373587019543); + ASSERT_EQ(result.GetRawData(), expect.GetRawData()); +} + +// Math.asinh(null) +TEST_F(BuiltinsMathTest, Asinh_2) +{ + auto ecmaRuntimeCallInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread_, JSTaggedValue::Undefined(), 6); + ecmaRuntimeCallInfo->SetFunction(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetThis(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetCallArg(0, JSTaggedValue::Null()); + + [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread_, ecmaRuntimeCallInfo.get()); + JSTaggedValue result = BuiltinsMath::Asinh(ecmaRuntimeCallInfo.get()); + TestHelper::TearDownFrame(thread_, prev); + TestHelper::TearDownFrame(thread_, prev); + JSTaggedValue expect = BuiltinsBase::GetTaggedDouble(0); + ASSERT_EQ(result.GetRawData(), expect.GetRawData()); +} + +// Math.asinh(-NaN) +TEST_F(BuiltinsMathTest, Asinh_3) +{ + auto ecmaRuntimeCallInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread_, JSTaggedValue::Undefined(), 6); + ecmaRuntimeCallInfo->SetFunction(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetThis(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetCallArg(0, JSTaggedValue(-base::NAN_VALUE)); + + [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread_, ecmaRuntimeCallInfo.get()); + JSTaggedValue result = BuiltinsMath::Asinh(ecmaRuntimeCallInfo.get()); + TestHelper::TearDownFrame(thread_, prev); + JSTaggedValue expect = BuiltinsBase::GetTaggedDouble(base::NAN_VALUE); + ASSERT_EQ(result.GetRawData(), expect.GetRawData()); +} + +// Math.asinh(NEGATIVE_INFINITY) +TEST_F(BuiltinsMathTest, Asinh_4) +{ + auto ecmaRuntimeCallInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread_, JSTaggedValue::Undefined(), 6); + ecmaRuntimeCallInfo->SetFunction(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetThis(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetCallArg(0, JSTaggedValue(-base::POSITIVE_INFINITY)); + + [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread_, ecmaRuntimeCallInfo.get()); + JSTaggedValue result = BuiltinsMath::Asinh(ecmaRuntimeCallInfo.get()); + TestHelper::TearDownFrame(thread_, prev); + JSTaggedValue expect = BuiltinsBase::GetTaggedDouble(-base::POSITIVE_INFINITY); + ASSERT_EQ(result.GetRawData(), expect.GetRawData()); +} + +// Math.asinh(true) +TEST_F(BuiltinsMathTest, Asinh_5) +{ + auto ecmaRuntimeCallInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread_, JSTaggedValue::Undefined(), 6); + ecmaRuntimeCallInfo->SetFunction(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetThis(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetCallArg(0, JSTaggedValue::True()); + + [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread_, ecmaRuntimeCallInfo.get()); + JSTaggedValue result = BuiltinsMath::Asinh(ecmaRuntimeCallInfo.get()); + TestHelper::TearDownFrame(thread_, prev); + JSTaggedValue expect = BuiltinsBase::GetTaggedDouble(0.881373587019543); + ASSERT_EQ(result.GetRawData(), expect.GetRawData()); +} + +// Math.asinh(false) +TEST_F(BuiltinsMathTest, Asinh_6) +{ + auto ecmaRuntimeCallInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread_, JSTaggedValue::Undefined(), 6); + ecmaRuntimeCallInfo->SetFunction(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetThis(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetCallArg(0, JSTaggedValue::False()); + + [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread_, ecmaRuntimeCallInfo.get()); + JSTaggedValue result = BuiltinsMath::Asinh(ecmaRuntimeCallInfo.get()); + TestHelper::TearDownFrame(thread_, prev); + JSTaggedValue expect = BuiltinsBase::GetTaggedDouble(0); + ASSERT_EQ(result.GetRawData(), expect.GetRawData()); +} + +// Math.asinh("") +TEST_F(BuiltinsMathTest, Asinh_7) +{ + JSHandle test = thread_->GetEcmaVM()->GetFactory()->NewFromCanBeCompressString(""); + auto ecmaRuntimeCallInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread_, JSTaggedValue::Undefined(), 6); + ecmaRuntimeCallInfo->SetFunction(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetThis(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetCallArg(0, test.GetTaggedValue()); + + [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread_, ecmaRuntimeCallInfo.get()); + JSTaggedValue result = BuiltinsMath::Asinh(ecmaRuntimeCallInfo.get()); + TestHelper::TearDownFrame(thread_, prev); + JSTaggedValue expect = BuiltinsBase::GetTaggedDouble(0); + ASSERT_EQ(result.GetRawData(), expect.GetRawData()); +} + +// Math.asinh("-5.7") +TEST_F(BuiltinsMathTest, Asinh_8) +{ + JSHandle test = thread_->GetEcmaVM()->GetFactory()->NewFromCanBeCompressString("-5.7"); + auto ecmaRuntimeCallInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread_, JSTaggedValue::Undefined(), 6); + ecmaRuntimeCallInfo->SetFunction(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetThis(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetCallArg(0, test.GetTaggedValue()); + + [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread_, ecmaRuntimeCallInfo.get()); + JSTaggedValue result = BuiltinsMath::Asinh(ecmaRuntimeCallInfo.get()); + TestHelper::TearDownFrame(thread_, prev); + JSTaggedValue expect = BuiltinsBase::GetTaggedDouble(-2.44122070725561); + ASSERT_EQ(result.GetRawData(), expect.GetRawData()); +} + +// Math.atan(-1) +TEST_F(BuiltinsMathTest, Atan) +{ + auto ecmaRuntimeCallInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread_, JSTaggedValue::Undefined(), 6); + ecmaRuntimeCallInfo->SetFunction(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetThis(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetCallArg(0, JSTaggedValue(static_cast(-1))); + + [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread_, ecmaRuntimeCallInfo.get()); + JSTaggedValue result = BuiltinsMath::Atan(ecmaRuntimeCallInfo.get()); + TestHelper::TearDownFrame(thread_, prev); + JSTaggedValue expect = BuiltinsBase::GetTaggedDouble(-0.7853981633974483); + ASSERT_EQ(result.GetRawData(), expect.GetRawData()); +} + +// Math.atan(1) +TEST_F(BuiltinsMathTest, Atan_1) +{ + auto ecmaRuntimeCallInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread_, JSTaggedValue::Undefined(), 6); + ecmaRuntimeCallInfo->SetFunction(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetThis(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetCallArg(0, JSTaggedValue(static_cast(1))); + + [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread_, ecmaRuntimeCallInfo.get()); + JSTaggedValue result = BuiltinsMath::Atan(ecmaRuntimeCallInfo.get()); + TestHelper::TearDownFrame(thread_, prev); + JSTaggedValue expect = BuiltinsBase::GetTaggedDouble(0.7853981633974483); + ASSERT_EQ(result.GetRawData(), expect.GetRawData()); +} + +// Math.atan(null) +TEST_F(BuiltinsMathTest, Atan_2) +{ + auto ecmaRuntimeCallInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread_, JSTaggedValue::Undefined(), 6); + ecmaRuntimeCallInfo->SetFunction(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetThis(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetCallArg(0, JSTaggedValue::Null()); + + [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread_, ecmaRuntimeCallInfo.get()); + JSTaggedValue result = BuiltinsMath::Atan(ecmaRuntimeCallInfo.get()); + TestHelper::TearDownFrame(thread_, prev); + JSTaggedValue expect = BuiltinsBase::GetTaggedDouble(0); + ASSERT_EQ(result.GetRawData(), expect.GetRawData()); +} + +// Math.atan(-NaN) +TEST_F(BuiltinsMathTest, Atan_3) +{ + auto ecmaRuntimeCallInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread_, JSTaggedValue::Undefined(), 6); + ecmaRuntimeCallInfo->SetFunction(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetThis(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetCallArg(0, JSTaggedValue(-base::NAN_VALUE)); + + [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread_, ecmaRuntimeCallInfo.get()); + JSTaggedValue result = BuiltinsMath::Atan(ecmaRuntimeCallInfo.get()); + TestHelper::TearDownFrame(thread_, prev); + JSTaggedValue expect = BuiltinsBase::GetTaggedDouble(base::NAN_VALUE); + ASSERT_EQ(result.GetRawData(), expect.GetRawData()); +} + +// Math.atan(POSITIVE_INFINITY) +TEST_F(BuiltinsMathTest, Atan_4) +{ + auto ecmaRuntimeCallInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread_, JSTaggedValue::Undefined(), 6); + ecmaRuntimeCallInfo->SetFunction(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetThis(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetCallArg(0, JSTaggedValue(base::POSITIVE_INFINITY)); + + [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread_, ecmaRuntimeCallInfo.get()); + JSTaggedValue result = BuiltinsMath::Atan(ecmaRuntimeCallInfo.get()); + TestHelper::TearDownFrame(thread_, prev); + JSTaggedValue expect = BuiltinsBase::GetTaggedDouble(BuiltinsMath::PI / 2); + ASSERT_EQ(result.GetRawData(), expect.GetRawData()); +} + +// Math.atan(true) +TEST_F(BuiltinsMathTest, Atan_5) +{ + auto ecmaRuntimeCallInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread_, JSTaggedValue::Undefined(), 6); + ecmaRuntimeCallInfo->SetFunction(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetThis(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetCallArg(0, JSTaggedValue::True()); + + [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread_, ecmaRuntimeCallInfo.get()); + JSTaggedValue result = BuiltinsMath::Atan(ecmaRuntimeCallInfo.get()); + TestHelper::TearDownFrame(thread_, prev); + JSTaggedValue expect = BuiltinsBase::GetTaggedDouble(0.7853981633974483); + ASSERT_EQ(result.GetRawData(), expect.GetRawData()); +} + +// Math.atan(false) +TEST_F(BuiltinsMathTest, Atan_6) +{ + auto ecmaRuntimeCallInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread_, JSTaggedValue::Undefined(), 6); + ecmaRuntimeCallInfo->SetFunction(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetThis(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetCallArg(0, JSTaggedValue::False()); + + [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread_, ecmaRuntimeCallInfo.get()); + JSTaggedValue result = BuiltinsMath::Atan(ecmaRuntimeCallInfo.get()); + TestHelper::TearDownFrame(thread_, prev); + JSTaggedValue expect = BuiltinsBase::GetTaggedDouble(0); + ASSERT_EQ(result.GetRawData(), expect.GetRawData()); +} + +// Math.atan("") +TEST_F(BuiltinsMathTest, Atan_7) +{ + JSHandle test = thread_->GetEcmaVM()->GetFactory()->NewFromCanBeCompressString(" "); + auto ecmaRuntimeCallInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread_, JSTaggedValue::Undefined(), 6); + ecmaRuntimeCallInfo->SetFunction(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetThis(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetCallArg(0, test.GetTaggedValue()); + + [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread_, ecmaRuntimeCallInfo.get()); + JSTaggedValue result = BuiltinsMath::Atan(ecmaRuntimeCallInfo.get()); + TestHelper::TearDownFrame(thread_, prev); + JSTaggedValue expect = BuiltinsBase::GetTaggedDouble(0); + ASSERT_EQ(result.GetRawData(), expect.GetRawData()); +} + +// Math.atan("-1") +TEST_F(BuiltinsMathTest, Atan_8) +{ + JSHandle test = thread_->GetEcmaVM()->GetFactory()->NewFromCanBeCompressString("-1"); + auto ecmaRuntimeCallInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread_, JSTaggedValue::Undefined(), 6); + ecmaRuntimeCallInfo->SetFunction(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetThis(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetCallArg(0, test.GetTaggedValue()); + + [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread_, ecmaRuntimeCallInfo.get()); + JSTaggedValue result = BuiltinsMath::Atan(ecmaRuntimeCallInfo.get()); + TestHelper::TearDownFrame(thread_, prev); + JSTaggedValue expect = BuiltinsBase::GetTaggedDouble(-0.7853981633974483); + ASSERT_EQ(result.GetRawData(), expect.GetRawData()); +} + +// Math.atanh(-1) +TEST_F(BuiltinsMathTest, Atanh) +{ + auto ecmaRuntimeCallInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread_, JSTaggedValue::Undefined(), 6); + ecmaRuntimeCallInfo->SetFunction(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetThis(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetCallArg(0, JSTaggedValue(static_cast(-1))); + + [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread_, ecmaRuntimeCallInfo.get()); + JSTaggedValue result = BuiltinsMath::Atanh(ecmaRuntimeCallInfo.get()); + TestHelper::TearDownFrame(thread_, prev); + JSTaggedValue expect = BuiltinsBase::GetTaggedDouble(-base::POSITIVE_INFINITY); + ASSERT_EQ(result.GetRawData(), expect.GetRawData()); +} + +// Math.atanh(1) +TEST_F(BuiltinsMathTest, Atanh_1) +{ + auto ecmaRuntimeCallInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread_, JSTaggedValue::Undefined(), 6); + ecmaRuntimeCallInfo->SetFunction(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetThis(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetCallArg(0, JSTaggedValue(static_cast(1))); + + [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread_, ecmaRuntimeCallInfo.get()); + JSTaggedValue result = BuiltinsMath::Atanh(ecmaRuntimeCallInfo.get()); + TestHelper::TearDownFrame(thread_, prev); + JSTaggedValue expect = BuiltinsBase::GetTaggedDouble(base::POSITIVE_INFINITY); + ASSERT_EQ(result.GetRawData(), expect.GetRawData()); +} + +// Math.atanh(null) +TEST_F(BuiltinsMathTest, Atanh_2) +{ + auto ecmaRuntimeCallInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread_, JSTaggedValue::Undefined(), 6); + ecmaRuntimeCallInfo->SetFunction(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetThis(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetCallArg(0, JSTaggedValue::Null()); + + [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread_, ecmaRuntimeCallInfo.get()); + JSTaggedValue result = BuiltinsMath::Atanh(ecmaRuntimeCallInfo.get()); + TestHelper::TearDownFrame(thread_, prev); + JSTaggedValue expect = BuiltinsBase::GetTaggedDouble(0); + ASSERT_EQ(result.GetRawData(), expect.GetRawData()); +} + +// Math.atanh(-NaN) +TEST_F(BuiltinsMathTest, Atanh_3) +{ + auto ecmaRuntimeCallInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread_, JSTaggedValue::Undefined(), 6); + ecmaRuntimeCallInfo->SetFunction(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetThis(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetCallArg(0, JSTaggedValue(-base::NAN_VALUE)); + + [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread_, ecmaRuntimeCallInfo.get()); + JSTaggedValue result = BuiltinsMath::Atanh(ecmaRuntimeCallInfo.get()); + TestHelper::TearDownFrame(thread_, prev); + JSTaggedValue expect = BuiltinsBase::GetTaggedDouble(base::NAN_VALUE); + ASSERT_EQ(result.GetRawData(), expect.GetRawData()); +} + +// Math.atanh(1.5) +TEST_F(BuiltinsMathTest, Atanh_4) +{ + auto ecmaRuntimeCallInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread_, JSTaggedValue::Undefined(), 6); + ecmaRuntimeCallInfo->SetFunction(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetThis(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetCallArg(0, JSTaggedValue(1.5)); + + [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread_, ecmaRuntimeCallInfo.get()); + JSTaggedValue result = BuiltinsMath::Atanh(ecmaRuntimeCallInfo.get()); + TestHelper::TearDownFrame(thread_, prev); + JSTaggedValue expect = BuiltinsBase::GetTaggedDouble(base::NAN_VALUE); + ASSERT_EQ(result.GetRawData(), expect.GetRawData()); +} + +// Math.atanh(true) +TEST_F(BuiltinsMathTest, Atanh_5) +{ + auto ecmaRuntimeCallInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread_, JSTaggedValue::Undefined(), 6); + ecmaRuntimeCallInfo->SetFunction(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetThis(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetCallArg(0, JSTaggedValue::True()); + + [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread_, ecmaRuntimeCallInfo.get()); + JSTaggedValue result = BuiltinsMath::Atanh(ecmaRuntimeCallInfo.get()); + TestHelper::TearDownFrame(thread_, prev); + JSTaggedValue expect = BuiltinsBase::GetTaggedDouble(base::POSITIVE_INFINITY); + ASSERT_EQ(result.GetRawData(), expect.GetRawData()); +} + +// Math.atanh(false) +TEST_F(BuiltinsMathTest, Atanh_6) +{ + auto ecmaRuntimeCallInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread_, JSTaggedValue::Undefined(), 6); + ecmaRuntimeCallInfo->SetFunction(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetThis(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetCallArg(0, JSTaggedValue::False()); + + [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread_, ecmaRuntimeCallInfo.get()); + JSTaggedValue result = BuiltinsMath::Atanh(ecmaRuntimeCallInfo.get()); + TestHelper::TearDownFrame(thread_, prev); + JSTaggedValue expect = BuiltinsBase::GetTaggedDouble(0); + ASSERT_EQ(result.GetRawData(), expect.GetRawData()); +} + +// Math.atanh("") +TEST_F(BuiltinsMathTest, Atanh_7) +{ + JSHandle test = thread_->GetEcmaVM()->GetFactory()->NewFromCanBeCompressString(" "); + auto ecmaRuntimeCallInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread_, JSTaggedValue::Undefined(), 6); + ecmaRuntimeCallInfo->SetFunction(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetThis(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetCallArg(0, test.GetTaggedValue()); + + [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread_, ecmaRuntimeCallInfo.get()); + JSTaggedValue result = BuiltinsMath::Atanh(ecmaRuntimeCallInfo.get()); + TestHelper::TearDownFrame(thread_, prev); + JSTaggedValue expect = BuiltinsBase::GetTaggedDouble(0); + ASSERT_EQ(result.GetRawData(), expect.GetRawData()); +} + +// Math.atanh("-1") +TEST_F(BuiltinsMathTest, Atanh_8) +{ + JSHandle test = thread_->GetEcmaVM()->GetFactory()->NewFromCanBeCompressString("-1"); + auto ecmaRuntimeCallInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread_, JSTaggedValue::Undefined(), 6); + ecmaRuntimeCallInfo->SetFunction(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetThis(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetCallArg(0, test.GetTaggedValue()); + + [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread_, ecmaRuntimeCallInfo.get()); + JSTaggedValue result = BuiltinsMath::Atanh(ecmaRuntimeCallInfo.get()); + TestHelper::TearDownFrame(thread_, prev); + JSTaggedValue expect = BuiltinsBase::GetTaggedDouble(-base::POSITIVE_INFINITY); + ASSERT_EQ(result.GetRawData(), expect.GetRawData()); +} + +// Math.atan2(NaN, 1.5) +TEST_F(BuiltinsMathTest, Atan2) +{ + auto ecmaRuntimeCallInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread_, JSTaggedValue::Undefined(), 8); + ecmaRuntimeCallInfo->SetFunction(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetThis(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetCallArg(0, JSTaggedValue(base::NAN_VALUE)); + ecmaRuntimeCallInfo->SetCallArg(1, JSTaggedValue(1.5)); + + [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread_, ecmaRuntimeCallInfo.get()); + JSTaggedValue result = BuiltinsMath::Atan2(ecmaRuntimeCallInfo.get()); + TestHelper::TearDownFrame(thread_, prev); + JSTaggedValue expect = BuiltinsBase::GetTaggedDouble(base::NAN_VALUE); + ASSERT_EQ(result.GetRawData(), expect.GetRawData()); +} + +// Math.atan2(-1, 1.5) +TEST_F(BuiltinsMathTest, Atan2_1) +{ + auto ecmaRuntimeCallInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread_, JSTaggedValue::Undefined(), 8); + ecmaRuntimeCallInfo->SetFunction(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetThis(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetCallArg(0, JSTaggedValue(static_cast(-1))); + ecmaRuntimeCallInfo->SetCallArg(1, JSTaggedValue(1.5)); + + [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread_, ecmaRuntimeCallInfo.get()); + JSTaggedValue result = BuiltinsMath::Atan2(ecmaRuntimeCallInfo.get()); + TestHelper::TearDownFrame(thread_, prev); + JSTaggedValue expect = BuiltinsBase::GetTaggedDouble(-0.5880026035475675); + ASSERT_EQ(result.GetRawData(), expect.GetRawData()); +} + +// Math.atan2(1, -0) +TEST_F(BuiltinsMathTest, Atan2_2) +{ + auto ecmaRuntimeCallInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread_, JSTaggedValue::Undefined(), 8); + ecmaRuntimeCallInfo->SetFunction(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetThis(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetCallArg(0, JSTaggedValue(static_cast(1))); + ecmaRuntimeCallInfo->SetCallArg(1, JSTaggedValue(-0.0)); + + [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread_, ecmaRuntimeCallInfo.get()); + JSTaggedValue result = BuiltinsMath::Atan2(ecmaRuntimeCallInfo.get()); + TestHelper::TearDownFrame(thread_, prev); + JSTaggedValue expect = BuiltinsBase::GetTaggedDouble(BuiltinsMath::PI / 2); + ASSERT_EQ(result.GetRawData(), expect.GetRawData()); +} + +// Math.atan2(0, 1) +TEST_F(BuiltinsMathTest, Atan2_3) +{ + auto ecmaRuntimeCallInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread_, JSTaggedValue::Undefined(), 8); + ecmaRuntimeCallInfo->SetFunction(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetThis(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetCallArg(0, JSTaggedValue(static_cast(0))); + ecmaRuntimeCallInfo->SetCallArg(1, JSTaggedValue(static_cast(1))); + + [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread_, ecmaRuntimeCallInfo.get()); + JSTaggedValue result = BuiltinsMath::Atan2(ecmaRuntimeCallInfo.get()); + TestHelper::TearDownFrame(thread_, prev); + JSTaggedValue expect = BuiltinsBase::GetTaggedDouble(0); + ASSERT_EQ(result.GetRawData(), expect.GetRawData()); +} + +// Math.atan2(0, -0) +TEST_F(BuiltinsMathTest, Atan2_4) +{ + auto ecmaRuntimeCallInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread_, JSTaggedValue::Undefined(), 8); + ecmaRuntimeCallInfo->SetFunction(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetThis(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetCallArg(0, JSTaggedValue(static_cast(0))); + ecmaRuntimeCallInfo->SetCallArg(1, JSTaggedValue(-0.0)); + + [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread_, ecmaRuntimeCallInfo.get()); + JSTaggedValue result = BuiltinsMath::Atan2(ecmaRuntimeCallInfo.get()); + TestHelper::TearDownFrame(thread_, prev); + JSTaggedValue expect = BuiltinsBase::GetTaggedDouble(BuiltinsMath::PI); + ASSERT_EQ(result.GetRawData(), expect.GetRawData()); +} + +// Math.atan2(-0, 0) +TEST_F(BuiltinsMathTest, Atan2_5) +{ + auto ecmaRuntimeCallInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread_, JSTaggedValue::Undefined(), 8); + ecmaRuntimeCallInfo->SetFunction(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetThis(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetCallArg(0, JSTaggedValue(-0.0)); + ecmaRuntimeCallInfo->SetCallArg(1, JSTaggedValue(static_cast(0))); + + [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread_, ecmaRuntimeCallInfo.get()); + JSTaggedValue result = BuiltinsMath::Atan2(ecmaRuntimeCallInfo.get()); + TestHelper::TearDownFrame(thread_, prev); + JSTaggedValue expect = BuiltinsBase::GetTaggedDouble(-0.0); + ASSERT_EQ(result.GetRawData(), expect.GetRawData()); +} + +// Math.atan2(-0, -0) +TEST_F(BuiltinsMathTest, Atan2_6) +{ + auto ecmaRuntimeCallInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread_, JSTaggedValue::Undefined(), 8); + ecmaRuntimeCallInfo->SetFunction(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetThis(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetCallArg(0, JSTaggedValue(-0.0)); + ecmaRuntimeCallInfo->SetCallArg(1, JSTaggedValue(-0.0)); + + [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread_, ecmaRuntimeCallInfo.get()); + JSTaggedValue result = BuiltinsMath::Atan2(ecmaRuntimeCallInfo.get()); + TestHelper::TearDownFrame(thread_, prev); + JSTaggedValue expect = BuiltinsBase::GetTaggedDouble(-BuiltinsMath::PI); + ASSERT_EQ(result.GetRawData(), expect.GetRawData()); +} + +// Math.atan2(true, false) +TEST_F(BuiltinsMathTest, Atan2_7) +{ + auto ecmaRuntimeCallInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread_, JSTaggedValue::Undefined(), 8); + ecmaRuntimeCallInfo->SetFunction(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetThis(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetCallArg(0, JSTaggedValue::True()); + ecmaRuntimeCallInfo->SetCallArg(1, JSTaggedValue::False()); + + [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread_, ecmaRuntimeCallInfo.get()); + JSTaggedValue result = BuiltinsMath::Atan2(ecmaRuntimeCallInfo.get()); + TestHelper::TearDownFrame(thread_, prev); + JSTaggedValue expect = BuiltinsBase::GetTaggedDouble(1.5707963267948966); + ASSERT_EQ(result.GetRawData(), expect.GetRawData()); +} + +// Math.atan2(false, true) +TEST_F(BuiltinsMathTest, Atan2_8) +{ + auto ecmaRuntimeCallInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread_, JSTaggedValue::Undefined(), 8); + ecmaRuntimeCallInfo->SetFunction(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetThis(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetCallArg(0, JSTaggedValue::False()); + ecmaRuntimeCallInfo->SetCallArg(1, JSTaggedValue::True()); + + [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread_, ecmaRuntimeCallInfo.get()); + JSTaggedValue result = BuiltinsMath::Atan2(ecmaRuntimeCallInfo.get()); + TestHelper::TearDownFrame(thread_, prev); + JSTaggedValue expect = BuiltinsBase::GetTaggedDouble(0); + ASSERT_EQ(result.GetRawData(), expect.GetRawData()); +} + +// Math.atan2("-1","") +TEST_F(BuiltinsMathTest, Atan2_9) +{ + JSHandle test_1 = thread_->GetEcmaVM()->GetFactory()->NewFromCanBeCompressString("-1"); + JSHandle test_2 = thread_->GetEcmaVM()->GetFactory()->NewFromCanBeCompressString(""); + auto ecmaRuntimeCallInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread_, JSTaggedValue::Undefined(), 8); + ecmaRuntimeCallInfo->SetFunction(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetThis(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetCallArg(0, test_1.GetTaggedValue()); + ecmaRuntimeCallInfo->SetCallArg(1, test_2.GetTaggedValue()); + + [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread_, ecmaRuntimeCallInfo.get()); + JSTaggedValue result = BuiltinsMath::Atan2(ecmaRuntimeCallInfo.get()); + TestHelper::TearDownFrame(thread_, prev); + JSTaggedValue expect = BuiltinsBase::GetTaggedDouble(-1.5707963267948966); + ASSERT_EQ(result.GetRawData(), expect.GetRawData()); +} + +// Math.atan2("0.23","0.72") +TEST_F(BuiltinsMathTest, Atan2_10) +{ + JSHandle test_1 = thread_->GetEcmaVM()->GetFactory()->NewFromCanBeCompressString("0.23"); + JSHandle test_2 = thread_->GetEcmaVM()->GetFactory()->NewFromCanBeCompressString("0.72"); + auto ecmaRuntimeCallInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread_, JSTaggedValue::Undefined(), 8); + ecmaRuntimeCallInfo->SetFunction(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetThis(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetCallArg(0, test_1.GetTaggedValue()); + ecmaRuntimeCallInfo->SetCallArg(1, test_2.GetTaggedValue()); + + [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread_, ecmaRuntimeCallInfo.get()); + JSTaggedValue result = BuiltinsMath::Atan2(ecmaRuntimeCallInfo.get()); + TestHelper::TearDownFrame(thread_, prev); + JSTaggedValue expect = BuiltinsBase::GetTaggedDouble(0.3091989123270746); + ASSERT_EQ(result.GetRawData(), expect.GetRawData()); +} + +// Math.atan2(-NaN, 1.5) +TEST_F(BuiltinsMathTest, Atan2_11) +{ + auto ecmaRuntimeCallInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread_, JSTaggedValue::Undefined(), 8); + ecmaRuntimeCallInfo->SetFunction(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetThis(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetCallArg(0, JSTaggedValue(-base::NAN_VALUE)); + ecmaRuntimeCallInfo->SetCallArg(1, JSTaggedValue(-1.5)); + + [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread_, ecmaRuntimeCallInfo.get()); + JSTaggedValue result = BuiltinsMath::Atan2(ecmaRuntimeCallInfo.get()); + TestHelper::TearDownFrame(thread_, prev); + JSTaggedValue expect = BuiltinsBase::GetTaggedDouble(base::NAN_VALUE); + ASSERT_EQ(result.GetRawData(), expect.GetRawData()); +} + +// Math.cbrt(0) +TEST_F(BuiltinsMathTest, Cbrt) +{ + auto ecmaRuntimeCallInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread_, JSTaggedValue::Undefined(), 6); + ecmaRuntimeCallInfo->SetFunction(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetThis(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetCallArg(0, JSTaggedValue(static_cast(0))); + + [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread_, ecmaRuntimeCallInfo.get()); + JSTaggedValue result = BuiltinsMath::Cbrt(ecmaRuntimeCallInfo.get()); + TestHelper::TearDownFrame(thread_, prev); + JSTaggedValue expect = BuiltinsBase::GetTaggedDouble(0); + ASSERT_EQ(result.GetRawData(), expect.GetRawData()); +} + +// Math.cbrt(-0) +TEST_F(BuiltinsMathTest, Cbrt_1) +{ + auto ecmaRuntimeCallInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread_, JSTaggedValue::Undefined(), 6); + ecmaRuntimeCallInfo->SetFunction(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetThis(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetCallArg(0, JSTaggedValue(-0.0)); + + [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread_, ecmaRuntimeCallInfo.get()); + JSTaggedValue result = BuiltinsMath::Cbrt(ecmaRuntimeCallInfo.get()); + TestHelper::TearDownFrame(thread_, prev); + JSTaggedValue expect = BuiltinsBase::GetTaggedDouble(-0.0); + ASSERT_EQ(result.GetRawData(), expect.GetRawData()); +} + +// Math.cbrt(NEGATIVE_INFINITY) +TEST_F(BuiltinsMathTest, Cbrt_2) +{ + auto ecmaRuntimeCallInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread_, JSTaggedValue::Undefined(), 6); + ecmaRuntimeCallInfo->SetFunction(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetThis(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetCallArg(0, JSTaggedValue(-base::POSITIVE_INFINITY)); + + [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread_, ecmaRuntimeCallInfo.get()); + JSTaggedValue result = BuiltinsMath::Cbrt(ecmaRuntimeCallInfo.get()); + TestHelper::TearDownFrame(thread_, prev); + JSTaggedValue expect = BuiltinsBase::GetTaggedDouble(-base::POSITIVE_INFINITY); + ASSERT_EQ(result.GetRawData(), expect.GetRawData()); +} + +// Math.cbrt(POSITIVE_INFINITY) +TEST_F(BuiltinsMathTest, Cbrt_3) +{ + auto ecmaRuntimeCallInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread_, JSTaggedValue::Undefined(), 6); + ecmaRuntimeCallInfo->SetFunction(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetThis(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetCallArg(0, JSTaggedValue(base::POSITIVE_INFINITY)); + + [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread_, ecmaRuntimeCallInfo.get()); + JSTaggedValue result = BuiltinsMath::Cbrt(ecmaRuntimeCallInfo.get()); + TestHelper::TearDownFrame(thread_, prev); + JSTaggedValue expect = BuiltinsBase::GetTaggedDouble(base::POSITIVE_INFINITY); + ASSERT_EQ(result.GetRawData(), expect.GetRawData()); +} + +// Math.cbrt(VALUE_UNDEFINED) +TEST_F(BuiltinsMathTest, Cbrt_4) +{ + auto ecmaRuntimeCallInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread_, JSTaggedValue::Undefined(), 6); + ecmaRuntimeCallInfo->SetFunction(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetThis(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetCallArg(0, JSTaggedValue::Undefined()); + + [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread_, ecmaRuntimeCallInfo.get()); + JSTaggedValue result = BuiltinsMath::Cbrt(ecmaRuntimeCallInfo.get()); + TestHelper::TearDownFrame(thread_, prev); + JSTaggedValue expect = BuiltinsBase::GetTaggedDouble(base::NAN_VALUE); + ASSERT_EQ(result.GetRawData(), expect.GetRawData()); +} + +// Math.cbrt(true) +TEST_F(BuiltinsMathTest, Cbrt_5) +{ + auto ecmaRuntimeCallInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread_, JSTaggedValue::Undefined(), 6); + ecmaRuntimeCallInfo->SetFunction(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetThis(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetCallArg(0, JSTaggedValue::True()); + + [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread_, ecmaRuntimeCallInfo.get()); + JSTaggedValue result = BuiltinsMath::Cbrt(ecmaRuntimeCallInfo.get()); + TestHelper::TearDownFrame(thread_, prev); + JSTaggedValue expect = BuiltinsBase::GetTaggedDouble(1.0); + ASSERT_EQ(result.GetRawData(), expect.GetRawData()); +} + +// Math.cbrt(false) +TEST_F(BuiltinsMathTest, Cbrt_6) +{ + auto ecmaRuntimeCallInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread_, JSTaggedValue::Undefined(), 6); + ecmaRuntimeCallInfo->SetFunction(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetThis(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetCallArg(0, JSTaggedValue::False()); + + [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread_, ecmaRuntimeCallInfo.get()); + JSTaggedValue result = BuiltinsMath::Cbrt(ecmaRuntimeCallInfo.get()); + TestHelper::TearDownFrame(thread_, prev); + JSTaggedValue expect = BuiltinsBase::GetTaggedDouble(0); + ASSERT_EQ(result.GetRawData(), expect.GetRawData()); +} + +// Math.cbrt("") +TEST_F(BuiltinsMathTest, Cbrt_7) +{ + JSHandle test = thread_->GetEcmaVM()->GetFactory()->NewFromCanBeCompressString(" "); + auto ecmaRuntimeCallInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread_, JSTaggedValue::Undefined(), 6); + ecmaRuntimeCallInfo->SetFunction(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetThis(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetCallArg(0, test.GetTaggedValue()); + + [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread_, ecmaRuntimeCallInfo.get()); + JSTaggedValue result = BuiltinsMath::Cbrt(ecmaRuntimeCallInfo.get()); + TestHelper::TearDownFrame(thread_, prev); + JSTaggedValue expect = BuiltinsBase::GetTaggedDouble(0); + ASSERT_EQ(result.GetRawData(), expect.GetRawData()); +} + +// Math.cbrt("1.23") +TEST_F(BuiltinsMathTest, Cbrt_8) +{ + JSHandle test = thread_->GetEcmaVM()->GetFactory()->NewFromCanBeCompressString("1.23"); + auto ecmaRuntimeCallInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread_, JSTaggedValue::Undefined(), 6); + ecmaRuntimeCallInfo->SetFunction(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetThis(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetCallArg(0, test.GetTaggedValue()); + + [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread_, ecmaRuntimeCallInfo.get()); + JSTaggedValue result = BuiltinsMath::Cbrt(ecmaRuntimeCallInfo.get()); + TestHelper::TearDownFrame(thread_, prev); + JSTaggedValue expect = BuiltinsBase::GetTaggedDouble(1.0714412696907731); + ASSERT_EQ(result.GetRawData(), expect.GetRawData()); +} + +// Math.cbrt(-NaN) +TEST_F(BuiltinsMathTest, Cbrt_9) +{ + auto ecmaRuntimeCallInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread_, JSTaggedValue::Undefined(), 6); + ecmaRuntimeCallInfo->SetFunction(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetThis(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetCallArg(0, JSTaggedValue(-base::NAN_VALUE)); + + [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread_, ecmaRuntimeCallInfo.get()); + JSTaggedValue result = BuiltinsMath::Cbrt(ecmaRuntimeCallInfo.get()); + TestHelper::TearDownFrame(thread_, prev); + JSTaggedValue expect = BuiltinsBase::GetTaggedDouble(base::NAN_VALUE); + ASSERT_EQ(result.GetRawData(), expect.GetRawData()); +} + +// Math.ceil(3.25) +TEST_F(BuiltinsMathTest, Ceil) +{ + auto ecmaRuntimeCallInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread_, JSTaggedValue::Undefined(), 6); + ecmaRuntimeCallInfo->SetFunction(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetThis(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetCallArg(0, JSTaggedValue(3.25)); + + [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread_, ecmaRuntimeCallInfo.get()); + JSTaggedValue result = BuiltinsMath::Ceil(ecmaRuntimeCallInfo.get()); + TestHelper::TearDownFrame(thread_, prev); + JSTaggedValue expect = BuiltinsBase::GetTaggedDouble(4.0); + ASSERT_EQ(result.GetRawData(), expect.GetRawData()); +} + +// Math.ceil(POSITIVE_INFINITY) +TEST_F(BuiltinsMathTest, Ceil_1) +{ + auto ecmaRuntimeCallInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread_, JSTaggedValue::Undefined(), 6); + ecmaRuntimeCallInfo->SetFunction(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetThis(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetCallArg(0, JSTaggedValue(base::POSITIVE_INFINITY)); + + [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread_, ecmaRuntimeCallInfo.get()); + JSTaggedValue result = BuiltinsMath::Ceil(ecmaRuntimeCallInfo.get()); + TestHelper::TearDownFrame(thread_, prev); + JSTaggedValue expect = BuiltinsBase::GetTaggedDouble(base::POSITIVE_INFINITY); + ASSERT_EQ(result.GetRawData(), expect.GetRawData()); +} + +// Math.ceil(-0.0) +TEST_F(BuiltinsMathTest, Ceil_2) +{ + auto ecmaRuntimeCallInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread_, JSTaggedValue::Undefined(), 6); + ecmaRuntimeCallInfo->SetFunction(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetThis(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetCallArg(0, JSTaggedValue(-0.0)); + + [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread_, ecmaRuntimeCallInfo.get()); + JSTaggedValue result = BuiltinsMath::Ceil(ecmaRuntimeCallInfo.get()); + TestHelper::TearDownFrame(thread_, prev); + JSTaggedValue expect = BuiltinsBase::GetTaggedDouble(-0.0); + ASSERT_EQ(result.GetRawData(), expect.GetRawData()); +} + +// Math.ceil(null) +TEST_F(BuiltinsMathTest, Ceil_3) +{ + auto ecmaRuntimeCallInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread_, JSTaggedValue::Undefined(), 6); + ecmaRuntimeCallInfo->SetFunction(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetThis(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetCallArg(0, JSTaggedValue::Null()); + + [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread_, ecmaRuntimeCallInfo.get()); + JSTaggedValue result = BuiltinsMath::Ceil(ecmaRuntimeCallInfo.get()); + TestHelper::TearDownFrame(thread_, prev); + JSTaggedValue expect = BuiltinsBase::GetTaggedDouble(0); + ASSERT_EQ(result.GetRawData(), expect.GetRawData()); +} + +// Math.ceil(0) +TEST_F(BuiltinsMathTest, Ceil_4) +{ + auto ecmaRuntimeCallInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread_, JSTaggedValue::Undefined(), 6); + ecmaRuntimeCallInfo->SetFunction(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetThis(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetCallArg(0, JSTaggedValue(static_cast(0))); + + [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread_, ecmaRuntimeCallInfo.get()); + JSTaggedValue result = BuiltinsMath::Ceil(ecmaRuntimeCallInfo.get()); + TestHelper::TearDownFrame(thread_, prev); + JSTaggedValue expect = BuiltinsBase::GetTaggedDouble(0); + ASSERT_EQ(result.GetRawData(), expect.GetRawData()); +} + +// Math.ceil(true) +TEST_F(BuiltinsMathTest, Ceil_5) +{ + auto ecmaRuntimeCallInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread_, JSTaggedValue::Undefined(), 6); + ecmaRuntimeCallInfo->SetFunction(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetThis(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetCallArg(0, JSTaggedValue::True()); + + [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread_, ecmaRuntimeCallInfo.get()); + JSTaggedValue result = BuiltinsMath::Ceil(ecmaRuntimeCallInfo.get()); + TestHelper::TearDownFrame(thread_, prev); + JSTaggedValue expect = BuiltinsBase::GetTaggedDouble(1.0); + ASSERT_EQ(result.GetRawData(), expect.GetRawData()); +} + +// Math.ceil(false) +TEST_F(BuiltinsMathTest, Ceil_6) +{ + auto ecmaRuntimeCallInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread_, JSTaggedValue::Undefined(), 6); + ecmaRuntimeCallInfo->SetFunction(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetThis(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetCallArg(0, JSTaggedValue::False()); + + [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread_, ecmaRuntimeCallInfo.get()); + JSTaggedValue result = BuiltinsMath::Ceil(ecmaRuntimeCallInfo.get()); + TestHelper::TearDownFrame(thread_, prev); + JSTaggedValue expect = BuiltinsBase::GetTaggedDouble(0); + ASSERT_EQ(result.GetRawData(), expect.GetRawData()); +} + +// Math.ceil("") +TEST_F(BuiltinsMathTest, Ceil_7) +{ + JSHandle test = thread_->GetEcmaVM()->GetFactory()->NewFromCanBeCompressString(""); + auto ecmaRuntimeCallInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread_, JSTaggedValue::Undefined(), 6); + ecmaRuntimeCallInfo->SetFunction(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetThis(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetCallArg(0, test.GetTaggedValue()); + + [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread_, ecmaRuntimeCallInfo.get()); + JSTaggedValue result = BuiltinsMath::Ceil(ecmaRuntimeCallInfo.get()); + TestHelper::TearDownFrame(thread_, prev); + JSTaggedValue expect = BuiltinsBase::GetTaggedDouble(0); + ASSERT_EQ(result.GetRawData(), expect.GetRawData()); +} + +// Math.ceil("3.23") +TEST_F(BuiltinsMathTest, Ceil_8) +{ + JSHandle test = thread_->GetEcmaVM()->GetFactory()->NewFromCanBeCompressString("3.23"); + auto ecmaRuntimeCallInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread_, JSTaggedValue::Undefined(), 6); + ecmaRuntimeCallInfo->SetFunction(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetThis(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetCallArg(0, test.GetTaggedValue()); + + [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread_, ecmaRuntimeCallInfo.get()); + JSTaggedValue result = BuiltinsMath::Ceil(ecmaRuntimeCallInfo.get()); + TestHelper::TearDownFrame(thread_, prev); + JSTaggedValue expect = BuiltinsBase::GetTaggedDouble(4.0); + ASSERT_EQ(result.GetRawData(), expect.GetRawData()); +} + +// Math.ceil(-NaN) +TEST_F(BuiltinsMathTest, Ceil_9) +{ + auto ecmaRuntimeCallInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread_, JSTaggedValue::Undefined(), 6); + ecmaRuntimeCallInfo->SetFunction(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetThis(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetCallArg(0, JSTaggedValue(-base::NAN_VALUE)); + + [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread_, ecmaRuntimeCallInfo.get()); + JSTaggedValue result = BuiltinsMath::Ceil(ecmaRuntimeCallInfo.get()); + TestHelper::TearDownFrame(thread_, prev); + JSTaggedValue expect = BuiltinsBase::GetTaggedDouble(base::NAN_VALUE); + ASSERT_EQ(result.GetRawData(), expect.GetRawData()); +} + +// Math.cos(0) +TEST_F(BuiltinsMathTest, Cos) +{ + auto ecmaRuntimeCallInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread_, JSTaggedValue::Undefined(), 6); + ecmaRuntimeCallInfo->SetFunction(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetThis(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetCallArg(0, JSTaggedValue(static_cast(0))); + + [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread_, ecmaRuntimeCallInfo.get()); + JSTaggedValue result = BuiltinsMath::Cos(ecmaRuntimeCallInfo.get()); + TestHelper::TearDownFrame(thread_, prev); + JSTaggedValue expect = BuiltinsBase::GetTaggedDouble(1.0); + ASSERT_EQ(result.GetRawData(), expect.GetRawData()); +} + +// Math.cos(-NAN) +TEST_F(BuiltinsMathTest, Cos_1) +{ + auto ecmaRuntimeCallInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread_, JSTaggedValue::Undefined(), 6); + ecmaRuntimeCallInfo->SetFunction(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetThis(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetCallArg(0, JSTaggedValue(-base::NAN_VALUE)); + + [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread_, ecmaRuntimeCallInfo.get()); + JSTaggedValue result = BuiltinsMath::Cos(ecmaRuntimeCallInfo.get()); + TestHelper::TearDownFrame(thread_, prev); + JSTaggedValue expect = BuiltinsBase::GetTaggedDouble(base::NAN_VALUE); + ASSERT_EQ(result.GetRawData(), expect.GetRawData()); +} + +// Math.cos(POSITIVE_INFINITY) +TEST_F(BuiltinsMathTest, Cos_2) +{ + auto ecmaRuntimeCallInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread_, JSTaggedValue::Undefined(), 6); + ecmaRuntimeCallInfo->SetFunction(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetThis(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetCallArg(0, JSTaggedValue(base::POSITIVE_INFINITY)); + + [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread_, ecmaRuntimeCallInfo.get()); + JSTaggedValue result = BuiltinsMath::Cos(ecmaRuntimeCallInfo.get()); + TestHelper::TearDownFrame(thread_, prev); + JSTaggedValue expect = BuiltinsBase::GetTaggedDouble(base::NAN_VALUE); + ASSERT_EQ(result.GetRawData(), expect.GetRawData()); +} + +// Math.cos(-POSITIVE_INFINITY) +TEST_F(BuiltinsMathTest, Cos_3) +{ + auto ecmaRuntimeCallInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread_, JSTaggedValue::Undefined(), 6); + ecmaRuntimeCallInfo->SetFunction(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetThis(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetCallArg(0, JSTaggedValue(-base::POSITIVE_INFINITY)); + + [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread_, ecmaRuntimeCallInfo.get()); + JSTaggedValue result = BuiltinsMath::Cos(ecmaRuntimeCallInfo.get()); + TestHelper::TearDownFrame(thread_, prev); + JSTaggedValue expect = BuiltinsBase::GetTaggedDouble(base::NAN_VALUE); + ASSERT_EQ(result.GetRawData(), expect.GetRawData()); +} + +// Math.cos(true) +TEST_F(BuiltinsMathTest, Cos_4) +{ + auto ecmaRuntimeCallInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread_, JSTaggedValue::Undefined(), 6); + ecmaRuntimeCallInfo->SetFunction(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetThis(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetCallArg(0, JSTaggedValue::True()); + + [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread_, ecmaRuntimeCallInfo.get()); + JSTaggedValue result = BuiltinsMath::Cos(ecmaRuntimeCallInfo.get()); + TestHelper::TearDownFrame(thread_, prev); + JSTaggedValue expect = BuiltinsBase::GetTaggedDouble(0.5403023058681398); + ASSERT_EQ(result.GetRawData(), expect.GetRawData()); +} + +// Math.cos(false) +TEST_F(BuiltinsMathTest, Cos_5) +{ + auto ecmaRuntimeCallInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread_, JSTaggedValue::Undefined(), 6); + ecmaRuntimeCallInfo->SetFunction(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetThis(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetCallArg(0, JSTaggedValue::False()); + + [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread_, ecmaRuntimeCallInfo.get()); + JSTaggedValue result = BuiltinsMath::Cos(ecmaRuntimeCallInfo.get()); + TestHelper::TearDownFrame(thread_, prev); + JSTaggedValue expect = BuiltinsBase::GetTaggedDouble(1.0); + ASSERT_EQ(result.GetRawData(), expect.GetRawData()); +} + +// Math.cos("") +TEST_F(BuiltinsMathTest, Cos_6) +{ + JSHandle test = thread_->GetEcmaVM()->GetFactory()->NewFromCanBeCompressString(""); + auto ecmaRuntimeCallInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread_, JSTaggedValue::Undefined(), 6); + ecmaRuntimeCallInfo->SetFunction(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetThis(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetCallArg(0, test.GetTaggedValue()); + + [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread_, ecmaRuntimeCallInfo.get()); + JSTaggedValue result = BuiltinsMath::Cos(ecmaRuntimeCallInfo.get()); + TestHelper::TearDownFrame(thread_, prev); + JSTaggedValue expect = BuiltinsBase::GetTaggedDouble(1.0); + ASSERT_EQ(result.GetRawData(), expect.GetRawData()); +} + +// Math.cos("3.23") +TEST_F(BuiltinsMathTest, Cos_7) +{ + JSHandle test = thread_->GetEcmaVM()->GetFactory()->NewFromCanBeCompressString("3.23"); + auto ecmaRuntimeCallInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread_, JSTaggedValue::Undefined(), 6); + ecmaRuntimeCallInfo->SetFunction(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetThis(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetCallArg(0, test.GetTaggedValue()); + + [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread_, ecmaRuntimeCallInfo.get()); + JSTaggedValue result = BuiltinsMath::Cos(ecmaRuntimeCallInfo.get()); + TestHelper::TearDownFrame(thread_, prev); + JSTaggedValue expect = BuiltinsBase::GetTaggedDouble(-0.9960946152060809); + ASSERT_EQ(result.GetRawData(), expect.GetRawData()); +} + +// Math.cosh(0) +TEST_F(BuiltinsMathTest, Cosh) +{ + auto ecmaRuntimeCallInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread_, JSTaggedValue::Undefined(), 6); + ecmaRuntimeCallInfo->SetFunction(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetThis(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetCallArg(0, JSTaggedValue(static_cast(0))); + + [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread_, ecmaRuntimeCallInfo.get()); + JSTaggedValue result = BuiltinsMath::Cosh(ecmaRuntimeCallInfo.get()); + TestHelper::TearDownFrame(thread_, prev); + JSTaggedValue expect = BuiltinsBase::GetTaggedDouble(1.0); + ASSERT_EQ(result.GetRawData(), expect.GetRawData()); +} + +// Math.cosh(-NAN) +TEST_F(BuiltinsMathTest, Cosh_1) +{ + auto ecmaRuntimeCallInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread_, JSTaggedValue::Undefined(), 6); + ecmaRuntimeCallInfo->SetFunction(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetThis(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetCallArg(0, JSTaggedValue(-base::NAN_VALUE)); + + [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread_, ecmaRuntimeCallInfo.get()); + JSTaggedValue result = BuiltinsMath::Cosh(ecmaRuntimeCallInfo.get()); + TestHelper::TearDownFrame(thread_, prev); + JSTaggedValue expect = BuiltinsBase::GetTaggedDouble(base::NAN_VALUE); + ASSERT_EQ(result.GetRawData(), expect.GetRawData()); +} + +// Math.cosh(POSITIVE_INFINITY) +TEST_F(BuiltinsMathTest, Cosh_2) +{ + auto ecmaRuntimeCallInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread_, JSTaggedValue::Undefined(), 6); + ecmaRuntimeCallInfo->SetFunction(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetThis(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetCallArg(0, JSTaggedValue(base::POSITIVE_INFINITY)); + + [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread_, ecmaRuntimeCallInfo.get()); + JSTaggedValue result = BuiltinsMath::Cosh(ecmaRuntimeCallInfo.get()); + TestHelper::TearDownFrame(thread_, prev); + JSTaggedValue expect = BuiltinsBase::GetTaggedDouble(base::POSITIVE_INFINITY); + ASSERT_EQ(result.GetRawData(), expect.GetRawData()); +} + +// Math.cosh(-POSITIVE_INFINITY) +TEST_F(BuiltinsMathTest, Cosh_3) +{ + auto ecmaRuntimeCallInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread_, JSTaggedValue::Undefined(), 6); + ecmaRuntimeCallInfo->SetFunction(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetThis(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetCallArg(0, JSTaggedValue(-base::POSITIVE_INFINITY)); + + [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread_, ecmaRuntimeCallInfo.get()); + JSTaggedValue result = BuiltinsMath::Cosh(ecmaRuntimeCallInfo.get()); + TestHelper::TearDownFrame(thread_, prev); + JSTaggedValue expect = BuiltinsBase::GetTaggedDouble(base::POSITIVE_INFINITY); + ASSERT_EQ(result.GetRawData(), expect.GetRawData()); +} + +// Math.cosh(true) +TEST_F(BuiltinsMathTest, Cosh_4) +{ + auto ecmaRuntimeCallInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread_, JSTaggedValue::Undefined(), 6); + ecmaRuntimeCallInfo->SetFunction(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetThis(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetCallArg(0, JSTaggedValue::True()); + + [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread_, ecmaRuntimeCallInfo.get()); + JSTaggedValue result = BuiltinsMath::Cosh(ecmaRuntimeCallInfo.get()); + TestHelper::TearDownFrame(thread_, prev); + JSTaggedValue expect = BuiltinsBase::GetTaggedDouble(1.5430806348152437); + ASSERT_EQ(result.GetRawData(), expect.GetRawData()); +} + +// Math.cosh(false) +TEST_F(BuiltinsMathTest, Cosh_5) +{ + auto ecmaRuntimeCallInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread_, JSTaggedValue::Undefined(), 6); + ecmaRuntimeCallInfo->SetFunction(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetThis(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetCallArg(0, JSTaggedValue::False()); + + [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread_, ecmaRuntimeCallInfo.get()); + JSTaggedValue result = BuiltinsMath::Cosh(ecmaRuntimeCallInfo.get()); + TestHelper::TearDownFrame(thread_, prev); + JSTaggedValue expect = BuiltinsBase::GetTaggedDouble(1.0); + ASSERT_EQ(result.GetRawData(), expect.GetRawData()); +} + +// Math.cosh("") +TEST_F(BuiltinsMathTest, Cosh_6) +{ + JSHandle test = thread_->GetEcmaVM()->GetFactory()->NewFromCanBeCompressString(" "); + auto ecmaRuntimeCallInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread_, JSTaggedValue::Undefined(), 6); + ecmaRuntimeCallInfo->SetFunction(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetThis(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetCallArg(0, test.GetTaggedValue()); + + [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread_, ecmaRuntimeCallInfo.get()); + JSTaggedValue result = BuiltinsMath::Cosh(ecmaRuntimeCallInfo.get()); + TestHelper::TearDownFrame(thread_, prev); + JSTaggedValue expect = BuiltinsBase::GetTaggedDouble(1.0); + ASSERT_EQ(result.GetRawData(), expect.GetRawData()); +} + +// Math.cosh("3.23") +TEST_F(BuiltinsMathTest, Cosh_7) +{ + JSHandle test = thread_->GetEcmaVM()->GetFactory()->NewFromCanBeCompressString("3.23"); + auto ecmaRuntimeCallInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread_, JSTaggedValue::Undefined(), 6); + ecmaRuntimeCallInfo->SetFunction(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetThis(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetCallArg(0, test.GetTaggedValue()); + + [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread_, ecmaRuntimeCallInfo.get()); + JSTaggedValue result = BuiltinsMath::Cosh(ecmaRuntimeCallInfo.get()); + TestHelper::TearDownFrame(thread_, prev); + JSTaggedValue expect = BuiltinsBase::GetTaggedDouble(12.659607234875645); + ASSERT_EQ(result.GetRawData(), expect.GetRawData()); +} + +// Math.exp(0) +TEST_F(BuiltinsMathTest, Exp) +{ + auto ecmaRuntimeCallInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread_, JSTaggedValue::Undefined(), 6); + ecmaRuntimeCallInfo->SetFunction(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetThis(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetCallArg(0, JSTaggedValue(static_cast(0))); + + [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread_, ecmaRuntimeCallInfo.get()); + JSTaggedValue result = BuiltinsMath::Exp(ecmaRuntimeCallInfo.get()); + TestHelper::TearDownFrame(thread_, prev); + JSTaggedValue expect = BuiltinsBase::GetTaggedDouble(1.0); + ASSERT_EQ(result.GetRawData(), expect.GetRawData()); +} + +// Math.exp(-NAN) +TEST_F(BuiltinsMathTest, Exp_1) +{ + auto ecmaRuntimeCallInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread_, JSTaggedValue::Undefined(), 6); + ecmaRuntimeCallInfo->SetFunction(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetThis(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetCallArg(0, JSTaggedValue(-base::NAN_VALUE)); + + [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread_, ecmaRuntimeCallInfo.get()); + JSTaggedValue result = BuiltinsMath::Exp(ecmaRuntimeCallInfo.get()); + TestHelper::TearDownFrame(thread_, prev); + JSTaggedValue expect = BuiltinsBase::GetTaggedDouble(base::NAN_VALUE); + ASSERT_EQ(result.GetRawData(), expect.GetRawData()); +} + +// Math.exp(POSITIVE_INFINITY) +TEST_F(BuiltinsMathTest, Exp_2) +{ + auto ecmaRuntimeCallInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread_, JSTaggedValue::Undefined(), 6); + ecmaRuntimeCallInfo->SetFunction(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetThis(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetCallArg(0, JSTaggedValue(base::POSITIVE_INFINITY)); + + [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread_, ecmaRuntimeCallInfo.get()); + JSTaggedValue result = BuiltinsMath::Exp(ecmaRuntimeCallInfo.get()); + TestHelper::TearDownFrame(thread_, prev); + JSTaggedValue expect = BuiltinsBase::GetTaggedDouble(base::POSITIVE_INFINITY); + ASSERT_EQ(result.GetRawData(), expect.GetRawData()); +} + +// Math.exp(-POSITIVE_INFINITY) +TEST_F(BuiltinsMathTest, Exp_3) +{ + auto ecmaRuntimeCallInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread_, JSTaggedValue::Undefined(), 6); + ecmaRuntimeCallInfo->SetFunction(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetThis(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetCallArg(0, JSTaggedValue(-base::POSITIVE_INFINITY)); + + [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread_, ecmaRuntimeCallInfo.get()); + JSTaggedValue result = BuiltinsMath::Exp(ecmaRuntimeCallInfo.get()); + TestHelper::TearDownFrame(thread_, prev); + JSTaggedValue expect = BuiltinsBase::GetTaggedDouble(0); + ASSERT_EQ(result.GetRawData(), expect.GetRawData()); +} + +// Math.exp(true) +TEST_F(BuiltinsMathTest, Exp_4) +{ + auto ecmaRuntimeCallInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread_, JSTaggedValue::Undefined(), 6); + ecmaRuntimeCallInfo->SetFunction(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetThis(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetCallArg(0, JSTaggedValue::True()); + + [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread_, ecmaRuntimeCallInfo.get()); + JSTaggedValue result = BuiltinsMath::Exp(ecmaRuntimeCallInfo.get()); + TestHelper::TearDownFrame(thread_, prev); + JSTaggedValue expect = BuiltinsBase::GetTaggedDouble(2.718281828459045); + ASSERT_EQ(result.GetRawData(), expect.GetRawData()); +} + +// Math.exp(false) +TEST_F(BuiltinsMathTest, Exp_5) +{ + auto ecmaRuntimeCallInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread_, JSTaggedValue::Undefined(), 6); + ecmaRuntimeCallInfo->SetFunction(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetThis(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetCallArg(0, JSTaggedValue::False()); + + [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread_, ecmaRuntimeCallInfo.get()); + JSTaggedValue result = BuiltinsMath::Exp(ecmaRuntimeCallInfo.get()); + TestHelper::TearDownFrame(thread_, prev); + JSTaggedValue expect = BuiltinsBase::GetTaggedDouble(1.0); + ASSERT_EQ(result.GetRawData(), expect.GetRawData()); +} + +// Math.exp("") +TEST_F(BuiltinsMathTest, Exp_6) +{ + JSHandle test = thread_->GetEcmaVM()->GetFactory()->NewFromCanBeCompressString(""); + auto ecmaRuntimeCallInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread_, JSTaggedValue::Undefined(), 6); + ecmaRuntimeCallInfo->SetFunction(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetThis(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetCallArg(0, test.GetTaggedValue()); + + [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread_, ecmaRuntimeCallInfo.get()); + JSTaggedValue result = BuiltinsMath::Exp(ecmaRuntimeCallInfo.get()); + TestHelper::TearDownFrame(thread_, prev); + JSTaggedValue expect = BuiltinsBase::GetTaggedDouble(1.0); + ASSERT_EQ(result.GetRawData(), expect.GetRawData()); +} + +// Math.exp("-3.23") +TEST_F(BuiltinsMathTest, Exp_7) +{ + JSHandle test = thread_->GetEcmaVM()->GetFactory()->NewFromCanBeCompressString("-3.23"); + auto ecmaRuntimeCallInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread_, JSTaggedValue::Undefined(), 6); + ecmaRuntimeCallInfo->SetFunction(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetThis(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetCallArg(0, test.GetTaggedValue()); + + [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread_, ecmaRuntimeCallInfo.get()); + JSTaggedValue result = BuiltinsMath::Exp(ecmaRuntimeCallInfo.get()); + TestHelper::TearDownFrame(thread_, prev); + JSTaggedValue expect = BuiltinsBase::GetTaggedDouble(0.039557498788398725); + ASSERT_EQ(result.GetRawData(), expect.GetRawData()); +} + +// Math.Expm1(0) +TEST_F(BuiltinsMathTest, Expm1) +{ + auto ecmaRuntimeCallInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread_, JSTaggedValue::Undefined(), 6); + ecmaRuntimeCallInfo->SetFunction(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetThis(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetCallArg(0, JSTaggedValue(static_cast(0))); + + [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread_, ecmaRuntimeCallInfo.get()); + JSTaggedValue result = BuiltinsMath::Expm1(ecmaRuntimeCallInfo.get()); + TestHelper::TearDownFrame(thread_, prev); + JSTaggedValue expect = BuiltinsBase::GetTaggedDouble(0); + ASSERT_EQ(result.GetRawData(), expect.GetRawData()); +} + +// Math.Expm1(-0.0) +TEST_F(BuiltinsMathTest, Expm1_1) +{ + auto ecmaRuntimeCallInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread_, JSTaggedValue::Undefined(), 6); + ecmaRuntimeCallInfo->SetFunction(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetThis(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetCallArg(0, JSTaggedValue(-0.0)); + + [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread_, ecmaRuntimeCallInfo.get()); + JSTaggedValue result = BuiltinsMath::Expm1(ecmaRuntimeCallInfo.get()); + TestHelper::TearDownFrame(thread_, prev); + JSTaggedValue expect = BuiltinsBase::GetTaggedDouble(-0.0); + ASSERT_EQ(result.GetRawData(), expect.GetRawData()); +} + +// Math.Expm1(-NAN) +TEST_F(BuiltinsMathTest, Expm1_2) +{ + auto ecmaRuntimeCallInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread_, JSTaggedValue::Undefined(), 6); + ecmaRuntimeCallInfo->SetFunction(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetThis(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetCallArg(0, JSTaggedValue(-base::NAN_VALUE)); + + [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread_, ecmaRuntimeCallInfo.get()); + JSTaggedValue result = BuiltinsMath::Expm1(ecmaRuntimeCallInfo.get()); + TestHelper::TearDownFrame(thread_, prev); + JSTaggedValue expect = BuiltinsBase::GetTaggedDouble(base::NAN_VALUE); + ASSERT_EQ(result.GetRawData(), expect.GetRawData()); +} + +// Math.Expm1(POSITIVE_INFINITY) +TEST_F(BuiltinsMathTest, Expm1_3) +{ + auto ecmaRuntimeCallInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread_, JSTaggedValue::Undefined(), 6); + ecmaRuntimeCallInfo->SetFunction(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetThis(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetCallArg(0, JSTaggedValue(base::POSITIVE_INFINITY)); + + [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread_, ecmaRuntimeCallInfo.get()); + JSTaggedValue result = BuiltinsMath::Expm1(ecmaRuntimeCallInfo.get()); + TestHelper::TearDownFrame(thread_, prev); + JSTaggedValue expect = BuiltinsBase::GetTaggedDouble(base::POSITIVE_INFINITY); + ASSERT_EQ(result.GetRawData(), expect.GetRawData()); +} + +// Math.Expm1(-POSITIVE_INFINITY) +TEST_F(BuiltinsMathTest, Expm1_4) +{ + auto ecmaRuntimeCallInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread_, JSTaggedValue::Undefined(), 6); + ecmaRuntimeCallInfo->SetFunction(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetThis(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetCallArg(0, JSTaggedValue(-base::POSITIVE_INFINITY)); + + [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread_, ecmaRuntimeCallInfo.get()); + JSTaggedValue result = BuiltinsMath::Expm1(ecmaRuntimeCallInfo.get()); + TestHelper::TearDownFrame(thread_, prev); + JSTaggedValue expect = BuiltinsBase::GetTaggedDouble(-1.0); + ASSERT_EQ(result.GetRawData(), expect.GetRawData()); +} + +// Math.expm1(true) +TEST_F(BuiltinsMathTest, Expm1_5) +{ + auto ecmaRuntimeCallInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread_, JSTaggedValue::Undefined(), 6); + ecmaRuntimeCallInfo->SetFunction(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetThis(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetCallArg(0, JSTaggedValue::True()); + + [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread_, ecmaRuntimeCallInfo.get()); + JSTaggedValue result = BuiltinsMath::Expm1(ecmaRuntimeCallInfo.get()); + TestHelper::TearDownFrame(thread_, prev); + double expect = 1.718281828459045; + ASSERT_TRUE(result.IsDouble()); + ASSERT_TRUE(std::abs(result.GetDouble() - expect) < 0.00000001); +} + +// Math.expm1(false) +TEST_F(BuiltinsMathTest, Expm1_6) +{ + auto ecmaRuntimeCallInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread_, JSTaggedValue::Undefined(), 6); + ecmaRuntimeCallInfo->SetFunction(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetThis(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetCallArg(0, JSTaggedValue::False()); + + [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread_, ecmaRuntimeCallInfo.get()); + JSTaggedValue result = BuiltinsMath::Expm1(ecmaRuntimeCallInfo.get()); + TestHelper::TearDownFrame(thread_, prev); + JSTaggedValue expect = BuiltinsBase::GetTaggedDouble(0); + ASSERT_EQ(result.GetRawData(), expect.GetRawData()); +} + +// Math.expm1("") +TEST_F(BuiltinsMathTest, Expm1_7) +{ + JSHandle test = thread_->GetEcmaVM()->GetFactory()->NewFromCanBeCompressString(" "); + auto ecmaRuntimeCallInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread_, JSTaggedValue::Undefined(), 6); + ecmaRuntimeCallInfo->SetFunction(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetThis(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetCallArg(0, test.GetTaggedValue()); + + [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread_, ecmaRuntimeCallInfo.get()); + JSTaggedValue result = BuiltinsMath::Expm1(ecmaRuntimeCallInfo.get()); + TestHelper::TearDownFrame(thread_, prev); + JSTaggedValue expect = BuiltinsBase::GetTaggedDouble(0); + ASSERT_EQ(result.GetRawData(), expect.GetRawData()); +} + +// Math.expm1("-3.23") +TEST_F(BuiltinsMathTest, Expm1_8) +{ + JSHandle test = thread_->GetEcmaVM()->GetFactory()->NewFromCanBeCompressString("-3.23"); + auto ecmaRuntimeCallInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread_, JSTaggedValue::Undefined(), 6); + ecmaRuntimeCallInfo->SetFunction(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetThis(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetCallArg(0, test.GetTaggedValue()); + + [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread_, ecmaRuntimeCallInfo.get()); + JSTaggedValue result = BuiltinsMath::Expm1(ecmaRuntimeCallInfo.get()); + TestHelper::TearDownFrame(thread_, prev); + JSTaggedValue expect = BuiltinsBase::GetTaggedDouble(-0.9604425012116012); + ASSERT_EQ(result.GetRawData(), expect.GetRawData()); +} + +// Math.expm1("0x12") +TEST_F(BuiltinsMathTest, Expm1_9) +{ + JSHandle test = thread_->GetEcmaVM()->GetFactory()->NewFromCanBeCompressString("0x12"); + auto ecmaRuntimeCallInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread_, JSTaggedValue::Undefined(), 6); + ecmaRuntimeCallInfo->SetFunction(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetThis(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetCallArg(0, test.GetTaggedValue()); + + [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread_, ecmaRuntimeCallInfo.get()); + JSTaggedValue result = BuiltinsMath::Expm1(ecmaRuntimeCallInfo.get()); + TestHelper::TearDownFrame(thread_, prev); + JSTaggedValue expect = BuiltinsBase::GetTaggedDouble(65659968.13733051); + ASSERT_EQ(result.GetRawData(), expect.GetRawData()); +} + +// Math.floor(-0.0) +TEST_F(BuiltinsMathTest, Floor) +{ + auto ecmaRuntimeCallInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread_, JSTaggedValue::Undefined(), 6); + ecmaRuntimeCallInfo->SetFunction(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetThis(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetCallArg(0, JSTaggedValue(-0.0)); + + [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread_, ecmaRuntimeCallInfo.get()); + JSTaggedValue result = BuiltinsMath::Floor(ecmaRuntimeCallInfo.get()); + TestHelper::TearDownFrame(thread_, prev); + JSTaggedValue expect = BuiltinsBase::GetTaggedDouble(-0.0); + ASSERT_EQ(result.GetRawData(), expect.GetRawData()); +} + +// Math.floor(-NAN) +TEST_F(BuiltinsMathTest, Floor_1) +{ + auto ecmaRuntimeCallInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread_, JSTaggedValue::Undefined(), 6); + ecmaRuntimeCallInfo->SetFunction(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetThis(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetCallArg(0, JSTaggedValue(-base::NAN_VALUE)); + + [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread_, ecmaRuntimeCallInfo.get()); + JSTaggedValue result = BuiltinsMath::Floor(ecmaRuntimeCallInfo.get()); + TestHelper::TearDownFrame(thread_, prev); + JSTaggedValue expect = BuiltinsBase::GetTaggedDouble(base::NAN_VALUE); + ASSERT_EQ(result.GetRawData(), expect.GetRawData()); +} + +// Math.floor(POSITIVE_INFINITY) +TEST_F(BuiltinsMathTest, Floor_2) +{ + auto ecmaRuntimeCallInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread_, JSTaggedValue::Undefined(), 6); + ecmaRuntimeCallInfo->SetFunction(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetThis(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetCallArg(0, JSTaggedValue(base::POSITIVE_INFINITY)); + + [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread_, ecmaRuntimeCallInfo.get()); + JSTaggedValue result = BuiltinsMath::Floor(ecmaRuntimeCallInfo.get()); + TestHelper::TearDownFrame(thread_, prev); + JSTaggedValue expect = BuiltinsBase::GetTaggedDouble(base::POSITIVE_INFINITY); + ASSERT_EQ(result.GetRawData(), expect.GetRawData()); +} + +// Math.floor(true) +TEST_F(BuiltinsMathTest, Floor_3) +{ + auto ecmaRuntimeCallInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread_, JSTaggedValue::Undefined(), 6); + ecmaRuntimeCallInfo->SetFunction(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetThis(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetCallArg(0, JSTaggedValue::True()); + + [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread_, ecmaRuntimeCallInfo.get()); + JSTaggedValue result = BuiltinsMath::Floor(ecmaRuntimeCallInfo.get()); + TestHelper::TearDownFrame(thread_, prev); + JSTaggedValue expect = BuiltinsBase::GetTaggedDouble(1.0); + ASSERT_EQ(result.GetRawData(), expect.GetRawData()); +} + +// Math.floor("-3.23") +TEST_F(BuiltinsMathTest, Floor_4) +{ + JSHandle test = thread_->GetEcmaVM()->GetFactory()->NewFromCanBeCompressString("-3.23"); + auto ecmaRuntimeCallInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread_, JSTaggedValue::Undefined(), 6); + ecmaRuntimeCallInfo->SetFunction(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetThis(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetCallArg(0, test.GetTaggedValue()); + + [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread_, ecmaRuntimeCallInfo.get()); + JSTaggedValue result = BuiltinsMath::Floor(ecmaRuntimeCallInfo.get()); + TestHelper::TearDownFrame(thread_, prev); + JSTaggedValue expect = BuiltinsBase::GetTaggedDouble(-4.0); + ASSERT_EQ(result.GetRawData(), expect.GetRawData()); +} + +// Math.log(-0.0) +TEST_F(BuiltinsMathTest, Log) +{ + auto ecmaRuntimeCallInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread_, JSTaggedValue::Undefined(), 6); + ecmaRuntimeCallInfo->SetFunction(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetThis(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetCallArg(0, JSTaggedValue(-0.0)); + + [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread_, ecmaRuntimeCallInfo.get()); + JSTaggedValue result = BuiltinsMath::Log(ecmaRuntimeCallInfo.get()); + TestHelper::TearDownFrame(thread_, prev); + JSTaggedValue expect = BuiltinsBase::GetTaggedDouble(-base::POSITIVE_INFINITY); + ASSERT_EQ(result.GetRawData(), expect.GetRawData()); +} + +// Math.log(-NAN) +TEST_F(BuiltinsMathTest, Log_1) +{ + auto ecmaRuntimeCallInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread_, JSTaggedValue::Undefined(), 6); + ecmaRuntimeCallInfo->SetFunction(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetThis(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetCallArg(0, JSTaggedValue(-base::NAN_VALUE)); + + [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread_, ecmaRuntimeCallInfo.get()); + JSTaggedValue result = BuiltinsMath::Log(ecmaRuntimeCallInfo.get()); + TestHelper::TearDownFrame(thread_, prev); + JSTaggedValue expect = BuiltinsBase::GetTaggedDouble(base::NAN_VALUE); + ASSERT_EQ(result.GetRawData(), expect.GetRawData()); +} + +// Math.log(POSITIVE_INFINITY) +TEST_F(BuiltinsMathTest, Log_2) +{ + auto ecmaRuntimeCallInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread_, JSTaggedValue::Undefined(), 6); + ecmaRuntimeCallInfo->SetFunction(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetThis(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetCallArg(0, JSTaggedValue(base::POSITIVE_INFINITY)); + + [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread_, ecmaRuntimeCallInfo.get()); + JSTaggedValue result = BuiltinsMath::Log(ecmaRuntimeCallInfo.get()); + TestHelper::TearDownFrame(thread_, prev); + JSTaggedValue expect = BuiltinsBase::GetTaggedDouble(base::POSITIVE_INFINITY); + ASSERT_EQ(result.GetRawData(), expect.GetRawData()); +} + +// Math.log(true) +TEST_F(BuiltinsMathTest, Log_3) +{ + auto ecmaRuntimeCallInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread_, JSTaggedValue::Undefined(), 6); + ecmaRuntimeCallInfo->SetFunction(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetThis(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetCallArg(0, JSTaggedValue::True()); + + [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread_, ecmaRuntimeCallInfo.get()); + JSTaggedValue result = BuiltinsMath::Log(ecmaRuntimeCallInfo.get()); + TestHelper::TearDownFrame(thread_, prev); + JSTaggedValue expect = BuiltinsBase::GetTaggedDouble(0); + ASSERT_EQ(result.GetRawData(), expect.GetRawData()); +} + +// Math.log("-3.23") +TEST_F(BuiltinsMathTest, Log_4) +{ + JSHandle test = thread_->GetEcmaVM()->GetFactory()->NewFromCanBeCompressString("-3.23"); + auto ecmaRuntimeCallInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread_, JSTaggedValue::Undefined(), 6); + ecmaRuntimeCallInfo->SetFunction(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetThis(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetCallArg(0, test.GetTaggedValue()); + + [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread_, ecmaRuntimeCallInfo.get()); + JSTaggedValue result = BuiltinsMath::Log(ecmaRuntimeCallInfo.get()); + TestHelper::TearDownFrame(thread_, prev); + JSTaggedValue expect = BuiltinsBase::GetTaggedDouble(base::NAN_VALUE); + ASSERT_EQ(result.GetRawData(), expect.GetRawData()); +} + +// Math.log(0.12) +TEST_F(BuiltinsMathTest, Log_5) +{ + auto ecmaRuntimeCallInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread_, JSTaggedValue::Undefined(), 6); + ecmaRuntimeCallInfo->SetFunction(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetThis(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetCallArg(0, JSTaggedValue(0.12)); + + [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread_, ecmaRuntimeCallInfo.get()); + JSTaggedValue result = BuiltinsMath::Log(ecmaRuntimeCallInfo.get()); + TestHelper::TearDownFrame(thread_, prev); + JSTaggedValue expect = BuiltinsBase::GetTaggedDouble(-2.120263536200091); + ASSERT_EQ(result.GetRawData(), expect.GetRawData()); +} + +// Math.log1p(-0.0) +TEST_F(BuiltinsMathTest, Log1p) +{ + auto ecmaRuntimeCallInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread_, JSTaggedValue::Undefined(), 6); + ecmaRuntimeCallInfo->SetFunction(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetThis(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetCallArg(0, JSTaggedValue(-0.0)); + + [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread_, ecmaRuntimeCallInfo.get()); + JSTaggedValue result = BuiltinsMath::Log1p(ecmaRuntimeCallInfo.get()); + TestHelper::TearDownFrame(thread_, prev); + JSTaggedValue expect = BuiltinsBase::GetTaggedDouble(-0.0); + ASSERT_EQ(result.GetRawData(), expect.GetRawData()); +} + +// Math.log1p(-NAN) +TEST_F(BuiltinsMathTest, Log1p_1) +{ + auto ecmaRuntimeCallInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread_, JSTaggedValue::Undefined(), 6); + ecmaRuntimeCallInfo->SetFunction(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetThis(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetCallArg(0, JSTaggedValue(-base::NAN_VALUE)); + + [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread_, ecmaRuntimeCallInfo.get()); + JSTaggedValue result = BuiltinsMath::Log1p(ecmaRuntimeCallInfo.get()); + TestHelper::TearDownFrame(thread_, prev); + JSTaggedValue expect = BuiltinsBase::GetTaggedDouble(base::NAN_VALUE); + ASSERT_EQ(result.GetRawData(), expect.GetRawData()); +} + +// Math.log1p(POSITIVE_INFINITY) +TEST_F(BuiltinsMathTest, Log1p_2) +{ + auto ecmaRuntimeCallInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread_, JSTaggedValue::Undefined(), 6); + ecmaRuntimeCallInfo->SetFunction(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetThis(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetCallArg(0, JSTaggedValue(base::POSITIVE_INFINITY)); + + [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread_, ecmaRuntimeCallInfo.get()); + JSTaggedValue result = BuiltinsMath::Log1p(ecmaRuntimeCallInfo.get()); + TestHelper::TearDownFrame(thread_, prev); + JSTaggedValue expect = BuiltinsBase::GetTaggedDouble(base::POSITIVE_INFINITY); + ASSERT_EQ(result.GetRawData(), expect.GetRawData()); +} + +// Math.log1p(true) +TEST_F(BuiltinsMathTest, Log1p_3) +{ + auto ecmaRuntimeCallInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread_, JSTaggedValue::Undefined(), 6); + ecmaRuntimeCallInfo->SetFunction(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetThis(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetCallArg(0, JSTaggedValue::True()); + + [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread_, ecmaRuntimeCallInfo.get()); + JSTaggedValue result = BuiltinsMath::Log1p(ecmaRuntimeCallInfo.get()); + TestHelper::TearDownFrame(thread_, prev); + JSTaggedValue expect = BuiltinsBase::GetTaggedDouble(0.6931471805599453); + ASSERT_EQ(result.GetRawData(), expect.GetRawData()); +} + +// Math.log1p("-3.23") +TEST_F(BuiltinsMathTest, Log1p_4) +{ + JSHandle test = thread_->GetEcmaVM()->GetFactory()->NewFromCanBeCompressString("-3.23"); + auto ecmaRuntimeCallInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread_, JSTaggedValue::Undefined(), 6); + ecmaRuntimeCallInfo->SetFunction(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetThis(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetCallArg(0, test.GetTaggedValue()); + + [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread_, ecmaRuntimeCallInfo.get()); + JSTaggedValue result = BuiltinsMath::Log1p(ecmaRuntimeCallInfo.get()); + TestHelper::TearDownFrame(thread_, prev); + JSTaggedValue expect = BuiltinsBase::GetTaggedDouble(base::NAN_VALUE); + ASSERT_EQ(result.GetRawData(), expect.GetRawData()); +} + +// Math.log1p(0.12) +TEST_F(BuiltinsMathTest, Log1p_5) +{ + auto ecmaRuntimeCallInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread_, JSTaggedValue::Undefined(), 6); + ecmaRuntimeCallInfo->SetFunction(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetThis(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetCallArg(0, JSTaggedValue(0.12)); + + [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread_, ecmaRuntimeCallInfo.get()); + JSTaggedValue result = BuiltinsMath::Log1p(ecmaRuntimeCallInfo.get()); + TestHelper::TearDownFrame(thread_, prev); + JSTaggedValue expect = BuiltinsBase::GetTaggedDouble(0.11332868530700317); + ASSERT_EQ(result.GetRawData(), expect.GetRawData()); +} + +// Math.log10(-0.0) +TEST_F(BuiltinsMathTest, Log10) +{ + auto ecmaRuntimeCallInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread_, JSTaggedValue::Undefined(), 6); + ecmaRuntimeCallInfo->SetFunction(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetThis(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetCallArg(0, JSTaggedValue(-0.0)); + + [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread_, ecmaRuntimeCallInfo.get()); + JSTaggedValue result = BuiltinsMath::Log10(ecmaRuntimeCallInfo.get()); + TestHelper::TearDownFrame(thread_, prev); + JSTaggedValue expect = BuiltinsBase::GetTaggedDouble(-base::POSITIVE_INFINITY); + ASSERT_EQ(result.GetRawData(), expect.GetRawData()); +} + +// Math.Log10(-NAN) +TEST_F(BuiltinsMathTest, Log10_1) +{ + auto ecmaRuntimeCallInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread_, JSTaggedValue::Undefined(), 6); + ecmaRuntimeCallInfo->SetFunction(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetThis(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetCallArg(0, JSTaggedValue(-base::NAN_VALUE)); + + [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread_, ecmaRuntimeCallInfo.get()); + JSTaggedValue result = BuiltinsMath::Log10(ecmaRuntimeCallInfo.get()); + TestHelper::TearDownFrame(thread_, prev); + JSTaggedValue expect = BuiltinsBase::GetTaggedDouble(base::NAN_VALUE); + ASSERT_EQ(result.GetRawData(), expect.GetRawData()); +} + +// Math.Log10(POSITIVE_INFINITY) +TEST_F(BuiltinsMathTest, Log10_2) +{ + auto ecmaRuntimeCallInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread_, JSTaggedValue::Undefined(), 6); + ecmaRuntimeCallInfo->SetFunction(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetThis(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetCallArg(0, JSTaggedValue(base::POSITIVE_INFINITY)); + + [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread_, ecmaRuntimeCallInfo.get()); + JSTaggedValue result = BuiltinsMath::Log10(ecmaRuntimeCallInfo.get()); + TestHelper::TearDownFrame(thread_, prev); + JSTaggedValue expect = BuiltinsBase::GetTaggedDouble(base::POSITIVE_INFINITY); + ASSERT_EQ(result.GetRawData(), expect.GetRawData()); +} + +// Math.Log10(true) +TEST_F(BuiltinsMathTest, Log10_3) +{ + auto ecmaRuntimeCallInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread_, JSTaggedValue::Undefined(), 6); + ecmaRuntimeCallInfo->SetFunction(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetThis(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetCallArg(0, JSTaggedValue::True()); + + [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread_, ecmaRuntimeCallInfo.get()); + JSTaggedValue result = BuiltinsMath::Log10(ecmaRuntimeCallInfo.get()); + TestHelper::TearDownFrame(thread_, prev); + JSTaggedValue expect = BuiltinsBase::GetTaggedDouble(0); + ASSERT_EQ(result.GetRawData(), expect.GetRawData()); +} + +// Math.Log10("2") +TEST_F(BuiltinsMathTest, Log10_4) +{ + JSHandle test = thread_->GetEcmaVM()->GetFactory()->NewFromCanBeCompressString("2"); + auto ecmaRuntimeCallInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread_, JSTaggedValue::Undefined(), 6); + ecmaRuntimeCallInfo->SetFunction(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetThis(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetCallArg(0, test.GetTaggedValue()); + + [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread_, ecmaRuntimeCallInfo.get()); + JSTaggedValue result = BuiltinsMath::Log10(ecmaRuntimeCallInfo.get()); + TestHelper::TearDownFrame(thread_, prev); + JSTaggedValue expect = BuiltinsBase::GetTaggedDouble(0.3010299956639812); + ASSERT_EQ(result.GetRawData(), expect.GetRawData()); +} + +// Math.Log10(0.12) +TEST_F(BuiltinsMathTest, Log10_5) +{ + auto ecmaRuntimeCallInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread_, JSTaggedValue::Undefined(), 6); + ecmaRuntimeCallInfo->SetFunction(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetThis(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetCallArg(0, JSTaggedValue(0.12)); + + [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread_, ecmaRuntimeCallInfo.get()); + JSTaggedValue result = BuiltinsMath::Log10(ecmaRuntimeCallInfo.get()); + TestHelper::TearDownFrame(thread_, prev); + JSTaggedValue expect = BuiltinsBase::GetTaggedDouble(-0.9208187539523752); + ASSERT_EQ(result.GetRawData(), expect.GetRawData()); +} + +// Math.log2(-0.0) +TEST_F(BuiltinsMathTest, Log2) +{ + auto ecmaRuntimeCallInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread_, JSTaggedValue::Undefined(), 6); + ecmaRuntimeCallInfo->SetFunction(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetThis(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetCallArg(0, JSTaggedValue(-0.0)); + + [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread_, ecmaRuntimeCallInfo.get()); + JSTaggedValue result = BuiltinsMath::Log2(ecmaRuntimeCallInfo.get()); + TestHelper::TearDownFrame(thread_, prev); + JSTaggedValue expect = BuiltinsBase::GetTaggedDouble(-base::POSITIVE_INFINITY); + ASSERT_EQ(result.GetRawData(), expect.GetRawData()); +} + +// Math.log2(-NAN) +TEST_F(BuiltinsMathTest, Log2_1) +{ + auto ecmaRuntimeCallInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread_, JSTaggedValue::Undefined(), 6); + ecmaRuntimeCallInfo->SetFunction(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetThis(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetCallArg(0, JSTaggedValue(-base::NAN_VALUE)); + + [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread_, ecmaRuntimeCallInfo.get()); + JSTaggedValue result = BuiltinsMath::Log2(ecmaRuntimeCallInfo.get()); + TestHelper::TearDownFrame(thread_, prev); + JSTaggedValue expect = BuiltinsBase::GetTaggedDouble(base::NAN_VALUE); + ASSERT_EQ(result.GetRawData(), expect.GetRawData()); +} + +// Math.log2(POSITIVE_INFINITY) +TEST_F(BuiltinsMathTest, Log2_2) +{ + auto ecmaRuntimeCallInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread_, JSTaggedValue::Undefined(), 6); + ecmaRuntimeCallInfo->SetFunction(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetThis(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetCallArg(0, JSTaggedValue(base::POSITIVE_INFINITY)); + + [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread_, ecmaRuntimeCallInfo.get()); + JSTaggedValue result = BuiltinsMath::Log2(ecmaRuntimeCallInfo.get()); + TestHelper::TearDownFrame(thread_, prev); + JSTaggedValue expect = BuiltinsBase::GetTaggedDouble(base::POSITIVE_INFINITY); + ASSERT_EQ(result.GetRawData(), expect.GetRawData()); +} + +// Math.log2(true) +TEST_F(BuiltinsMathTest, Log2_3) +{ + auto ecmaRuntimeCallInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread_, JSTaggedValue::Undefined(), 6); + ecmaRuntimeCallInfo->SetFunction(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetThis(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetCallArg(0, JSTaggedValue::True()); + + [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread_, ecmaRuntimeCallInfo.get()); + JSTaggedValue result = BuiltinsMath::Log2(ecmaRuntimeCallInfo.get()); + TestHelper::TearDownFrame(thread_, prev); + JSTaggedValue expect = BuiltinsBase::GetTaggedDouble(0); + ASSERT_EQ(result.GetRawData(), expect.GetRawData()); +} + +// Math.log2("2") +TEST_F(BuiltinsMathTest, Log2_4) +{ + JSHandle test = thread_->GetEcmaVM()->GetFactory()->NewFromCanBeCompressString("2"); + auto ecmaRuntimeCallInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread_, JSTaggedValue::Undefined(), 6); + ecmaRuntimeCallInfo->SetFunction(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetThis(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetCallArg(0, test.GetTaggedValue()); + + [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread_, ecmaRuntimeCallInfo.get()); + JSTaggedValue result = BuiltinsMath::Log2(ecmaRuntimeCallInfo.get()); + TestHelper::TearDownFrame(thread_, prev); + JSTaggedValue expect = BuiltinsBase::GetTaggedDouble(1.0); + ASSERT_EQ(result.GetRawData(), expect.GetRawData()); +} + +// Math.log2(1) +TEST_F(BuiltinsMathTest, Log2_5) +{ + auto ecmaRuntimeCallInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread_, JSTaggedValue::Undefined(), 6); + ecmaRuntimeCallInfo->SetFunction(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetThis(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetCallArg(0, JSTaggedValue(static_cast(1))); + + [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread_, ecmaRuntimeCallInfo.get()); + JSTaggedValue result = BuiltinsMath::Log2(ecmaRuntimeCallInfo.get()); + TestHelper::TearDownFrame(thread_, prev); + JSTaggedValue expect = BuiltinsBase::GetTaggedDouble(0); + ASSERT_EQ(result.GetRawData(), expect.GetRawData()); +} + +// Math.Max(NaN,1,POSITIVE_INFINITY) +TEST_F(BuiltinsMathTest, Max) +{ + auto ecmaRuntimeCallInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread_, JSTaggedValue::Undefined(), 10); + ecmaRuntimeCallInfo->SetFunction(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetThis(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetCallArg(0, JSTaggedValue(base::NAN_VALUE)); + ecmaRuntimeCallInfo->SetCallArg(1, JSTaggedValue(static_cast(1))); + ecmaRuntimeCallInfo->SetCallArg(2, JSTaggedValue(base::POSITIVE_INFINITY)); + + [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread_, ecmaRuntimeCallInfo.get()); + JSTaggedValue result = BuiltinsMath::Max(ecmaRuntimeCallInfo.get()); + TestHelper::TearDownFrame(thread_, prev); + JSTaggedValue expect = BuiltinsBase::GetTaggedDouble(base::NAN_VALUE); + ASSERT_EQ(result.GetRawData(), expect.GetRawData()); +} + +// Math.Max() +TEST_F(BuiltinsMathTest, Max_1) +{ + auto ecmaRuntimeCallInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread_, JSTaggedValue::Undefined(), 4); + ecmaRuntimeCallInfo->SetFunction(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetThis(JSTaggedValue::Undefined()); + + [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread_, ecmaRuntimeCallInfo.get()); + JSTaggedValue result = BuiltinsMath::Max(ecmaRuntimeCallInfo.get()); + TestHelper::TearDownFrame(thread_, prev); + JSTaggedValue expect = BuiltinsBase::GetTaggedDouble(-base::POSITIVE_INFINITY); + ASSERT_EQ(result.GetRawData(), expect.GetRawData()); +} + +// Math.Max("3",100,2.5) +TEST_F(BuiltinsMathTest, Max_2) +{ + JSHandle test = thread_->GetEcmaVM()->GetFactory()->NewFromCanBeCompressString("3"); + auto ecmaRuntimeCallInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread_, JSTaggedValue::Undefined(), 10); + ecmaRuntimeCallInfo->SetFunction(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetThis(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetCallArg(0, test.GetTaggedValue()); + ecmaRuntimeCallInfo->SetCallArg(1, JSTaggedValue(static_cast(100))); + ecmaRuntimeCallInfo->SetCallArg(2, JSTaggedValue(2.5)); + + [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread_, ecmaRuntimeCallInfo.get()); + JSTaggedValue result = BuiltinsMath::Max(ecmaRuntimeCallInfo.get()); + TestHelper::TearDownFrame(thread_, prev); + JSTaggedValue expect = BuiltinsBase::GetTaggedInt(100); + ASSERT_EQ(result.GetRawData(), expect.GetRawData()); +} + +// Math.Max(3,"100",-101.5) +TEST_F(BuiltinsMathTest, Max_3) +{ + JSHandle test = thread_->GetEcmaVM()->GetFactory()->NewFromCanBeCompressString("100"); + auto ecmaRuntimeCallInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread_, JSTaggedValue::Undefined(), 10); + ecmaRuntimeCallInfo->SetFunction(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetThis(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetCallArg(0, JSTaggedValue(static_cast(3))); + ecmaRuntimeCallInfo->SetCallArg(1, test.GetTaggedValue()); + ecmaRuntimeCallInfo->SetCallArg(2, JSTaggedValue(-101.5)); + + [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread_, ecmaRuntimeCallInfo.get()); + JSTaggedValue result = BuiltinsMath::Max(ecmaRuntimeCallInfo.get()); + TestHelper::TearDownFrame(thread_, prev); + JSTaggedValue expect = BuiltinsBase::GetTaggedDouble(100.0); + ASSERT_EQ(result.GetRawData(), expect.GetRawData()); +} + +// Math.Max(-3,"-100",true) +TEST_F(BuiltinsMathTest, Max_4) +{ + JSHandle test = thread_->GetEcmaVM()->GetFactory()->NewFromCanBeCompressString("-100"); + auto ecmaRuntimeCallInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread_, JSTaggedValue::Undefined(), 10); + ecmaRuntimeCallInfo->SetFunction(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetThis(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetCallArg(0, JSTaggedValue(static_cast(-3))); + ecmaRuntimeCallInfo->SetCallArg(1, test.GetTaggedValue()); + ecmaRuntimeCallInfo->SetCallArg(2, JSTaggedValue::True()); + + [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread_, ecmaRuntimeCallInfo.get()); + JSTaggedValue result = BuiltinsMath::Max(ecmaRuntimeCallInfo.get()); + TestHelper::TearDownFrame(thread_, prev); + JSTaggedValue expect = BuiltinsBase::GetTaggedInt(1); + ASSERT_EQ(result.GetRawData(), expect.GetRawData()); +} + +// Math.min(NaN,1,POSITIVE_INFINITY) +TEST_F(BuiltinsMathTest, Min) +{ + auto ecmaRuntimeCallInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread_, JSTaggedValue::Undefined(), 10); + ecmaRuntimeCallInfo->SetFunction(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetThis(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetCallArg(0, JSTaggedValue(base::NAN_VALUE)); + ecmaRuntimeCallInfo->SetCallArg(1, JSTaggedValue(static_cast(1))); + ecmaRuntimeCallInfo->SetCallArg(2, JSTaggedValue(base::POSITIVE_INFINITY)); + + [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread_, ecmaRuntimeCallInfo.get()); + JSTaggedValue result = BuiltinsMath::Min(ecmaRuntimeCallInfo.get()); + TestHelper::TearDownFrame(thread_, prev); + JSTaggedValue expect = BuiltinsBase::GetTaggedDouble(base::NAN_VALUE); + ASSERT_EQ(result.GetRawData(), expect.GetRawData()); +} + +// Math.min() +TEST_F(BuiltinsMathTest, Min_1) +{ + auto ecmaRuntimeCallInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread_, JSTaggedValue::Undefined(), 4); + ecmaRuntimeCallInfo->SetFunction(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetThis(JSTaggedValue::Undefined()); + + [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread_, ecmaRuntimeCallInfo.get()); + JSTaggedValue result = BuiltinsMath::Min(ecmaRuntimeCallInfo.get()); + TestHelper::TearDownFrame(thread_, prev); + JSTaggedValue expect = BuiltinsBase::GetTaggedDouble(base::POSITIVE_INFINITY); + ASSERT_EQ(result.GetRawData(), expect.GetRawData()); +} + +// Math.min("3",100,2.5) +TEST_F(BuiltinsMathTest, Min_2) +{ + JSHandle test = thread_->GetEcmaVM()->GetFactory()->NewFromCanBeCompressString("3"); + auto ecmaRuntimeCallInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread_, JSTaggedValue::Undefined(), 10); + ecmaRuntimeCallInfo->SetFunction(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetThis(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetCallArg(0, test.GetTaggedValue()); + ecmaRuntimeCallInfo->SetCallArg(1, JSTaggedValue(static_cast(100))); + ecmaRuntimeCallInfo->SetCallArg(2, JSTaggedValue(2.5)); + + [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread_, ecmaRuntimeCallInfo.get()); + JSTaggedValue result = BuiltinsMath::Min(ecmaRuntimeCallInfo.get()); + TestHelper::TearDownFrame(thread_, prev); + JSTaggedValue expect = BuiltinsBase::GetTaggedDouble(2.5); + ASSERT_EQ(result.GetRawData(), expect.GetRawData()); +} + +// Math.min(3,"100",-101.5) +TEST_F(BuiltinsMathTest, Min_3) +{ + JSHandle test = thread_->GetEcmaVM()->GetFactory()->NewFromCanBeCompressString("100"); + auto ecmaRuntimeCallInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread_, JSTaggedValue::Undefined(), 10); + ecmaRuntimeCallInfo->SetFunction(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetThis(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetCallArg(0, JSTaggedValue(static_cast(3))); + ecmaRuntimeCallInfo->SetCallArg(1, test.GetTaggedValue()); + ecmaRuntimeCallInfo->SetCallArg(2, JSTaggedValue(-101.5)); + + [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread_, ecmaRuntimeCallInfo.get()); + JSTaggedValue result = BuiltinsMath::Min(ecmaRuntimeCallInfo.get()); + TestHelper::TearDownFrame(thread_, prev); + JSTaggedValue expect = BuiltinsBase::GetTaggedDouble(-101.5); + ASSERT_EQ(result.GetRawData(), expect.GetRawData()); +} + +// Math.min(3,100,false) +TEST_F(BuiltinsMathTest, Min_4) +{ + auto ecmaRuntimeCallInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread_, JSTaggedValue::Undefined(), 10); + ecmaRuntimeCallInfo->SetFunction(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetThis(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetCallArg(0, JSTaggedValue(static_cast(3))); + ecmaRuntimeCallInfo->SetCallArg(1, JSTaggedValue(static_cast(100))); + ecmaRuntimeCallInfo->SetCallArg(2, JSTaggedValue::False()); + + [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread_, ecmaRuntimeCallInfo.get()); + JSTaggedValue result = BuiltinsMath::Min(ecmaRuntimeCallInfo.get()); + TestHelper::TearDownFrame(thread_, prev); + JSTaggedValue expect = BuiltinsBase::GetTaggedInt(0); + ASSERT_EQ(result.GetRawData(), expect.GetRawData()); +} + +// Math.pow(2,"-2") +TEST_F(BuiltinsMathTest, Pow) +{ + JSHandle test = thread_->GetEcmaVM()->GetFactory()->NewFromCanBeCompressString("-2"); + auto ecmaRuntimeCallInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread_, JSTaggedValue::Undefined(), 8); + ecmaRuntimeCallInfo->SetFunction(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetThis(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetCallArg(0, JSTaggedValue(static_cast(2))); + ecmaRuntimeCallInfo->SetCallArg(1, test.GetTaggedValue()); + + [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread_, ecmaRuntimeCallInfo.get()); + JSTaggedValue result = BuiltinsMath::Pow(ecmaRuntimeCallInfo.get()); + TestHelper::TearDownFrame(thread_, prev); + JSTaggedValue expect = BuiltinsBase::GetTaggedDouble(0.25); + ASSERT_EQ(result.GetRawData(), expect.GetRawData()); +} + +// Math.pow(-NaN,-2) +TEST_F(BuiltinsMathTest, Pow_1) +{ + auto ecmaRuntimeCallInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread_, JSTaggedValue::Undefined(), 8); + ecmaRuntimeCallInfo->SetFunction(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetThis(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetCallArg(0, JSTaggedValue(-base::NAN_VALUE)); + ecmaRuntimeCallInfo->SetCallArg(1, JSTaggedValue(static_cast(-2))); + + [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread_, ecmaRuntimeCallInfo.get()); + JSTaggedValue result = BuiltinsMath::Pow(ecmaRuntimeCallInfo.get()); + ASSERT_TRUE(std::isnan(result.GetDouble())); +} + +// Math.pow() +TEST_F(BuiltinsMathTest, Pow_2) +{ + auto ecmaRuntimeCallInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread_, JSTaggedValue::Undefined(), 4); + ecmaRuntimeCallInfo->SetFunction(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetThis(JSTaggedValue::Undefined()); + + [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread_, ecmaRuntimeCallInfo.get()); + JSTaggedValue result = BuiltinsMath::Pow(ecmaRuntimeCallInfo.get()); + TestHelper::TearDownFrame(thread_, prev); + JSTaggedValue expect = BuiltinsBase::GetTaggedDouble(base::NAN_VALUE); + ASSERT_EQ(result.GetRawData(), expect.GetRawData()); +} + +// Math.pow(false,-2) +TEST_F(BuiltinsMathTest, Pow_3) +{ + auto ecmaRuntimeCallInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread_, JSTaggedValue::Undefined(), 8); + ecmaRuntimeCallInfo->SetFunction(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetThis(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetCallArg(0, JSTaggedValue::False()); + ecmaRuntimeCallInfo->SetCallArg(1, JSTaggedValue(static_cast(-2))); + + [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread_, ecmaRuntimeCallInfo.get()); + JSTaggedValue result = BuiltinsMath::Pow(ecmaRuntimeCallInfo.get()); + TestHelper::TearDownFrame(thread_, prev); + JSTaggedValue expect = BuiltinsBase::GetTaggedDouble(base::POSITIVE_INFINITY); + ASSERT_EQ(result.GetRawData(), expect.GetRawData()); +} + +// Math.random() +TEST_F(BuiltinsMathTest, Random) +{ + auto ecmaRuntimeCallInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread_, JSTaggedValue::Undefined(), 4); + ecmaRuntimeCallInfo->SetFunction(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetThis(JSTaggedValue::Undefined()); + + [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread_, ecmaRuntimeCallInfo.get()); + JSTaggedValue result1 = BuiltinsMath::Random(ecmaRuntimeCallInfo.get()); + TestHelper::TearDownFrame(thread_, prev); + JSTaggedValue result2 = BuiltinsMath::Random(ecmaRuntimeCallInfo.get()); + TestHelper::TearDownFrame(thread_, prev); + ASSERT_NE(result1.GetRawData(), result2.GetRawData()); +} + +// Math.random() +TEST_F(BuiltinsMathTest, Random_1) +{ + auto ecmaRuntimeCallInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread_, JSTaggedValue::Undefined(), 4); + ecmaRuntimeCallInfo->SetFunction(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetThis(JSTaggedValue::Undefined()); + + [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread_, ecmaRuntimeCallInfo.get()); + JSTaggedValue result1 = BuiltinsMath::Random(ecmaRuntimeCallInfo.get()); + TestHelper::TearDownFrame(thread_, prev); + JSTaggedValue result2 = BuiltinsMath::Random(ecmaRuntimeCallInfo.get()); + TestHelper::TearDownFrame(thread_, prev); + double value1 = JSTaggedValue(static_cast(result1.GetRawData())).GetDouble(); + double value2 = JSTaggedValue(static_cast(result2.GetRawData())).GetDouble(); + ASSERT_TRUE(value1 >= 0); + ASSERT_TRUE(value1 < 1.0); + ASSERT_TRUE(value2 >= 0); + ASSERT_TRUE(value2 < 1.0); +} + +// Math.round(-NaN) +TEST_F(BuiltinsMathTest, Round) +{ + auto ecmaRuntimeCallInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread_, JSTaggedValue::Undefined(), 6); + ecmaRuntimeCallInfo->SetFunction(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetThis(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetCallArg(0, JSTaggedValue(-base::NAN_VALUE)); + + [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread_, ecmaRuntimeCallInfo.get()); + JSTaggedValue result = BuiltinsMath::Round(ecmaRuntimeCallInfo.get()); + TestHelper::TearDownFrame(thread_, prev); + JSTaggedValue expect = BuiltinsBase::GetTaggedDouble(base::NAN_VALUE); + ASSERT_EQ(result.GetRawData(), expect.GetRawData()); +} + +// Math.round(1.25) +TEST_F(BuiltinsMathTest, Round_1) +{ + auto ecmaRuntimeCallInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread_, JSTaggedValue::Undefined(), 6); + ecmaRuntimeCallInfo->SetFunction(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetThis(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetCallArg(0, JSTaggedValue(1.25)); + + [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread_, ecmaRuntimeCallInfo.get()); + JSTaggedValue result = BuiltinsMath::Round(ecmaRuntimeCallInfo.get()); + TestHelper::TearDownFrame(thread_, prev); + JSTaggedValue expect = BuiltinsBase::GetTaggedDouble(1.0); + ASSERT_EQ(result.GetRawData(), expect.GetRawData()); +} + +// Math.round(-0.14) +TEST_F(BuiltinsMathTest, Round_2) +{ + auto ecmaRuntimeCallInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread_, JSTaggedValue::Undefined(), 6); + ecmaRuntimeCallInfo->SetFunction(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetThis(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetCallArg(0, JSTaggedValue(-0.14)); + + [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread_, ecmaRuntimeCallInfo.get()); + JSTaggedValue result = BuiltinsMath::Round(ecmaRuntimeCallInfo.get()); + TestHelper::TearDownFrame(thread_, prev); + JSTaggedValue expect = BuiltinsBase::GetTaggedDouble(-0.0); + ASSERT_EQ(result.GetRawData(), expect.GetRawData()); +} + +// Math.round(-0.7) +TEST_F(BuiltinsMathTest, Round_3) +{ + auto ecmaRuntimeCallInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread_, JSTaggedValue::Undefined(), 6); + ecmaRuntimeCallInfo->SetFunction(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetThis(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetCallArg(0, JSTaggedValue(-0.7)); + + [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread_, ecmaRuntimeCallInfo.get()); + JSTaggedValue result = BuiltinsMath::Round(ecmaRuntimeCallInfo.get()); + TestHelper::TearDownFrame(thread_, prev); + JSTaggedValue expect = BuiltinsBase::GetTaggedDouble(-1.0); + ASSERT_EQ(result.GetRawData(), expect.GetRawData()); +} + +// Math.round(POSITIVE_INFINITY) +TEST_F(BuiltinsMathTest, Round_4) +{ + auto ecmaRuntimeCallInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread_, JSTaggedValue::Undefined(), 6); + ecmaRuntimeCallInfo->SetFunction(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetThis(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetCallArg(0, JSTaggedValue(base::POSITIVE_INFINITY)); + + [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread_, ecmaRuntimeCallInfo.get()); + JSTaggedValue result = BuiltinsMath::Round(ecmaRuntimeCallInfo.get()); + TestHelper::TearDownFrame(thread_, prev); + JSTaggedValue expect = BuiltinsBase::GetTaggedDouble(base::POSITIVE_INFINITY); + ASSERT_EQ(result.GetRawData(), expect.GetRawData()); +} + +// Math.fround(POSITIVE_INFINITY) +TEST_F(BuiltinsMathTest, Fround) +{ + auto ecmaRuntimeCallInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread_, JSTaggedValue::Undefined(), 6); + ecmaRuntimeCallInfo->SetFunction(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetThis(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetCallArg(0, JSTaggedValue(base::POSITIVE_INFINITY)); + + [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread_, ecmaRuntimeCallInfo.get()); + JSTaggedValue result = BuiltinsMath::Fround(ecmaRuntimeCallInfo.get()); + TestHelper::TearDownFrame(thread_, prev); + JSTaggedValue expect = BuiltinsBase::GetTaggedDouble(base::POSITIVE_INFINITY); + ASSERT_EQ(result.GetRawData(), expect.GetRawData()); +} + +// Math.fround(-NaN) +TEST_F(BuiltinsMathTest, Fround_1) +{ + auto ecmaRuntimeCallInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread_, JSTaggedValue::Undefined(), 6); + ecmaRuntimeCallInfo->SetFunction(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetThis(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetCallArg(0, JSTaggedValue(-base::NAN_VALUE)); + + [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread_, ecmaRuntimeCallInfo.get()); + JSTaggedValue result = BuiltinsMath::Fround(ecmaRuntimeCallInfo.get()); + TestHelper::TearDownFrame(thread_, prev); + JSTaggedValue expect = BuiltinsBase::GetTaggedDouble(base::NAN_VALUE); + ASSERT_EQ(result.GetRawData(), expect.GetRawData()); +} + +// Math.fround(-0) +TEST_F(BuiltinsMathTest, Fround_2) +{ + auto ecmaRuntimeCallInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread_, JSTaggedValue::Undefined(), 6); + ecmaRuntimeCallInfo->SetFunction(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetThis(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetCallArg(0, JSTaggedValue(-0.0)); + + [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread_, ecmaRuntimeCallInfo.get()); + JSTaggedValue result = BuiltinsMath::Fround(ecmaRuntimeCallInfo.get()); + TestHelper::TearDownFrame(thread_, prev); + JSTaggedValue expect = BuiltinsBase::GetTaggedDouble(-0.0); + ASSERT_EQ(result.GetRawData(), expect.GetRawData()); +} + +// Math.fround(1.337) +TEST_F(BuiltinsMathTest, Fround_3) +{ + auto ecmaRuntimeCallInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread_, JSTaggedValue::Undefined(), 6); + ecmaRuntimeCallInfo->SetFunction(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetThis(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetCallArg(0, JSTaggedValue(1.337)); + + [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread_, ecmaRuntimeCallInfo.get()); + JSTaggedValue result = BuiltinsMath::Fround(ecmaRuntimeCallInfo.get()); + TestHelper::TearDownFrame(thread_, prev); + JSTaggedValue expect = BuiltinsBase::GetTaggedDouble(1.3370000123977661); + ASSERT_EQ(result.GetRawData(), expect.GetRawData()); +} + +// Math.fround(-668523145.253485) +TEST_F(BuiltinsMathTest, Fround_4) +{ + auto ecmaRuntimeCallInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread_, JSTaggedValue::Undefined(), 6); + ecmaRuntimeCallInfo->SetFunction(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetThis(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetCallArg(0, JSTaggedValue(-668523145.253485)); + + [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread_, ecmaRuntimeCallInfo.get()); + JSTaggedValue result = BuiltinsMath::Fround(ecmaRuntimeCallInfo.get()); + TestHelper::TearDownFrame(thread_, prev); + JSTaggedValue expect = BuiltinsBase::GetTaggedDouble(-668523136.0); + ASSERT_EQ(result.GetRawData(), expect.GetRawData()); +} + +// Math.clz32(NaN) +TEST_F(BuiltinsMathTest, Clz32) +{ + auto ecmaRuntimeCallInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread_, JSTaggedValue::Undefined(), 6); + ecmaRuntimeCallInfo->SetFunction(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetThis(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetCallArg(0, JSTaggedValue(-base::NAN_VALUE)); + + [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread_, ecmaRuntimeCallInfo.get()); + JSTaggedValue result = BuiltinsMath::Clz32(ecmaRuntimeCallInfo.get()); + TestHelper::TearDownFrame(thread_, prev); + JSTaggedValue expect = BuiltinsBase::GetTaggedInt(32); + ASSERT_EQ(result.GetRawData(), expect.GetRawData()); +} + +// Math.clz32(-0) +TEST_F(BuiltinsMathTest, Clz32_1) +{ + auto ecmaRuntimeCallInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread_, JSTaggedValue::Undefined(), 6); + ecmaRuntimeCallInfo->SetFunction(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetThis(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetCallArg(0, JSTaggedValue(-0.0)); + + [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread_, ecmaRuntimeCallInfo.get()); + JSTaggedValue result = BuiltinsMath::Clz32(ecmaRuntimeCallInfo.get()); + TestHelper::TearDownFrame(thread_, prev); + JSTaggedValue expect = BuiltinsBase::GetTaggedInt(32); + ASSERT_EQ(result.GetRawData(), expect.GetRawData()); +} + +// Math.clz32(1) +TEST_F(BuiltinsMathTest, Clz32_2) +{ + auto ecmaRuntimeCallInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread_, JSTaggedValue::Undefined(), 6); + ecmaRuntimeCallInfo->SetFunction(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetThis(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetCallArg(0, JSTaggedValue(static_cast(1))); + + [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread_, ecmaRuntimeCallInfo.get()); + JSTaggedValue result = BuiltinsMath::Clz32(ecmaRuntimeCallInfo.get()); + TestHelper::TearDownFrame(thread_, prev); + JSTaggedValue expect = BuiltinsBase::GetTaggedInt(31); + ASSERT_EQ(result.GetRawData(), expect.GetRawData()); +} + +// Math.clz32(568243) +TEST_F(BuiltinsMathTest, Clz32_3) +{ + auto ecmaRuntimeCallInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread_, JSTaggedValue::Undefined(), 6); + ecmaRuntimeCallInfo->SetFunction(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetThis(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetCallArg(0, JSTaggedValue(static_cast(568243))); + + [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread_, ecmaRuntimeCallInfo.get()); + JSTaggedValue result = BuiltinsMath::Clz32(ecmaRuntimeCallInfo.get()); + TestHelper::TearDownFrame(thread_, prev); + JSTaggedValue expect = BuiltinsBase::GetTaggedInt(12); + ASSERT_EQ(result.GetRawData(), expect.GetRawData()); +} + +// Math.clz32(4294967295) +TEST_F(BuiltinsMathTest, Clz32_4) +{ + auto ecmaRuntimeCallInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread_, JSTaggedValue::Undefined(), 6); + ecmaRuntimeCallInfo->SetFunction(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetThis(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetCallArg(0, JSTaggedValue(static_cast(4294967295))); + + [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread_, ecmaRuntimeCallInfo.get()); + JSTaggedValue result = BuiltinsMath::Clz32(ecmaRuntimeCallInfo.get()); + TestHelper::TearDownFrame(thread_, prev); + JSTaggedValue expect = BuiltinsBase::GetTaggedInt(0); + ASSERT_EQ(result.GetRawData(), expect.GetRawData()); +} + +// Math.clz32(10000000000.123) +TEST_F(BuiltinsMathTest, Clz32_5) +{ + auto ecmaRuntimeCallInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread_, JSTaggedValue::Undefined(), 6); + ecmaRuntimeCallInfo->SetFunction(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetThis(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetCallArg(0, JSTaggedValue(10000000000.123)); + + [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread_, ecmaRuntimeCallInfo.get()); + JSTaggedValue result = BuiltinsMath::Clz32(ecmaRuntimeCallInfo.get()); + TestHelper::TearDownFrame(thread_, prev); + JSTaggedValue expect = BuiltinsBase::GetTaggedInt(1); + ASSERT_EQ(result.GetRawData(), expect.GetRawData()); +} + +// Math.clz32() +TEST_F(BuiltinsMathTest, Clz32_6) +{ + auto ecmaRuntimeCallInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread_, JSTaggedValue::Undefined(), 4); + ecmaRuntimeCallInfo->SetFunction(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetThis(JSTaggedValue::Undefined()); + + [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread_, ecmaRuntimeCallInfo.get()); + JSTaggedValue result = BuiltinsMath::Clz32(ecmaRuntimeCallInfo.get()); + TestHelper::TearDownFrame(thread_, prev); + JSTaggedValue expect = BuiltinsBase::GetTaggedInt(32); + ASSERT_EQ(result.GetRawData(), expect.GetRawData()); +} + +// Math.hypot() +TEST_F(BuiltinsMathTest, Hypot) +{ + auto ecmaRuntimeCallInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread_, JSTaggedValue::Undefined(), 4); + ecmaRuntimeCallInfo->SetFunction(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetThis(JSTaggedValue::Undefined()); + + [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread_, ecmaRuntimeCallInfo.get()); + JSTaggedValue result = BuiltinsMath::Hypot(ecmaRuntimeCallInfo.get()); + TestHelper::TearDownFrame(thread_, prev); + JSTaggedValue expect = BuiltinsBase::GetTaggedDouble(0); + ASSERT_EQ(result.GetRawData(), expect.GetRawData()); +} + +// Math.hypot(-2.1) +TEST_F(BuiltinsMathTest, Hypot_1) +{ + auto ecmaRuntimeCallInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread_, JSTaggedValue::Undefined(), 6); + ecmaRuntimeCallInfo->SetFunction(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetThis(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetCallArg(0, JSTaggedValue(-2.1)); + + [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread_, ecmaRuntimeCallInfo.get()); + JSTaggedValue result = BuiltinsMath::Hypot(ecmaRuntimeCallInfo.get()); + TestHelper::TearDownFrame(thread_, prev); + JSTaggedValue expect = BuiltinsBase::GetTaggedDouble(2.1); + ASSERT_EQ(result.GetRawData(), expect.GetRawData()); +} + +// Math.hypot(-NaN, 1) +TEST_F(BuiltinsMathTest, Hypot_2) +{ + auto ecmaRuntimeCallInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread_, JSTaggedValue::Undefined(), 6); + ecmaRuntimeCallInfo->SetFunction(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetThis(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetCallArg(0, JSTaggedValue(-base::NAN_VALUE)); + + [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread_, ecmaRuntimeCallInfo.get()); + JSTaggedValue result = BuiltinsMath::Hypot(ecmaRuntimeCallInfo.get()); + TestHelper::TearDownFrame(thread_, prev); + ASSERT_TRUE(result.IsDouble()); + ASSERT_TRUE(std::isnan(result.GetDouble())); +} + +// Math.hypot(true, 5, 8, -0.2, 90000) +TEST_F(BuiltinsMathTest, Hypot_3) +{ + auto ecmaRuntimeCallInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread_, JSTaggedValue::Undefined(), 14); + ecmaRuntimeCallInfo->SetFunction(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetThis(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetCallArg(0, JSTaggedValue::True()); + ecmaRuntimeCallInfo->SetCallArg(1, JSTaggedValue(static_cast(5))); + ecmaRuntimeCallInfo->SetCallArg(2, JSTaggedValue(static_cast(8))); + ecmaRuntimeCallInfo->SetCallArg(3, JSTaggedValue(-0.2)); + ecmaRuntimeCallInfo->SetCallArg(4, JSTaggedValue(static_cast(90000))); + + [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread_, ecmaRuntimeCallInfo.get()); + JSTaggedValue result = BuiltinsMath::Hypot(ecmaRuntimeCallInfo.get()); + TestHelper::TearDownFrame(thread_, prev); + JSTaggedValue expect = BuiltinsBase::GetTaggedDouble(90000.00050022222); + ASSERT_EQ(result.GetRawData(), expect.GetRawData()); +} + +// Math.Imul() +TEST_F(BuiltinsMathTest, Imul) +{ + auto ecmaRuntimeCallInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread_, JSTaggedValue::Undefined(), 4); + ecmaRuntimeCallInfo->SetFunction(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetThis(JSTaggedValue::Undefined()); + + [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread_, ecmaRuntimeCallInfo.get()); + JSTaggedValue result = BuiltinsMath::Imul(ecmaRuntimeCallInfo.get()); + TestHelper::TearDownFrame(thread_, prev); + JSTaggedValue expect = BuiltinsBase::GetTaggedInt(0); + ASSERT_EQ(result.GetRawData(), expect.GetRawData()); +} + +// Math.Imul("-2",9.256) +TEST_F(BuiltinsMathTest, Imul_1) +{ + JSHandle test = thread_->GetEcmaVM()->GetFactory()->NewFromCanBeCompressString("-2"); + auto ecmaRuntimeCallInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread_, JSTaggedValue::Undefined(), 8); + ecmaRuntimeCallInfo->SetFunction(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetThis(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetCallArg(0, test.GetTaggedValue()); + ecmaRuntimeCallInfo->SetCallArg(1, JSTaggedValue(9.256)); + + [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread_, ecmaRuntimeCallInfo.get()); + JSTaggedValue result = BuiltinsMath::Imul(ecmaRuntimeCallInfo.get()); + TestHelper::TearDownFrame(thread_, prev); + JSTaggedValue expect = BuiltinsBase::GetTaggedInt(-18); + ASSERT_EQ(result.GetRawData(), expect.GetRawData()); +} + +// Math.Imul(5,0xffffffff) +TEST_F(BuiltinsMathTest, Imul_2) +{ + auto ecmaRuntimeCallInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread_, JSTaggedValue::Undefined(), 8); + ecmaRuntimeCallInfo->SetFunction(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetThis(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetCallArg(0, JSTaggedValue(static_cast(5))); + ecmaRuntimeCallInfo->SetCallArg(1, JSTaggedValue(static_cast(0xffffffff))); + + [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread_, ecmaRuntimeCallInfo.get()); + JSTaggedValue result = BuiltinsMath::Imul(ecmaRuntimeCallInfo.get()); + TestHelper::TearDownFrame(thread_, prev); + JSTaggedValue expect = BuiltinsBase::GetTaggedInt(-5); + ASSERT_EQ(result.GetRawData(), expect.GetRawData()); +} + +// Math.Imul(5,0xfffffffe) +TEST_F(BuiltinsMathTest, Imul_3) +{ + auto ecmaRuntimeCallInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread_, JSTaggedValue::Undefined(), 8); + ecmaRuntimeCallInfo->SetFunction(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetThis(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetCallArg(0, JSTaggedValue(static_cast(5))); + ecmaRuntimeCallInfo->SetCallArg(1, JSTaggedValue(static_cast(0xfffffffe))); + + [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread_, ecmaRuntimeCallInfo.get()); + JSTaggedValue result = BuiltinsMath::Imul(ecmaRuntimeCallInfo.get()); + TestHelper::TearDownFrame(thread_, prev); + JSTaggedValue expect = BuiltinsBase::GetTaggedInt(-10); + ASSERT_EQ(result.GetRawData(), expect.GetRawData()); +} + +// Math.sin(-1) +TEST_F(BuiltinsMathTest, Sin) +{ + auto ecmaRuntimeCallInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread_, JSTaggedValue::Undefined(), 6); + ecmaRuntimeCallInfo->SetFunction(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetThis(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetCallArg(0, JSTaggedValue(static_cast(-1))); + + [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread_, ecmaRuntimeCallInfo.get()); + JSTaggedValue result = BuiltinsMath::Sin(ecmaRuntimeCallInfo.get()); + TestHelper::TearDownFrame(thread_, prev); + JSTaggedValue expect = BuiltinsBase::GetTaggedDouble(-0.8414709848078965); + ASSERT_EQ(result.GetRawData(), expect.GetRawData()); +} + +// Math.sin(-1.5) +TEST_F(BuiltinsMathTest, Sin_2) +{ + auto ecmaRuntimeCallInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread_, JSTaggedValue::Undefined(), 6); + ecmaRuntimeCallInfo->SetFunction(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetThis(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetCallArg(0, JSTaggedValue(-1.5)); + + [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread_, ecmaRuntimeCallInfo.get()); + JSTaggedValue result = BuiltinsMath::Sin(ecmaRuntimeCallInfo.get()); + TestHelper::TearDownFrame(thread_, prev); + JSTaggedValue expect = BuiltinsBase::GetTaggedDouble(-0.9974949866040544); + ASSERT_EQ(result.GetRawData(), expect.GetRawData()); +} + +// Math.sin(null) +TEST_F(BuiltinsMathTest, Sin_3) +{ + auto ecmaRuntimeCallInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread_, JSTaggedValue::Undefined(), 6); + ecmaRuntimeCallInfo->SetFunction(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetThis(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetCallArg(0, JSTaggedValue::Null()); + + [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread_, ecmaRuntimeCallInfo.get()); + JSTaggedValue result = BuiltinsMath::Sin(ecmaRuntimeCallInfo.get()); + TestHelper::TearDownFrame(thread_, prev); + JSTaggedValue expect = BuiltinsBase::GetTaggedDouble(0); + ASSERT_EQ(result.GetRawData(), expect.GetRawData()); +} + +// Math.sin(UNDEFINED) +TEST_F(BuiltinsMathTest, Sin_4) +{ + auto ecmaRuntimeCallInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread_, JSTaggedValue::Undefined(), 6); + ecmaRuntimeCallInfo->SetFunction(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetThis(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetCallArg(0, JSTaggedValue::Undefined()); + + [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread_, ecmaRuntimeCallInfo.get()); + JSTaggedValue result = BuiltinsMath::Sin(ecmaRuntimeCallInfo.get()); + TestHelper::TearDownFrame(thread_, prev); + JSTaggedValue expect = BuiltinsBase::GetTaggedDouble(base::NAN_VALUE); + ASSERT_EQ(result.GetRawData(), expect.GetRawData()); +} + +// Math.sin(true) +TEST_F(BuiltinsMathTest, Sin_5) +{ + auto ecmaRuntimeCallInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread_, JSTaggedValue::Undefined(), 6); + ecmaRuntimeCallInfo->SetFunction(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetThis(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetCallArg(0, JSTaggedValue::True()); + + [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread_, ecmaRuntimeCallInfo.get()); + JSTaggedValue result = BuiltinsMath::Sin(ecmaRuntimeCallInfo.get()); + TestHelper::TearDownFrame(thread_, prev); + JSTaggedValue expect = BuiltinsBase::GetTaggedDouble(0.8414709848078965); + ASSERT_EQ(result.GetRawData(), expect.GetRawData()); +} + +// Math.sin("0.1") +TEST_F(BuiltinsMathTest, Sin_6) +{ + JSHandle test = thread_->GetEcmaVM()->GetFactory()->NewFromCanBeCompressString("0.1"); + auto ecmaRuntimeCallInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread_, JSTaggedValue::Undefined(), 6); + ecmaRuntimeCallInfo->SetFunction(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetThis(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetCallArg(0, test.GetTaggedValue()); + + [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread_, ecmaRuntimeCallInfo.get()); + JSTaggedValue result = BuiltinsMath::Sin(ecmaRuntimeCallInfo.get()); + TestHelper::TearDownFrame(thread_, prev); + JSTaggedValue expect = BuiltinsBase::GetTaggedDouble(0.09983341664682815); + ASSERT_EQ(result.GetRawData(), expect.GetRawData()); +} + +// Math.sin(Number.POSITIVE_INFINITY) +TEST_F(BuiltinsMathTest, Sin_7) +{ + auto ecmaRuntimeCallInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread_, JSTaggedValue::Undefined(), 6); + ecmaRuntimeCallInfo->SetFunction(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetThis(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetCallArg(0, JSTaggedValue(base::POSITIVE_INFINITY)); + + [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread_, ecmaRuntimeCallInfo.get()); + JSTaggedValue result = BuiltinsMath::Sin(ecmaRuntimeCallInfo.get()); + TestHelper::TearDownFrame(thread_, prev); + JSTaggedValue expect = BuiltinsBase::GetTaggedDouble(base::NAN_VALUE); + ASSERT_EQ(result.GetRawData(), expect.GetRawData()); +} + +// Math.sin(-NaN) +TEST_F(BuiltinsMathTest, Sin_8) +{ + auto ecmaRuntimeCallInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread_, JSTaggedValue::Undefined(), 6); + ecmaRuntimeCallInfo->SetFunction(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetThis(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetCallArg(0, JSTaggedValue(-base::NAN_VALUE)); + + [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread_, ecmaRuntimeCallInfo.get()); + JSTaggedValue result = BuiltinsMath::Sin(ecmaRuntimeCallInfo.get()); + TestHelper::TearDownFrame(thread_, prev); + JSTaggedValue expect = BuiltinsBase::GetTaggedDouble(base::NAN_VALUE); + ASSERT_EQ(result.GetRawData(), expect.GetRawData()); +} + +// Math.sinh(-1) +TEST_F(BuiltinsMathTest, Sinh) +{ + auto ecmaRuntimeCallInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread_, JSTaggedValue::Undefined(), 6); + ecmaRuntimeCallInfo->SetFunction(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetThis(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetCallArg(0, JSTaggedValue(static_cast(-1))); + + [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread_, ecmaRuntimeCallInfo.get()); + JSTaggedValue result = BuiltinsMath::Sinh(ecmaRuntimeCallInfo.get()); + TestHelper::TearDownFrame(thread_, prev); + JSTaggedValue expect = BuiltinsBase::GetTaggedDouble(-1.1752011936438014); + ASSERT_EQ(result.GetRawData(), expect.GetRawData()); +} + +// Math.sinh(-1.5) +TEST_F(BuiltinsMathTest, Sinh_1) +{ + auto ecmaRuntimeCallInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread_, JSTaggedValue::Undefined(), 6); + ecmaRuntimeCallInfo->SetFunction(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetThis(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetCallArg(0, JSTaggedValue(-1.5)); + + [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread_, ecmaRuntimeCallInfo.get()); + JSTaggedValue result = BuiltinsMath::Sinh(ecmaRuntimeCallInfo.get()); + TestHelper::TearDownFrame(thread_, prev); + JSTaggedValue expect = BuiltinsBase::GetTaggedDouble(-2.1292794550948173); + ASSERT_EQ(result.GetRawData(), expect.GetRawData()); +} + +// Math.sinh(null) +TEST_F(BuiltinsMathTest, Sinh_2) +{ + auto ecmaRuntimeCallInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread_, JSTaggedValue::Undefined(), 6); + ecmaRuntimeCallInfo->SetFunction(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetThis(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetCallArg(0, JSTaggedValue::Null()); + + [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread_, ecmaRuntimeCallInfo.get()); + JSTaggedValue result = BuiltinsMath::Sinh(ecmaRuntimeCallInfo.get()); + TestHelper::TearDownFrame(thread_, prev); + JSTaggedValue expect = BuiltinsBase::GetTaggedDouble(0); + ASSERT_EQ(result.GetRawData(), expect.GetRawData()); +} + +// Math.sinh(UNDEFINED) +TEST_F(BuiltinsMathTest, Sinh_3) +{ + auto ecmaRuntimeCallInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread_, JSTaggedValue::Undefined(), 6); + ecmaRuntimeCallInfo->SetFunction(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetThis(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetCallArg(0, JSTaggedValue::Undefined()); + + [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread_, ecmaRuntimeCallInfo.get()); + JSTaggedValue result = BuiltinsMath::Sinh(ecmaRuntimeCallInfo.get()); + TestHelper::TearDownFrame(thread_, prev); + JSTaggedValue expect = BuiltinsBase::GetTaggedDouble(base::NAN_VALUE); + ASSERT_EQ(result.GetRawData(), expect.GetRawData()); +} + +// Math.sinh(true) +TEST_F(BuiltinsMathTest, Sinh_4) +{ + auto ecmaRuntimeCallInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread_, JSTaggedValue::Undefined(), 6); + ecmaRuntimeCallInfo->SetFunction(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetThis(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetCallArg(0, JSTaggedValue::True()); + + [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread_, ecmaRuntimeCallInfo.get()); + JSTaggedValue result = BuiltinsMath::Sinh(ecmaRuntimeCallInfo.get()); + TestHelper::TearDownFrame(thread_, prev); + JSTaggedValue expect = BuiltinsBase::GetTaggedDouble(1.1752011936438014); + ASSERT_EQ(result.GetRawData(), expect.GetRawData()); +} + +// Math.sinh("0.1") +TEST_F(BuiltinsMathTest, Sinh_5) +{ + JSHandle test = thread_->GetEcmaVM()->GetFactory()->NewFromCanBeCompressString("0.1"); + auto ecmaRuntimeCallInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread_, JSTaggedValue::Undefined(), 6); + ecmaRuntimeCallInfo->SetFunction(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetThis(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetCallArg(0, test.GetTaggedValue()); + + [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread_, ecmaRuntimeCallInfo.get()); + JSTaggedValue result = BuiltinsMath::Sinh(ecmaRuntimeCallInfo.get()); + TestHelper::TearDownFrame(thread_, prev); + JSTaggedValue expect = BuiltinsBase::GetTaggedDouble(0.10016675001984403); + ASSERT_EQ(result.GetRawData(), expect.GetRawData()); +} + +// Math.sinh(-Number.POSITIVE_INFINITY) +TEST_F(BuiltinsMathTest, Sinh_6) +{ + auto ecmaRuntimeCallInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread_, JSTaggedValue::Undefined(), 6); + ecmaRuntimeCallInfo->SetFunction(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetThis(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetCallArg(0, JSTaggedValue(-base::POSITIVE_INFINITY)); + + [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread_, ecmaRuntimeCallInfo.get()); + JSTaggedValue result = BuiltinsMath::Sinh(ecmaRuntimeCallInfo.get()); + TestHelper::TearDownFrame(thread_, prev); + JSTaggedValue expect = BuiltinsBase::GetTaggedDouble(-base::POSITIVE_INFINITY); + ASSERT_EQ(result.GetRawData(), expect.GetRawData()); +} + +// Math.sinh(-NaN) +TEST_F(BuiltinsMathTest, Sinh_7) +{ + auto ecmaRuntimeCallInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread_, JSTaggedValue::Undefined(), 6); + ecmaRuntimeCallInfo->SetFunction(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetThis(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetCallArg(0, JSTaggedValue(-base::NAN_VALUE)); + + [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread_, ecmaRuntimeCallInfo.get()); + JSTaggedValue result = BuiltinsMath::Sinh(ecmaRuntimeCallInfo.get()); + TestHelper::TearDownFrame(thread_, prev); + JSTaggedValue expect = BuiltinsBase::GetTaggedDouble(base::NAN_VALUE); + ASSERT_EQ(result.GetRawData(), expect.GetRawData()); +} + +// Math.sqrt(-1) +TEST_F(BuiltinsMathTest, Sqrt) +{ + auto ecmaRuntimeCallInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread_, JSTaggedValue::Undefined(), 6); + ecmaRuntimeCallInfo->SetFunction(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetThis(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetCallArg(0, JSTaggedValue(static_cast(-1))); + + [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread_, ecmaRuntimeCallInfo.get()); + JSTaggedValue result = BuiltinsMath::Sqrt(ecmaRuntimeCallInfo.get()); + TestHelper::TearDownFrame(thread_, prev); + JSTaggedValue expect = BuiltinsBase::GetTaggedDouble(base::NAN_VALUE); + ASSERT_EQ(result.GetRawData(), expect.GetRawData()); +} + +// Math.sqrt(-0) +TEST_F(BuiltinsMathTest, Sqrt_1) +{ + auto ecmaRuntimeCallInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread_, JSTaggedValue::Undefined(), 6); + ecmaRuntimeCallInfo->SetFunction(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetThis(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetCallArg(0, JSTaggedValue(-0.0)); + + [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread_, ecmaRuntimeCallInfo.get()); + JSTaggedValue result = BuiltinsMath::Sqrt(ecmaRuntimeCallInfo.get()); + TestHelper::TearDownFrame(thread_, prev); + JSTaggedValue expect = BuiltinsBase::GetTaggedDouble(-0.0); + ASSERT_EQ(result.GetRawData(), expect.GetRawData()); +} + +// Math.sqrt(null) +TEST_F(BuiltinsMathTest, Sqrt_2) +{ + auto ecmaRuntimeCallInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread_, JSTaggedValue::Undefined(), 6); + ecmaRuntimeCallInfo->SetFunction(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetThis(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetCallArg(0, JSTaggedValue::Null()); + + [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread_, ecmaRuntimeCallInfo.get()); + JSTaggedValue result = BuiltinsMath::Sqrt(ecmaRuntimeCallInfo.get()); + TestHelper::TearDownFrame(thread_, prev); + JSTaggedValue expect = BuiltinsBase::GetTaggedDouble(0); + ASSERT_EQ(result.GetRawData(), expect.GetRawData()); +} + +// Math.sqrt(true) +TEST_F(BuiltinsMathTest, Sqrt_3) +{ + auto ecmaRuntimeCallInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread_, JSTaggedValue::Undefined(), 6); + ecmaRuntimeCallInfo->SetFunction(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetThis(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetCallArg(0, JSTaggedValue::True()); + + [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread_, ecmaRuntimeCallInfo.get()); + JSTaggedValue result = BuiltinsMath::Sqrt(ecmaRuntimeCallInfo.get()); + TestHelper::TearDownFrame(thread_, prev); + JSTaggedValue expect = BuiltinsBase::GetTaggedDouble(1.0); + ASSERT_EQ(result.GetRawData(), expect.GetRawData()); +} + +// Math.sqrt("0.1") +TEST_F(BuiltinsMathTest, Sqrt_4) +{ + JSHandle test = thread_->GetEcmaVM()->GetFactory()->NewFromCanBeCompressString("0.1"); + auto ecmaRuntimeCallInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread_, JSTaggedValue::Undefined(), 6); + ecmaRuntimeCallInfo->SetFunction(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetThis(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetCallArg(0, test.GetTaggedValue()); + + [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread_, ecmaRuntimeCallInfo.get()); + JSTaggedValue result = BuiltinsMath::Sqrt(ecmaRuntimeCallInfo.get()); + TestHelper::TearDownFrame(thread_, prev); + JSTaggedValue expect = BuiltinsBase::GetTaggedDouble(0.31622776601683794); + ASSERT_EQ(result.GetRawData(), expect.GetRawData()); +} + +// Math.sqrt(Number.POSITIVE_INFINITY) +TEST_F(BuiltinsMathTest, Sqrt_5) +{ + auto ecmaRuntimeCallInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread_, JSTaggedValue::Undefined(), 6); + ecmaRuntimeCallInfo->SetFunction(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetThis(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetCallArg(0, JSTaggedValue(base::POSITIVE_INFINITY)); + + [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread_, ecmaRuntimeCallInfo.get()); + JSTaggedValue result = BuiltinsMath::Sqrt(ecmaRuntimeCallInfo.get()); + TestHelper::TearDownFrame(thread_, prev); + JSTaggedValue expect = BuiltinsBase::GetTaggedDouble(base::POSITIVE_INFINITY); + ASSERT_EQ(result.GetRawData(), expect.GetRawData()); +} + +// Math.sqrt(-NaN) +TEST_F(BuiltinsMathTest, Sqrt_6) +{ + auto ecmaRuntimeCallInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread_, JSTaggedValue::Undefined(), 6); + ecmaRuntimeCallInfo->SetFunction(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetThis(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetCallArg(0, JSTaggedValue(-base::NAN_VALUE)); + + [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread_, ecmaRuntimeCallInfo.get()); + JSTaggedValue result = BuiltinsMath::Sqrt(ecmaRuntimeCallInfo.get()); + TestHelper::TearDownFrame(thread_, prev); + JSTaggedValue expect = BuiltinsBase::GetTaggedDouble(base::NAN_VALUE); + ASSERT_EQ(result.GetRawData(), expect.GetRawData()); +} + +// Math.tan(-1) +TEST_F(BuiltinsMathTest, Tan) +{ + auto ecmaRuntimeCallInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread_, JSTaggedValue::Undefined(), 6); + ecmaRuntimeCallInfo->SetFunction(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetThis(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetCallArg(0, JSTaggedValue(static_cast(-1))); + + [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread_, ecmaRuntimeCallInfo.get()); + JSTaggedValue result = BuiltinsMath::Tan(ecmaRuntimeCallInfo.get()); + TestHelper::TearDownFrame(thread_, prev); + JSTaggedValue expect = BuiltinsBase::GetTaggedDouble(-1.5574077246549023); + ASSERT_EQ(result.GetRawData(), expect.GetRawData()); +} + +// Math.tan(-0) +TEST_F(BuiltinsMathTest, Tan_1) +{ + auto ecmaRuntimeCallInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread_, JSTaggedValue::Undefined(), 6); + ecmaRuntimeCallInfo->SetFunction(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetThis(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetCallArg(0, JSTaggedValue(-0.0)); + + [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread_, ecmaRuntimeCallInfo.get()); + JSTaggedValue result = BuiltinsMath::Tan(ecmaRuntimeCallInfo.get()); + TestHelper::TearDownFrame(thread_, prev); + JSTaggedValue expect = BuiltinsBase::GetTaggedDouble(-0.0); + ASSERT_EQ(result.GetRawData(), expect.GetRawData()); +} + +// Math.tan(null) +TEST_F(BuiltinsMathTest, Tan_2) +{ + auto ecmaRuntimeCallInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread_, JSTaggedValue::Undefined(), 6); + ecmaRuntimeCallInfo->SetFunction(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetThis(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetCallArg(0, JSTaggedValue::Null()); + + [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread_, ecmaRuntimeCallInfo.get()); + JSTaggedValue result = BuiltinsMath::Tan(ecmaRuntimeCallInfo.get()); + TestHelper::TearDownFrame(thread_, prev); + JSTaggedValue expect = BuiltinsBase::GetTaggedDouble(0); + ASSERT_EQ(result.GetRawData(), expect.GetRawData()); +} + +// Math.tan(true) +TEST_F(BuiltinsMathTest, Tan_3) +{ + auto ecmaRuntimeCallInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread_, JSTaggedValue::Undefined(), 6); + ecmaRuntimeCallInfo->SetFunction(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetThis(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetCallArg(0, JSTaggedValue::True()); + + [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread_, ecmaRuntimeCallInfo.get()); + JSTaggedValue result = BuiltinsMath::Tan(ecmaRuntimeCallInfo.get()); + TestHelper::TearDownFrame(thread_, prev); + JSTaggedValue expect = BuiltinsBase::GetTaggedDouble(1.5574077246549023); + ASSERT_EQ(result.GetRawData(), expect.GetRawData()); +} + +// Math.tan("0.1") +TEST_F(BuiltinsMathTest, Tan_4) +{ + JSHandle test = thread_->GetEcmaVM()->GetFactory()->NewFromCanBeCompressString("0.1"); + auto ecmaRuntimeCallInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread_, JSTaggedValue::Undefined(), 6); + ecmaRuntimeCallInfo->SetFunction(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetThis(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetCallArg(0, test.GetTaggedValue()); + + [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread_, ecmaRuntimeCallInfo.get()); + JSTaggedValue result = BuiltinsMath::Tan(ecmaRuntimeCallInfo.get()); + TestHelper::TearDownFrame(thread_, prev); + JSTaggedValue expect = BuiltinsBase::GetTaggedDouble(0.10033467208545055); + ASSERT_EQ(result.GetRawData(), expect.GetRawData()); +} + +// Math.tan(Number.POSITIVE_INFINITY) +TEST_F(BuiltinsMathTest, Tan_5) +{ + auto ecmaRuntimeCallInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread_, JSTaggedValue::Undefined(), 6); + ecmaRuntimeCallInfo->SetFunction(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetThis(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetCallArg(0, JSTaggedValue(base::POSITIVE_INFINITY)); + + [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread_, ecmaRuntimeCallInfo.get()); + JSTaggedValue result = BuiltinsMath::Tan(ecmaRuntimeCallInfo.get()); + TestHelper::TearDownFrame(thread_, prev); + JSTaggedValue expect = BuiltinsBase::GetTaggedDouble(base::NAN_VALUE); + ASSERT_EQ(result.GetRawData(), expect.GetRawData()); +} + +// Math.tan(-NaN) +TEST_F(BuiltinsMathTest, Tan_6) +{ + auto ecmaRuntimeCallInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread_, JSTaggedValue::Undefined(), 6); + ecmaRuntimeCallInfo->SetFunction(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetThis(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetCallArg(0, JSTaggedValue(-base::NAN_VALUE)); + + [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread_, ecmaRuntimeCallInfo.get()); + JSTaggedValue result = BuiltinsMath::Tan(ecmaRuntimeCallInfo.get()); + TestHelper::TearDownFrame(thread_, prev); + JSTaggedValue expect = BuiltinsBase::GetTaggedDouble(base::NAN_VALUE); + ASSERT_EQ(result.GetRawData(), expect.GetRawData()); +} + +// Math.tanh(-1) +TEST_F(BuiltinsMathTest, Tanh) +{ + auto ecmaRuntimeCallInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread_, JSTaggedValue::Undefined(), 6); + ecmaRuntimeCallInfo->SetFunction(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetThis(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetCallArg(0, JSTaggedValue(static_cast(-1))); + + [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread_, ecmaRuntimeCallInfo.get()); + JSTaggedValue result = BuiltinsMath::Tanh(ecmaRuntimeCallInfo.get()); + TestHelper::TearDownFrame(thread_, prev); + JSTaggedValue expect = BuiltinsBase::GetTaggedDouble(-0.7615941559557649); + ASSERT_EQ(result.GetRawData(), expect.GetRawData()); +} + +// Math.tanh(-0) +TEST_F(BuiltinsMathTest, Tanh_1) +{ + auto ecmaRuntimeCallInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread_, JSTaggedValue::Undefined(), 6); + ecmaRuntimeCallInfo->SetFunction(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetThis(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetCallArg(0, JSTaggedValue(-0.0)); + + [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread_, ecmaRuntimeCallInfo.get()); + JSTaggedValue result = BuiltinsMath::Tanh(ecmaRuntimeCallInfo.get()); + TestHelper::TearDownFrame(thread_, prev); + JSTaggedValue expect = BuiltinsBase::GetTaggedDouble(-0.0); + ASSERT_EQ(result.GetRawData(), expect.GetRawData()); +} + +// Math.tanh(null) +TEST_F(BuiltinsMathTest, Tanh_2) +{ + auto ecmaRuntimeCallInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread_, JSTaggedValue::Undefined(), 6); + ecmaRuntimeCallInfo->SetFunction(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetThis(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetCallArg(0, JSTaggedValue::Null()); + + [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread_, ecmaRuntimeCallInfo.get()); + JSTaggedValue result = BuiltinsMath::Tanh(ecmaRuntimeCallInfo.get()); + TestHelper::TearDownFrame(thread_, prev); + JSTaggedValue expect = BuiltinsBase::GetTaggedDouble(0); + ASSERT_EQ(result.GetRawData(), expect.GetRawData()); +} + +// Math.tanh(true) +TEST_F(BuiltinsMathTest, Tanh_3) +{ + auto ecmaRuntimeCallInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread_, JSTaggedValue::Undefined(), 6); + ecmaRuntimeCallInfo->SetFunction(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetThis(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetCallArg(0, JSTaggedValue::True()); + + [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread_, ecmaRuntimeCallInfo.get()); + JSTaggedValue result = BuiltinsMath::Tanh(ecmaRuntimeCallInfo.get()); + TestHelper::TearDownFrame(thread_, prev); + JSTaggedValue expect = BuiltinsBase::GetTaggedDouble(0.7615941559557649); + ASSERT_EQ(result.GetRawData(), expect.GetRawData()); +} + +// Math.tanh("0.1") +TEST_F(BuiltinsMathTest, Tanh_4) +{ + JSHandle test = thread_->GetEcmaVM()->GetFactory()->NewFromCanBeCompressString("0.1"); + auto ecmaRuntimeCallInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread_, JSTaggedValue::Undefined(), 6); + ecmaRuntimeCallInfo->SetFunction(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetThis(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetCallArg(0, test.GetTaggedValue()); + + [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread_, ecmaRuntimeCallInfo.get()); + JSTaggedValue result = BuiltinsMath::Tanh(ecmaRuntimeCallInfo.get()); + TestHelper::TearDownFrame(thread_, prev); + JSTaggedValue expect = BuiltinsBase::GetTaggedDouble(0.09966799462495582); + ASSERT_EQ(result.GetRawData(), expect.GetRawData()); +} + +// Math.tanh(Number.POSITIVE_INFINITY) +TEST_F(BuiltinsMathTest, Tanh_5) +{ + auto ecmaRuntimeCallInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread_, JSTaggedValue::Undefined(), 6); + ecmaRuntimeCallInfo->SetFunction(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetThis(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetCallArg(0, JSTaggedValue(base::POSITIVE_INFINITY)); + + [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread_, ecmaRuntimeCallInfo.get()); + JSTaggedValue result = BuiltinsMath::Tanh(ecmaRuntimeCallInfo.get()); + TestHelper::TearDownFrame(thread_, prev); + JSTaggedValue expect = BuiltinsBase::GetTaggedDouble(1.0); + ASSERT_EQ(result.GetRawData(), expect.GetRawData()); +} + +// Math.tanh(-NaN) +TEST_F(BuiltinsMathTest, Tanh_6) +{ + auto ecmaRuntimeCallInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread_, JSTaggedValue::Undefined(), 6); + ecmaRuntimeCallInfo->SetFunction(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetThis(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetCallArg(0, JSTaggedValue(-base::NAN_VALUE)); + + [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread_, ecmaRuntimeCallInfo.get()); + JSTaggedValue result = BuiltinsMath::Tanh(ecmaRuntimeCallInfo.get()); + TestHelper::TearDownFrame(thread_, prev); + JSTaggedValue expect = BuiltinsBase::GetTaggedDouble(base::NAN_VALUE); + ASSERT_EQ(result.GetRawData(), expect.GetRawData()); +} + +// Math.trunc(-1) +TEST_F(BuiltinsMathTest, Trunc) +{ + auto ecmaRuntimeCallInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread_, JSTaggedValue::Undefined(), 6); + ecmaRuntimeCallInfo->SetFunction(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetThis(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetCallArg(0, JSTaggedValue(static_cast(-1))); + + [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread_, ecmaRuntimeCallInfo.get()); + JSTaggedValue result = BuiltinsMath::Trunc(ecmaRuntimeCallInfo.get()); + TestHelper::TearDownFrame(thread_, prev); + JSTaggedValue expect = BuiltinsBase::GetTaggedDouble(-1.0); + ASSERT_EQ(result.GetRawData(), expect.GetRawData()); +} + +// Math.trunc(-0) +TEST_F(BuiltinsMathTest, Trunc_1) +{ + auto ecmaRuntimeCallInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread_, JSTaggedValue::Undefined(), 6); + ecmaRuntimeCallInfo->SetFunction(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetThis(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetCallArg(0, JSTaggedValue(-0.0)); + + [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread_, ecmaRuntimeCallInfo.get()); + JSTaggedValue result = BuiltinsMath::Trunc(ecmaRuntimeCallInfo.get()); + TestHelper::TearDownFrame(thread_, prev); + JSTaggedValue expect = BuiltinsBase::GetTaggedDouble(-0.0); + ASSERT_EQ(result.GetRawData(), expect.GetRawData()); +} + +// Math.trunc(null) +TEST_F(BuiltinsMathTest, Trunc_2) +{ + auto ecmaRuntimeCallInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread_, JSTaggedValue::Undefined(), 6); + ecmaRuntimeCallInfo->SetFunction(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetThis(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetCallArg(0, JSTaggedValue::Null()); + + [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread_, ecmaRuntimeCallInfo.get()); + JSTaggedValue result = BuiltinsMath::Trunc(ecmaRuntimeCallInfo.get()); + TestHelper::TearDownFrame(thread_, prev); + JSTaggedValue expect = BuiltinsBase::GetTaggedDouble(0); + ASSERT_EQ(result.GetRawData(), expect.GetRawData()); +} + +// Math.trunc(true) +TEST_F(BuiltinsMathTest, Trunc_3) +{ + auto ecmaRuntimeCallInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread_, JSTaggedValue::Undefined(), 6); + ecmaRuntimeCallInfo->SetFunction(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetThis(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetCallArg(0, JSTaggedValue::True()); + + [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread_, ecmaRuntimeCallInfo.get()); + JSTaggedValue result = BuiltinsMath::Trunc(ecmaRuntimeCallInfo.get()); + TestHelper::TearDownFrame(thread_, prev); + JSTaggedValue expect = BuiltinsBase::GetTaggedDouble(1.0); + ASSERT_EQ(result.GetRawData(), expect.GetRawData()); +} + +// Math.trunc("-0.1") +TEST_F(BuiltinsMathTest, Trunc_4) +{ + JSHandle test = thread_->GetEcmaVM()->GetFactory()->NewFromCanBeCompressString("-0.1"); + auto ecmaRuntimeCallInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread_, JSTaggedValue::Undefined(), 6); + ecmaRuntimeCallInfo->SetFunction(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetThis(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetCallArg(0, test.GetTaggedValue()); + + [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread_, ecmaRuntimeCallInfo.get()); + JSTaggedValue result = BuiltinsMath::Trunc(ecmaRuntimeCallInfo.get()); + TestHelper::TearDownFrame(thread_, prev); + JSTaggedValue expect = BuiltinsBase::GetTaggedDouble(-0.0); + ASSERT_EQ(result.GetRawData(), expect.GetRawData()); +} + +// Math.trunc(Number.POSITIVE_INFINITY) +TEST_F(BuiltinsMathTest, Trunc_5) +{ + auto ecmaRuntimeCallInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread_, JSTaggedValue::Undefined(), 6); + ecmaRuntimeCallInfo->SetFunction(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetThis(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetCallArg(0, JSTaggedValue(base::POSITIVE_INFINITY)); + + [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread_, ecmaRuntimeCallInfo.get()); + JSTaggedValue result = BuiltinsMath::Trunc(ecmaRuntimeCallInfo.get()); + TestHelper::TearDownFrame(thread_, prev); + JSTaggedValue expect = BuiltinsBase::GetTaggedDouble(base::POSITIVE_INFINITY); + ASSERT_EQ(result.GetRawData(), expect.GetRawData()); +} + +// Math.trunc(-NaN) +TEST_F(BuiltinsMathTest, Trunc_6) +{ + auto ecmaRuntimeCallInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread_, JSTaggedValue::Undefined(), 6); + ecmaRuntimeCallInfo->SetFunction(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetThis(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetCallArg(0, JSTaggedValue(-base::NAN_VALUE)); + + [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread_, ecmaRuntimeCallInfo.get()); + JSTaggedValue result = BuiltinsMath::Trunc(ecmaRuntimeCallInfo.get()); + TestHelper::TearDownFrame(thread_, prev); + JSTaggedValue expect = BuiltinsBase::GetTaggedDouble(base::NAN_VALUE); + ASSERT_EQ(result.GetRawData(), expect.GetRawData()); +} +} // namespace panda::test diff --git a/tests/runtime/builtins/builtins_number_test.cpp b/tests/runtime/builtins/builtins_number_test.cpp new file mode 100644 index 000000000..ababc24cc --- /dev/null +++ b/tests/runtime/builtins/builtins_number_test.cpp @@ -0,0 +1,599 @@ +/* + * Copyright (c) 2021-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 +#include +#include +#include "plugins/ecmascript/runtime/base/number_helper.h" +#include "plugins/ecmascript/runtime/base/string_helper.h" +#include "plugins/ecmascript/runtime/builtins/builtins_number.h" +#include "plugins/ecmascript/runtime/ecma_runtime_call_info.h" +#include "plugins/ecmascript/runtime/ecma_string.h" +#include "plugins/ecmascript/runtime/ecma_vm.h" +#include "plugins/ecmascript/runtime/global_env.h" +#include "plugins/ecmascript/runtime/js_function.h" +#include "plugins/ecmascript/runtime/js_global_object.h" +#include "plugins/ecmascript/runtime/js_handle.h" +#include "plugins/ecmascript/runtime/js_primitive_ref.h" +#include "plugins/ecmascript/runtime/js_tagged_value-inl.h" +#include "plugins/ecmascript/runtime/js_thread.h" +#include "plugins/ecmascript/runtime/object_factory.h" +#include "plugins/ecmascript/tests/runtime/common/test_helper.h" + +using namespace panda::ecmascript; +using namespace panda::ecmascript::builtins; + +namespace panda::test { +class BuiltinsNumberTest : public testing::Test { +public: + static void SetUpTestCase() + { + GTEST_LOG_(INFO) << "SetUpTestCase"; + } + + static void TearDownTestCase() + { + GTEST_LOG_(INFO) << "TearDownCase"; + } + + void SetUp() override + { + TestHelper::CreateEcmaVMWithScope(instance, thread, scope); + } + + void TearDown() override + { + TestHelper::DestroyEcmaVMWithScope(instance, scope); + } + + PandaVM *instance {nullptr}; + EcmaHandleScope *scope {nullptr}; + JSThread *thread {nullptr}; +}; + +// new Number(10) +TEST_F(BuiltinsNumberTest, NumberConstructor) +{ + JSHandle env = thread->GetEcmaVM()->GetGlobalEnv(); + + JSHandle number(env->GetNumberFunction()); + JSHandle globalObject(thread, env->GetGlobalObject()); + + auto ecmaRuntimeCallInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread, JSTaggedValue(*number), 6); + ecmaRuntimeCallInfo->SetFunction(number.GetTaggedValue()); + ecmaRuntimeCallInfo->SetThis(globalObject.GetTaggedValue()); + ecmaRuntimeCallInfo->SetCallArg(0, JSTaggedValue(static_cast(5))); + + [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread, ecmaRuntimeCallInfo.get()); + JSTaggedValue result = BuiltinsNumber::NumberConstructor(ecmaRuntimeCallInfo.get()); + JSTaggedValue value(static_cast(result.GetRawData())); + ASSERT_TRUE(value.IsECMAObject()); + JSPrimitiveRef *ref = JSPrimitiveRef::Cast(value.GetTaggedObject()); + ASSERT_EQ(ref->GetValue().GetDouble(), 5.0); +} + +// Number.isFinite(-10) +TEST_F(BuiltinsNumberTest, IsFinite) +{ + const double value = -10; + auto ecmaRuntimeCallInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread, JSTaggedValue::Undefined(), 6); + ecmaRuntimeCallInfo->SetFunction(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetThis(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetCallArg(0, JSTaggedValue(static_cast(value))); + + [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread, ecmaRuntimeCallInfo.get()); + JSTaggedValue result = BuiltinsNumber::IsFinite(ecmaRuntimeCallInfo.get()); + ASSERT_EQ(result.GetRawData(), JSTaggedValue::True().GetRawData()); +} + +// Number.isFinite(Number.MAX_VALUE) +TEST_F(BuiltinsNumberTest, IsFinite1) +{ + auto ecmaRuntimeCallInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread, JSTaggedValue::Undefined(), 6); + ecmaRuntimeCallInfo->SetFunction(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetThis(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetCallArg(0, JSTaggedValue(base::MAX_VALUE)); + + [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread, ecmaRuntimeCallInfo.get()); + JSTaggedValue result = BuiltinsNumber::IsFinite(ecmaRuntimeCallInfo.get()); + ASSERT_EQ(result.GetRawData(), JSTaggedValue::True().GetRawData()); +} + +// Number.isFinite("helloworld") +TEST_F(BuiltinsNumberTest, IsFinite2) +{ + JSHandle test = thread->GetEcmaVM()->GetFactory()->NewFromCanBeCompressString("helloworld"); + auto ecmaRuntimeCallInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread, JSTaggedValue::Undefined(), 6); + ecmaRuntimeCallInfo->SetFunction(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetThis(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetCallArg(0, test.GetTaggedValue()); + + [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread, ecmaRuntimeCallInfo.get()); + JSTaggedValue result = BuiltinsNumber::IsFinite(ecmaRuntimeCallInfo.get()); + ASSERT_EQ(result.GetRawData(), JSTaggedValue::False().GetRawData()); +} + +// Number.isFinite(NaN) +TEST_F(BuiltinsNumberTest, IsFinite3) +{ + auto ecmaRuntimeCallInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread, JSTaggedValue::Undefined(), 6); + ecmaRuntimeCallInfo->SetFunction(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetThis(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetCallArg(0, JSTaggedValue(base::NAN_VALUE)); + + [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread, ecmaRuntimeCallInfo.get()); + JSTaggedValue result = BuiltinsNumber::IsFinite(ecmaRuntimeCallInfo.get()); + ASSERT_EQ(result.GetRawData(), JSTaggedValue::False().GetRawData()); +} + +// Number.isFinite(Infinity) +TEST_F(BuiltinsNumberTest, IsFinite4) +{ + auto ecmaRuntimeCallInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread, JSTaggedValue::Undefined(), 6); + ecmaRuntimeCallInfo->SetFunction(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetThis(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetCallArg(0, JSTaggedValue(base::POSITIVE_INFINITY)); + + [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread, ecmaRuntimeCallInfo.get()); + JSTaggedValue result = BuiltinsNumber::IsFinite(ecmaRuntimeCallInfo.get()); + ASSERT_EQ(result.GetRawData(), JSTaggedValue::False().GetRawData()); +} + +// Number.isFinite(undefined) +TEST_F(BuiltinsNumberTest, IsFinite5) +{ + auto ecmaRuntimeCallInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread, JSTaggedValue::Undefined(), 6); + ecmaRuntimeCallInfo->SetFunction(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetThis(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetCallArg(0, JSTaggedValue::Undefined()); + + [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread, ecmaRuntimeCallInfo.get()); + JSTaggedValue result = BuiltinsNumber::IsFinite(ecmaRuntimeCallInfo.get()); + ASSERT_EQ(result.GetRawData(), JSTaggedValue::False().GetRawData()); +} + +// Number.isFinite(null) +TEST_F(BuiltinsNumberTest, IsFinite6) +{ + auto ecmaRuntimeCallInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread, JSTaggedValue::Undefined(), 6); + ecmaRuntimeCallInfo->SetFunction(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetThis(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetCallArg(0, JSTaggedValue::Null()); + + [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread, ecmaRuntimeCallInfo.get()); + JSTaggedValue result = BuiltinsNumber::IsFinite(ecmaRuntimeCallInfo.get()); + ASSERT_EQ(result.GetRawData(), JSTaggedValue::False().GetRawData()); +} + +// Number.isInteger(0.1) +TEST_F(BuiltinsNumberTest, IsInteger) +{ + auto ecmaRuntimeCallInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread, JSTaggedValue::Undefined(), 6); + ecmaRuntimeCallInfo->SetFunction(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetThis(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetCallArg(0, JSTaggedValue(0.1)); + + [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread, ecmaRuntimeCallInfo.get()); + JSTaggedValue result = BuiltinsNumber::IsInteger(ecmaRuntimeCallInfo.get()); + ASSERT_EQ(result.GetRawData(), JSTaggedValue::False().GetRawData()); +} + +// Number.isNaN(0.1) +TEST_F(BuiltinsNumberTest, IsNaN) +{ + auto ecmaRuntimeCallInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread, JSTaggedValue::Undefined(), 6); + ecmaRuntimeCallInfo->SetFunction(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetThis(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetCallArg(0, JSTaggedValue(0.1)); + + [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread, ecmaRuntimeCallInfo.get()); + JSTaggedValue result = BuiltinsNumber::IsNaN(ecmaRuntimeCallInfo.get()); + ASSERT_EQ(result.GetRawData(), JSTaggedValue::False().GetRawData()); +} + +// new Number(123.456).toString(7) +TEST_F(BuiltinsNumberTest, ToString) +{ + ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); + auto ecmaVM = thread->GetEcmaVM(); + JSHandle env = ecmaVM->GetGlobalEnv(); + + JSHandle numberObject(env->GetNumberFunction()); + JSHandle value(thread, JSTaggedValue(123.456)); + JSHandle number = thread->GetEcmaVM()->GetFactory()->NewJSPrimitiveRef(numberObject, value); + + auto ecmaRuntimeCallInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread, JSTaggedValue::Undefined(), 6); + ecmaRuntimeCallInfo->SetFunction(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetThis(number.GetTaggedValue()); + ecmaRuntimeCallInfo->SetCallArg(0, JSTaggedValue(7.0)); + + [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread, ecmaRuntimeCallInfo.get()); + JSTaggedValue result = BuiltinsNumber::ToString(ecmaRuntimeCallInfo.get()); + ASSERT_TRUE(result.IsString()); + JSHandle res(thread, reinterpret_cast(result.GetRawData())); + JSHandle correctResult = factory->NewFromCanBeCompressString("234.312256641535441"); + CVector test(res->GetLength() + 1); + res->CopyDataUtf8(test.data(), res->GetLength()); + ASSERT_TRUE(EcmaString::StringsAreEqual(*res, *correctResult)); +} + +// new Number(123.456).toExponential(5) +TEST_F(BuiltinsNumberTest, IsExponential) +{ + ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); + auto ecmaVM = thread->GetEcmaVM(); + JSHandle env = ecmaVM->GetGlobalEnv(); + + JSHandle numberObject(env->GetNumberFunction()); + JSHandle value(thread, JSTaggedValue(123.456)); + JSHandle number = factory->NewJSPrimitiveRef(numberObject, value); + + auto ecmaRuntimeCallInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread, JSTaggedValue::Undefined(), 6); + ecmaRuntimeCallInfo->SetFunction(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetThis(number.GetTaggedValue()); + ecmaRuntimeCallInfo->SetCallArg(0, JSTaggedValue(5.0)); + + [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread, ecmaRuntimeCallInfo.get()); + JSTaggedValue result = BuiltinsNumber::ToExponential(ecmaRuntimeCallInfo.get()); + ASSERT_TRUE(result.IsString()); + JSHandle res(thread, reinterpret_cast(result.GetRawData())); + JSHandle correctResult = factory->NewFromCanBeCompressString("1.23456e+2"); + CVector test(res->GetLength() + 1); + res->CopyDataUtf8(test.data(), res->GetLength()); + ASSERT_TRUE(EcmaString::StringsAreEqual(*res, *correctResult)); +} + +// new Number(123.456).toFixed(10) +TEST_F(BuiltinsNumberTest, ToFixed) +{ + ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); + auto ecmaVM = thread->GetEcmaVM(); + JSHandle env = ecmaVM->GetGlobalEnv(); + + JSHandle numberObject(env->GetNumberFunction()); + JSHandle value(thread, JSTaggedValue(123.456)); + JSHandle number = factory->NewJSPrimitiveRef(numberObject, value); + + auto ecmaRuntimeCallInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread, JSTaggedValue::Undefined(), 6); + ecmaRuntimeCallInfo->SetFunction(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetThis(number.GetTaggedValue()); + ecmaRuntimeCallInfo->SetCallArg(0, JSTaggedValue(10.0)); + + [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread, ecmaRuntimeCallInfo.get()); + JSTaggedValue result = BuiltinsNumber::ToFixed(ecmaRuntimeCallInfo.get()); + ASSERT_TRUE(result.IsString()); + JSHandle res(thread, reinterpret_cast(result.GetRawData())); + JSHandle correctResult = factory->NewFromCanBeCompressString("123.4560000000"); + ASSERT_TRUE(EcmaString::StringsAreEqual(*res, *correctResult)); +} + +// new Number(123.456).toFixed(30) +TEST_F(BuiltinsNumberTest, ToFixed1) +{ + ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); + auto ecmaVM = thread->GetEcmaVM(); + JSHandle env = ecmaVM->GetGlobalEnv(); + + JSHandle numberObject(env->GetNumberFunction()); + JSHandle value(thread, JSTaggedValue(123.456)); + JSHandle number = factory->NewJSPrimitiveRef(numberObject, value); + + auto ecmaRuntimeCallInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread, JSTaggedValue::Undefined(), 6); + ecmaRuntimeCallInfo->SetFunction(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetThis(number.GetTaggedValue()); + ecmaRuntimeCallInfo->SetCallArg(0, JSTaggedValue(30.0)); + + [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread, ecmaRuntimeCallInfo.get()); + JSTaggedValue result = BuiltinsNumber::ToFixed(ecmaRuntimeCallInfo.get()); + ASSERT_TRUE(result.IsString()); + JSHandle res(thread, reinterpret_cast(result.GetRawData())); + JSHandle correctResult = factory->NewFromCanBeCompressString("123.456000000000003069544618483633"); + ASSERT_TRUE(EcmaString::StringsAreEqual(*res, *correctResult)); +} + +// new Number(1e21).toFixed(20) +TEST_F(BuiltinsNumberTest, ToFixed2) { + ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); + auto ecmaVM = thread->GetEcmaVM(); + JSHandle env = ecmaVM->GetGlobalEnv(); + + JSHandle numberObject(env->GetNumberFunction()); + JSHandle value(thread, JSTaggedValue(1e21)); + JSHandle number = factory->NewJSPrimitiveRef(numberObject, value); + + auto ecmaRuntimeCallInfo = + TestHelper::CreateEcmaRuntimeCallInfo(thread, JSTaggedValue::Undefined(), 6); + ecmaRuntimeCallInfo->SetFunction(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetThis(number.GetTaggedValue()); + ecmaRuntimeCallInfo->SetCallArg(0, JSTaggedValue(20.0)); + + [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread, ecmaRuntimeCallInfo.get()); + JSTaggedValue result = BuiltinsNumber::ToFixed(ecmaRuntimeCallInfo.get()); + ASSERT_TRUE(result.IsString()); + JSHandle res(thread, reinterpret_cast(result.GetRawData())); + JSHandle correctResult = factory->NewFromCanBeCompressString("1e+21"); + CVector test(res->GetLength() + 1); + res->CopyDataUtf8(test.data(), res->GetLength()); + std::cout << test.data(); + ASSERT_TRUE(EcmaString::StringsAreEqual(*res, *correctResult)); +} + +// new Number(123.456).toPrecision(8) +TEST_F(BuiltinsNumberTest, ToPrecision) +{ + ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); + auto ecmaVM = thread->GetEcmaVM(); + JSHandle env = ecmaVM->GetGlobalEnv(); + + JSHandle numberObject(env->GetNumberFunction()); + JSHandle value(thread, JSTaggedValue(123.456)); + JSHandle number = factory->NewJSPrimitiveRef(numberObject, value); + + auto ecmaRuntimeCallInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread, JSTaggedValue::Undefined(), 6); + ecmaRuntimeCallInfo->SetFunction(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetThis(number.GetTaggedValue()); + ecmaRuntimeCallInfo->SetCallArg(0, JSTaggedValue(8.0)); + + [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread, ecmaRuntimeCallInfo.get()); + JSTaggedValue result = BuiltinsNumber::ToPrecision(ecmaRuntimeCallInfo.get()); + ASSERT_TRUE(result.IsString()); + JSHandle res(thread, reinterpret_cast(result.GetRawData())); + JSHandle correctResult = factory->NewFromCanBeCompressString("123.45600"); + ASSERT_TRUE(EcmaString::StringsAreEqual(*res, *correctResult)); +} + +// Number.parseFloat(0x123) +TEST_F(BuiltinsNumberTest, parseFloat) +{ + JSHandle param = thread->GetEcmaVM()->GetFactory()->NewFromCanBeCompressString("0x123"); + auto ecmaRuntimeCallInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread, JSTaggedValue::Undefined(), 6); + ecmaRuntimeCallInfo->SetFunction(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetThis(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetCallArg(0, param.GetTaggedValue()); + + [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread, ecmaRuntimeCallInfo.get()); + JSTaggedValue result = BuiltinsNumber::ParseFloat(ecmaRuntimeCallInfo.get()); + ASSERT_EQ(result.GetRawData(), JSTaggedValue(static_cast(0)).GetRawData()); +} + +// Number.parseFloat(0x123xx) +TEST_F(BuiltinsNumberTest, parseFloat1) +{ + JSHandle param = thread->GetEcmaVM()->GetFactory()->NewFromCanBeCompressString("0x123xx"); + auto ecmaRuntimeCallInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread, JSTaggedValue::Undefined(), 6); + ecmaRuntimeCallInfo->SetFunction(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetThis(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetCallArg(0, param.GetTaggedValue()); + + [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread, ecmaRuntimeCallInfo.get()); + JSTaggedValue result = BuiltinsNumber::ParseFloat(ecmaRuntimeCallInfo.get()); + ASSERT_EQ(result.GetRawData(), JSTaggedValue(static_cast(0)).GetRawData()); +} + +// Number.parseInt(0x123) +TEST_F(BuiltinsNumberTest, parseInt) +{ + const char *number = "0x123"; + + JSHandle param = thread->GetEcmaVM()->GetFactory()->NewFromCanBeCompressString(number); + auto ecmaRuntimeCallInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread, JSTaggedValue::Undefined(), 8); + ecmaRuntimeCallInfo->SetFunction(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetThis(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetCallArg(0, param.GetTaggedValue()); + ecmaRuntimeCallInfo->SetCallArg(1, JSTaggedValue(16.0)); + + [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread, ecmaRuntimeCallInfo.get()); + JSTaggedValue result = BuiltinsNumber::ParseInt(ecmaRuntimeCallInfo.get()); + ASSERT_EQ(result.GetRawData(), JSTaggedValue(static_cast(291)).GetRawData()); +} + +// testcases of StringToDouble flags +TEST_F(BuiltinsNumberTest, StringToDoubleFlags) +{ + JSHandle str; + Span sp; + + // flags of IGNORE_TRAILING + + str = thread->GetEcmaVM()->GetFactory()->NewFromCanBeCompressString("0a"); + sp = Span(str->GetDataUtf8(), str->GetUtf8Length() - 1); + ASSERT_EQ(base::NumberHelper::StringToDouble(sp.begin(), sp.end(), 0, base::IGNORE_TRAILING), 0); + str = thread->GetEcmaVM()->GetFactory()->NewFromCanBeCompressString("0b"); + sp = Span(str->GetDataUtf8(), str->GetUtf8Length() - 1); + ASSERT_EQ(base::NumberHelper::StringToDouble(sp.begin(), sp.end(), 0, base::IGNORE_TRAILING), 0); + str = thread->GetEcmaVM()->GetFactory()->NewFromCanBeCompressString("0o"); + sp = Span(str->GetDataUtf8(), str->GetUtf8Length() - 1); + ASSERT_EQ(base::NumberHelper::StringToDouble(sp.begin(), sp.end(), 0, base::IGNORE_TRAILING), 0); + str = thread->GetEcmaVM()->GetFactory()->NewFromCanBeCompressString(" 00x"); + sp = Span(str->GetDataUtf8(), str->GetUtf8Length() - 1); + ASSERT_EQ(base::NumberHelper::StringToDouble(sp.begin(), sp.end(), 0, base::IGNORE_TRAILING), 0); + str = thread->GetEcmaVM()->GetFactory()->NewFromCanBeCompressString(" 000.4_"); + sp = Span(str->GetDataUtf8(), str->GetUtf8Length() - 1); + ASSERT_EQ(base::NumberHelper::StringToDouble(sp.begin(), sp.end(), 0, base::IGNORE_TRAILING), 0.4); + str = thread->GetEcmaVM()->GetFactory()->NewFromCanBeCompressString(" 0010.s "); + sp = Span(str->GetDataUtf8(), str->GetUtf8Length() - 1); + ASSERT_EQ(base::NumberHelper::StringToDouble(sp.begin(), sp.end(), 0, base::IGNORE_TRAILING), 10); + str = thread->GetEcmaVM()->GetFactory()->NewFromCanBeCompressString(" 0010e2"); + sp = Span(str->GetDataUtf8(), str->GetUtf8Length() - 1); + ASSERT_EQ(base::NumberHelper::StringToDouble(sp.begin(), sp.end(), 0, base::IGNORE_TRAILING), 1000); + str = thread->GetEcmaVM()->GetFactory()->NewFromCanBeCompressString(" 0010e+3_0"); + sp = Span(str->GetDataUtf8(), str->GetUtf8Length() - 1); + ASSERT_EQ(base::NumberHelper::StringToDouble(sp.begin(), sp.end(), 0, base::IGNORE_TRAILING), 10000); + + // flags of ALLOW_HEX + str = thread->GetEcmaVM()->GetFactory()->NewFromCanBeCompressString("0x"); + sp = Span(str->GetDataUtf8(), str->GetUtf8Length() - 1); + ASSERT_TRUE(std::isnan(base::NumberHelper::StringToDouble(sp.begin(), sp.end(), 0, base::ALLOW_HEX))); + str = thread->GetEcmaVM()->GetFactory()->NewFromCanBeCompressString(" 0x10 "); + sp = Span(str->GetDataUtf8(), str->GetUtf8Length() - 1); + ASSERT_EQ(base::NumberHelper::StringToDouble(sp.begin(), sp.end(), 0, base::ALLOW_HEX), 16); + str = thread->GetEcmaVM()->GetFactory()->NewFromCanBeCompressString("0x1g"); + sp = Span(str->GetDataUtf8(), str->GetUtf8Length() - 1); + ASSERT_EQ(base::NumberHelper::StringToDouble(sp.begin(), sp.end(), 0, base::ALLOW_HEX + base::IGNORE_TRAILING), 1); + str = thread->GetEcmaVM()->GetFactory()->NewFromCanBeCompressString("0xh"); + sp = Span(str->GetDataUtf8(), str->GetUtf8Length() - 1); + ASSERT_TRUE(std::isnan( + base::NumberHelper::StringToDouble(sp.begin(), sp.end(), 0, base::ALLOW_HEX + base::IGNORE_TRAILING))); + + // flags of ALLOW_OCTAL + str = thread->GetEcmaVM()->GetFactory()->NewFromCanBeCompressString("0O"); + sp = Span(str->GetDataUtf8(), str->GetUtf8Length() - 1); + ASSERT_TRUE(std::isnan(base::NumberHelper::StringToDouble(sp.begin(), sp.end(), 0, base::ALLOW_OCTAL))); + str = thread->GetEcmaVM()->GetFactory()->NewFromCanBeCompressString(" 0o10 "); + sp = Span(str->GetDataUtf8(), str->GetUtf8Length() - 1); + ASSERT_EQ(base::NumberHelper::StringToDouble(sp.begin(), sp.end(), 0, base::ALLOW_OCTAL), 8); + str = thread->GetEcmaVM()->GetFactory()->NewFromCanBeCompressString("0o1d"); + sp = Span(str->GetDataUtf8(), str->GetUtf8Length() - 1); + ASSERT_EQ(base::NumberHelper::StringToDouble(sp.begin(), sp.end(), 0, base::ALLOW_OCTAL | base::IGNORE_TRAILING), + 1); + str = thread->GetEcmaVM()->GetFactory()->NewFromCanBeCompressString("0o8"); + sp = Span(str->GetDataUtf8(), str->GetUtf8Length() - 1); + ASSERT_TRUE(std::isnan( + base::NumberHelper::StringToDouble(sp.begin(), sp.end(), 0, base::ALLOW_OCTAL | base::IGNORE_TRAILING))); + + // flags of ALLOW_BINARY + str = thread->GetEcmaVM()->GetFactory()->NewFromCanBeCompressString("0b"); + sp = Span(str->GetDataUtf8(), str->GetUtf8Length() - 1); + ASSERT_TRUE(std::isnan(base::NumberHelper::StringToDouble(sp.begin(), sp.end(), 0, base::ALLOW_BINARY))); + str = thread->GetEcmaVM()->GetFactory()->NewFromCanBeCompressString(" 0b10 "); + sp = Span(str->GetDataUtf8(), str->GetUtf8Length() - 1); + ASSERT_EQ(base::NumberHelper::StringToDouble(sp.begin(), sp.end(), 0, base::ALLOW_BINARY), 2); + str = thread->GetEcmaVM()->GetFactory()->NewFromCanBeCompressString("0b1d"); + sp = Span(str->GetDataUtf8(), str->GetUtf8Length() - 1); + ASSERT_EQ(base::NumberHelper::StringToDouble(sp.begin(), sp.end(), 0, base::ALLOW_BINARY | base::IGNORE_TRAILING), + 1); + str = thread->GetEcmaVM()->GetFactory()->NewFromCanBeCompressString("0b2"); + sp = Span(str->GetDataUtf8(), str->GetUtf8Length() - 1); + ASSERT_TRUE(std::isnan( + base::NumberHelper::StringToDouble(sp.begin(), sp.end(), 0, base::ALLOW_BINARY | base::IGNORE_TRAILING))); +} + +// testcases of StringToDouble radix +TEST_F(BuiltinsNumberTest, StringToDoubleRadix) +{ + JSHandle str; + Span sp; + int radix; + + radix = 0; // default 10 + str = thread->GetEcmaVM()->GetFactory()->NewFromCanBeCompressString(" 100 "); + sp = Span(str->GetDataUtf8(), str->GetUtf8Length() - 1); + ASSERT_EQ(base::NumberHelper::StringToDouble(sp.begin(), sp.end(), radix, base::NO_FLAGS), 100); + str = thread->GetEcmaVM()->GetFactory()->NewFromCanBeCompressString(" 100.3e2 "); + sp = Span(str->GetDataUtf8(), str->GetUtf8Length() - 1); + ASSERT_EQ(base::NumberHelper::StringToDouble(sp.begin(), sp.end(), radix, base::NO_FLAGS), 10030); + radix = 1; + str = thread->GetEcmaVM()->GetFactory()->NewFromCanBeCompressString(" 0000 "); + sp = Span(str->GetDataUtf8(), str->GetUtf8Length() - 1); + ASSERT_EQ(base::NumberHelper::StringToDouble(sp.begin(), sp.end(), radix, base::NO_FLAGS), 0); + str = thread->GetEcmaVM()->GetFactory()->NewFromCanBeCompressString(" 0001 "); + sp = Span(str->GetDataUtf8(), str->GetUtf8Length() - 1); + ASSERT_TRUE(std::isnan(base::NumberHelper::StringToDouble(sp.begin(), sp.end(), radix, base::NO_FLAGS))); + radix = 2; + str = thread->GetEcmaVM()->GetFactory()->NewFromCanBeCompressString(" 100 "); + sp = Span(str->GetDataUtf8(), str->GetUtf8Length() - 1); + ASSERT_EQ(base::NumberHelper::StringToDouble(sp.begin(), sp.end(), radix, base::NO_FLAGS), 4); + str = thread->GetEcmaVM()->GetFactory()->NewFromCanBeCompressString(" 11 "); + sp = Span(str->GetDataUtf8(), str->GetUtf8Length() - 1); + ASSERT_EQ(base::NumberHelper::StringToDouble(sp.begin(), sp.end(), radix, base::NO_FLAGS), 3); + radix = 3; + str = thread->GetEcmaVM()->GetFactory()->NewFromCanBeCompressString(" 100 "); + sp = Span(str->GetDataUtf8(), str->GetUtf8Length() - 1); + ASSERT_EQ(base::NumberHelper::StringToDouble(sp.begin(), sp.end(), radix, base::NO_FLAGS), 9); + str = thread->GetEcmaVM()->GetFactory()->NewFromCanBeCompressString(" 21 "); + sp = Span(str->GetDataUtf8(), str->GetUtf8Length() - 1); + ASSERT_EQ(base::NumberHelper::StringToDouble(sp.begin(), sp.end(), radix, base::NO_FLAGS), 7); + radix = 4; + str = thread->GetEcmaVM()->GetFactory()->NewFromCanBeCompressString(" 100 "); + sp = Span(str->GetDataUtf8(), str->GetUtf8Length() - 1); + ASSERT_EQ(base::NumberHelper::StringToDouble(sp.begin(), sp.end(), radix, base::NO_FLAGS), 16); + str = thread->GetEcmaVM()->GetFactory()->NewFromCanBeCompressString(" 31 "); + sp = Span(str->GetDataUtf8(), str->GetUtf8Length() - 1); + ASSERT_EQ(base::NumberHelper::StringToDouble(sp.begin(), sp.end(), radix, base::NO_FLAGS), 13); + radix = 8; + str = thread->GetEcmaVM()->GetFactory()->NewFromCanBeCompressString(" 100 "); + sp = Span(str->GetDataUtf8(), str->GetUtf8Length() - 1); + ASSERT_EQ(base::NumberHelper::StringToDouble(sp.begin(), sp.end(), radix, base::NO_FLAGS), 64); + str = thread->GetEcmaVM()->GetFactory()->NewFromCanBeCompressString(" 71 "); + sp = Span(str->GetDataUtf8(), str->GetUtf8Length() - 1); + ASSERT_EQ(base::NumberHelper::StringToDouble(sp.begin(), sp.end(), radix, base::NO_FLAGS), 57); + radix = 10; + str = thread->GetEcmaVM()->GetFactory()->NewFromCanBeCompressString(" 100 "); + sp = Span(str->GetDataUtf8(), str->GetUtf8Length() - 1); + ASSERT_EQ(base::NumberHelper::StringToDouble(sp.begin(), sp.end(), radix, base::NO_FLAGS), 100); + str = thread->GetEcmaVM()->GetFactory()->NewFromCanBeCompressString(" 0020 "); + sp = Span(str->GetDataUtf8(), str->GetUtf8Length() - 1); + ASSERT_EQ(base::NumberHelper::StringToDouble(sp.begin(), sp.end(), radix, base::NO_FLAGS), 20); + radix = 16; + str = thread->GetEcmaVM()->GetFactory()->NewFromCanBeCompressString(" 100 "); + sp = Span(str->GetDataUtf8(), str->GetUtf8Length() - 1); + ASSERT_EQ(base::NumberHelper::StringToDouble(sp.begin(), sp.end(), radix, base::NO_FLAGS), 256); + str = thread->GetEcmaVM()->GetFactory()->NewFromCanBeCompressString(" 1e "); + sp = Span(str->GetDataUtf8(), str->GetUtf8Length() - 1); + ASSERT_EQ(base::NumberHelper::StringToDouble(sp.begin(), sp.end(), radix, base::NO_FLAGS), 30); + radix = 18; + str = thread->GetEcmaVM()->GetFactory()->NewFromCanBeCompressString(" 100 "); + sp = Span(str->GetDataUtf8(), str->GetUtf8Length() - 1); + ASSERT_EQ(base::NumberHelper::StringToDouble(sp.begin(), sp.end(), radix, base::NO_FLAGS), 324); + str = thread->GetEcmaVM()->GetFactory()->NewFromCanBeCompressString(" 1g "); + sp = Span(str->GetDataUtf8(), str->GetUtf8Length() - 1); + ASSERT_EQ(base::NumberHelper::StringToDouble(sp.begin(), sp.end(), radix, base::NO_FLAGS), 34); + radix = 25; + str = thread->GetEcmaVM()->GetFactory()->NewFromCanBeCompressString(" 100 "); + sp = Span(str->GetDataUtf8(), str->GetUtf8Length() - 1); + ASSERT_EQ(base::NumberHelper::StringToDouble(sp.begin(), sp.end(), radix, base::NO_FLAGS), 625); + str = thread->GetEcmaVM()->GetFactory()->NewFromCanBeCompressString(" 1g "); + sp = Span(str->GetDataUtf8(), str->GetUtf8Length() - 1); + ASSERT_EQ(base::NumberHelper::StringToDouble(sp.begin(), sp.end(), radix, base::NO_FLAGS), 41); + radix = 36; + str = thread->GetEcmaVM()->GetFactory()->NewFromCanBeCompressString(" 100 "); + sp = Span(str->GetDataUtf8(), str->GetUtf8Length() - 1); + ASSERT_EQ(base::NumberHelper::StringToDouble(sp.begin(), sp.end(), radix, base::NO_FLAGS), 1296); + str = thread->GetEcmaVM()->GetFactory()->NewFromCanBeCompressString(" 1z "); + sp = Span(str->GetDataUtf8(), str->GetUtf8Length() - 1); + ASSERT_EQ(base::NumberHelper::StringToDouble(sp.begin(), sp.end(), radix, base::NO_FLAGS), 71); +} + +TEST_F(BuiltinsNumberTest, NumberToString) +{ + ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); + JSHandle res = factory->NewFromCanBeCompressString("100"); + ASSERT_EQ(base::NumberHelper::NumberToString(thread, JSTaggedValue(100))->Compare(*res), 0); + res = factory->NewFromCanBeCompressString("11223344"); + ASSERT_EQ(base::NumberHelper::NumberToString(thread, JSTaggedValue(11223344))->Compare(*res), 0); + res = factory->NewFromCanBeCompressString("1234567890"); + ASSERT_EQ(base::NumberHelper::NumberToString(thread, JSTaggedValue(1234567890))->Compare(*res), 0); + res = factory->NewFromCanBeCompressString("100"); + ASSERT_EQ(base::NumberHelper::NumberToString(thread, JSTaggedValue(double(100.0)))->Compare(*res), 0); + res = factory->NewFromCanBeCompressString("100.5"); + ASSERT_EQ(base::NumberHelper::NumberToString(thread, JSTaggedValue(double(100.5)))->Compare(*res), 0); + res = factory->NewFromCanBeCompressString("100.25"); + ASSERT_EQ(base::NumberHelper::NumberToString(thread, JSTaggedValue(double(100.25)))->Compare(*res), 0); + res = factory->NewFromCanBeCompressString("100.125"); + ASSERT_EQ(base::NumberHelper::NumberToString(thread, JSTaggedValue(double(100.125)))->Compare(*res), 0); + res = factory->NewFromCanBeCompressString("100.6125"); + ASSERT_EQ(base::NumberHelper::NumberToString(thread, JSTaggedValue(double(100.6125)))->Compare(*res), 0); + res = factory->NewFromCanBeCompressString("0.0006125"); + ASSERT_EQ(base::NumberHelper::NumberToString(thread, JSTaggedValue(double(0.0006125)))->Compare(*res), 0); + res = factory->NewFromCanBeCompressString("-0.0006125"); + ASSERT_EQ(base::NumberHelper::NumberToString(thread, JSTaggedValue(double(-0.0006125)))->Compare(*res), 0); + res = factory->NewFromCanBeCompressString("-1234567890.0006125"); + ASSERT_EQ(base::NumberHelper::NumberToString(thread, JSTaggedValue(double(-1234567890.0006125)))->Compare(*res), 0); + res = factory->NewFromCanBeCompressString("1234567890.0006125"); + ASSERT_EQ(base::NumberHelper::NumberToString(thread, JSTaggedValue(double(1234567890.0006125)))->Compare(*res), 0); + res = factory->NewFromCanBeCompressString("11234567890.000612"); + ASSERT_EQ(base::NumberHelper::NumberToString(thread, JSTaggedValue(double(11234567890.0006125)))->Compare(*res), 0); +} +} // namespace panda::test diff --git a/tests/runtime/builtins/builtins_object_test.cpp b/tests/runtime/builtins/builtins_object_test.cpp new file mode 100644 index 000000000..3a971905e --- /dev/null +++ b/tests/runtime/builtins/builtins_object_test.cpp @@ -0,0 +1,1206 @@ +/* + * Copyright (c) 2021-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 "plugins/ecmascript/runtime/builtins/builtins_object.h" +#include "plugins/ecmascript/runtime/ecma_runtime_call_info.h" +#include "plugins/ecmascript/runtime/ecma_string.h" +#include "plugins/ecmascript/runtime/ecma_vm.h" +#include "plugins/ecmascript/runtime/global_env.h" +#include "plugins/ecmascript/runtime/js_array.h" +#include "plugins/ecmascript/runtime/js_handle.h" +#include "plugins/ecmascript/runtime/js_object-inl.h" +#include "plugins/ecmascript/runtime/js_primitive_ref.h" +#include "plugins/ecmascript/runtime/js_tagged_value-inl.h" +#include "plugins/ecmascript/runtime/js_thread.h" +#include "plugins/ecmascript/runtime/object_factory.h" +#include "plugins/ecmascript/tests/runtime/common/test_helper.h" +#include "file_items.h" +#include "utils/bit_utils.h" + +using namespace panda::ecmascript; +using namespace panda::ecmascript::builtins; + +namespace panda::test { +class BuiltinsObjectTest : public testing::Test { +public: + static void SetUpTestCase() + { + GTEST_LOG_(INFO) << "SetUpTestCase"; + } + + static void TearDownTestCase() + { + GTEST_LOG_(INFO) << "TearDownCase"; + } + + void SetUp() override + { + TestHelper::CreateEcmaVMWithScope(instance, thread, scope); + } + + void TearDown() override + { + TestHelper::DestroyEcmaVMWithScope(instance, scope); + } + + PandaVM *instance {nullptr}; + EcmaHandleScope *scope {nullptr}; + JSThread *thread {nullptr}; +}; + +JSTaggedValue CreateBuiltinJSObject(JSThread *thread, const CString keyCStr) +{ + EcmaVM *ecmaVM = thread->GetEcmaVM(); + JSHandle globalEnv = ecmaVM->GetGlobalEnv(); + JSHandle objFun = globalEnv->GetObjectFunction(); + ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); + + JSHandle obj(factory->NewJSObjectByConstructor(JSHandle(objFun), objFun)); + JSHandle key(factory->NewFromCanBeCompressString(&keyCStr[0])); + JSHandle value(thread, JSTaggedValue(1)); + JSObject::SetProperty(thread, obj, key, value); + return obj.GetTaggedValue(); +} + +JSFunction *BuiltinsObjectTestCreate(JSThread *thread) +{ + EcmaVM *ecmaVM = thread->GetEcmaVM(); + JSHandle globalEnv = ecmaVM->GetGlobalEnv(); + return globalEnv->GetObjectFunction().GetObject(); +} + +JSObject *TestNewJSObject(JSThread *thread, const JSHandle &dynClass) +{ + ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); + JSObject *obj = JSObject::Cast(factory->NewDynObject(dynClass)); + + obj->SetElements(thread, factory->EmptyArray().GetTaggedValue(), SKIP_BARRIER); + return obj; +} + +// 19.1.1.1Object ( [ value ] ) +TEST_F(BuiltinsObjectTest, ObjectConstructor) +{ + JSHandle function(thread, BuiltinsObjectTestCreate(thread)); + JSHandle objectFunc(thread, BuiltinsObjectTestCreate(thread)); + JSHandle globalEnv = thread->GetEcmaVM()->GetGlobalEnv(); + JSHandle objFun(globalEnv->GetObjectFunction()); + + auto objCallInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread, JSTaggedValue::Undefined(), 4); + objCallInfo->SetFunction(JSTaggedValue::Undefined()); + objCallInfo->SetThis(JSTaggedValue::Undefined()); + + [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread, objCallInfo.get()); + JSTaggedValue result = BuiltinsObject::ObjectConstructor(objCallInfo.get()); + TestHelper::TearDownFrame(thread, prev); + + ASSERT_TRUE(result.IsECMAObject()); + JSHandle jtHandle(thread, JSTaggedValue(reinterpret_cast(result.GetRawData()))); + JSTaggedValue resultProto = jtHandle->GetPrototype(thread); + JSTaggedValue funcProto = objectFunc->GetFunctionPrototype(); + ASSERT_EQ(resultProto, funcProto); + ASSERT_TRUE(jtHandle->IsExtensible()); + + // num_args = 0 + JSHandle object = + thread->GetEcmaVM()->GetFactory()->NewJSObjectByConstructor(JSHandle::Cast(function), function); + auto tgObjCallInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread, JSTaggedValue(*object), 4); + tgObjCallInfo->SetFunction(JSTaggedValue(*objFun)); + tgObjCallInfo->SetThis(JSTaggedValue::Undefined()); + tgObjCallInfo->SetNewTarget(JSTaggedValue(*objFun)); + + prev = TestHelper::SetupFrame(thread, tgObjCallInfo.get()); + JSTaggedValue resultTg = BuiltinsObject::ObjectConstructor(tgObjCallInfo.get()); + TestHelper::TearDownFrame(thread, prev); + ASSERT_TRUE(resultTg.IsObject()); + JSHandle jtHandleTg(thread, JSTaggedValue(reinterpret_cast(resultTg.GetRawData()))); + JSTaggedValue resultProtoTg = jtHandleTg->GetPrototype(thread); + JSTaggedValue funcProtoTg = objectFunc->GetFunctionPrototype(); + ASSERT_EQ(resultProtoTg, funcProtoTg); + ASSERT_TRUE(jtHandleTg->IsExtensible()); + + // value is null + JSHandle objectVn = + thread->GetEcmaVM()->GetFactory()->NewJSObjectByConstructor(JSHandle::Cast(function), function); + auto vnObjCallInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread, JSTaggedValue(*objectVn), 6); + vnObjCallInfo->SetFunction(JSTaggedValue(*objFun)); + vnObjCallInfo->SetThis(JSTaggedValue::Undefined()); + vnObjCallInfo->SetCallArg(0, JSTaggedValue::Null()); + vnObjCallInfo->SetNewTarget(JSTaggedValue(*objFun)); + + prev = TestHelper::SetupFrame(thread, vnObjCallInfo.get()); + JSTaggedValue resultVn = BuiltinsObject::ObjectConstructor(vnObjCallInfo.get()); + TestHelper::TearDownFrame(thread, prev); + + ASSERT_TRUE(resultVn.IsObject()); + JSHandle jtHandleVn(thread, JSTaggedValue(reinterpret_cast(resultVn.GetRawData()))); + JSTaggedValue resultProtoVn = jtHandleVn->GetPrototype(thread); + JSTaggedValue funcProtoVn = objectFunc->GetFunctionPrototype(); + ASSERT_EQ(resultProtoVn, funcProtoVn); + ASSERT_TRUE(jtHandleVn->IsExtensible()); +} + +// 19.1.2.1Object.assign ( target, ...sources ) +TEST_F(BuiltinsObjectTest, Assign) +{ + JSHandle function(thread, BuiltinsObjectTestCreate(thread)); + JSHandle objHandle1 = + thread->GetEcmaVM()->GetFactory()->NewJSObjectByConstructor(JSHandle(function), function); + JSHandle objHandle2 = + thread->GetEcmaVM()->GetFactory()->NewJSObjectByConstructor(JSHandle(function), function); + + JSHandle key1(thread->GetEcmaVM()->GetFactory()->NewFromCanBeCompressString("x")); + JSHandle value1(thread, JSTaggedValue(1)); + JSObject::SetProperty(thread, JSHandle(objHandle1), key1, value1); + EXPECT_EQ(JSObject::GetProperty(thread, JSHandle(objHandle1), key1).GetValue()->GetInt(), 1); + + JSHandle key2(thread->GetEcmaVM()->GetFactory()->NewFromCanBeCompressString("y")); + JSHandle value2(thread, JSTaggedValue(2)); + JSObject::SetProperty(thread, JSHandle(objHandle2), key2, value2); + EXPECT_EQ(JSObject::GetProperty(thread, JSHandle(objHandle2), key2).GetValue()->GetInt(), 2); + + auto assignObjCallInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread, JSTaggedValue::Undefined(), 8); + assignObjCallInfo->SetFunction(JSTaggedValue::Undefined()); + assignObjCallInfo->SetThis(JSTaggedValue::Undefined()); + assignObjCallInfo->SetCallArg(0, objHandle1.GetTaggedValue()); + assignObjCallInfo->SetCallArg(1, objHandle2.GetTaggedValue()); + + [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread, assignObjCallInfo.get()); + JSTaggedValue result = BuiltinsObject::Assign(assignObjCallInfo.get()); + TestHelper::TearDownFrame(thread, prev); + + ASSERT_TRUE(result.IsECMAObject()); + JSHandle jtHandle(thread, JSTaggedValue(reinterpret_cast(result.GetRawData()))); + EXPECT_EQ(JSObject::GetProperty(thread, jtHandle, key1).GetValue()->GetInt(), 1); + EXPECT_EQ(JSObject::GetProperty(thread, jtHandle, key2).GetValue()->GetInt(), 2); +} + +// 19.1.2.2Object.create ( O [ , Properties ] ) +TEST_F(BuiltinsObjectTest, Create) +{ + JSHandle function(thread, BuiltinsObjectTestCreate(thread)); + JSHandle objectFunc(thread, BuiltinsObjectTestCreate(thread)); + JSHandle funcProto(thread, objectFunc->GetFunctionPrototype()); + + // no prop + auto objCallInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread, JSTaggedValue::Undefined(), 6); + objCallInfo->SetFunction(JSTaggedValue::Undefined()); + objCallInfo->SetThis(JSTaggedValue::Undefined()); + objCallInfo->SetCallArg(0, funcProto.GetTaggedValue()); + + [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread, objCallInfo.get()); + JSTaggedValue result = BuiltinsObject::Create(objCallInfo.get()); + TestHelper::TearDownFrame(thread, prev); + + ASSERT_TRUE(result.IsECMAObject()); + JSHandle jtHandle(thread, JSTaggedValue(reinterpret_cast(result.GetRawData()))); + JSTaggedValue resultProto = jtHandle->GetPrototype(thread); + ASSERT_EQ(resultProto, funcProto.GetTaggedValue()); + + // has prop + JSHandle key(thread->GetEcmaVM()->GetFactory()->NewFromCanBeCompressString("prop")); + JSHandle objHandle = + thread->GetEcmaVM()->GetFactory()->NewJSObjectByConstructor(JSHandle::Cast(function), function); + EXPECT_TRUE(*objHandle != nullptr); + + PropertyDescriptor desc(thread); + desc.SetWritable(false); + JSHandle descHandle(JSObject::FromPropertyDescriptor(thread, desc)); + + PropertyDescriptor descNw(thread, JSHandle::Cast(descHandle), true, true, true); + JSObject::DefineOwnProperty(thread, objHandle, key, descNw); + + auto hpObjCallInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread, JSTaggedValue::Undefined(), 8); + hpObjCallInfo->SetFunction(JSTaggedValue::Undefined()); + hpObjCallInfo->SetThis(JSTaggedValue::Undefined()); + hpObjCallInfo->SetCallArg(0, funcProto.GetTaggedValue()); + hpObjCallInfo->SetCallArg(1, objHandle.GetTaggedValue()); + + prev = TestHelper::SetupFrame(thread, hpObjCallInfo.get()); + JSTaggedValue resultHp = BuiltinsObject::Create(hpObjCallInfo.get()); + TestHelper::TearDownFrame(thread, prev); + + ASSERT_TRUE(resultHp.IsObject()); + PropertyDescriptor descRes(thread); + bool success = JSObject::GetOwnProperty(thread, JSHandle(thread, resultHp), key, descRes); + EXPECT_TRUE(success); + EXPECT_TRUE(!descRes.IsWritable()); + + // undefined + auto unCallInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread, JSTaggedValue::Undefined(), 6); + unCallInfo->SetFunction(JSTaggedValue::Undefined()); + unCallInfo->SetThis(JSTaggedValue::Undefined()); + unCallInfo->SetCallArg(0, JSTaggedValue::Undefined()); + + prev = TestHelper::SetupFrame(thread, unCallInfo.get()); + JSTaggedValue resultUn = BuiltinsObject::Create(unCallInfo.get()); + TestHelper::TearDownFrame(thread, prev); + ASSERT_EQ(resultUn.GetRawData(), JSTaggedValue::VALUE_EXCEPTION); +} + +// 19.1.2.3Object.defineProperties ( O, Properties ) +TEST_F(BuiltinsObjectTest, DefineProperties) +{ + JSHandle function(thread, BuiltinsObjectTestCreate(thread)); + JSHandle env = thread->GetEcmaVM()->GetGlobalEnv(); + JSHandle objFunc(env->GetObjectFunction()); + JSHandle objHandle = + thread->GetEcmaVM()->GetFactory()->NewJSObjectByConstructor(JSHandle(function), function); + JSHandle key(thread->GetEcmaVM()->GetFactory()->NewFromCanBeCompressString("prop")); + JSHandle jsobjHandle = + thread->GetEcmaVM()->GetFactory()->NewJSObjectByConstructor(JSHandle(function), function); + + PropertyDescriptor desc(thread); + desc.SetWritable(false); + JSHandle descHandle(JSObject::FromPropertyDescriptor(thread, desc)); + + PropertyDescriptor descNw(thread, JSHandle::Cast(descHandle), true, true, true); + JSObject::DefineOwnProperty(thread, jsobjHandle, key, descNw); + + auto objCallInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread, JSTaggedValue::Undefined(), 8); + objCallInfo->SetFunction(JSTaggedValue::Undefined()); + objCallInfo->SetThis(JSTaggedValue::Undefined()); + objCallInfo->SetCallArg(0, objHandle.GetTaggedValue()); + objCallInfo->SetCallArg(1, jsobjHandle.GetTaggedValue()); + + [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread, objCallInfo.get()); + JSTaggedValue result = BuiltinsObject::DefineProperties(objCallInfo.get()); + TestHelper::TearDownFrame(thread, prev); + + ASSERT_TRUE(result.IsECMAObject()); + JSTaggedValue res(reinterpret_cast(result.GetRawData())); + PropertyDescriptor descRes(thread); + JSObject::GetOwnProperty(thread, JSHandle(thread, res), key, descRes); + EXPECT_TRUE(!descRes.IsWritable()); +} + +// 19.1.2.4Object.defineProperty ( O, P, Attributes ) +TEST_F(BuiltinsObjectTest, DefineProperty) +{ + JSHandle function(thread, BuiltinsObjectTestCreate(thread)); + + JSHandle env = thread->GetEcmaVM()->GetGlobalEnv(); + JSHandle objFunc(env->GetObjectFunction()); + JSHandle attHandle = + thread->GetEcmaVM()->GetFactory()->NewJSObjectByConstructor(JSHandle(function), function); + JSHandle objHandle = + thread->GetEcmaVM()->GetFactory()->NewJSObjectByConstructor(JSHandle(function), function); + + PropertyDescriptor desc(thread); + desc.SetWritable(true); + JSHandle writableStr = thread->GlobalConstants()->GetHandledWritableString(); + JSHandle writable(thread, JSTaggedValue(desc.IsWritable())); + JSObject::CreateDataProperty(thread, attHandle, writableStr, writable); + + JSHandle key(thread->GetEcmaVM()->GetFactory()->NewFromCanBeCompressString("x")); + + PropertyDescriptor descNw(thread); + JSObject::GetOwnProperty(thread, objHandle, key, descNw); + EXPECT_TRUE(!descNw.HasWritable()); + + auto objCallInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread, JSTaggedValue::Undefined(), 10); + objCallInfo->SetFunction(JSTaggedValue::Undefined()); + objCallInfo->SetThis(JSTaggedValue::Undefined()); + objCallInfo->SetCallArg(0, objHandle.GetTaggedValue()); + objCallInfo->SetCallArg(1, key.GetTaggedValue()); + objCallInfo->SetCallArg(2, attHandle.GetTaggedValue()); + + [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread, objCallInfo.get()); + JSTaggedValue result = BuiltinsObject::DefineProperty(objCallInfo.get()); + TestHelper::TearDownFrame(thread, prev); + + ASSERT_TRUE(result.IsECMAObject()); + JSTaggedValue res(reinterpret_cast(result.GetRawData())); + PropertyDescriptor descRes(thread); + JSObject::GetOwnProperty(thread, JSHandle(thread, res), key, descRes); + EXPECT_TRUE(descRes.HasWritable()); +} + +// B.2.2.2 Object.prototype.__defineGetter__ ( P, getter ) +TEST_F(BuiltinsObjectTest, DefineGetter) +{ + ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); + + JSHandle obj(thread, CreateBuiltinJSObject(thread, "prop")); + JSHandle key(factory->NewFromString("prop")); + JSHandle getter(factory->NewJSFunction(thread->GetEcmaVM()->GetGlobalEnv())); + + auto ecmaRuntimeCallInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread, JSTaggedValue::Undefined(), 8); + ecmaRuntimeCallInfo->SetFunction(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetThis(obj.GetTaggedValue()); + ecmaRuntimeCallInfo->SetCallArg(0, key.GetTaggedValue()); + ecmaRuntimeCallInfo->SetCallArg(1, getter.GetTaggedValue()); + + [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread, ecmaRuntimeCallInfo.get()); + JSTaggedValue result = BuiltinsObject::DefineSetter(ecmaRuntimeCallInfo.get()); + TestHelper::TearDownFrame(thread, prev); + + ASSERT_EQ(result.GetRawData(), JSTaggedValue::VALUE_UNDEFINED); +} + +// B.2.2.3 Object.prototype.__defineSetter__ ( P, setter ) +TEST_F(BuiltinsObjectTest, DefineSetter) +{ + ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); + + JSHandle obj(thread, CreateBuiltinJSObject(thread, "prop")); + JSHandle key(factory->NewFromString("prop")); + JSHandle setter(factory->NewJSFunction(thread->GetEcmaVM()->GetGlobalEnv())); + + auto ecmaRuntimeCallInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread, JSTaggedValue::Undefined(), 8); + ecmaRuntimeCallInfo->SetFunction(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetThis(obj.GetTaggedValue()); + ecmaRuntimeCallInfo->SetCallArg(0, key.GetTaggedValue()); + ecmaRuntimeCallInfo->SetCallArg(1, setter.GetTaggedValue()); + + [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread, ecmaRuntimeCallInfo.get()); + JSTaggedValue result = BuiltinsObject::DefineGetter(ecmaRuntimeCallInfo.get()); + TestHelper::TearDownFrame(thread, prev); + + ASSERT_EQ(result.GetRawData(), JSTaggedValue::VALUE_UNDEFINED); +} + +// B.2.2.4 Object.prototype.__lookupGetter__ ( P ) +TEST_F(BuiltinsObjectTest, LookupGetter) +{ + ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); + + JSHandle obj(thread, CreateBuiltinJSObject(thread, "prop")); + JSHandle key(factory->NewFromString("prop")); + JSHandle getter(factory->NewJSFunction(thread->GetEcmaVM()->GetGlobalEnv())); + + // First - getter should not be defined. + auto ecmaRuntimeCallInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread, JSTaggedValue::Undefined(), 6); + ecmaRuntimeCallInfo->SetFunction(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetThis(obj.GetTaggedValue()); + ecmaRuntimeCallInfo->SetCallArg(0, key.GetTaggedValue()); + + [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread, ecmaRuntimeCallInfo.get()); + JSTaggedValue result = BuiltinsObject::LookupGetter(ecmaRuntimeCallInfo.get()); + TestHelper::TearDownFrame(thread, prev); + + ASSERT_EQ(result.GetRawData(), JSTaggedValue::VALUE_UNDEFINED); + + // Second - set a getter function. + ecmaRuntimeCallInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread, JSTaggedValue::Undefined(), 8); + ecmaRuntimeCallInfo->SetFunction(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetThis(obj.GetTaggedValue()); + ecmaRuntimeCallInfo->SetCallArg(0, key.GetTaggedValue()); + ecmaRuntimeCallInfo->SetCallArg(1, getter.GetTaggedValue()); + + prev = TestHelper::SetupFrame(thread, ecmaRuntimeCallInfo.get()); + result = BuiltinsObject::DefineGetter(ecmaRuntimeCallInfo.get()); + TestHelper::TearDownFrame(thread, prev); + + ASSERT_EQ(result.GetRawData(), JSTaggedValue::VALUE_UNDEFINED); + + // Third - check the getter function if it really exists. + ecmaRuntimeCallInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread, JSTaggedValue::Undefined(), 6); + ecmaRuntimeCallInfo->SetFunction(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetThis(obj.GetTaggedValue()); + ecmaRuntimeCallInfo->SetCallArg(0, key.GetTaggedValue()); + + prev = TestHelper::SetupFrame(thread, ecmaRuntimeCallInfo.get()); + result = BuiltinsObject::LookupGetter(ecmaRuntimeCallInfo.get()); + TestHelper::TearDownFrame(thread, prev); + + ASSERT_EQ(result, getter.GetTaggedValue()); +} + +// B.2.2.5 Object.prototype.__lookupSetter__ ( P ) +TEST_F(BuiltinsObjectTest, LookupSetter) +{ + ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); + + JSHandle obj(thread, CreateBuiltinJSObject(thread, "prop")); + JSHandle key(factory->NewFromString("prop")); + JSHandle setter(factory->NewJSFunction(thread->GetEcmaVM()->GetGlobalEnv())); + + // First - setter should not be defined. + auto ecmaRuntimeCallInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread, JSTaggedValue::Undefined(), 6); + ecmaRuntimeCallInfo->SetFunction(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetThis(obj.GetTaggedValue()); + ecmaRuntimeCallInfo->SetCallArg(0, key.GetTaggedValue()); + + [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread, ecmaRuntimeCallInfo.get()); + JSTaggedValue result = BuiltinsObject::LookupSetter(ecmaRuntimeCallInfo.get()); + TestHelper::TearDownFrame(thread, prev); + + ASSERT_EQ(result.GetRawData(), JSTaggedValue::VALUE_UNDEFINED); + + // Second - set a setter function. + ecmaRuntimeCallInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread, JSTaggedValue::Undefined(), 8); + ecmaRuntimeCallInfo->SetFunction(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetThis(obj.GetTaggedValue()); + ecmaRuntimeCallInfo->SetCallArg(0, key.GetTaggedValue()); + ecmaRuntimeCallInfo->SetCallArg(1, setter.GetTaggedValue()); + + prev = TestHelper::SetupFrame(thread, ecmaRuntimeCallInfo.get()); + result = BuiltinsObject::DefineSetter(ecmaRuntimeCallInfo.get()); + TestHelper::TearDownFrame(thread, prev); + + ASSERT_EQ(result.GetRawData(), JSTaggedValue::VALUE_UNDEFINED); + + // Third - check the setter function if it really exists. + ecmaRuntimeCallInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread, JSTaggedValue::Undefined(), 6); + ecmaRuntimeCallInfo->SetFunction(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetThis(obj.GetTaggedValue()); + ecmaRuntimeCallInfo->SetCallArg(0, key.GetTaggedValue()); + + prev = TestHelper::SetupFrame(thread, ecmaRuntimeCallInfo.get()); + result = BuiltinsObject::LookupSetter(ecmaRuntimeCallInfo.get()); + TestHelper::TearDownFrame(thread, prev); + + ASSERT_EQ(result, setter.GetTaggedValue()); +} + +// 19.1.2.5Object.freeze ( O ) +TEST_F(BuiltinsObjectTest, Freeze) +{ + JSHandle function(thread, BuiltinsObjectTestCreate(thread)); + // An object is extensible by default, so it is also non-frozen. + JSHandle emptyObj = + thread->GetEcmaVM()->GetFactory()->NewJSObjectByConstructor(JSHandle(function), function); + emptyObj->GetJSHClass()->SetExtensible(true); + auto nofreezeObjCallInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread, JSTaggedValue::Undefined(), 6); + nofreezeObjCallInfo->SetFunction(JSTaggedValue::Undefined()); + nofreezeObjCallInfo->SetThis(JSTaggedValue::Undefined()); + nofreezeObjCallInfo->SetCallArg(0, emptyObj.GetTaggedValue()); + + [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread, nofreezeObjCallInfo.get()); + JSTaggedValue result = BuiltinsObject::IsFrozen(nofreezeObjCallInfo.get()); + + ASSERT_EQ(result.GetRawData(), JSTaggedValue::False().GetRawData()); + + BuiltinsObject::Freeze(nofreezeObjCallInfo.get()); + JSTaggedValue resultIs = BuiltinsObject::IsFrozen(nofreezeObjCallInfo.get()); + TestHelper::TearDownFrame(thread, prev); + ASSERT_EQ(resultIs.GetRawData(), JSTaggedValue::True().GetRawData()); +} + +// ES2021 20.1.2.7 Object.fromEntries ( iterable ) +TEST_F(BuiltinsObjectTest, FromEntries) +{ + ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); + + JSArray *arr1 = JSArray::Cast(JSArray::ArrayCreate(thread, JSTaggedNumber(0)).GetTaggedValue().GetHeapObject()); + EXPECT_TRUE(arr1 != nullptr); + + JSArray *arr2 = JSArray::Cast(JSArray::ArrayCreate(thread, JSTaggedNumber(0)).GetTaggedValue().GetHeapObject()); + EXPECT_TRUE(arr2 != nullptr); + + JSHandle obj(thread, arr1); + + JSHandle prop(thread, arr2); + JSHandle fooStr = JSHandle(factory->NewFromString("foo")); + JSHandle barStr = JSHandle(factory->NewFromString("bar")); + + PropertyDescriptor desc0(thread, fooStr, true, true, true); + PropertyDescriptor desc1(thread, barStr, true, true, true); + + JSHandle key0(thread, JSTaggedValue(0)); + JSHandle key1(thread, JSTaggedValue(1)); + + JSArray::DefineOwnProperty(thread, prop, key0, desc0); + JSArray::DefineOwnProperty(thread, prop, key1, desc1); + + PropertyDescriptor descProp(thread, JSHandle(prop), true, true, true); + JSArray::DefineOwnProperty(thread, obj, key0, descProp); + + auto objCallInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread, JSTaggedValue::Undefined(), 6); + objCallInfo->SetFunction(JSTaggedValue::Undefined()); + objCallInfo->SetThis(JSTaggedValue::Undefined()); + objCallInfo->SetCallArg(0, obj.GetTaggedValue()); + + [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread, objCallInfo.get()); + JSTaggedValue result = BuiltinsObject::FromEntries(objCallInfo.get()); + TestHelper::TearDownFrame(thread, prev); + + ASSERT_TRUE(result.IsECMAObject()); + + PropertyDescriptor desc(thread); + JSObject::GetOwnProperty(thread, JSHandle(thread, result), fooStr, desc); + + ASSERT_TRUE(JSTaggedValue::SameValue(desc.GetValue().GetTaggedValue(), barStr.GetTaggedValue())); +} + +// 19.1.2.6 Object.getOwnPropertyDescriptor ( O, P ) +TEST_F(BuiltinsObjectTest, GetOwnPropertyDesciptor) +{ + JSHandle function(thread, BuiltinsObjectTestCreate(thread)); + JSHandle objHandle = + thread->GetEcmaVM()->GetFactory()->NewJSObjectByConstructor(JSHandle(function), function); + + JSHandle key(thread->GetEcmaVM()->GetFactory()->NewFromCanBeCompressString("x")); + + PropertyDescriptor descEnum(thread); + descEnum.SetWritable(true); + JSTaggedValue::DefinePropertyOrThrow(thread, JSHandle(objHandle), key, descEnum); + + auto objCallInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread, JSTaggedValue::Undefined(), 8); + objCallInfo->SetFunction(JSTaggedValue::Undefined()); + objCallInfo->SetThis(JSTaggedValue::Undefined()); + objCallInfo->SetCallArg(0, objHandle.GetTaggedValue()); + objCallInfo->SetCallArg(1, key.GetTaggedValue()); + + [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread, objCallInfo.get()); + JSTaggedValue result = BuiltinsObject::GetOwnPropertyDesciptor(objCallInfo.get()); + TestHelper::TearDownFrame(thread, prev); + + ASSERT_TRUE(result.IsECMAObject()); + + JSHandle writableStr = thread->GlobalConstants()->GetHandledWritableString(); + JSTaggedValue jt(reinterpret_cast(result.GetRawData())); + PropertyDescriptor desc(thread); + JSObject::GetOwnProperty(thread, JSHandle(thread, jt), writableStr, desc); + ASSERT_TRUE(JSTaggedValue::SameValue(desc.GetValue().GetTaggedValue(), JSTaggedValue(true))); +} + +// ES2021 20.1.2.9 Object.getOwnPropertyDescriptors ( O ) +TEST_F(BuiltinsObjectTest, GetOwnPropertyDesciptors) +{ + JSHandle function(thread, BuiltinsObjectTestCreate(thread)); + JSHandle obj_handle = + thread->GetEcmaVM()->GetFactory()->NewJSObjectByConstructor(JSHandle(function), function); + + JSHandle keyX(thread->GetEcmaVM()->GetFactory()->NewFromString("x")); + JSHandle value(thread, JSTaggedValue(22)); + + JSObject::SetProperty(thread, obj_handle, keyX, value); + + auto objCallInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread, JSTaggedValue::Undefined(), 8); + objCallInfo->SetFunction(JSTaggedValue::Undefined()); + objCallInfo->SetThis(JSTaggedValue::Undefined()); + objCallInfo->SetCallArg(0, obj_handle.GetTaggedValue()); + + [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread, objCallInfo.get()); + JSTaggedValue result = BuiltinsObject::GetOwnPropertyDesciptors(objCallInfo.get()); + TestHelper::TearDownFrame(thread, prev); + + ASSERT_TRUE(result.IsECMAObject()); + + JSHandle handle(thread, result); + + JSHandle prop = JSObject::GetProperty(thread, handle, keyX).GetValue(); + + JSHandle value_str = thread->GlobalConstants()->GetHandledValueString(); + ASSERT_EQ(JSObject::GetProperty(thread, prop, value_str).GetValue()->GetInt(), 22); + + JSHandle enumerable_str = thread->GlobalConstants()->GetHandledEnumerableString(); + ASSERT_EQ(JSObject::GetProperty(thread, prop, enumerable_str).GetValue().GetTaggedValue(), JSTaggedValue(true)); + + JSHandle configurable_str = thread->GlobalConstants()->GetHandledConfigurableString(); + ASSERT_EQ(JSObject::GetProperty(thread, prop, configurable_str).GetValue().GetTaggedValue(), JSTaggedValue(true)); + + JSHandle writable_str = thread->GlobalConstants()->GetHandledWritableString(); + ASSERT_EQ(JSObject::GetProperty(thread, prop, writable_str).GetValue().GetTaggedValue(), JSTaggedValue(true)); +} + +// 19.1.2.7 Object.getOwnPropertyNames ( O ) +TEST_F(BuiltinsObjectTest, GetOwnPropertyNames) +{ + JSHandle function(thread, BuiltinsObjectTestCreate(thread)); + JSHandle objHandle = + thread->GetEcmaVM()->GetFactory()->NewJSObjectByConstructor(JSHandle(function), function); + + JSHandle key(thread->GetEcmaVM()->GetFactory()->NewFromCanBeCompressString("x")); + JSHandle value(thread, JSTaggedValue(1)); + JSObject::SetProperty(thread, JSHandle(objHandle), key, value); + + auto objCallInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread, JSTaggedValue::Undefined(), 6); + objCallInfo->SetFunction(JSTaggedValue::Undefined()); + objCallInfo->SetThis(JSTaggedValue::Undefined()); + objCallInfo->SetCallArg(0, objHandle.GetTaggedValue()); + + [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread, objCallInfo.get()); + JSTaggedValue result = BuiltinsObject::GetOwnPropertyNames(objCallInfo.get()); + TestHelper::TearDownFrame(thread, prev); + + ASSERT_TRUE(result.IsECMAObject()); +} + +// 19.1.2.8 Object.getOwnPropertySymbols ( O ) +TEST_F(BuiltinsObjectTest, GetOwnPropertySymbols) +{ + JSHandle function(thread, BuiltinsObjectTestCreate(thread)); + JSHandle objHandle = + thread->GetEcmaVM()->GetFactory()->NewJSObjectByConstructor(JSHandle(function), function); + JSHandle symbolKey = thread->GetEcmaVM()->GetFactory()->NewJSSymbol(); + JSHandle key(symbolKey); + JSHandle value(thread, JSTaggedValue(1)); + JSObject::SetProperty(thread, JSHandle(objHandle), key, value); + thread->ClearException(); + + auto objCallInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread, JSTaggedValue::Undefined(), 6); + objCallInfo->SetFunction(JSTaggedValue::Undefined()); + objCallInfo->SetThis(JSTaggedValue::Undefined()); + objCallInfo->SetCallArg(0, objHandle.GetTaggedValue()); + + [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread, objCallInfo.get()); + JSTaggedValue result = BuiltinsObject::GetOwnPropertySymbols(objCallInfo.get()); + TestHelper::TearDownFrame(thread, prev); + + ASSERT_TRUE(result.IsECMAObject()); +} + +// 19.1.2.10 Object.is ( value1, value2 ) +TEST_F(BuiltinsObjectTest, Is) +{ + // js object compare + JSHandle function(thread, BuiltinsObjectTestCreate(thread)); + + JSHandle obj1 = + thread->GetEcmaVM()->GetFactory()->NewJSObjectByConstructor(JSHandle(function), function); + JSHandle obj2 = + thread->GetEcmaVM()->GetFactory()->NewJSObjectByConstructor(JSHandle(function), function); + + auto objCallInfo1 = TestHelper::CreateEcmaRuntimeCallInfo(thread, JSTaggedValue::Undefined(), 8); + objCallInfo1->SetFunction(JSTaggedValue::Undefined()); + objCallInfo1->SetThis(JSTaggedValue::Undefined()); + objCallInfo1->SetCallArg(0, obj1.GetTaggedValue()); + objCallInfo1->SetCallArg(1, obj2.GetTaggedValue()); + + [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread, objCallInfo1.get()); + JSTaggedValue objResult1 = BuiltinsObject::Is(objCallInfo1.get()); + TestHelper::TearDownFrame(thread, prev); + + ASSERT_EQ(objResult1.GetRawData(), JSTaggedValue::False().GetRawData()); + + objCallInfo1->SetCallArg(1, obj1.GetTaggedValue()); + JSTaggedValue objResult2 = BuiltinsObject::Is(objCallInfo1.get()); + ASSERT_EQ(objResult2.GetRawData(), JSTaggedValue::True().GetRawData()); + + // string compare + JSHandle testStrValue1 = thread->GetEcmaVM()->GetFactory()->NewFromCanBeCompressString("helloworld"); + JSHandle testStrValue2 = thread->GetEcmaVM()->GetFactory()->NewFromCanBeCompressString("helloworld"); + + auto strCallInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread, JSTaggedValue::Undefined(), 8); + strCallInfo->SetFunction(JSTaggedValue::Undefined()); + strCallInfo->SetThis(JSTaggedValue::Undefined()); + strCallInfo->SetCallArg(0, testStrValue1.GetTaggedValue()); + strCallInfo->SetCallArg(1, testStrValue2.GetTaggedValue()); + + prev = TestHelper::SetupFrame(thread, strCallInfo.get()); + JSTaggedValue strResult = BuiltinsObject::Is(strCallInfo.get()); + TestHelper::TearDownFrame(thread, prev); + + ASSERT_EQ(strResult.GetRawData(), JSTaggedValue::True().GetRawData()); + + // bool compare + auto boolCallInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread, JSTaggedValue::Undefined(), 8); + boolCallInfo->SetFunction(JSTaggedValue::Undefined()); + boolCallInfo->SetThis(JSTaggedValue::Undefined()); + boolCallInfo->SetCallArg(0, JSTaggedValue::True()); + boolCallInfo->SetCallArg(1, JSTaggedValue::False()); + + prev = TestHelper::SetupFrame(thread, boolCallInfo.get()); + JSTaggedValue boolResult = BuiltinsObject::Is(boolCallInfo.get()); + TestHelper::TearDownFrame(thread, prev); + + ASSERT_EQ(boolResult.GetRawData(), JSTaggedValue::False().GetRawData()); + + // number compare + auto numCallInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread, JSTaggedValue::Undefined(), 8); + numCallInfo->SetFunction(JSTaggedValue::Undefined()); + numCallInfo->SetThis(JSTaggedValue::Undefined()); + numCallInfo->SetCallArg(0, JSTaggedValue(static_cast(0))); + numCallInfo->SetCallArg(1, JSTaggedValue(-0.0)); + + prev = TestHelper::SetupFrame(thread, numCallInfo.get()); + JSTaggedValue numResult = BuiltinsObject::Is(numCallInfo.get()); + TestHelper::TearDownFrame(thread, prev); + + ASSERT_EQ(numResult.GetRawData(), JSTaggedValue::False().GetRawData()); + + // undefined or null compare + auto nullCallInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread, JSTaggedValue::Undefined(), 8); + nullCallInfo->SetFunction(JSTaggedValue::Undefined()); + nullCallInfo->SetThis(JSTaggedValue::Undefined()); + nullCallInfo->SetCallArg(0, JSTaggedValue::Null()); + nullCallInfo->SetCallArg(1, JSTaggedValue::Null()); + + prev = TestHelper::SetupFrame(thread, nullCallInfo.get()); + JSTaggedValue nullResult = BuiltinsObject::Is(nullCallInfo.get()); + TestHelper::TearDownFrame(thread, prev); + + ASSERT_EQ(nullResult.GetRawData(), JSTaggedValue::True().GetRawData()); + + auto undefineCallInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread, JSTaggedValue::Undefined(), 8); + undefineCallInfo->SetFunction(JSTaggedValue::Undefined()); + undefineCallInfo->SetThis(JSTaggedValue::Undefined()); + undefineCallInfo->SetCallArg(0, JSTaggedValue::Undefined()); + undefineCallInfo->SetCallArg(1, JSTaggedValue::Undefined()); + + prev = TestHelper::SetupFrame(thread, undefineCallInfo.get()); + JSTaggedValue undefineResult = BuiltinsObject::Is(undefineCallInfo.get()); + TestHelper::TearDownFrame(thread, prev); + + ASSERT_EQ(undefineResult.GetRawData(), JSTaggedValue::True().GetRawData()); +} + +// 19.1.2.11 Object.isExtensible ( O ) +TEST_F(BuiltinsObjectTest, IsExtensible) +{ + JSHandle function(thread, BuiltinsObjectTestCreate(thread)); + + // New objects can be extended by default. + JSHandle emptyObj = + thread->GetEcmaVM()->GetFactory()->NewJSObjectByConstructor(JSHandle(function), function); + emptyObj->GetJSHClass()->SetExtensible(true); + auto emptyObjCallInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread, JSTaggedValue::Undefined(), 6); + emptyObjCallInfo->SetFunction(JSTaggedValue::Undefined()); + emptyObjCallInfo->SetThis(JSTaggedValue::Undefined()); + emptyObjCallInfo->SetCallArg(0, emptyObj.GetTaggedValue()); + + [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread, emptyObjCallInfo.get()); + JSTaggedValue result = BuiltinsObject::IsExtensible(emptyObjCallInfo.get()); + TestHelper::TearDownFrame(thread, prev); + + ASSERT_EQ(result.GetRawData(), JSTaggedValue::True().GetRawData()); + + emptyObj->GetJSHClass()->SetExtensible(false); + JSTaggedValue result2 = BuiltinsObject::IsExtensible(emptyObjCallInfo.get()); + ASSERT_EQ(result2.GetRawData(), JSTaggedValue::False().GetRawData()); +} + +// 19.1.2.12 Object.isFrozen ( O ) +TEST_F(BuiltinsObjectTest, IsFrozen) +{ + JSHandle function(thread, BuiltinsObjectTestCreate(thread)); + JSHandle key(thread->GetEcmaVM()->GetFactory()->NewFromString("x")); + JSHandle value(thread, JSTaggedValue(1)); + + // An object is extensible by default, so it is also non-frozen. + JSHandle obj = + thread->GetEcmaVM()->GetFactory()->NewJSObjectByConstructor(JSHandle::Cast(function), function); + obj->GetJSHClass()->SetExtensible(true); + auto emptyObjCallInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread, JSTaggedValue::Undefined(), 6); + emptyObjCallInfo->SetFunction(JSTaggedValue::Undefined()); + emptyObjCallInfo->SetThis(JSTaggedValue::Undefined()); + emptyObjCallInfo->SetCallArg(0, obj.GetTaggedValue()); + + [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread, emptyObjCallInfo.get()); + + JSTaggedValue result = BuiltinsObject::IsFrozen(emptyObjCallInfo.get()); + ASSERT_EQ(result.GetRawData(), JSTaggedValue::False().GetRawData()); + obj->GetJSHClass()->SetExtensible(false); + JSTaggedValue result_nex = BuiltinsObject::IsFrozen(emptyObjCallInfo.get()); + ASSERT_EQ(result_nex.GetRawData(), JSTaggedValue::True().GetRawData()); + + TestHelper::TearDownFrame(thread, prev); + + PropertyDescriptor desc_enum(thread); + desc_enum.SetConfigurable(true); + desc_enum.SetWritable(false); + obj->GetJSHClass()->SetExtensible(true); + JSTaggedValue::DefinePropertyOrThrow(thread, JSHandle(obj), key, desc_enum); + obj->GetJSHClass()->SetExtensible(false); + auto emptyObjCallInfo2 = TestHelper::CreateEcmaRuntimeCallInfo(thread, JSTaggedValue::Undefined(), 6); + emptyObjCallInfo2->SetFunction(JSTaggedValue::Undefined()); + emptyObjCallInfo2->SetThis(JSTaggedValue::Undefined()); + emptyObjCallInfo2->SetCallArg(0, obj.GetTaggedValue()); + + prev = TestHelper::SetupFrame(thread, emptyObjCallInfo2.get()); + JSTaggedValue result_nw = BuiltinsObject::IsFrozen(emptyObjCallInfo2.get()); + TestHelper::TearDownFrame(thread, prev); + + ASSERT_EQ(result_nw.GetRawData(), JSTaggedValue::False().GetRawData()); + + desc_enum.SetConfigurable(false); + obj->GetJSHClass()->SetExtensible(true); + JSTaggedValue::DefinePropertyOrThrow(thread, JSHandle(obj), key, desc_enum); + obj->GetJSHClass()->SetExtensible(false); + auto emptyObjCallInfo3 = TestHelper::CreateEcmaRuntimeCallInfo(thread, JSTaggedValue::Undefined(), 6); + emptyObjCallInfo3->SetFunction(JSTaggedValue::Undefined()); + emptyObjCallInfo3->SetThis(JSTaggedValue::Undefined()); + emptyObjCallInfo3->SetCallArg(0, obj.GetTaggedValue()); + + prev = TestHelper::SetupFrame(thread, emptyObjCallInfo3.get()); + JSTaggedValue result_nc = BuiltinsObject::IsFrozen(emptyObjCallInfo3.get()); + TestHelper::TearDownFrame(thread, prev); + + ASSERT_EQ(result_nc.GetRawData(), JSTaggedValue::True().GetRawData()); +} + +// 19.1.2.13 Object.isSealed ( O ) +TEST_F(BuiltinsObjectTest, IsSealed) +{ + JSHandle function(thread, BuiltinsObjectTestCreate(thread)); + + // An object is extensible by default, so it is also non-frozen. + JSHandle emptyObj = + thread->GetEcmaVM()->GetFactory()->NewJSObjectByConstructor(JSHandle(function), function); + emptyObj->GetJSHClass()->SetExtensible(true); + auto emptyObjCallInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread, JSTaggedValue::Undefined(), 6); + emptyObjCallInfo->SetFunction(JSTaggedValue::Undefined()); + emptyObjCallInfo->SetThis(JSTaggedValue::Undefined()); + emptyObjCallInfo->SetCallArg(0, emptyObj.GetTaggedValue()); + + [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread, emptyObjCallInfo.get()); + JSTaggedValue result = BuiltinsObject::IsSealed(emptyObjCallInfo.get()); + TestHelper::TearDownFrame(thread, prev); + + ASSERT_EQ(result.GetRawData(), JSTaggedValue::False().GetRawData()); +} + +// Object.keys(obj) +TEST_F(BuiltinsObjectTest, Keys) +{ + JSHandle obj(thread, CreateBuiltinJSObject(thread, "x")); + auto ecmaRuntimeCallInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread, JSTaggedValue::Undefined(), 6); + ecmaRuntimeCallInfo->SetFunction(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetThis(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetCallArg(0, obj.GetTaggedValue()); + + [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread, ecmaRuntimeCallInfo.get()); + JSTaggedValue result = BuiltinsObject::Keys(ecmaRuntimeCallInfo.get()); + TestHelper::TearDownFrame(thread, prev); + + ASSERT_TRUE(result.IsECMAObject()); +} + +// Object.preventExtensions(obj) +TEST_F(BuiltinsObjectTest, PreventExtensions) +{ + JSHandle obj = JSHandle(thread, CreateBuiltinJSObject(thread, "x")); + obj->GetJSHClass()->SetExtensible(true); + auto ecmaRuntimeCallInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread, JSTaggedValue::Undefined(), 6); + ecmaRuntimeCallInfo->SetFunction(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetThis(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetCallArg(0, obj.GetTaggedValue()); + + [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread, ecmaRuntimeCallInfo.get()); + JSTaggedValue result = BuiltinsObject::PreventExtensions(ecmaRuntimeCallInfo.get()); + TestHelper::TearDownFrame(thread, prev); + + ASSERT_TRUE(result.IsECMAObject()); + JSTaggedValue jt(reinterpret_cast(result.GetRawData())); + JSHandle jtHandle(thread, jt); + ASSERT_TRUE(!jtHandle->IsExtensible()); +} + +// Object.seal(obj) +TEST_F(BuiltinsObjectTest, Seal) +{ + JSHandle obj(thread, CreateBuiltinJSObject(thread, "x")); + auto ecmaRuntimeCallInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread, JSTaggedValue::Undefined(), 6); + ecmaRuntimeCallInfo->SetFunction(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetThis(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetCallArg(0, obj.GetTaggedValue()); + + [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread, ecmaRuntimeCallInfo.get()); + JSTaggedValue result = BuiltinsObject::Seal(ecmaRuntimeCallInfo.get()); + + ASSERT_TRUE(result.IsECMAObject()); + ASSERT_EQ(result.GetRawData(), obj->GetRawData()); + + // test isSealed(). + JSTaggedValue res = BuiltinsObject::IsSealed(ecmaRuntimeCallInfo.get()); + TestHelper::TearDownFrame(thread, prev); + ASSERT_EQ(res.GetRawData(), JSTaggedValue::True().GetRawData()); +} + +// Object.setPrototypeOf(obj, prototype) +TEST_F(BuiltinsObjectTest, SetPrototypeOf) +{ + JSHandle obj(thread, CreateBuiltinJSObject(thread, "x")); + JSHandle objFather(thread, CreateBuiltinJSObject(thread, "y")); + + auto ecmaRuntimeCallInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread, JSTaggedValue::Undefined(), 8); + ecmaRuntimeCallInfo->SetFunction(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetThis(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetCallArg(0, obj.GetTaggedValue()); + ecmaRuntimeCallInfo->SetCallArg(1, objFather.GetTaggedValue()); + + [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread, ecmaRuntimeCallInfo.get()); + JSTaggedValue result = BuiltinsObject::SetPrototypeOf(ecmaRuntimeCallInfo.get()); + TestHelper::TearDownFrame(thread, prev); + + ASSERT_TRUE(result.IsECMAObject()); + ASSERT_EQ(result.GetRawData(), obj.GetTaggedValue().GetRawData()); + + // test obj has property "y". + JSHandle key(thread->GetEcmaVM()->GetFactory()->NewFromCanBeCompressString("y")); + EXPECT_EQ(JSObject::GetProperty(thread, JSHandle(obj), key).GetValue()->GetInt(), 1); +} + +// Object.values(obj) +TEST_F(BuiltinsObjectTest, Values) +{ + JSHandle obj(thread, CreateBuiltinJSObject(thread, "x")); + JSHandle key(thread->GetEcmaVM()->GetFactory()->NewFromString("y")); + JSHandle value(thread, JSTaggedValue(22)); + + JSObject::SetProperty(thread, obj, key, value); + + auto ecmaRuntimeCallInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread, JSTaggedValue::Undefined(), 6); + ecmaRuntimeCallInfo->SetFunction(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetThis(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetCallArg(0, obj.GetTaggedValue()); + + [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread, ecmaRuntimeCallInfo.get()); + JSTaggedValue result = BuiltinsObject::Values(ecmaRuntimeCallInfo.get()); + + EXPECT_TRUE(result.IsECMAObject()); + + JSHandle handle(thread, result); + JSHandle index0(thread, JSTaggedValue(0)); + JSHandle index1(thread, JSTaggedValue(1)); + + + ASSERT_EQ(JSObject::GetProperty(thread, JSHandle(handle), index0).GetValue()->GetInt(), 1); + ASSERT_EQ(JSObject::GetProperty(thread, JSHandle(handle), index1).GetValue()->GetInt(), 22); +} + +// obj.hasOwnProperty(prop) +TEST_F(BuiltinsObjectTest, HasOwnProperty) +{ + JSHandle obj(thread, CreateBuiltinJSObject(thread, "x")); + CString keyCStr = "x"; + JSHandle keyString = thread->GetEcmaVM()->GetFactory()->NewFromCanBeCompressString(&keyCStr[0]); + JSHandle key(keyString); + + auto ecmaRuntimeCallInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread, JSTaggedValue::Undefined(), 6); + ecmaRuntimeCallInfo->SetFunction(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetThis(obj.GetTaggedValue()); + ecmaRuntimeCallInfo->SetCallArg(0, key.GetTaggedValue()); + + [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread, ecmaRuntimeCallInfo.get()); + JSTaggedValue result = BuiltinsObject::HasOwnProperty(ecmaRuntimeCallInfo.get()); + TestHelper::TearDownFrame(thread, prev); + + ASSERT_EQ(result.GetRawData(), JSTaggedValue::True().GetRawData()); +} + +// prototypeObj.isPrototypeOf(object) +TEST_F(BuiltinsObjectTest, IsPrototypeOfFalse) +{ + JSHandle obj(thread, CreateBuiltinJSObject(thread, "x")); + JSHandle objFather(thread, CreateBuiltinJSObject(thread, "y")); + + auto ecmaRuntimeCallInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread, JSTaggedValue::Undefined(), 8); + ecmaRuntimeCallInfo->SetFunction(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetThis(objFather.GetTaggedValue()); + ecmaRuntimeCallInfo->SetCallArg(0, obj.GetTaggedValue()); + + [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread, ecmaRuntimeCallInfo.get()); + JSTaggedValue result1 = BuiltinsObject::IsPrototypeOf(ecmaRuntimeCallInfo.get()); + TestHelper::TearDownFrame(thread, prev); + + ASSERT_EQ(result1.GetRawData(), JSTaggedValue::False().GetRawData()); +} + +TEST_F(BuiltinsObjectTest, IsPrototypeOfTrue) +{ + JSHandle obj(thread, CreateBuiltinJSObject(thread, "x")); + JSHandle objFather(thread, CreateBuiltinJSObject(thread, "y")); + + auto ecmaRuntimeCallInfo1 = TestHelper::CreateEcmaRuntimeCallInfo(thread, JSTaggedValue::Undefined(), 8); + ecmaRuntimeCallInfo1->SetFunction(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo1->SetThis(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo1->SetCallArg(0, obj.GetTaggedValue()); + ecmaRuntimeCallInfo1->SetCallArg(1, objFather.GetTaggedValue()); + + [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread, ecmaRuntimeCallInfo1.get()); + JSTaggedValue result1 = BuiltinsObject::SetPrototypeOf(ecmaRuntimeCallInfo1.get()); + TestHelper::TearDownFrame(thread, prev); + + ASSERT_TRUE(result1.IsObject()); + ASSERT_EQ(result1.GetRawData(), obj->GetRawData()); + + auto ecmaRuntimeCallInfo2 = TestHelper::CreateEcmaRuntimeCallInfo(thread, JSTaggedValue::Undefined(), 6); + ecmaRuntimeCallInfo2->SetFunction(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo2->SetThis(objFather.GetTaggedValue()); + ecmaRuntimeCallInfo2->SetCallArg(0, obj.GetTaggedValue()); + + prev = TestHelper::SetupFrame(thread, ecmaRuntimeCallInfo2.get()); + JSTaggedValue result2 = BuiltinsObject::IsPrototypeOf(ecmaRuntimeCallInfo2.get()); + TestHelper::TearDownFrame(thread, prev); + + ASSERT_EQ(result2.GetRawData(), JSTaggedValue::True().GetRawData()); +} + +// obj.propertyIsEnumerable(prop) +TEST_F(BuiltinsObjectTest, PropertyIsEnumerable) +{ + JSHandle obj(thread, CreateBuiltinJSObject(thread, "x")); + JSHandle key(thread->GetEcmaVM()->GetFactory()->NewFromCanBeCompressString("x")); + + auto ecmaRuntimeCallInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread, JSTaggedValue::Undefined(), 6); + ecmaRuntimeCallInfo->SetFunction(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetThis(obj.GetTaggedValue()); + ecmaRuntimeCallInfo->SetCallArg(0, key.GetTaggedValue()); + + [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread, ecmaRuntimeCallInfo.get()); + JSTaggedValue result = BuiltinsObject::PropertyIsEnumerable(ecmaRuntimeCallInfo.get()); + TestHelper::TearDownFrame(thread, prev); + + ASSERT_EQ(result.GetRawData(), JSTaggedValue::True().GetRawData()); +} + +// obj.toLocaleString() +TEST_F(BuiltinsObjectTest, ToLocaleString) +{ + JSHandle obj(thread, CreateBuiltinJSObject(thread, "x")); + JSHandle calleeFunc = thread->GetEcmaVM()->GetFactory()->NewJSFunction( + thread->GetEcmaVM()->GetGlobalEnv(), reinterpret_cast(BuiltinsObject::ToString)); + JSHandle calleeValue(calleeFunc); + JSHandle calleeKey(thread->GetEcmaVM()->GetFactory()->NewFromCanBeCompressString("toString")); + JSObject::SetProperty(thread, obj, calleeKey, calleeValue); + + JSHandle resultValue = thread->GetEcmaVM()->GetFactory()->NewFromCanBeCompressString("[object Object]"); + + auto ecmaRuntimeCallInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread, JSTaggedValue::Undefined(), 6); + ecmaRuntimeCallInfo->SetFunction(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetThis(obj.GetTaggedValue()); + ecmaRuntimeCallInfo->SetCallArg(0, obj.GetTaggedValue()); + + [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread, ecmaRuntimeCallInfo.get()); + JSTaggedValue result = BuiltinsObject::ToLocaleString(ecmaRuntimeCallInfo.get()); + TestHelper::TearDownFrame(thread, prev); + + ASSERT_TRUE(result.IsString()); + ASSERT_EQ(resultValue->Compare(reinterpret_cast(result.GetRawData())), 0); +} + +// obj.toString() +TEST_F(BuiltinsObjectTest, ToString) +{ + JSHandle obj = JSHandle(thread, CreateBuiltinJSObject(thread, "x")); + + // object + JSHandle resultValue = thread->GetEcmaVM()->GetFactory()->NewFromCanBeCompressString("[object Object]"); + auto ecmaRuntimeCallInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread, JSTaggedValue::Undefined(), 4); + ecmaRuntimeCallInfo->SetFunction(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetThis(obj.GetTaggedValue()); + + [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread, ecmaRuntimeCallInfo.get()); + JSTaggedValue result = BuiltinsObject::ToString(ecmaRuntimeCallInfo.get()); + TestHelper::TearDownFrame(thread, prev); + + ASSERT_TRUE(result.IsString()); + ASSERT_EQ(resultValue->Compare(reinterpret_cast(result.GetRawData())), 0); + + // array + JSHandle arr = thread->GetEcmaVM()->GetFactory()->NewJSArray(); + JSHandle resultArrValue = + thread->GetEcmaVM()->GetFactory()->NewFromCanBeCompressString("[object Array]"); + auto arrEcmaRuntimeCallInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread, JSTaggedValue::Undefined(), 4); + arrEcmaRuntimeCallInfo->SetFunction(JSTaggedValue::Undefined()); + arrEcmaRuntimeCallInfo->SetThis(arr.GetTaggedValue()); + + prev = TestHelper::SetupFrame(thread, arrEcmaRuntimeCallInfo.get()); + JSTaggedValue resultArr = BuiltinsObject::ToString(arrEcmaRuntimeCallInfo.get()); + TestHelper::TearDownFrame(thread, prev); + + ASSERT_TRUE(resultArr.IsString()); + ASSERT_EQ(resultArrValue->Compare(reinterpret_cast(resultArr.GetRawData())), 0); + + // string + JSHandle str = thread->GetEcmaVM()->GetFactory()->NewFromCanBeCompressString("hello"); + JSHandle resultStrValue = + thread->GetEcmaVM()->GetFactory()->NewFromCanBeCompressString("[object String]"); + auto strEcmaRuntimeCallInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread, JSTaggedValue::Undefined(), 4); + strEcmaRuntimeCallInfo->SetFunction(JSTaggedValue::Undefined()); + strEcmaRuntimeCallInfo->SetThis(str.GetTaggedValue()); + + prev = TestHelper::SetupFrame(thread, strEcmaRuntimeCallInfo.get()); + JSTaggedValue resultStr = BuiltinsObject::ToString(strEcmaRuntimeCallInfo.get()); + TestHelper::TearDownFrame(thread, prev); + + ASSERT_TRUE(resultStr.IsString()); + ASSERT_EQ(resultStrValue->Compare(reinterpret_cast(resultStr.GetRawData())), 0); + + // function + JSHandle func = thread->GetEcmaVM()->GetFactory()->NewJSFunction(thread->GetEcmaVM()->GetGlobalEnv()); + JSHandle resultFuncValue = + thread->GetEcmaVM()->GetFactory()->NewFromCanBeCompressString("[object Function]"); + auto funcEcmaRuntimeCallInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread, JSTaggedValue::Undefined(), 4); + funcEcmaRuntimeCallInfo->SetFunction(JSTaggedValue::Undefined()); + funcEcmaRuntimeCallInfo->SetThis(func.GetTaggedValue()); + + prev = TestHelper::SetupFrame(thread, funcEcmaRuntimeCallInfo.get()); + JSTaggedValue resultFunc = BuiltinsObject::ToString(funcEcmaRuntimeCallInfo.get()); + TestHelper::TearDownFrame(thread, prev); + + ASSERT_TRUE(resultFunc.IsString()); + ASSERT_EQ(resultFuncValue->Compare(reinterpret_cast(resultFunc.GetRawData())), 0); + + // error + auto ecmaVM = thread->GetEcmaVM(); + JSHandle env = ecmaVM->GetGlobalEnv(); + JSHandle errorObject = env->GetErrorFunction(); + JSHandle error = + thread->GetEcmaVM()->GetFactory()->NewJSObjectByConstructor(JSHandle(errorObject), errorObject); + JSHandle errorValue = thread->GetEcmaVM()->GetFactory()->NewFromCanBeCompressString("[object Error]"); + auto errorEcmaRuntimeCallInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread, JSTaggedValue::Undefined(), 4); + errorEcmaRuntimeCallInfo->SetFunction(JSTaggedValue::Undefined()); + errorEcmaRuntimeCallInfo->SetThis(error.GetTaggedValue()); + + prev = TestHelper::SetupFrame(thread, errorEcmaRuntimeCallInfo.get()); + JSTaggedValue resultError = BuiltinsObject::ToString(errorEcmaRuntimeCallInfo.get()); + TestHelper::TearDownFrame(thread, prev); + + ASSERT_TRUE(resultError.IsString()); + ASSERT_EQ(errorValue->Compare(reinterpret_cast(resultError.GetRawData())), 0); + + // boolean + JSHandle value(thread, JSTaggedValue::False()); + JSHandle booleanObject(env->GetBooleanFunction()); + JSHandle boolean = thread->GetEcmaVM()->GetFactory()->NewJSPrimitiveRef(booleanObject, value); + JSHandle resultBoolValue = + thread->GetEcmaVM()->GetFactory()->NewFromCanBeCompressString("[object Boolean]"); + auto boolEcmaRuntimeCallInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread, JSTaggedValue::Undefined(), 4); + boolEcmaRuntimeCallInfo->SetFunction(JSTaggedValue::Undefined()); + boolEcmaRuntimeCallInfo->SetThis(boolean.GetTaggedValue()); + + prev = TestHelper::SetupFrame(thread, boolEcmaRuntimeCallInfo.get()); + JSTaggedValue resultBool = BuiltinsObject::ToString(boolEcmaRuntimeCallInfo.get()); + TestHelper::TearDownFrame(thread, prev); + + ASSERT_TRUE(resultBool.IsString()); + ASSERT_EQ(resultBoolValue->Compare(reinterpret_cast(resultBool.GetRawData())), 0); + + // number + JSHandle resultNumValue = + thread->GetEcmaVM()->GetFactory()->NewFromCanBeCompressString("[object Number]"); + auto numEcmaRuntimeCallInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread, JSTaggedValue::Undefined(), 4); + numEcmaRuntimeCallInfo->SetFunction(JSTaggedValue::Undefined()); + numEcmaRuntimeCallInfo->SetThis(JSTaggedValue(static_cast(0))); + + prev = TestHelper::SetupFrame(thread, numEcmaRuntimeCallInfo.get()); + JSTaggedValue resultNum = BuiltinsObject::ToString(numEcmaRuntimeCallInfo.get()); + TestHelper::TearDownFrame(thread, prev); + + ASSERT_TRUE(resultNum.IsString()); + ASSERT_EQ(resultNumValue->Compare(reinterpret_cast(resultNum.GetRawData())), 0); +} + +// object.valueOf() +TEST_F(BuiltinsObjectTest, ValueOf) +{ + JSHandle obj(thread, CreateBuiltinJSObject(thread, "x")); + auto ecmaRuntimeCallInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread, JSTaggedValue::Undefined(), 6); + ecmaRuntimeCallInfo->SetFunction(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetThis(obj.GetTaggedValue()); + ecmaRuntimeCallInfo->SetCallArg(0, obj.GetTaggedValue()); + + [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread, ecmaRuntimeCallInfo.get()); + JSTaggedValue result = BuiltinsObject::ValueOf(ecmaRuntimeCallInfo.get()); + TestHelper::TearDownFrame(thread, prev); + + ASSERT_TRUE(result.IsECMAObject()); +} +} // namespace panda::test \ No newline at end of file diff --git a/tests/runtime/builtins/builtins_promise_test.cpp b/tests/runtime/builtins/builtins_promise_test.cpp new file mode 100644 index 000000000..b924f244a --- /dev/null +++ b/tests/runtime/builtins/builtins_promise_test.cpp @@ -0,0 +1,727 @@ +/* + * Copyright (c) 2021-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 "plugins/ecmascript/runtime/builtins/builtins_promise.h" +#include "plugins/ecmascript/runtime/builtins/builtins_array.h" +#include "plugins/ecmascript/runtime/ecma_string.h" +#include "plugins/ecmascript/runtime/ecma_vm.h" +#include "plugins/ecmascript/runtime/global_env.h" +#include "plugins/ecmascript/runtime/jobs/micro_job_queue.h" +#include "plugins/ecmascript/runtime/js_array.h" +#include "plugins/ecmascript/runtime/js_promise.h" +#include "plugins/ecmascript/runtime/js_tagged_value-inl.h" +#include "plugins/ecmascript/runtime/js_tagged_value.h" +#include "plugins/ecmascript/runtime/js_thread.h" +#include "plugins/ecmascript/runtime/object_factory.h" +#include "plugins/ecmascript/tests/runtime/common/test_helper.h" +#include "utils/bit_utils.h" + +using namespace panda::ecmascript; +using namespace panda::ecmascript::builtins; + +namespace panda::test { +using BuiltinsBase = panda::ecmascript::base::BuiltinsBase; +using JSArray = panda::ecmascript::JSArray; + +class BuiltinsPromiseTest : public testing::Test { +public: + static void SetUpTestCase() + { + GTEST_LOG_(INFO) << "SetUpTestCase"; + } + + static void TearDownTestCase() + { + GTEST_LOG_(INFO) << "TearDownCase"; + } + + void SetUp() override + { + TestHelper::CreateEcmaVMWithScope(instance, thread, scope); + } + + void TearDown() override + { + TestHelper::DestroyEcmaVMWithScope(instance, scope); + } + + PandaVM *instance {nullptr}; + EcmaHandleScope *scope {nullptr}; + JSThread *thread {nullptr}; +}; + +// native function for race2 then_on_rejected() +JSTaggedValue TestPromiseRaceThenOnRejectd(EcmaRuntimeCallInfo *argv) +{ + JSHandle result = BuiltinsBase::GetCallArg(argv, 0); + // 12345 : test case + EXPECT_EQ(JSTaggedValue::SameValue(result.GetTaggedValue(), JSTaggedValue(12345)), true); + return JSTaggedValue::Undefined(); +} + +// native function for all then_on_resolved() +JSTaggedValue TestPromiseAllThenOnResolved(EcmaRuntimeCallInfo *argv) +{ + JSHandle array = BuiltinsBase::GetCallArg(argv, 0); + JSHandle objectArray = JSHandle::Cast(array); + [[maybe_unused]] PropertyDescriptor desc(argv->GetThread()); + [[maybe_unused]] bool result1 = JSObject::GetOwnProperty( + argv->GetThread(), objectArray, JSHandle(argv->GetThread(), JSTaggedValue(0)), desc); + EXPECT_TRUE(result1); + JSHandle value1 = desc.GetValue(); + // 111 : test case + EXPECT_EQ(JSTaggedValue::SameValue(value1.GetTaggedValue(), JSTaggedValue(111)), true); + [[maybe_unused]] bool result2 = JSObject::GetOwnProperty( + argv->GetThread(), objectArray, JSHandle(argv->GetThread(), JSTaggedValue(1)), desc); + EXPECT_TRUE(result2); + JSHandle value2 = desc.GetValue(); + // 222 : test case + EXPECT_EQ(JSTaggedValue::SameValue(value2.GetTaggedValue(), JSTaggedValue(222)), true); + return JSTaggedValue::Undefined(); +} + +// native function for catch catch_on_rejected() +JSTaggedValue TestPromiseCatch(EcmaRuntimeCallInfo *argv) +{ + JSHandle result = BuiltinsBase::GetCallArg(argv, 0); + // 3 : test case + EXPECT_EQ(JSTaggedValue::SameValue(result.GetTaggedValue(), JSTaggedValue(3)), true); + return JSTaggedValue::Undefined(); +} + +// native function for then then_on_resolved() +JSTaggedValue TestPromiseThenOnResolved(EcmaRuntimeCallInfo *argv) +{ + auto factory = argv->GetThread()->GetEcmaVM()->GetFactory(); + JSHandle result = BuiltinsBase::GetCallArg(argv, 0); + auto expect = factory->NewFromCanBeCompressString("resolve"); + EXPECT_EQ(JSTaggedValue::SameValue(result.GetTaggedValue(), expect.GetTaggedValue()), true); + return JSTaggedValue::Undefined(); +} + +// native function for then then_on_rejected() +JSTaggedValue TestPromiseThenOnRejected(EcmaRuntimeCallInfo *argv) +{ + auto factory = argv->GetThread()->GetEcmaVM()->GetFactory(); + JSHandle result = BuiltinsBase::GetCallArg(argv, 0); + auto expect = factory->NewFromCanBeCompressString("reject"); + EXPECT_EQ(JSTaggedValue::SameValue(result.GetTaggedValue(), expect.GetTaggedValue()), true); + return JSTaggedValue::Undefined(); +} + +/* + * @tc.name: Reject1 + * @tc.desc: The reject method receives a number. + * @tc.type: FUNC + */ +TEST_F(BuiltinsPromiseTest, Reject1) +{ + JSHandle env = EcmaVM::Cast(instance)->GetGlobalEnv(); + + JSHandle promise = JSHandle::Cast(env->GetPromiseFunction()); + JSHandle paramMsg(thread, JSTaggedValue(3)); + + auto ecmaRuntimeCallInfo1 = TestHelper::CreateEcmaRuntimeCallInfo(thread, JSTaggedValue(*promise), 6); + ecmaRuntimeCallInfo1->SetFunction(promise.GetTaggedValue()); + ecmaRuntimeCallInfo1->SetThis(promise.GetTaggedValue()); + ecmaRuntimeCallInfo1->SetCallArg(0, paramMsg.GetTaggedValue()); + + /** + * @tc.steps: var p1 = Promise.reject(3). + */ + [[maybe_unused]] auto prevReject = TestHelper::SetupFrame(thread, ecmaRuntimeCallInfo1.get()); + JSTaggedValue result = BuiltinsPromise::Reject(ecmaRuntimeCallInfo1.get()); + JSHandle rejectPromise(thread, result); + EXPECT_EQ(JSTaggedValue::SameValue(rejectPromise->GetPromiseState(), + JSTaggedValue(static_cast(PromiseStatus::REJECTED))), + true); + EXPECT_EQ(JSTaggedValue::SameValue(rejectPromise->GetPromiseResult(), JSTaggedValue(3)), true); +} + +/* + * @tc.name: Reject2 + * @tc.desc: The reject method receives a promise object. + * @tc.type: FUNC + */ +TEST_F(BuiltinsPromiseTest, Reject2) +{ + ObjectFactory *factory = EcmaVM::Cast(instance)->GetFactory(); + JSHandle env = EcmaVM::Cast(instance)->GetGlobalEnv(); + + // constructor promise1 + JSHandle promise = JSHandle::Cast(env->GetPromiseFunction()); + JSHandle paramMsg1 = + JSHandle::Cast(factory->NewFromCanBeCompressString("Promise reject")); + + /** + * @tc.steps: step1. var p1 = Promise.reject("Promise reject") + */ + auto ecmaRuntimeCallInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread, promise.GetTaggedValue(), 6); + ecmaRuntimeCallInfo->SetFunction(promise.GetTaggedValue()); + ecmaRuntimeCallInfo->SetThis(promise.GetTaggedValue()); + ecmaRuntimeCallInfo->SetCallArg(0, paramMsg1.GetTaggedValue()); + + [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread, ecmaRuntimeCallInfo.get()); + JSTaggedValue result = BuiltinsPromise::Reject(ecmaRuntimeCallInfo.get()); + JSHandle promise1(thread, result); + EXPECT_EQ(JSTaggedValue::SameValue(promise1->GetPromiseState(), + JSTaggedValue(static_cast(PromiseStatus::REJECTED))), + true); + EXPECT_EQ(JSTaggedValue::SameValue(promise1->GetPromiseResult(), paramMsg1.GetTaggedValue()), true); + + /** + * @tc.steps: step2. var p2 = Promise.reject(p1) + */ + auto ecmaRuntimeCallInfo1 = TestHelper::CreateEcmaRuntimeCallInfo(thread, promise.GetTaggedValue(), 6); + ecmaRuntimeCallInfo1->SetFunction(promise.GetTaggedValue()); + ecmaRuntimeCallInfo1->SetThis(promise.GetTaggedValue()); + ecmaRuntimeCallInfo1->SetCallArg(0, promise1.GetTaggedValue()); + [[maybe_unused]] auto prev1 = TestHelper::SetupFrame(thread, ecmaRuntimeCallInfo1.get()); + + JSTaggedValue result1 = BuiltinsPromise::Reject(ecmaRuntimeCallInfo1.get()); + JSHandle promise2(thread, result1); + EXPECT_NE(*promise1, *promise2); + EXPECT_EQ(JSTaggedValue::SameValue(promise2->GetPromiseState(), + JSTaggedValue(static_cast(PromiseStatus::REJECTED))), + true); + EXPECT_EQ( + JSTaggedValue::SameValue(promise2->GetPromiseResult(), JSTaggedValue(promise1.GetTaggedValue().GetRawData())), + true); +} + +/* + * @tc.name: Resolve1 + * @tc.desc: The resolve method receives a number. + * @tc.type: FUNC + */ +TEST_F(BuiltinsPromiseTest, Resolve1) +{ + JSHandle env = EcmaVM::Cast(instance)->GetGlobalEnv(); + + JSHandle promise = JSHandle::Cast(env->GetPromiseFunction()); + JSHandle paramMsg(thread, JSTaggedValue(5)); + + /** + * @tc.steps: step1. var p1 = Promise.resolve(12345) + */ + auto ecmaRuntimeCallInfo1 = TestHelper::CreateEcmaRuntimeCallInfo(thread, JSTaggedValue(*promise), 6); + ecmaRuntimeCallInfo1->SetFunction(promise.GetTaggedValue()); + ecmaRuntimeCallInfo1->SetThis(promise.GetTaggedValue()); + ecmaRuntimeCallInfo1->SetCallArg(0, paramMsg.GetTaggedValue()); + + [[maybe_unused]] auto prevReject = TestHelper::SetupFrame(thread, ecmaRuntimeCallInfo1.get()); + JSTaggedValue result = BuiltinsPromise::Resolve(ecmaRuntimeCallInfo1.get()); + JSHandle rejectPromise(thread, result); + EXPECT_EQ(JSTaggedValue::SameValue(rejectPromise->GetPromiseState(), + JSTaggedValue(static_cast(PromiseStatus::FULFILLED))), + true); + EXPECT_EQ(JSTaggedValue::SameValue(rejectPromise->GetPromiseResult(), JSTaggedValue(5)), true); +} + +/* + * @tc.name: Resolve2 + * @tc.desc: The resolve method receives a promise object. + * @tc.type: FUNC + */ +TEST_F(BuiltinsPromiseTest, Resolve2) +{ + ObjectFactory *factory = EcmaVM::Cast(instance)->GetFactory(); + JSHandle env = EcmaVM::Cast(instance)->GetGlobalEnv(); + + // constructor promise1 + JSHandle promise = JSHandle::Cast(env->GetPromiseFunction()); + JSHandle paramMsg1 = + JSHandle::Cast(factory->NewFromCanBeCompressString("Promise reject")); + + /** + * @tc.steps: step1. var p1 = Promise.reject("Promise reject") + */ + auto ecmaRuntimeCallInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread, promise.GetTaggedValue(), 6); + ecmaRuntimeCallInfo->SetFunction(promise.GetTaggedValue()); + ecmaRuntimeCallInfo->SetThis(promise.GetTaggedValue()); + ecmaRuntimeCallInfo->SetCallArg(0, paramMsg1.GetTaggedValue()); + + [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread, ecmaRuntimeCallInfo.get()); + JSTaggedValue result = BuiltinsPromise::Reject(ecmaRuntimeCallInfo.get()); + JSHandle promise1(thread, result); + EXPECT_EQ(JSTaggedValue::SameValue(promise1->GetPromiseState(), + JSTaggedValue(static_cast(PromiseStatus::REJECTED))), + true); + EXPECT_EQ(JSTaggedValue::SameValue(promise1->GetPromiseResult(), paramMsg1.GetTaggedValue()), true); + + // promise1 Enter Reject() as a parameter. + /** + * @tc.steps: step2. var p2 = Promise.resolve(p1) + */ + auto ecmaRuntimeCallInfo1 = TestHelper::CreateEcmaRuntimeCallInfo(thread, promise.GetTaggedValue(), 6); + ecmaRuntimeCallInfo1->SetFunction(promise.GetTaggedValue()); + ecmaRuntimeCallInfo1->SetThis(promise.GetTaggedValue()); + ecmaRuntimeCallInfo1->SetCallArg(0, promise1.GetTaggedValue()); + [[maybe_unused]] auto prev1 = TestHelper::SetupFrame(thread, ecmaRuntimeCallInfo1.get()); + + JSTaggedValue result1 = BuiltinsPromise::Resolve(ecmaRuntimeCallInfo1.get()); + JSHandle promise2(thread, result1); + EXPECT_EQ(*promise1, *promise2); + EXPECT_EQ(JSTaggedValue::SameValue(promise2->GetPromiseState(), + JSTaggedValue(static_cast(PromiseStatus::REJECTED))), + true); + EXPECT_EQ(JSTaggedValue::SameValue(promise2->GetPromiseResult(), paramMsg1.GetTaggedValue()), true); +} + +/* + * @tc.name: Race1 + * @tc.desc: The race method receives an array. + * @tc.type: FUNC + */ +TEST_F(BuiltinsPromiseTest, Race1) +{ + JSHandle env = EcmaVM::Cast(instance)->GetGlobalEnv(); + + JSHandle promise = JSHandle::Cast(env->GetPromiseFunction()); + JSHandle paramMsg1(thread, JSTaggedValue(12345)); + JSHandle paramMsg2(thread, JSTaggedValue(6789)); + + /** + * @tc.steps: step1. var p1 = Promise.reject(12345) + */ + auto ecmaRuntimeCallInfo1 = TestHelper::CreateEcmaRuntimeCallInfo(thread, JSTaggedValue(*promise), 6); + ecmaRuntimeCallInfo1->SetFunction(promise.GetTaggedValue()); + ecmaRuntimeCallInfo1->SetThis(promise.GetTaggedValue()); + ecmaRuntimeCallInfo1->SetCallArg(0, paramMsg1.GetTaggedValue()); + + [[maybe_unused]] auto prevReject = TestHelper::SetupFrame(thread, ecmaRuntimeCallInfo1.get()); + JSTaggedValue result1 = BuiltinsPromise::Reject(ecmaRuntimeCallInfo1.get()); + JSHandle rejectPromise(thread, result1); + EXPECT_EQ(JSTaggedValue::SameValue(rejectPromise->GetPromiseState(), + JSTaggedValue(static_cast(PromiseStatus::REJECTED))), + true); + EXPECT_EQ(JSTaggedValue::SameValue(rejectPromise->GetPromiseResult(), JSTaggedValue(12345)), true); + + /** + * @tc.steps: step2. var p2 = Promise.resolve(6789) + */ + auto ecmaRuntimeCallInfo2 = TestHelper::CreateEcmaRuntimeCallInfo(thread, JSTaggedValue(*promise), 6); + ecmaRuntimeCallInfo2->SetFunction(promise.GetTaggedValue()); + ecmaRuntimeCallInfo2->SetThis(promise.GetTaggedValue()); + ecmaRuntimeCallInfo2->SetCallArg(0, paramMsg2.GetTaggedValue()); + + [[maybe_unused]] auto prevResolve = TestHelper::SetupFrame(thread, ecmaRuntimeCallInfo2.get()); + JSTaggedValue result2 = BuiltinsPromise::Resolve(ecmaRuntimeCallInfo2.get()); + JSHandle resolvePromise(thread, result2); + EXPECT_EQ(JSTaggedValue::SameValue(resolvePromise->GetPromiseState(), + JSTaggedValue(static_cast(PromiseStatus::FULFILLED))), + true); + EXPECT_EQ(JSTaggedValue::SameValue(resolvePromise->GetPromiseResult(), JSTaggedValue(6789)), true); + + /** + * @tc.steps: step3. Construct an array with two elements p1 and p2. array = [p1. p2] + */ + JSHandle array(JSArray::ArrayCreate(thread, JSTaggedNumber(2))); + PropertyDescriptor desc(thread, JSHandle::Cast(rejectPromise)); + JSArray::DefineOwnProperty(thread, array, JSHandle(thread, JSTaggedValue(0)), desc); + + PropertyDescriptor desc1(thread, JSHandle::Cast(resolvePromise)); + JSArray::DefineOwnProperty(thread, array, JSHandle(thread, JSTaggedValue(1)), desc1); + + /** + * @tc.steps: step4. var p3 = Promise.race([p1,p2]); + */ + auto ecmaRuntimeCallInfo4 = TestHelper::CreateEcmaRuntimeCallInfo(thread, JSTaggedValue(*promise), 6); + ecmaRuntimeCallInfo4->SetFunction(promise.GetTaggedValue()); + ecmaRuntimeCallInfo4->SetThis(promise.GetTaggedValue()); + ecmaRuntimeCallInfo4->SetCallArg(0, array.GetTaggedValue()); + + [[maybe_unused]] auto prev4 = TestHelper::SetupFrame(thread, ecmaRuntimeCallInfo4.get()); + JSTaggedValue result4 = BuiltinsPromise::Race(ecmaRuntimeCallInfo4.get()); + JSHandle racePromise(thread, result4); + EXPECT_EQ(JSTaggedValue::SameValue(racePromise->GetPromiseState(), + JSTaggedValue(static_cast(PromiseStatus::PENDING))), + true); + EXPECT_EQ(racePromise->GetPromiseResult().IsUndefined(), true); +} + +/* + * @tc.name: Race2 + * @tc.desc: The Race method receives an array, uses the Then method to save the task in the task queue, and outputs + * the execution result of the queue. + * @tc.type: FUNC + */ +TEST_F(BuiltinsPromiseTest, Race2) +{ + ObjectFactory *factory = EcmaVM::Cast(instance)->GetFactory(); + JSHandle env = EcmaVM::Cast(instance)->GetGlobalEnv(); + + JSHandle promise = JSHandle::Cast(env->GetPromiseFunction()); + JSHandle paramMsg1(thread, JSTaggedValue(12345)); + JSHandle paramMsg2(thread, JSTaggedValue(6789)); + + /** + * @tc.steps: step1. var p1 = Promise.reject(12345) + */ + auto ecmaRuntimeCallInfo1 = TestHelper::CreateEcmaRuntimeCallInfo(thread, JSTaggedValue(*promise), 6); + ecmaRuntimeCallInfo1->SetFunction(promise.GetTaggedValue()); + ecmaRuntimeCallInfo1->SetThis(promise.GetTaggedValue()); + ecmaRuntimeCallInfo1->SetCallArg(0, paramMsg1.GetTaggedValue()); + + [[maybe_unused]] auto prevReject = TestHelper::SetupFrame(thread, ecmaRuntimeCallInfo1.get()); + JSTaggedValue result1 = BuiltinsPromise::Reject(ecmaRuntimeCallInfo1.get()); + JSHandle rejectPromise(thread, result1); + EXPECT_EQ(JSTaggedValue::SameValue(rejectPromise->GetPromiseState(), + JSTaggedValue(static_cast(PromiseStatus::REJECTED))), + true); + EXPECT_EQ(JSTaggedValue::SameValue(rejectPromise->GetPromiseResult(), JSTaggedValue(12345)), true); + + /** + * @tc.steps: step2. var p2 = Promise.resolve(6789) + */ + auto ecmaRuntimeCallInfo2 = TestHelper::CreateEcmaRuntimeCallInfo(thread, JSTaggedValue(*promise), 6); + ecmaRuntimeCallInfo2->SetFunction(promise.GetTaggedValue()); + ecmaRuntimeCallInfo2->SetThis(promise.GetTaggedValue()); + ecmaRuntimeCallInfo2->SetCallArg(0, paramMsg2.GetTaggedValue()); + + [[maybe_unused]] auto prevResolve = TestHelper::SetupFrame(thread, ecmaRuntimeCallInfo2.get()); + JSTaggedValue result2 = BuiltinsPromise::Resolve(ecmaRuntimeCallInfo2.get()); + JSHandle resolvePromise(thread, result2); + EXPECT_EQ(JSTaggedValue::SameValue(resolvePromise->GetPromiseState(), + JSTaggedValue(static_cast(PromiseStatus::FULFILLED))), + true); + EXPECT_EQ(JSTaggedValue::SameValue(resolvePromise->GetPromiseResult(), JSTaggedValue(6789)), true); + + /** + * @tc.steps: step3. Construct an array with two elements p1 and p2. array = [p1. p2] + */ + JSHandle array(JSArray::ArrayCreate(thread, JSTaggedNumber(2))); + PropertyDescriptor desc(thread, JSHandle::Cast(rejectPromise)); + JSArray::DefineOwnProperty(thread, array, JSHandle(thread, JSTaggedValue(0)), desc); + + PropertyDescriptor desc1(thread, JSHandle::Cast(resolvePromise)); + JSArray::DefineOwnProperty(thread, array, JSHandle(thread, JSTaggedValue(1)), desc1); + + /** + * @tc.steps: step4. var p3 = Promise.race([p1,p2]); + */ + auto ecmaRuntimeCallInfo4 = TestHelper::CreateEcmaRuntimeCallInfo(thread, JSTaggedValue(*promise), 6); + ecmaRuntimeCallInfo4->SetFunction(promise.GetTaggedValue()); + ecmaRuntimeCallInfo4->SetThis(promise.GetTaggedValue()); + ecmaRuntimeCallInfo4->SetCallArg(0, array.GetTaggedValue()); + + [[maybe_unused]] auto prev4 = TestHelper::SetupFrame(thread, ecmaRuntimeCallInfo4.get()); + JSTaggedValue result4 = BuiltinsPromise::Race(ecmaRuntimeCallInfo4.get()); + JSHandle racePromise(thread, result4); + EXPECT_EQ(JSTaggedValue::SameValue(racePromise->GetPromiseState(), + JSTaggedValue(static_cast(PromiseStatus::PENDING))), + true); + EXPECT_EQ(racePromise->GetPromiseResult().IsUndefined(), true); + + /** + * @tc.steps: step5. p3.then((resolve)=>{print(resolve)}, (reject)=>{print(reject)}) + */ + JSHandle native_func_race_then_onrejected = + factory->NewJSFunction(env, reinterpret_cast(TestPromiseRaceThenOnRejectd)); + auto ecmaRuntimeCallInfo5 = TestHelper::CreateEcmaRuntimeCallInfo(thread, racePromise.GetTaggedValue(), 8); + ecmaRuntimeCallInfo5->SetFunction(racePromise.GetTaggedValue()); + ecmaRuntimeCallInfo5->SetThis(racePromise.GetTaggedValue()); + ecmaRuntimeCallInfo5->SetCallArg(0, JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo5->SetCallArg(1, native_func_race_then_onrejected.GetTaggedValue()); + + [[maybe_unused]] auto prev5 = TestHelper::SetupFrame(thread, ecmaRuntimeCallInfo5.get()); + JSTaggedValue thenResult = BuiltinsPromise::Then(ecmaRuntimeCallInfo5.get()); + JSHandle thenPromise(thread, thenResult); + + EXPECT_TRUE(JSTaggedValue::SameValue(thenPromise->GetPromiseState(), + JSTaggedValue(static_cast(PromiseStatus::PENDING)))); + EXPECT_TRUE(thenPromise->GetPromiseResult().IsUndefined()); + + /** + * @tc.steps: step6. execute promise queue + */ + auto microJobQueue = EcmaVM::Cast(instance)->GetMicroJobQueue(); + if (!thread->HasPendingException()) { + job::MicroJobQueue::ExecutePendingJob(thread, microJobQueue); + } +} + +/* + * @tc.name: All + * @tc.desc: The All method receives an array, uses the Then method to save the task in the task queue, and outputs the + * execution result of the queue. + * @tc.type: FUNC + */ +TEST_F(BuiltinsPromiseTest, All) +{ + ObjectFactory *factory = EcmaVM::Cast(instance)->GetFactory(); + JSHandle env = EcmaVM::Cast(instance)->GetGlobalEnv(); + + JSHandle promise = JSHandle::Cast(env->GetPromiseFunction()); + JSHandle paramMsg1(thread, JSTaggedValue(111)); + JSHandle paramMsg2(thread, JSTaggedValue(222)); + + /** + * @tc.steps: step1. var p1 = Promise.resolve(111) + */ + auto ecmaRuntimeCallInfo1 = TestHelper::CreateEcmaRuntimeCallInfo(thread, JSTaggedValue(*promise), 6); + ecmaRuntimeCallInfo1->SetFunction(promise.GetTaggedValue()); + ecmaRuntimeCallInfo1->SetThis(promise.GetTaggedValue()); + ecmaRuntimeCallInfo1->SetCallArg(0, paramMsg1.GetTaggedValue()); + + [[maybe_unused]] auto prevReject = TestHelper::SetupFrame(thread, ecmaRuntimeCallInfo1.get()); + JSTaggedValue result1 = BuiltinsPromise::Resolve(ecmaRuntimeCallInfo1.get()); + JSHandle resolvePromise1(thread, result1); + EXPECT_EQ(JSTaggedValue::SameValue(resolvePromise1->GetPromiseState(), + JSTaggedValue(static_cast(PromiseStatus::FULFILLED))), + true); + EXPECT_EQ(JSTaggedValue::SameValue(resolvePromise1->GetPromiseResult(), JSTaggedValue(111)), true); + + /** + * @tc.steps: step2. var p2 = Promise.resolve(222) + */ + auto ecmaRuntimeCallInfo2 = TestHelper::CreateEcmaRuntimeCallInfo(thread, JSTaggedValue(*promise), 6); + ecmaRuntimeCallInfo2->SetFunction(promise.GetTaggedValue()); + ecmaRuntimeCallInfo2->SetThis(promise.GetTaggedValue()); + ecmaRuntimeCallInfo2->SetCallArg(0, paramMsg2.GetTaggedValue()); + + [[maybe_unused]] auto prevResolve = TestHelper::SetupFrame(thread, ecmaRuntimeCallInfo2.get()); + JSTaggedValue result2 = BuiltinsPromise::Resolve(ecmaRuntimeCallInfo2.get()); + JSHandle resolvePromise2(thread, result2); + EXPECT_EQ(JSTaggedValue::SameValue(resolvePromise2->GetPromiseState(), + JSTaggedValue(static_cast(PromiseStatus::FULFILLED))), + true); + EXPECT_EQ(JSTaggedValue::SameValue(resolvePromise2->GetPromiseResult(), JSTaggedValue(222)), true); + + /** + * @tc.steps: step3. Construct an array with two elements p1 and p2. array = [p1. p2] + */ + JSHandle array(JSArray::ArrayCreate(thread, JSTaggedNumber(2))); + PropertyDescriptor desc(thread, JSHandle::Cast(resolvePromise1)); + JSArray::DefineOwnProperty(thread, array, JSHandle(thread, JSTaggedValue(0)), desc); + + PropertyDescriptor desc1(thread, JSHandle::Cast(resolvePromise2)); + JSArray::DefineOwnProperty(thread, array, JSHandle(thread, JSTaggedValue(1)), desc1); + + /** + * @tc.steps: step4. var p3 = Promise.all([p1,p2]); + */ + auto ecmaRuntimeCallInfo4 = TestHelper::CreateEcmaRuntimeCallInfo(thread, JSTaggedValue(*promise), 6); + ecmaRuntimeCallInfo4->SetFunction(promise.GetTaggedValue()); + ecmaRuntimeCallInfo4->SetThis(promise.GetTaggedValue()); + ecmaRuntimeCallInfo4->SetCallArg(0, array.GetTaggedValue()); + + [[maybe_unused]] auto prev4 = TestHelper::SetupFrame(thread, ecmaRuntimeCallInfo4.get()); + JSTaggedValue result4 = BuiltinsPromise::All(ecmaRuntimeCallInfo4.get()); + JSHandle allPromise(thread, result4); + EXPECT_EQ(JSTaggedValue::SameValue(allPromise->GetPromiseState(), + JSTaggedValue(static_cast(PromiseStatus::PENDING))), + true); + EXPECT_EQ(allPromise->GetPromiseResult().IsUndefined(), true); + + /** + * @tc.steps: step5. p3.then((resolve)=>{print(resolve)}, (reject)=>{print(reject)}); + */ + JSHandle nativeFuncRaceThenOnResolved = + factory->NewJSFunction(env, reinterpret_cast(TestPromiseAllThenOnResolved)); + auto ecmaRuntimeCallInfo5 = TestHelper::CreateEcmaRuntimeCallInfo(thread, allPromise.GetTaggedValue(), 8); + ecmaRuntimeCallInfo5->SetFunction(allPromise.GetTaggedValue()); + ecmaRuntimeCallInfo5->SetThis(allPromise.GetTaggedValue()); + ecmaRuntimeCallInfo5->SetCallArg(0, nativeFuncRaceThenOnResolved.GetTaggedValue()); + ecmaRuntimeCallInfo5->SetCallArg(1, nativeFuncRaceThenOnResolved.GetTaggedValue()); + + [[maybe_unused]] auto prev5 = TestHelper::SetupFrame(thread, ecmaRuntimeCallInfo5.get()); + JSTaggedValue thenResult = BuiltinsPromise::Then(ecmaRuntimeCallInfo5.get()); + JSHandle thenPromise(thread, thenResult); + + EXPECT_TRUE(JSTaggedValue::SameValue(thenPromise->GetPromiseState(), + JSTaggedValue(static_cast(PromiseStatus::PENDING)))); + EXPECT_TRUE(thenPromise->GetPromiseResult().IsUndefined()); + + /** + * @tc.steps: step6. execute promise queue + */ + auto microJobQueue = EcmaVM::Cast(instance)->GetMicroJobQueue(); + if (!thread->HasPendingException()) { + job::MicroJobQueue::ExecutePendingJob(thread, microJobQueue); + } +} + +/* + * @tc.name: Catch + * @tc.desc: test Catch() method + * @tc.type: FUNC + */ +TEST_F(BuiltinsPromiseTest, Catch) +{ + auto env = EcmaVM::Cast(instance)->GetGlobalEnv(); + auto factory = EcmaVM::Cast(instance)->GetFactory(); + + JSHandle promise = JSHandle::Cast(env->GetPromiseFunction()); + JSHandle paramMsg1(thread, JSTaggedValue(3)); + + /** + * @tc.steps: step1. var p1 = Promise.reject(3) + */ + auto ecmaRuntimeCallInfo1 = TestHelper::CreateEcmaRuntimeCallInfo(thread, JSTaggedValue(*promise), 6); + ecmaRuntimeCallInfo1->SetFunction(promise.GetTaggedValue()); + ecmaRuntimeCallInfo1->SetThis(promise.GetTaggedValue()); + ecmaRuntimeCallInfo1->SetCallArg(0, paramMsg1.GetTaggedValue()); + + [[maybe_unused]] auto prevReject = TestHelper::SetupFrame(thread, ecmaRuntimeCallInfo1.get()); + JSTaggedValue result = BuiltinsPromise::Reject(ecmaRuntimeCallInfo1.get()); + JSHandle rejectPromise(thread, result); + EXPECT_EQ(JSTaggedValue::SameValue(rejectPromise->GetPromiseState(), + JSTaggedValue(static_cast(PromiseStatus::REJECTED))), + true); + EXPECT_EQ(JSTaggedValue::SameValue(rejectPromise->GetPromiseResult(), JSTaggedValue(3)), true); + + /** + * @tc.steps: step2. p1 invokes catch() + */ + JSHandle testPromiseCatch = factory->NewJSFunction(env, reinterpret_cast(TestPromiseCatch)); + auto ecmaRuntimeCallInfo2 = TestHelper::CreateEcmaRuntimeCallInfo(thread, rejectPromise.GetTaggedValue(), 6); + ecmaRuntimeCallInfo2->SetFunction(rejectPromise.GetTaggedValue()); + ecmaRuntimeCallInfo2->SetThis(rejectPromise.GetTaggedValue()); + ecmaRuntimeCallInfo2->SetCallArg(0, testPromiseCatch.GetTaggedValue()); + + [[maybe_unused]] auto prevCatch = TestHelper::SetupFrame(thread, ecmaRuntimeCallInfo2.get()); + JSTaggedValue catchResult = BuiltinsPromise::Catch(ecmaRuntimeCallInfo2.get()); + JSHandle catchPromise(thread, catchResult); + + EXPECT_EQ(JSTaggedValue::SameValue(catchPromise->GetPromiseState(), + JSTaggedValue(static_cast(PromiseStatus::PENDING))), + true); + EXPECT_EQ(catchPromise->GetPromiseResult().IsUndefined(), true); + + /** + * @tc.steps: step3. execute promise queue + */ + auto microJobQueue = EcmaVM::Cast(instance)->GetMicroJobQueue(); + if (!thread->HasPendingException()) { + job::MicroJobQueue::ExecutePendingJob(thread, microJobQueue); + } +} + +/* + * @tc.name: ThenResolve + * @tc.desc: Testing the Then() function with the Resolve() function + * @tc.type: FUNC + */ +TEST_F(BuiltinsPromiseTest, ThenResolve) +{ + auto env = EcmaVM::Cast(instance)->GetGlobalEnv(); + auto factory = EcmaVM::Cast(instance)->GetFactory(); + + JSHandle promise = JSHandle::Cast(env->GetPromiseFunction()); + JSHandle paramMsg = JSHandle::Cast(factory->NewFromCanBeCompressString("resolve")); + + /** + * @tc.steps: step1. var p1 = Promise.resolve("resolve") + */ + auto ecmaRuntimeCallInfo1 = TestHelper::CreateEcmaRuntimeCallInfo(thread, JSTaggedValue(*promise), 6); + ecmaRuntimeCallInfo1->SetFunction(promise.GetTaggedValue()); + ecmaRuntimeCallInfo1->SetThis(promise.GetTaggedValue()); + ecmaRuntimeCallInfo1->SetCallArg(0, paramMsg.GetTaggedValue()); + + [[maybe_unused]] auto prevReject = TestHelper::SetupFrame(thread, ecmaRuntimeCallInfo1.get()); + JSTaggedValue result = BuiltinsPromise::Resolve(ecmaRuntimeCallInfo1.get()); + JSHandle resolvePromise(thread, result); + EXPECT_EQ(JSTaggedValue::SameValue(resolvePromise->GetPromiseState(), + JSTaggedValue(static_cast(PromiseStatus::FULFILLED))), + true); + EXPECT_EQ(JSTaggedValue::SameValue(resolvePromise->GetPromiseResult(), paramMsg.GetTaggedValue()), true); + + /** + * @tc.steps: step2. p1 invokes then() + */ + JSHandle testPromiseThenOnResolved = + factory->NewJSFunction(env, reinterpret_cast(TestPromiseThenOnResolved)); + auto ecmaRuntimeCallInfo2 = TestHelper::CreateEcmaRuntimeCallInfo(thread, resolvePromise.GetTaggedValue(), 8); + ecmaRuntimeCallInfo2->SetFunction(resolvePromise.GetTaggedValue()); + ecmaRuntimeCallInfo2->SetThis(resolvePromise.GetTaggedValue()); + ecmaRuntimeCallInfo2->SetCallArg(0, testPromiseThenOnResolved.GetTaggedValue()); + ecmaRuntimeCallInfo2->SetCallArg(1, JSTaggedValue::Undefined()); + + [[maybe_unused]] auto prevThen = TestHelper::SetupFrame(thread, ecmaRuntimeCallInfo2.get()); + JSTaggedValue thenResult = BuiltinsPromise::Then(ecmaRuntimeCallInfo2.get()); + JSHandle thenPromise(thread, thenResult); + + EXPECT_EQ(JSTaggedValue::SameValue(thenPromise->GetPromiseState(), + JSTaggedValue(static_cast(PromiseStatus::PENDING))), + true); + EXPECT_EQ(thenPromise->GetPromiseResult().IsUndefined(), true); + + /** + * @tc.steps: step3. execute promise queue + */ + auto microJobQueue = EcmaVM::Cast(instance)->GetMicroJobQueue(); + if (!thread->HasPendingException()) { + job::MicroJobQueue::ExecutePendingJob(thread, microJobQueue); + } +} + +/* + * @tc.name: ThenReject + * @tc.desc: Testing the Then() function with the Reject() function + * @tc.type: FUNC + */ +TEST_F(BuiltinsPromiseTest, ThenReject) +{ + auto env = EcmaVM::Cast(instance)->GetGlobalEnv(); + auto factory = EcmaVM::Cast(instance)->GetFactory(); + + JSHandle promise = JSHandle::Cast(env->GetPromiseFunction()); + JSHandle paramMsg = JSHandle::Cast(factory->NewFromCanBeCompressString("reject")); + + /** + * @tc.steps: step1. var p1 = Promise.Reject(5) + */ + auto ecmaRuntimeCallInfo1 = TestHelper::CreateEcmaRuntimeCallInfo(thread, JSTaggedValue(*promise), 6); + ecmaRuntimeCallInfo1->SetFunction(promise.GetTaggedValue()); + ecmaRuntimeCallInfo1->SetThis(promise.GetTaggedValue()); + ecmaRuntimeCallInfo1->SetCallArg(0, paramMsg.GetTaggedValue()); + + [[maybe_unused]] auto prevReject = TestHelper::SetupFrame(thread, ecmaRuntimeCallInfo1.get()); + JSTaggedValue result = BuiltinsPromise::Reject(ecmaRuntimeCallInfo1.get()); + JSHandle rejectPromise(thread, result); + EXPECT_EQ(JSTaggedValue::SameValue(rejectPromise->GetPromiseState(), + JSTaggedValue(static_cast(PromiseStatus::REJECTED))), + true); + EXPECT_EQ(JSTaggedValue::SameValue(rejectPromise->GetPromiseResult(), paramMsg.GetTaggedValue()), true); + + /** + * @tc.steps: step1. p1 invokes then() + */ + JSHandle testPromiseThenOnRejected = + factory->NewJSFunction(env, reinterpret_cast(TestPromiseThenOnRejected)); + auto ecmaRuntimeCallInfo2 = TestHelper::CreateEcmaRuntimeCallInfo(thread, rejectPromise.GetTaggedValue(), 8); + ecmaRuntimeCallInfo2->SetFunction(rejectPromise.GetTaggedValue()); + ecmaRuntimeCallInfo2->SetThis(rejectPromise.GetTaggedValue()); + ecmaRuntimeCallInfo2->SetCallArg(0, testPromiseThenOnRejected.GetTaggedValue()); + ecmaRuntimeCallInfo2->SetCallArg(1, testPromiseThenOnRejected.GetTaggedValue()); + + [[maybe_unused]] auto prevThen = TestHelper::SetupFrame(thread, ecmaRuntimeCallInfo2.get()); + JSTaggedValue thenResult = BuiltinsPromise::Then(ecmaRuntimeCallInfo2.get()); + JSHandle thenPromise(thread, thenResult); + + EXPECT_EQ(JSTaggedValue::SameValue(thenPromise->GetPromiseState(), + JSTaggedValue(static_cast(PromiseStatus::PENDING))), + true); + EXPECT_EQ(thenPromise->GetPromiseResult().IsUndefined(), true); + /** + * @tc.steps: step3. execute promise queue + */ + auto microJobQueue = EcmaVM::Cast(instance)->GetMicroJobQueue(); + if (!thread->HasPendingException()) { + job::MicroJobQueue::ExecutePendingJob(thread, microJobQueue); + } +} +} // namespace panda::test diff --git a/tests/runtime/builtins/builtins_proxy_test.cpp b/tests/runtime/builtins/builtins_proxy_test.cpp new file mode 100644 index 000000000..b721cec31 --- /dev/null +++ b/tests/runtime/builtins/builtins_proxy_test.cpp @@ -0,0 +1,138 @@ +/* + * Copyright (c) 2021-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 "plugins/ecmascript/runtime/builtins/builtins_proxy.h" +#include "plugins/ecmascript/runtime/ecma_runtime_call_info.h" +#include "plugins/ecmascript/runtime/ecma_string.h" +#include "plugins/ecmascript/runtime/ecma_vm.h" +#include "plugins/ecmascript/runtime/global_env.h" +#include "plugins/ecmascript/runtime/js_array.h" +#include "plugins/ecmascript/runtime/js_handle.h" +#include "plugins/ecmascript/runtime/js_tagged_value-inl.h" +#include "plugins/ecmascript/runtime/js_thread.h" +#include "plugins/ecmascript/runtime/object_factory.h" +#include "plugins/ecmascript/tests/runtime/common/test_helper.h" +#include "file_items.h" +#include "utils/bit_utils.h" + +using namespace panda::ecmascript; +using namespace panda::ecmascript::builtins; + +namespace panda::test { +class BuiltinsProxyTest : public testing::Test { +public: + static void SetUpTestCase() + { + GTEST_LOG_(INFO) << "SetUpTestCase"; + } + + static void TearDownTestCase() + { + GTEST_LOG_(INFO) << "TearDownCase"; + } + + void SetUp() override + { + TestHelper::CreateEcmaVMWithScope(instance, thread, scope); + } + + void TearDown() override + { + TestHelper::DestroyEcmaVMWithScope(instance, scope); + } + + PandaVM *instance {nullptr}; + EcmaHandleScope *scope {nullptr}; + JSThread *thread {nullptr}; +}; + +JSHandle BuiltinsTestProxyCreate(JSThread *thread) +{ + EcmaVM *ecmaVM = thread->GetEcmaVM(); + JSHandle globalEnv = ecmaVM->GetGlobalEnv(); + JSHandle objFun(globalEnv->GetObjectFunction()); + ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); + + JSHandle obj = factory->NewJSObjectByConstructor(JSHandle(objFun), objFun); + return obj; +} + +// 26.2.1.1 Proxy( [ value ] ) +TEST_F(BuiltinsProxyTest, ProxyConstructor) +{ + JSHandle target = BuiltinsTestProxyCreate(thread); + JSHandle handler = BuiltinsTestProxyCreate(thread); + + auto ecmaRuntimeCallInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread, JSTaggedValue::Null(), 8); + ecmaRuntimeCallInfo->SetFunction(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetThis(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetCallArg(0, target.GetTaggedValue()); + ecmaRuntimeCallInfo->SetCallArg(1, handler.GetTaggedValue()); + + [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread, ecmaRuntimeCallInfo.get()); + JSTaggedValue result = BuiltinsProxy::ProxyConstructor(ecmaRuntimeCallInfo.get()); + ASSERT_TRUE(result.IsECMAObject()); + JSHandle resultHandle(thread, result); + EXPECT_TRUE(resultHandle->IsJSProxy()); +} + +// 26.2.2.1 Proxy.revocable ( target, handler ) +TEST_F(BuiltinsProxyTest, Revocable) +{ + JSHandle target = BuiltinsTestProxyCreate(thread); + JSHandle handler = BuiltinsTestProxyCreate(thread); + + JSHandle globalEnv = thread->GetEcmaVM()->GetGlobalEnv(); + JSHandle proxyFun(globalEnv->GetProxyFunction()); + JSHandle key(thread->GetEcmaVM()->GetFactory()->NewFromCanBeCompressString("prop")); + PropertyDescriptor desc(thread); + desc.SetWritable(false); + JSObject::DefineOwnProperty(thread, handler, key, desc); + + auto ecmaRuntimeCallInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread, JSTaggedValue::Undefined(), 8); + ecmaRuntimeCallInfo->SetFunction(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetThis(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetCallArg(0, target.GetTaggedValue()); + ecmaRuntimeCallInfo->SetCallArg(1, handler.GetTaggedValue()); + ecmaRuntimeCallInfo->SetNewTarget(JSTaggedValue(*proxyFun)); + + [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread, ecmaRuntimeCallInfo.get()); + JSTaggedValue result = BuiltinsProxy::Revocable(ecmaRuntimeCallInfo.get()); + ASSERT_TRUE(result.IsECMAObject()); + JSHandle resultHandle(thread, result); + + auto globalConst = thread->GlobalConstants(); + JSHandle proxyKey = globalConst->GetHandledProxyString(); + JSHandle revokeKey = globalConst->GetHandledRevokeString(); + + JSHandle keys = JSObject::GetOwnPropertyKeys(thread, resultHandle); + bool pflag = false; + bool rflag = false; + for (uint32_t i = 0; i < keys->GetLength(); i++) { + if (JSTaggedValue::SameValue(keys->Get(i), proxyKey.GetTaggedValue())) { + pflag = true; + } + if (JSTaggedValue::SameValue(keys->Get(i), revokeKey.GetTaggedValue())) { + rflag = true; + } + } + EXPECT_TRUE(pflag); + EXPECT_TRUE(rflag); + + PropertyDescriptor descRes(thread); + JSObject::GetOwnProperty(thread, resultHandle, revokeKey, descRes); + EXPECT_TRUE(descRes.GetValue()->IsProxyRevocFunction()); +} +} // namespace panda::test diff --git a/tests/runtime/builtins/builtins_reflect_test.cpp b/tests/runtime/builtins/builtins_reflect_test.cpp new file mode 100644 index 000000000..e46255cae --- /dev/null +++ b/tests/runtime/builtins/builtins_reflect_test.cpp @@ -0,0 +1,511 @@ +/* + * Copyright (c) 2021-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 "plugins/ecmascript/runtime/builtins/builtins_reflect.h" +#include "plugins/ecmascript/runtime/ecma_runtime_call_info.h" +#include "plugins/ecmascript/runtime/ecma_string.h" +#include "plugins/ecmascript/runtime/ecma_vm.h" +#include "plugins/ecmascript/runtime/global_env.h" +#include "plugins/ecmascript/runtime/js_array.h" +#include "plugins/ecmascript/runtime/js_function.h" +#include "plugins/ecmascript/runtime/js_object-inl.h" +#include "plugins/ecmascript/runtime/js_primitive_ref.h" +#include "plugins/ecmascript/runtime/tagged_array-inl.h" + +#include "plugins/ecmascript/runtime/object_factory.h" +#include "plugins/ecmascript/tests/runtime/common/test_helper.h" + +using namespace panda::ecmascript; +using namespace panda::ecmascript::builtins; +using BuiltinsBase = panda::ecmascript::base::BuiltinsBase; +using JSArray = panda::ecmascript::JSArray; + +namespace panda::test { +class BuiltinsReflectTest : public testing::Test { +public: + static void SetUpTestCase() + { + GTEST_LOG_(INFO) << "SetUpTestCase"; + } + + static void TearDownTestCase() + { + GTEST_LOG_(INFO) << "TearDownCase"; + } + + void SetUp() override + { + TestHelper::CreateEcmaVMWithScope(instance, thread, scope); + } + + void TearDown() override + { + TestHelper::DestroyEcmaVMWithScope(instance, scope); + } + + PandaVM *instance {nullptr}; + EcmaHandleScope *scope {nullptr}; + JSThread *thread {nullptr}; +}; + +// use for create a JSObject of test +static JSHandle TestObjectCreate(JSThread *thread) +{ + JSHandle env = thread->GetEcmaVM()->GetGlobalEnv(); + return JSHandle::Cast(env->GetObjectFunction()); +} + +// native function for test Reflect.apply +JSTaggedValue TestReflectApply(EcmaRuntimeCallInfo *argv) +{ + auto thread = argv->GetThread(); + ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); + + int result = 0; + for (uint32_t index = 0; index < argv->GetArgsNumber(); ++index) { + result += BuiltinsBase::GetCallArg(argv, index).GetTaggedValue().GetInt(); + } + JSHandle thisValue = BuiltinsBase::GetThis(argv); + + JSTaggedValue testA = + JSObject::GetProperty(thread, thisValue, + JSHandle(factory->NewFromCanBeCompressString("test_reflect_apply_a"))) + .GetValue() + .GetTaggedValue(); + JSTaggedValue testB = + JSObject::GetProperty(thread, thisValue, + JSHandle(factory->NewFromCanBeCompressString("test_reflect_apply_b"))) + .GetValue() + .GetTaggedValue(); + + result = result + testA.GetInt() + testB.GetInt(); + return BuiltinsBase::GetTaggedInt(result); +} + +// Reflect.apply (target, thisArgument, argumentsList) +TEST_F(BuiltinsReflectTest, ReflectApply) +{ + JSHandle env = thread->GetEcmaVM()->GetGlobalEnv(); + + ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); + + // target + JSHandle target = factory->NewJSFunction(env, reinterpret_cast(TestReflectApply)); + // thisArgument + JSHandle thisArgument = + factory->NewJSObjectByConstructor(TestObjectCreate(thread), JSHandle(TestObjectCreate(thread))); + JSObject::SetProperty(thread, JSHandle(thisArgument), + JSHandle(factory->NewFromCanBeCompressString("test_reflect_apply_a")), + JSHandle(thread, JSTaggedValue(11))); + JSObject::SetProperty(thread, JSHandle(thisArgument), + JSHandle(factory->NewFromCanBeCompressString("test_reflect_apply_b")), + JSHandle(thread, JSTaggedValue(22))); + // argumentsList + JSHandle argumentsList(JSArray::ArrayCreate(thread, JSTaggedNumber(2))); + PropertyDescriptor desc(thread, JSHandle(thread, JSTaggedValue(33))); + JSArray::DefineOwnProperty(thread, argumentsList, JSHandle(thread, JSTaggedValue(0)), desc); + + PropertyDescriptor desc1(thread, JSHandle(thread, JSTaggedValue(44))); + JSArray::DefineOwnProperty(thread, argumentsList, JSHandle(thread, JSTaggedValue(1)), desc1); + + auto ecmaRuntimeCallInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread, JSTaggedValue::Undefined(), 10); + ecmaRuntimeCallInfo->SetFunction(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetThis(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetCallArg(0, target.GetTaggedValue()); + ecmaRuntimeCallInfo->SetCallArg(1, JSTaggedValue(*thisArgument)); + ecmaRuntimeCallInfo->SetCallArg(2, JSTaggedValue(*argumentsList)); + + [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread, ecmaRuntimeCallInfo.get()); + JSTaggedValue result = BuiltinsReflect::ReflectApply(ecmaRuntimeCallInfo.get()); + ASSERT_EQ(result.GetRawData(), JSTaggedValue(110).GetRawData()); + + JSObject::DeleteProperty(thread, (thisArgument), + JSHandle(factory->NewFromCanBeCompressString("test_reflect_apply_a"))); + JSObject::DeleteProperty(thread, (thisArgument), + JSHandle(factory->NewFromCanBeCompressString("test_reflect_apply_b"))); +} + +// Reflect.construct (target, argumentsList [ , newTarget]) +TEST_F(BuiltinsReflectTest, ReflectConstruct) +{ + JSHandle env = thread->GetEcmaVM()->GetGlobalEnv(); + + ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); + + // target + JSHandle target = JSHandle::Cast(env->GetStringFunction()); + // argumentsList + JSHandle argumentsList(JSArray::ArrayCreate(thread, JSTaggedNumber(1))); + PropertyDescriptor desc(thread, + JSHandle::Cast(factory->NewFromCanBeCompressString("ReflectConstruct"))); + JSArray::DefineOwnProperty(thread, argumentsList, JSHandle(thread, JSTaggedValue(0)), desc); + + auto ecmaRuntimeCallInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread, JSTaggedValue::Undefined(), 8); + ecmaRuntimeCallInfo->SetFunction(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetThis(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetCallArg(0, target.GetTaggedValue()); + ecmaRuntimeCallInfo->SetCallArg(1, JSTaggedValue(*argumentsList)); + + [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread, ecmaRuntimeCallInfo.get()); + JSTaggedValue result = BuiltinsReflect::ReflectConstruct(ecmaRuntimeCallInfo.get()); + + ASSERT_TRUE(result.IsECMAObject()); + JSHandle taggedResult(thread, result); + JSHandle refResult = JSHandle::Cast(taggedResult); + JSHandle ruler = factory->NewFromCanBeCompressString("ReflectConstruct"); + ASSERT_EQ(EcmaString::Cast(refResult->GetValue().GetTaggedObject())->Compare(*ruler), 0); +} + +// Reflect.defineProperty (target, propertyKey, attributes) +TEST_F(BuiltinsReflectTest, ReflectDefineProperty) +{ + ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); + + // target + JSHandle target = + factory->NewJSObjectByConstructor(TestObjectCreate(thread), JSHandle(TestObjectCreate(thread))); + // propertyKey + JSHandle key(factory->NewFromCanBeCompressString("test_reflect_define_property")); + // attributes + JSHandle attributes = + factory->NewJSObjectByConstructor(TestObjectCreate(thread), JSHandle(TestObjectCreate(thread))); + // attributes value + auto globalConst = thread->GlobalConstants(); + JSHandle valueKey = globalConst->GetHandledValueString(); + JSHandle value(thread, JSTaggedValue(100)); + JSObject::SetProperty(thread, JSHandle(attributes), valueKey, value); + // attributes writable + JSHandle writableKey = globalConst->GetHandledWritableString(); + JSHandle writable(thread, JSTaggedValue::True()); + JSObject::SetProperty(thread, JSHandle(attributes), writableKey, writable); + // attributes enumerable + JSHandle enumerableKey = globalConst->GetHandledEnumerableString(); + JSHandle enumerable(thread, JSTaggedValue::False()); + JSObject::SetProperty(thread, JSHandle(attributes), enumerableKey, enumerable); + // attributes configurable + JSHandle configurableKey = globalConst->GetHandledConfigurableString(); + JSHandle configurable(thread, JSTaggedValue::True()); + JSObject::SetProperty(thread, JSHandle(attributes), configurableKey, configurable); + + auto ecmaRuntimeCallInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread, JSTaggedValue::Undefined(), 10); + ecmaRuntimeCallInfo->SetFunction(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetThis(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetCallArg(0, target.GetTaggedValue()); + ecmaRuntimeCallInfo->SetCallArg(1, key.GetTaggedValue()); + ecmaRuntimeCallInfo->SetCallArg(2, JSTaggedValue(*attributes)); + + [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread, ecmaRuntimeCallInfo.get()); + JSTaggedValue result = BuiltinsReflect::ReflectDefineProperty(ecmaRuntimeCallInfo.get()); + ASSERT_EQ(result.GetRawData(), JSTaggedValue::True().GetRawData()); + + PropertyDescriptor descRuler(thread); + JSObject::GetOwnProperty(thread, target, key, descRuler); + ASSERT_EQ(descRuler.GetValue()->GetInt(), 100); + ASSERT_EQ(descRuler.IsWritable(), true); + ASSERT_EQ(descRuler.IsEnumerable(), false); + ASSERT_EQ(descRuler.IsConfigurable(), true); +} + +// Reflect.deleteProperty (target, propertyKey) +TEST_F(BuiltinsReflectTest, ReflectDeleteProperty) +{ + ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); + + // target + JSHandle target = + factory->NewJSObjectByConstructor(TestObjectCreate(thread), JSHandle(TestObjectCreate(thread))); + // propertyKey + JSHandle key(factory->NewFromCanBeCompressString("test_reflect_delete_property")); + JSHandle value(thread, JSTaggedValue(101)); + JSObject::SetProperty(thread, JSHandle(target), key, value); + + PropertyDescriptor desc(thread); + ASSERT_EQ(JSObject::GetOwnProperty(thread, target, key, desc), true); + + auto ecmaRuntimeCallInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread, JSTaggedValue::Undefined(), 8); + ecmaRuntimeCallInfo->SetFunction(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetThis(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetCallArg(0, target.GetTaggedValue()); + ecmaRuntimeCallInfo->SetCallArg(1, key.GetTaggedValue()); + + [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread, ecmaRuntimeCallInfo.get()); + JSTaggedValue result = BuiltinsReflect::ReflectDeleteProperty(ecmaRuntimeCallInfo.get()); + ASSERT_EQ(result.GetRawData(), JSTaggedValue::True().GetRawData()); + ASSERT_EQ(JSObject::GetOwnProperty(thread, target, key, desc), false); +} + +// Reflect.get (target, propertyKey [ , receiver]) +TEST_F(BuiltinsReflectTest, ReflectGet) +{ + ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); + + // target + JSHandle target = + factory->NewJSObjectByConstructor(TestObjectCreate(thread), JSHandle(TestObjectCreate(thread))); + // propertyKey + JSHandle key(factory->NewFromCanBeCompressString("test_reflect_get")); + // set property + JSHandle value(thread, JSTaggedValue(101.5)); + JSObject::SetProperty(thread, JSHandle(target), key, value); + + auto ecmaRuntimeCallInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread, JSTaggedValue::Undefined(), 8); + ecmaRuntimeCallInfo->SetFunction(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetThis(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetCallArg(0, target.GetTaggedValue()); + ecmaRuntimeCallInfo->SetCallArg(1, key.GetTaggedValue()); + + [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread, ecmaRuntimeCallInfo.get()); + JSTaggedValue result = BuiltinsReflect::ReflectGet(ecmaRuntimeCallInfo.get()); + + JSHandle resultValue(thread, result); + ASSERT_EQ(resultValue->GetDouble(), 101.5); +} + +// Reflect.getOwnPropertyDescriptor ( target, propertyKey ) +TEST_F(BuiltinsReflectTest, ReflectGetOwnPropertyDescriptor) +{ + ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); + + // target + JSHandle target = + factory->NewJSObjectByConstructor(TestObjectCreate(thread), JSHandle(TestObjectCreate(thread))); + // propertyKey + JSHandle key(factory->NewFromCanBeCompressString("test_reflect_get_property_descriptor")); + PropertyDescriptor desc(thread, JSHandle(thread, JSTaggedValue(102)), true, false, true); + ASSERT_EQ(JSTaggedValue::DefinePropertyOrThrow(thread, JSHandle(target), key, desc), true); + + auto ecmaRuntimeCallInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread, JSTaggedValue::Undefined(), 8); + ecmaRuntimeCallInfo->SetFunction(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetThis(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetCallArg(0, target.GetTaggedValue()); + ecmaRuntimeCallInfo->SetCallArg(1, key.GetTaggedValue()); + + [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread, ecmaRuntimeCallInfo.get()); + JSTaggedValue result = BuiltinsReflect::ReflectGetOwnPropertyDescriptor(ecmaRuntimeCallInfo.get()); + + ASSERT_TRUE(result.IsECMAObject()); + + JSHandle resultObj(thread, result); + // test value + auto globalConst = thread->GlobalConstants(); + JSHandle valueKey = globalConst->GetHandledValueString(); + JSHandle resultValue = JSObject::GetProperty(thread, resultObj, valueKey).GetValue(); + ASSERT_EQ(resultValue->GetInt(), 102); + // test writable + JSHandle writableKey = globalConst->GetHandledWritableString(); + JSHandle resultWritable = JSObject::GetProperty(thread, resultObj, writableKey).GetValue(); + ASSERT_EQ(resultWritable->ToBoolean(), true); + // test enumerable + JSHandle enumerableKey = globalConst->GetHandledEnumerableString(); + JSHandle resultEnumerable = JSObject::GetProperty(thread, resultObj, enumerableKey).GetValue(); + ASSERT_EQ(resultEnumerable->ToBoolean(), false); + // test configurable + JSHandle configurableKey = globalConst->GetHandledConfigurableString(); + JSHandle resultConfigurable = JSObject::GetProperty(thread, resultObj, configurableKey).GetValue(); + ASSERT_EQ(resultConfigurable->ToBoolean(), true); +} + +// Reflect.getPrototypeOf (target) +TEST_F(BuiltinsReflectTest, ReflectGetPrototypeOf) +{ + ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); + + JSHandle target = + factory->NewJSObjectByConstructor(TestObjectCreate(thread), JSHandle(TestObjectCreate(thread))); + JSHandle proto = + factory->NewJSObjectByConstructor(TestObjectCreate(thread), JSHandle(TestObjectCreate(thread))); + + ASSERT_EQ(JSObject::SetPrototype(thread, target, JSHandle(proto)), true); + + auto ecmaRuntimeCallInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread, JSTaggedValue::Undefined(), 6); + ecmaRuntimeCallInfo->SetFunction(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetThis(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetCallArg(0, target.GetTaggedValue()); + + [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread, ecmaRuntimeCallInfo.get()); + JSTaggedValue result = BuiltinsReflect::ReflectGetPrototypeOf(ecmaRuntimeCallInfo.get()); + ASSERT_TRUE(result.IsECMAObject()); + JSHandle resultObj(thread, JSTaggedValue(reinterpret_cast(result.GetRawData()))); + ASSERT_EQ(JSTaggedValue::SameValue(resultObj.GetTaggedValue(), proto.GetTaggedValue()), true); +} + +// Reflect.has (target, propertyKey) +TEST_F(BuiltinsReflectTest, ReflectHas) +{ + ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); + + // target + JSHandle target = + factory->NewJSObjectByConstructor(TestObjectCreate(thread), JSHandle(TestObjectCreate(thread))); + // propertyKey + JSHandle key(factory->NewFromCanBeCompressString("test_reflect_has")); + JSHandle value(thread, JSTaggedValue(103)); + ASSERT_EQ(JSObject::SetProperty(thread, JSHandle(target), key, value), true); + + auto ecmaRuntimeCallInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread, JSTaggedValue::Undefined(), 8); + ecmaRuntimeCallInfo->SetFunction(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetThis(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetCallArg(0, target.GetTaggedValue()); + ecmaRuntimeCallInfo->SetCallArg(1, key.GetTaggedValue()); + + [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread, ecmaRuntimeCallInfo.get()); + JSTaggedValue result = BuiltinsReflect::ReflectHas(ecmaRuntimeCallInfo.get()); + + ASSERT_EQ(result.GetRawData(), JSTaggedValue::True().GetRawData()); +} + +// Reflect.isExtensible (target) +TEST_F(BuiltinsReflectTest, ReflectIsExtensible) +{ + ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); + + // target + JSHandle target = + factory->NewJSObjectByConstructor(TestObjectCreate(thread), JSHandle(TestObjectCreate(thread))); + target->GetJSHClass()->SetExtensible(false); + + auto ecmaRuntimeCallInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread, JSTaggedValue::Undefined(), 6); + ecmaRuntimeCallInfo->SetFunction(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetThis(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetCallArg(0, target.GetTaggedValue()); + + [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread, ecmaRuntimeCallInfo.get()); + JSTaggedValue result = BuiltinsReflect::ReflectIsExtensible(ecmaRuntimeCallInfo.get()); + + ASSERT_EQ(result.GetRawData(), JSTaggedValue::False().GetRawData()); +} + +// Reflect.ownKeys (target) +TEST_F(BuiltinsReflectTest, ReflectOwnKeys) +{ + ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); + + // target + JSHandle target = + factory->NewJSObjectByConstructor(TestObjectCreate(thread), JSHandle(TestObjectCreate(thread))); + JSHandle key0(factory->NewFromCanBeCompressString("test_reflect_own_keys1")); + JSHandle value0(thread, JSTaggedValue(104)); + ASSERT_EQ(JSObject::SetProperty(thread, JSHandle(target), key0, value0), true); + JSHandle key1(factory->NewFromCanBeCompressString("test_reflect_own_keys2")); + JSHandle value1(thread, JSTaggedValue(105)); + ASSERT_EQ(JSObject::SetProperty(thread, JSHandle(target), key1, value1), true); + + auto ecmaRuntimeCallInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread, JSTaggedValue::Undefined(), 6); + ecmaRuntimeCallInfo->SetFunction(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetThis(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetCallArg(0, target.GetTaggedValue()); + + [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread, ecmaRuntimeCallInfo.get()); + JSTaggedValue result = BuiltinsReflect::ReflectOwnKeys(ecmaRuntimeCallInfo.get()); + + ASSERT_TRUE(result.IsECMAObject()); + JSHandle resultTaggedValue(thread, reinterpret_cast(result.GetRawData())); + JSHandle resultArray = JSHandle::Cast(resultTaggedValue); + // test length + JSHandle resultLengthKey = thread->GlobalConstants()->GetHandledLengthString(); + JSHandle resultLength = + JSObject::GetProperty(thread, JSHandle(resultArray), resultLengthKey).GetValue(); + ASSERT_EQ(resultLength->GetInt(), 2); + // test array[0] + JSHandle resultKey0(thread, JSTaggedValue(0)); + JSHandle resultValue0 = + JSObject::GetProperty(thread, JSHandle(resultArray), resultKey0).GetValue(); + ASSERT_EQ(reinterpret_cast(resultValue0.GetTaggedValue().GetTaggedObject()) + ->Compare(reinterpret_cast(key0.GetTaggedValue().GetTaggedObject())), + 0); + // test array[1] + JSHandle resultKey1(thread, JSTaggedValue(1)); + JSHandle resultValue1 = + JSObject::GetProperty(thread, JSHandle(resultArray), resultKey1).GetValue(); + ASSERT_EQ(reinterpret_cast(resultValue1.GetTaggedValue().GetTaggedObject()) + ->Compare(reinterpret_cast(key1.GetTaggedValue().GetTaggedObject())), + 0); +} + +// Reflect.preventExtensions (target) +TEST_F(BuiltinsReflectTest, ReflectPreventExtensions) +{ + ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); + + // target + JSHandle target = + factory->NewJSObjectByConstructor(TestObjectCreate(thread), JSHandle(TestObjectCreate(thread))); + target->GetJSHClass()->SetExtensible(true); + + auto ecmaRuntimeCallInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread, JSTaggedValue::Undefined(), 6); + ecmaRuntimeCallInfo->SetFunction(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetThis(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetCallArg(0, target.GetTaggedValue()); + + [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread, ecmaRuntimeCallInfo.get()); + JSTaggedValue result = BuiltinsReflect::ReflectPreventExtensions(ecmaRuntimeCallInfo.get()); + + ASSERT_EQ(result.GetRawData(), JSTaggedValue::True().GetRawData()); + ASSERT_EQ(target->IsExtensible(), false); +} + +// Reflect.set (target, propertyKey, V [ , receiver]) +TEST_F(BuiltinsReflectTest, ReflectSet) +{ + ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); + + // target + JSHandle target = + factory->NewJSObjectByConstructor(TestObjectCreate(thread), JSHandle(TestObjectCreate(thread))); + // propertyKey + JSHandle key(factory->NewFromCanBeCompressString("test_reflect_set")); + // value + JSHandle value(thread, JSTaggedValue(106)); + + auto ecmaRuntimeCallInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread, JSTaggedValue::Undefined(), 10); + ecmaRuntimeCallInfo->SetFunction(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetThis(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetCallArg(0, target.GetTaggedValue()); + ecmaRuntimeCallInfo->SetCallArg(1, key.GetTaggedValue()); + ecmaRuntimeCallInfo->SetCallArg(2, value.GetTaggedValue()); + + [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread, ecmaRuntimeCallInfo.get()); + JSTaggedValue result = BuiltinsReflect::ReflectSet(ecmaRuntimeCallInfo.get()); + + ASSERT_EQ(result.GetRawData(), JSTaggedValue::True().GetRawData()); + + JSHandle ruler = JSObject::GetProperty(thread, JSHandle(target), key).GetValue(); + ASSERT_EQ(JSTaggedValue::ToInt32(thread, ruler), 106); +} + +// Reflect.setPrototypeOf (target, proto) +TEST_F(BuiltinsReflectTest, ReflectSetPrototypeOf) +{ + ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); + + JSHandle target = + factory->NewJSObjectByConstructor(TestObjectCreate(thread), JSHandle(TestObjectCreate(thread))); + JSHandle proto = + factory->NewJSObjectByConstructor(TestObjectCreate(thread), JSHandle(TestObjectCreate(thread))); + + auto ecmaRuntimeCallInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread, JSTaggedValue::Undefined(), 8); + ecmaRuntimeCallInfo->SetFunction(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetThis(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetCallArg(0, target.GetTaggedValue()); + ecmaRuntimeCallInfo->SetCallArg(1, proto.GetTaggedValue()); + + [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread, ecmaRuntimeCallInfo.get()); + JSTaggedValue result = BuiltinsReflect::ReflectSetPrototypeOf(ecmaRuntimeCallInfo.get()); + + ASSERT_EQ(result.GetRawData(), JSTaggedValue::True().GetRawData()); + JSHandle resultObj(thread, target->GetJSHClass()->GetPrototype()); + ASSERT_EQ(JSTaggedValue::SameValue(resultObj.GetTaggedValue(), proto.GetTaggedValue()), true); +} +} // namespace panda::test diff --git a/tests/runtime/builtins/builtins_regexp_test.cpp b/tests/runtime/builtins/builtins_regexp_test.cpp new file mode 100644 index 000000000..997ae260c --- /dev/null +++ b/tests/runtime/builtins/builtins_regexp_test.cpp @@ -0,0 +1,657 @@ +/* + * Copyright (c) 2021-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 "plugins/ecmascript/runtime/js_regexp.h" + +#include "plugins/ecmascript/runtime/builtins/builtins_regexp.h" +#include "plugins/ecmascript/runtime/ecma_runtime_call_info.h" +#include "plugins/ecmascript/runtime/ecma_string.h" +#include "plugins/ecmascript/runtime/ecma_vm.h" +#include "plugins/ecmascript/runtime/global_env.h" +#include "plugins/ecmascript/runtime/js_handle.h" +#include "plugins/ecmascript/runtime/js_hclass.h" +#include "plugins/ecmascript/runtime/js_object-inl.h" +#include "plugins/ecmascript/runtime/js_tagged_value.h" +#include "plugins/ecmascript/runtime/js_thread.h" +#include "plugins/ecmascript/runtime/regexp/regexp_parser_cache.h" + +#include "plugins/ecmascript/runtime/object_factory.h" +#include "plugins/ecmascript/tests/runtime/common/test_helper.h" +#include "utils/bit_utils.h" + +using namespace panda::ecmascript; +using namespace panda::ecmascript::builtins; + +namespace panda::test { +class BuiltinsRegExpTest : public testing::Test { +public: + static void SetUpTestCase() + { + GTEST_LOG_(INFO) << "SetUpTestCase"; + } + + static void TearDownTestCase() + { + GTEST_LOG_(INFO) << "TearDownCase"; + } + + void SetUp() override + { + TestHelper::CreateEcmaVMWithScope(instance, thread, scope); + } + + void TearDown() override + { + TestHelper::DestroyEcmaVMWithScope(instance, scope); + } + + PandaVM *instance {nullptr}; + EcmaHandleScope *scope {nullptr}; + JSThread *thread {nullptr}; +}; + +JSTaggedValue CreateRegExpObjByPatternAndFlags(JSThread *thread, const JSHandle &pattern, + const JSHandle &flags) +{ + JSHandle env = thread->GetEcmaVM()->GetGlobalEnv(); + JSHandle regexp(env->GetRegExpFunction()); + JSHandle globalObject(thread, env->GetGlobalObject()); + // make dyn_runtime_call_info + // 8 : test case + auto ecmaRuntimeCallInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread, JSTaggedValue(*regexp), 8); + ecmaRuntimeCallInfo->SetFunction(regexp.GetTaggedValue()); + ecmaRuntimeCallInfo->SetThis(globalObject.GetTaggedValue()); + ecmaRuntimeCallInfo->SetCallArg(0, pattern.GetTaggedValue()); + ecmaRuntimeCallInfo->SetCallArg(1, flags.GetTaggedValue()); + + [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread, ecmaRuntimeCallInfo.get()); + // invoke RegExpConstructor method + JSTaggedValue result = BuiltinsRegExp::RegExpConstructor(ecmaRuntimeCallInfo.get()); + return result; +} + +TEST_F(BuiltinsRegExpTest, RegExpConstructor1) +{ + // invoke RegExpConstructor method + JSHandle pattern = thread->GetEcmaVM()->GetFactory()->NewFromCanBeCompressString("\\w+"); + JSHandle flags = thread->GetEcmaVM()->GetFactory()->NewFromCanBeCompressString("i"); + JSTaggedValue result = CreateRegExpObjByPatternAndFlags(thread, pattern, flags); + + // ASSERT IsRegExp() + JSHandle regexpObject(thread, result); + ASSERT_TRUE(JSObject::IsRegExp(thread, regexpObject)); + + JSHandle jsRegexp(thread, JSRegExp::Cast(regexpObject->GetTaggedObject())); + JSHandle originalSource(thread, jsRegexp->GetOriginalSource()); + uint8_t flagsBits = static_cast(jsRegexp->GetOriginalFlags().GetInt()); + JSHandle originalFlags(thread, BuiltinsRegExp::FlagsBitsToString(thread, flagsBits)); + ASSERT_EQ(static_cast(originalSource->GetTaggedObject())->Compare(*pattern), 0); + ASSERT_EQ(static_cast(originalFlags->GetTaggedObject())->Compare(*flags), 0); +} + +TEST_F(BuiltinsRegExpTest, RegExpConstructor2) +{ + // invoke RegExpConstructor method + JSHandle pattern = thread->GetEcmaVM()->GetFactory()->NewFromCanBeCompressString("\\w+"); + JSHandle flags = thread->GetEcmaVM()->GetFactory()->NewFromCanBeCompressString("i"); + JSTaggedValue result1 = CreateRegExpObjByPatternAndFlags(thread, pattern, flags); + JSHandle value(thread, reinterpret_cast(result1.GetRawData())); + + JSHandle env = thread->GetEcmaVM()->GetGlobalEnv(); + JSHandle regexp(env->GetRegExpFunction()); + JSHandle globalObject(thread, env->GetGlobalObject()); + + auto ecmaRuntimeCallInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread, JSTaggedValue(*regexp), 8); + ecmaRuntimeCallInfo->SetFunction(regexp.GetTaggedValue()); + ecmaRuntimeCallInfo->SetThis(globalObject.GetTaggedValue()); + ecmaRuntimeCallInfo->SetCallArg(0, value.GetTaggedValue()); + ecmaRuntimeCallInfo->SetCallArg(1, JSTaggedValue::Undefined()); + + [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread, ecmaRuntimeCallInfo.get()); + // invoke RegExpConstructor method + JSTaggedValue result2 = BuiltinsRegExp::RegExpConstructor(ecmaRuntimeCallInfo.get()); + + // ASSERT IsRegExp() + JSHandle regexpObject(thread, result2); + ASSERT_TRUE(JSObject::IsRegExp(thread, regexpObject)); + + JSHandle jsRegexp(thread, JSRegExp::Cast(regexpObject->GetTaggedObject())); + JSHandle originalSource(thread, jsRegexp->GetOriginalSource()); + uint8_t flagsBits = static_cast(jsRegexp->GetOriginalFlags().GetInt()); + JSHandle originalFlags(thread, BuiltinsRegExp::FlagsBitsToString(thread, flagsBits)); + ASSERT_EQ(static_cast(originalSource->GetTaggedObject())->Compare(*pattern), 0); + ASSERT_EQ(static_cast(originalFlags->GetTaggedObject())->Compare(*flags), 0); +} + +TEST_F(BuiltinsRegExpTest, RegExpConstructor3) +{ + // invoke RegExpConstructor method + JSHandle pattern1 = thread->GetEcmaVM()->GetFactory()->NewFromCanBeCompressString("\\w+"); + JSHandle flags1 = thread->GetEcmaVM()->GetFactory()->NewFromCanBeCompressString("i"); + JSTaggedValue result1 = CreateRegExpObjByPatternAndFlags(thread, pattern1, flags1); + JSHandle value(thread, reinterpret_cast(result1.GetRawData())); + + JSHandle env = thread->GetEcmaVM()->GetGlobalEnv(); + JSHandle regexp(env->GetRegExpFunction()); + JSHandle globalObject(thread, env->GetGlobalObject()); + JSHandle flags2 = thread->GetEcmaVM()->GetFactory()->NewFromCanBeCompressString("gi"); + + auto ecmaRuntimeCallInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread, JSTaggedValue(*regexp), 8); + ecmaRuntimeCallInfo->SetFunction(regexp.GetTaggedValue()); + ecmaRuntimeCallInfo->SetThis(globalObject.GetTaggedValue()); + ecmaRuntimeCallInfo->SetCallArg(0, value.GetTaggedValue()); + ecmaRuntimeCallInfo->SetCallArg(1, flags2.GetTaggedValue()); + + [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread, ecmaRuntimeCallInfo.get()); + // invoke RegExpConstructor method + JSTaggedValue result2 = BuiltinsRegExp::RegExpConstructor(ecmaRuntimeCallInfo.get()); + + // ASSERT IsRegExp() + JSHandle regexpObject(thread, result2); + ASSERT_TRUE(JSObject::IsRegExp(thread, regexpObject)); + + JSHandle jsRegexp(thread, JSRegExp::Cast(regexpObject->GetTaggedObject())); + JSHandle originalSource(thread, jsRegexp->GetOriginalSource()); + uint8_t flagsBits = static_cast(jsRegexp->GetOriginalFlags().GetInt()); + JSHandle originalFlags(thread, BuiltinsRegExp::FlagsBitsToString(thread, flagsBits)); + ASSERT_EQ(static_cast(originalSource->GetTaggedObject())->Compare(*pattern1), 0); + ASSERT_EQ(static_cast(originalFlags->GetTaggedObject())->Compare(*flags2), 0); +} + +TEST_F(BuiltinsRegExpTest, GetSource1) +{ + // invoke RegExpConstructor method + JSHandle pattern1 = thread->GetEcmaVM()->GetFactory()->NewFromCanBeCompressString(""); + JSHandle flags1 = thread->GetEcmaVM()->GetFactory()->NewFromCanBeCompressString("i"); + JSTaggedValue result1 = CreateRegExpObjByPatternAndFlags(thread, pattern1, flags1); + JSHandle result1Handle(thread, result1); + + // invoke GetSource method + JSHandle source( + thread, thread->GetEcmaVM()->GetFactory()->NewFromCanBeCompressString("source").GetTaggedValue()); + JSHandle sourceResult(JSObject::GetProperty(thread, result1Handle, source).GetValue()); + + JSHandle expect = thread->GetEcmaVM()->GetFactory()->NewFromCanBeCompressString("(?:)"); + ASSERT_EQ(static_cast(sourceResult->GetTaggedObject())->Compare(*expect), 0); +} + +TEST_F(BuiltinsRegExpTest, GetSource2) +{ + // invoke RegExpConstructor method + JSHandle pattern1 = thread->GetEcmaVM()->GetFactory()->NewFromCanBeCompressString("/w+"); + JSHandle flags1 = thread->GetEcmaVM()->GetFactory()->NewFromCanBeCompressString("i"); + JSTaggedValue result1 = CreateRegExpObjByPatternAndFlags(thread, pattern1, flags1); + JSHandle result1Handle(thread, result1); + + // invoke GetSource method + JSHandle source(thread->GetEcmaVM()->GetFactory()->NewFromCanBeCompressString("source")); + JSHandle sourceResult(JSObject::GetProperty(thread, result1Handle, source).GetValue()); + + JSHandle expect = thread->GetEcmaVM()->GetFactory()->NewFromCanBeCompressString("\\/w+"); + ASSERT_EQ(static_cast(sourceResult->GetTaggedObject())->Compare(*expect), 0); +} + +TEST_F(BuiltinsRegExpTest, Get) +{ + // invoke RegExpConstructor method + JSHandle pattern1 = thread->GetEcmaVM()->GetFactory()->NewFromCanBeCompressString("\\w+"); + JSHandle flags1 = thread->GetEcmaVM()->GetFactory()->NewFromCanBeCompressString("gimuy"); + JSTaggedValue result1 = CreateRegExpObjByPatternAndFlags(thread, pattern1, flags1); + JSHandle result1Handle(thread, result1); + + JSHandle global(thread->GetEcmaVM()->GetFactory()->NewFromCanBeCompressString("global")); + JSTaggedValue taggedGlobalResult = + JSTaggedValue(JSObject::GetProperty(thread, result1Handle, global).GetValue().GetTaggedValue()); + ASSERT_EQ(taggedGlobalResult.GetRawData(), JSTaggedValue::True().GetRawData()); + + JSHandle ignoreCase(thread->GetEcmaVM()->GetFactory()->NewFromCanBeCompressString("ignoreCase")); + JSTaggedValue taggedIgnoreCaseResult = + JSTaggedValue(JSObject::GetProperty(thread, result1Handle, ignoreCase).GetValue().GetTaggedValue()); + ASSERT_EQ(taggedIgnoreCaseResult.GetRawData(), JSTaggedValue::True().GetRawData()); + + JSHandle multiline(thread->GetEcmaVM()->GetFactory()->NewFromCanBeCompressString("multiline")); + JSTaggedValue taggedMultilineResult = + JSTaggedValue(JSObject::GetProperty(thread, result1Handle, multiline).GetValue().GetTaggedValue()); + ASSERT_EQ(taggedMultilineResult.GetRawData(), JSTaggedValue::True().GetRawData()); + + JSHandle sticky(thread->GetEcmaVM()->GetFactory()->NewFromCanBeCompressString("sticky")); + JSTaggedValue taggedStickyResult = + JSTaggedValue(JSObject::GetProperty(thread, result1Handle, sticky).GetValue().GetTaggedValue()); + ASSERT_EQ(taggedStickyResult.GetRawData(), JSTaggedValue::True().GetRawData()); + + JSHandle unicode(thread->GetEcmaVM()->GetFactory()->NewFromCanBeCompressString("unicode")); + JSTaggedValue taggedUnicodeResult = + JSTaggedValue(JSObject::GetProperty(thread, result1Handle, unicode).GetValue().GetTaggedValue()); + ASSERT_EQ(taggedUnicodeResult.GetRawData(), JSTaggedValue::True().GetRawData()); +} + +TEST_F(BuiltinsRegExpTest, GetFlags) +{ + // invoke RegExpConstructor method + JSHandle pattern1 = thread->GetEcmaVM()->GetFactory()->NewFromCanBeCompressString("\\w+"); + JSHandle flags1 = thread->GetEcmaVM()->GetFactory()->NewFromCanBeCompressString("imuyg"); + JSTaggedValue result1 = CreateRegExpObjByPatternAndFlags(thread, pattern1, flags1); + JSHandle result1Handle(thread, result1); + + // invoke GetFlags method + JSHandle flags(thread->GetEcmaVM()->GetFactory()->NewFromCanBeCompressString("flags")); + JSHandle flagsResult(JSObject::GetProperty(thread, result1Handle, flags).GetValue()); + + JSHandle expectResult = thread->GetEcmaVM()->GetFactory()->NewFromCanBeCompressString("gimuy"); + ASSERT_EQ(static_cast(flagsResult->GetTaggedObject())->Compare(*expectResult), 0); +} + +TEST_F(BuiltinsRegExpTest, toString) +{ + // invoke RegExpConstructor method + JSHandle pattern1 = thread->GetEcmaVM()->GetFactory()->NewFromCanBeCompressString("\\w+"); + JSHandle flags1 = thread->GetEcmaVM()->GetFactory()->NewFromCanBeCompressString("imuyg"); + JSTaggedValue result1 = CreateRegExpObjByPatternAndFlags(thread, pattern1, flags1); + JSHandle value(thread, reinterpret_cast(result1.GetRawData())); + + auto ecmaRuntimeCallInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread, JSTaggedValue::Undefined(), 4); + ecmaRuntimeCallInfo->SetFunction(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetThis(value.GetTaggedValue()); + + [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread, ecmaRuntimeCallInfo.get()); + // invoke ToString method + JSTaggedValue toStringResult = BuiltinsRegExp::ToString(ecmaRuntimeCallInfo.get()); + ASSERT_TRUE(toStringResult.IsString()); + JSHandle toStringResultHandle(thread, toStringResult); + JSHandle expectResult = thread->GetEcmaVM()->GetFactory()->NewFromCanBeCompressString("/\\w+/gimuy"); + ASSERT_EQ(static_cast(toStringResultHandle->GetTaggedObject())->Compare(*expectResult), 0); +} + +TEST_F(BuiltinsRegExpTest, Exec1) +{ + // invoke RegExpConstructor method + JSHandle pattern1 = + thread->GetEcmaVM()->GetFactory()->NewFromCanBeCompressString("quick\\s(brown).+?(jumps)"); + JSHandle flags1 = thread->GetEcmaVM()->GetFactory()->NewFromCanBeCompressString("ig"); + JSTaggedValue result1 = CreateRegExpObjByPatternAndFlags(thread, pattern1, flags1); + JSHandle value(thread, reinterpret_cast(result1.GetRawData())); + + JSHandle inputString = + thread->GetEcmaVM()->GetFactory()->NewFromCanBeCompressString("The Quick Brown Fox Jumps Over The Lazy Dog"); + auto ecmaRuntimeCallInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread, JSTaggedValue::Undefined(), 6); + ecmaRuntimeCallInfo->SetFunction(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetThis(value.GetTaggedValue()); + ecmaRuntimeCallInfo->SetCallArg(0, inputString.GetTaggedValue()); + + [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread, ecmaRuntimeCallInfo.get()); + // invoke Exec method + JSTaggedValue results = BuiltinsRegExp::Exec(ecmaRuntimeCallInfo.get()); + + JSHandle execResult(thread, results); + JSHandle resultZero = + thread->GetEcmaVM()->GetFactory()->NewFromCanBeCompressString("Quick Brown Fox Jumps"); + JSHandle resultOne = thread->GetEcmaVM()->GetFactory()->NewFromCanBeCompressString("Brown"); + JSHandle resultTwo = thread->GetEcmaVM()->GetFactory()->NewFromCanBeCompressString("Jumps"); + + JSHandle index(thread->GetEcmaVM()->GetFactory()->NewFromCanBeCompressString("index")); + JSHandle indexHandle(JSObject::GetProperty(thread, execResult, index).GetValue()); + uint32_t resultIndex = JSTaggedValue::ToUint32(thread, indexHandle); + ASSERT_TRUE(resultIndex == 4); + + JSHandle input(thread->GetEcmaVM()->GetFactory()->NewFromCanBeCompressString("input")); + + JSHandle inputHandle(JSObject::GetProperty(thread, execResult, input).GetValue()); + JSHandle outputInput = JSTaggedValue::ToString(thread, inputHandle); + ASSERT_EQ(outputInput->Compare(*inputString), 0); + + JSHandle zero(thread->GetEcmaVM()->GetFactory()->NewFromCanBeCompressString("0")); + JSHandle zeroHandle(JSObject::GetProperty(thread, execResult, zero).GetValue()); + JSHandle outputZero = JSTaggedValue::ToString(thread, zeroHandle); + ASSERT_EQ(outputZero->Compare(*resultZero), 0); + + JSHandle first(thread->GetEcmaVM()->GetFactory()->NewFromCanBeCompressString("1")); + JSHandle oneHandle(JSObject::GetProperty(thread, execResult, first).GetValue()); + JSHandle outputOne = JSTaggedValue::ToString(thread, oneHandle); + ASSERT_EQ(outputOne->Compare(*resultOne), 0); + + JSHandle second(thread->GetEcmaVM()->GetFactory()->NewFromCanBeCompressString("2")); + JSHandle twoHandle(JSObject::GetProperty(thread, execResult, second).GetValue()); + JSHandle outputTwo = JSTaggedValue::ToString(thread, twoHandle); + ASSERT_EQ(outputTwo->Compare(*resultTwo), 0); + + JSHandle regexp = JSHandle::Cast(value); + JSHandle lastIndexHandle(thread->GetEcmaVM()->GetFactory()->NewFromCanBeCompressString("lastIndex")); + JSHandle lastIndexObj(JSObject::GetProperty(thread, regexp, lastIndexHandle).GetValue()); + int lastIndex = lastIndexObj->GetInt(); + ASSERT_TRUE(lastIndex == 25); +} + +TEST_F(BuiltinsRegExpTest, Exec2) +{ + // invoke RegExpConstructor method + JSHandle pattern1 = + thread->GetEcmaVM()->GetFactory()->NewFromCanBeCompressString("((1)|(12))((3)|(23))"); + JSHandle flags1 = thread->GetEcmaVM()->GetFactory()->NewFromCanBeCompressString("ig"); + JSTaggedValue result1 = CreateRegExpObjByPatternAndFlags(thread, pattern1, flags1); + JSHandle value(thread, reinterpret_cast(result1.GetRawData())); + + JSHandle inputString = thread->GetEcmaVM()->GetFactory()->NewFromCanBeCompressString("123"); + auto ecmaRuntimeCallInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread, JSTaggedValue::Undefined(), 6); + ecmaRuntimeCallInfo->SetFunction(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetThis(value.GetTaggedValue()); + ecmaRuntimeCallInfo->SetCallArg(0, inputString.GetTaggedValue()); + + [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread, ecmaRuntimeCallInfo.get()); + // invoke Exec method + JSTaggedValue results = BuiltinsRegExp::Exec(ecmaRuntimeCallInfo.get()); + + JSHandle execResult(thread, results); + JSHandle resultZero = thread->GetEcmaVM()->GetFactory()->NewFromCanBeCompressString("123"); + JSHandle resultOne = thread->GetEcmaVM()->GetFactory()->NewFromCanBeCompressString("1"); + JSHandle resultTwo = thread->GetEcmaVM()->GetFactory()->NewFromCanBeCompressString("1"); + JSHandle resultFour = thread->GetEcmaVM()->GetFactory()->NewFromCanBeCompressString("23"); + JSHandle resultSix = thread->GetEcmaVM()->GetFactory()->NewFromCanBeCompressString("23"); + + JSHandle index(thread->GetEcmaVM()->GetFactory()->NewFromCanBeCompressString("index")); + JSHandle indexHandle(JSObject::GetProperty(thread, execResult, index).GetValue()); + uint32_t resultIndex = JSTaggedValue::ToUint32(thread, indexHandle); + ASSERT_TRUE(resultIndex == 0); + + JSHandle input(thread->GetEcmaVM()->GetFactory()->NewFromCanBeCompressString("input")); + JSHandle inputHandle(JSObject::GetProperty(thread, execResult, input).GetValue()); + JSHandle outputInput = JSTaggedValue::ToString(thread, inputHandle); + ASSERT_EQ(outputInput->Compare(*inputString), 0); + + JSHandle zero(thread->GetEcmaVM()->GetFactory()->NewFromCanBeCompressString("0")); + JSHandle zeroHandle(JSObject::GetProperty(thread, execResult, zero).GetValue()); + JSHandle outputZero = JSTaggedValue::ToString(thread, zeroHandle); + ASSERT_EQ(outputZero->Compare(*resultZero), 0); + + JSHandle first(thread->GetEcmaVM()->GetFactory()->NewFromCanBeCompressString("1")); + JSHandle oneHandle(JSObject::GetProperty(thread, execResult, first).GetValue()); + JSHandle outputOne = JSTaggedValue::ToString(thread, oneHandle); + ASSERT_EQ(outputOne->Compare(*resultOne), 0); + + JSHandle second(thread->GetEcmaVM()->GetFactory()->NewFromCanBeCompressString("2")); + JSHandle twoHandle(JSObject::GetProperty(thread, execResult, second).GetValue()); + JSHandle outputTwo = JSTaggedValue::ToString(thread, twoHandle); + ASSERT_EQ(outputTwo->Compare(*resultTwo), 0); + + JSHandle regexp = JSHandle::Cast(value); + JSHandle lastIndexHandle(thread->GetEcmaVM()->GetFactory()->NewFromCanBeCompressString("lastIndex")); + JSHandle lastIndexObj(JSObject::GetProperty(thread, regexp, lastIndexHandle).GetValue()); + int lastIndex = lastIndexObj->GetInt(); + ASSERT_TRUE(lastIndex == 3); + + JSHandle third(thread->GetEcmaVM()->GetFactory()->NewFromCanBeCompressString("3")); + JSHandle thirdHandle(JSObject::GetProperty(thread, execResult, third).GetValue()); + ASSERT_TRUE(thirdHandle->IsUndefined()); + + JSHandle four(thread->GetEcmaVM()->GetFactory()->NewFromCanBeCompressString("4")); + JSHandle fourHandle(JSObject::GetProperty(thread, execResult, four).GetValue()); + JSHandle outputFour = JSTaggedValue::ToString(thread, fourHandle); + ASSERT_EQ(outputFour->Compare(*resultFour), 0); + + JSHandle five(thread->GetEcmaVM()->GetFactory()->NewFromCanBeCompressString("5")); + JSHandle fiveHandle(JSObject::GetProperty(thread, execResult, five).GetValue()); + ASSERT_TRUE(fiveHandle->IsUndefined()); + + JSHandle six(thread->GetEcmaVM()->GetFactory()->NewFromCanBeCompressString("6")); + JSHandle sixHandle(JSObject::GetProperty(thread, execResult, six).GetValue()); + JSHandle outputSix = JSTaggedValue::ToString(thread, sixHandle); + ASSERT_EQ(outputSix->Compare(*resultSix), 0); +} + +TEST_F(BuiltinsRegExpTest, Match1) +{ + // invoke RegExpConstructor method + JSHandle pattern1 = + thread->GetEcmaVM()->GetFactory()->NewFromCanBeCompressString("quick\\s(brown).+?(jumps)"); + JSHandle flags1 = thread->GetEcmaVM()->GetFactory()->NewFromCanBeCompressString("iug"); + JSTaggedValue result1 = CreateRegExpObjByPatternAndFlags(thread, pattern1, flags1); + JSHandle value(thread, reinterpret_cast(result1.GetRawData())); + + JSHandle inputString = + thread->GetEcmaVM()->GetFactory()->NewFromCanBeCompressString("The Quick Brown Fox Jumps Over The Lazy Dog"); + auto ecmaRuntimeCallInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread, JSTaggedValue::Undefined(), 6); + ecmaRuntimeCallInfo->SetFunction(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetThis(value.GetTaggedValue()); + ecmaRuntimeCallInfo->SetCallArg(0, inputString.GetTaggedValue()); + + [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread, ecmaRuntimeCallInfo.get()); + // invoke Match method + JSTaggedValue matchResults = BuiltinsRegExp::Match(ecmaRuntimeCallInfo.get()); + + JSHandle matchResult(thread, matchResults); + JSHandle zero(thread->GetEcmaVM()->GetFactory()->NewFromCanBeCompressString("0")); + JSHandle resultZero = + thread->GetEcmaVM()->GetFactory()->NewFromCanBeCompressString("Quick Brown Fox Jumps"); + JSHandle zeroHandle(JSObject::GetProperty(thread, matchResult, zero).GetValue()); + JSHandle outputZero = JSTaggedValue::ToString(thread, zeroHandle); + ASSERT_EQ(outputZero->Compare(*resultZero), 0); +} + +TEST_F(BuiltinsRegExpTest, Test1) +{ + // invoke RegExpConstructor method + JSHandle pattern1 = + thread->GetEcmaVM()->GetFactory()->NewFromCanBeCompressString("quick\\s(brown).+?(jumps)"); + JSHandle flags1 = thread->GetEcmaVM()->GetFactory()->NewFromCanBeCompressString("iug"); + JSTaggedValue result1 = CreateRegExpObjByPatternAndFlags(thread, pattern1, flags1); + JSHandle value(thread, reinterpret_cast(result1.GetRawData())); + + JSHandle inputString = + thread->GetEcmaVM()->GetFactory()->NewFromCanBeCompressString("The Quick Brown Fox Jumps Over The Lazy Dog"); + auto ecmaRuntimeCallInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread, JSTaggedValue::Undefined(), 6); + ecmaRuntimeCallInfo->SetFunction(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetThis(value.GetTaggedValue()); + ecmaRuntimeCallInfo->SetCallArg(0, inputString.GetTaggedValue()); + + [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread, ecmaRuntimeCallInfo.get()); + // invoke Test method + JSTaggedValue testResult = BuiltinsRegExp::Test(ecmaRuntimeCallInfo.get()); + ASSERT_EQ(testResult.GetRawData(), JSTaggedValue::True().GetRawData()); +} + +TEST_F(BuiltinsRegExpTest, Search1) +{ + // invoke RegExpConstructor method + JSHandle pattern1 = + thread->GetEcmaVM()->GetFactory()->NewFromCanBeCompressString("quick\\s(brown).+?(jumps)"); + JSHandle flags1 = thread->GetEcmaVM()->GetFactory()->NewFromCanBeCompressString("iug"); + JSTaggedValue result1 = CreateRegExpObjByPatternAndFlags(thread, pattern1, flags1); + JSHandle value(thread, reinterpret_cast(result1.GetRawData())); + + JSHandle inputString = + thread->GetEcmaVM()->GetFactory()->NewFromCanBeCompressString("The Quick Brown Fox Jumps Over The Lazy Dog"); + auto ecmaRuntimeCallInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread, JSTaggedValue::Undefined(), 6); + ecmaRuntimeCallInfo->SetFunction(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetThis(value.GetTaggedValue()); + ecmaRuntimeCallInfo->SetCallArg(0, inputString.GetTaggedValue()); + + [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread, ecmaRuntimeCallInfo.get()); + // invoke Search method + JSTaggedValue searchResult = BuiltinsRegExp::Search(ecmaRuntimeCallInfo.get()); + ASSERT_EQ(searchResult.GetRawData(), JSTaggedValue(4).GetRawData()); +} + +TEST_F(BuiltinsRegExpTest, Split1) +{ + // invoke RegExpConstructor method + JSHandle pattern1 = thread->GetEcmaVM()->GetFactory()->NewFromCanBeCompressString("-"); + JSHandle flags1 = thread->GetEcmaVM()->GetFactory()->NewFromCanBeCompressString("iug"); + JSTaggedValue result1 = CreateRegExpObjByPatternAndFlags(thread, pattern1, flags1); + JSHandle value(thread, reinterpret_cast(result1.GetRawData())); + + JSHandle inputString = thread->GetEcmaVM()->GetFactory()->NewFromCanBeCompressString(""); + + auto ecmaRuntimeCallInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread, JSTaggedValue::Undefined(), 8); + ecmaRuntimeCallInfo->SetFunction(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetThis(value.GetTaggedValue()); + ecmaRuntimeCallInfo->SetCallArg(0, inputString.GetTaggedValue()); + ecmaRuntimeCallInfo->SetCallArg(1, JSTaggedValue::Undefined()); + + [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread, ecmaRuntimeCallInfo.get()); + // invoke Split method + JSTaggedValue splitResults = BuiltinsRegExp::Split(ecmaRuntimeCallInfo.get()); + JSHandle splitResult(thread, splitResults); + + JSHandle zero(thread->GetEcmaVM()->GetFactory()->NewFromCanBeCompressString("0")); + JSHandle zeroHandle(JSObject::GetProperty(thread, splitResult, zero).GetValue()); + JSHandle outputZero = JSTaggedValue::ToString(thread, zeroHandle); + + ASSERT_EQ(outputZero->Compare(*inputString), 0); +} + +TEST_F(BuiltinsRegExpTest, Split2) +{ + // invoke RegExpConstructor method + JSHandle pattern1 = thread->GetEcmaVM()->GetFactory()->NewFromCanBeCompressString("-"); + JSHandle flags1 = thread->GetEcmaVM()->GetFactory()->NewFromCanBeCompressString("iug"); + JSTaggedValue result1 = CreateRegExpObjByPatternAndFlags(thread, pattern1, flags1); + JSHandle value(thread, reinterpret_cast(result1.GetRawData())); + + JSHandle inputString = thread->GetEcmaVM()->GetFactory()->NewFromCanBeCompressString("a-b-c"); + + auto ecmaRuntimeCallInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread, JSTaggedValue::Undefined(), 8); + ecmaRuntimeCallInfo->SetFunction(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetThis(value.GetTaggedValue()); + ecmaRuntimeCallInfo->SetCallArg(0, inputString.GetTaggedValue()); + ecmaRuntimeCallInfo->SetCallArg(1, JSTaggedValue::Undefined()); + + [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread, ecmaRuntimeCallInfo.get()); + // invoke Split method + JSTaggedValue splitResults = BuiltinsRegExp::Split(ecmaRuntimeCallInfo.get()); + JSHandle splitResult(thread, splitResults); + JSHandle resultZero = thread->GetEcmaVM()->GetFactory()->NewFromCanBeCompressString("a"); + JSHandle resultOne = thread->GetEcmaVM()->GetFactory()->NewFromCanBeCompressString("b"); + JSHandle resultTwo = thread->GetEcmaVM()->GetFactory()->NewFromCanBeCompressString("c"); + + JSHandle zero(thread->GetEcmaVM()->GetFactory()->NewFromCanBeCompressString("0")); + JSHandle zeroHandle(JSObject::GetProperty(thread, splitResult, zero).GetValue()); + JSHandle outputZero = JSTaggedValue::ToString(thread, zeroHandle); + ASSERT_EQ(outputZero->Compare(*resultZero), 0); + + JSHandle first(thread->GetEcmaVM()->GetFactory()->NewFromCanBeCompressString("1")); + JSHandle oneHandle(JSObject::GetProperty(thread, splitResult, first).GetValue()); + JSHandle outputOne = JSTaggedValue::ToString(thread, oneHandle); + ASSERT_EQ(outputOne->Compare(*resultOne), 0); + + JSHandle second(thread->GetEcmaVM()->GetFactory()->NewFromCanBeCompressString("2")); + JSHandle twoHandle(JSObject::GetProperty(thread, splitResult, second).GetValue()); + JSHandle outputTwo = JSTaggedValue::ToString(thread, twoHandle); + ASSERT_EQ(outputTwo->Compare(*resultTwo), 0); +} + +TEST_F(BuiltinsRegExpTest, GetSpecies) +{ + // invoke RegExpConstructor method + JSHandle env = thread->GetEcmaVM()->GetGlobalEnv(); + JSHandle speciesSymbol = env->GetSpeciesSymbol(); + EXPECT_TRUE(!speciesSymbol.GetTaggedValue().IsUndefined()); + + JSHandle newTarget(env->GetRegExpFunction()); + + JSTaggedValue value = + JSObject::GetProperty(thread, JSHandle(newTarget), speciesSymbol).GetValue().GetTaggedValue(); + EXPECT_EQ(value, newTarget.GetTaggedValue()); +} + +TEST_F(BuiltinsRegExpTest, Replace1) +{ + // invoke RegExpConstructor method + JSHandle pattern1 = + thread->GetEcmaVM()->GetFactory()->NewFromCanBeCompressString("quick\\s(brown).+?(jumps)"); + JSHandle flags1 = thread->GetEcmaVM()->GetFactory()->NewFromCanBeCompressString("iug"); + JSTaggedValue result1 = CreateRegExpObjByPatternAndFlags(thread, pattern1, flags1); + JSHandle value(thread, reinterpret_cast(result1.GetRawData())); + + JSHandle inputString = + thread->GetEcmaVM()->GetFactory()->NewFromCanBeCompressString("The Quick Brown Fox Jumps Over The Lazy Dog"); + JSHandle replaceString = + thread->GetEcmaVM()->GetFactory()->NewFromCanBeCompressString("$&a $` $\' $2 $01 $$1 $21 $32 a"); + auto ecmaRuntimeCallInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread, JSTaggedValue::Undefined(), 8); + ecmaRuntimeCallInfo->SetFunction(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetThis(value.GetTaggedValue()); + ecmaRuntimeCallInfo->SetCallArg(0, inputString.GetTaggedValue()); + ecmaRuntimeCallInfo->SetCallArg(1, replaceString.GetTaggedValue()); + + [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread, ecmaRuntimeCallInfo.get()); + // invoke replace method + JSTaggedValue results = BuiltinsRegExp::Replace(ecmaRuntimeCallInfo.get()); + JSHandle replaceResult(thread, results); + JSHandle resultZero = thread->GetEcmaVM()->GetFactory()->NewFromCanBeCompressString( + "The Quick Brown Fox Jumpsa The Over The Lazy Dog Jumps Brown $1 Jumps1 $32 a Over The Lazy Dog"); + ASSERT_EQ(static_cast(replaceResult->GetTaggedObject())->Compare(*resultZero), 0); +} + +TEST_F(BuiltinsRegExpTest, Replace2) +{ + // invoke RegExpConstructor method + ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); + JSHandle pattern1 = factory->NewFromCanBeCompressString("b(c)(z)?(.)"); + JSHandle flags1 = factory->NewFromCanBeCompressString(""); + JSTaggedValue result1 = CreateRegExpObjByPatternAndFlags(thread, pattern1, flags1); + JSHandle value(thread, reinterpret_cast(result1.GetRawData())); + + JSHandle inputString = factory->NewFromCanBeCompressString("abcde"); + JSHandle replaceString = factory->NewFromCanBeCompressString("[$01$02$03$04$00]"); + auto ecmaRuntimeCallInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread, JSTaggedValue::Undefined(), 8); + ecmaRuntimeCallInfo->SetFunction(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetThis(value.GetTaggedValue()); + ecmaRuntimeCallInfo->SetCallArg(0, inputString.GetTaggedValue()); + ecmaRuntimeCallInfo->SetCallArg(1, replaceString.GetTaggedValue()); + + [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread, ecmaRuntimeCallInfo.get()); + // invoke replace method + JSTaggedValue results = BuiltinsRegExp::Replace(ecmaRuntimeCallInfo.get()); + JSHandle replaceResult(thread, results); + JSHandle resultZero = factory->NewFromCanBeCompressString("a[cd$04$00]e"); + ASSERT_EQ(static_cast(replaceResult->GetTaggedObject())->Compare(*resultZero), 0); +} + +TEST_F(BuiltinsRegExpTest, Replace3) +{ + // invoke RegExpConstructor method + ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); + JSHandle pattern1 = factory->NewFromCanBeCompressString("abc"); + JSHandle flags1 = factory->NewFromCanBeCompressString("g"); + JSTaggedValue result1 = CreateRegExpObjByPatternAndFlags(thread, pattern1, flags1); + JSHandle value(thread, reinterpret_cast(result1.GetRawData())); + + JSHandle inputString = factory->NewFromCanBeCompressString("abcde"); + JSHandle replaceString = factory->NewFromCanBeCompressString(""); + auto ecmaRuntimeCallInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread, JSTaggedValue::Undefined(), 8); + ecmaRuntimeCallInfo->SetFunction(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetThis(value.GetTaggedValue()); + ecmaRuntimeCallInfo->SetCallArg(0, inputString.GetTaggedValue()); + ecmaRuntimeCallInfo->SetCallArg(1, replaceString.GetTaggedValue()); + + [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread, ecmaRuntimeCallInfo.get()); + // invoke replace method + JSTaggedValue results = BuiltinsRegExp::Replace(ecmaRuntimeCallInfo.get()); + JSHandle replaceResult(thread, results); + JSHandle resultZero = factory->NewFromCanBeCompressString("de"); + ASSERT_EQ(static_cast(replaceResult->GetTaggedObject())->Compare(*resultZero), 0); +} + +TEST_F(BuiltinsRegExpTest, RegExpParseCache) +{ + RegExpParserCache *regExpParserCache = thread->GetEcmaVM()->GetRegExpParserCache(); + ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); + JSHandle string1 = factory->NewFromCanBeCompressString("abc"); + JSHandle string2 = factory->NewFromCanBeCompressString("abcd"); + regExpParserCache->SetCache(*string1, 0, JSTaggedValue::True(), 2); + ASSERT_TRUE(regExpParserCache->GetCache(*string1, 0).first == JSTaggedValue::True()); + ASSERT_TRUE(regExpParserCache->GetCache(*string1, 0).second == 2); + ASSERT_TRUE(regExpParserCache->GetCache(*string1, RegExpParserCache::CACHE_SIZE).first == JSTaggedValue::Hole()); + ASSERT_TRUE(regExpParserCache->GetCache(*string2, 0).first == JSTaggedValue::Hole()); +} +} // namespace panda::test diff --git a/tests/runtime/builtins/builtins_set_test.cpp b/tests/runtime/builtins/builtins_set_test.cpp new file mode 100644 index 000000000..9be2b958f --- /dev/null +++ b/tests/runtime/builtins/builtins_set_test.cpp @@ -0,0 +1,363 @@ +/* + * Copyright (c) 2021-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 "plugins/ecmascript/runtime/builtins/builtins_set.h" +#include "plugins/ecmascript/runtime/ecma_string.h" +#include "plugins/ecmascript/runtime/ecma_vm.h" +#include "plugins/ecmascript/runtime/global_env.h" + +#include "plugins/ecmascript/runtime/js_array.h" +#include "plugins/ecmascript/runtime/js_handle.h" +#include "plugins/ecmascript/runtime/js_hclass.h" +#include "plugins/ecmascript/runtime/js_object-inl.h" +#include "plugins/ecmascript/runtime/js_set.h" +#include "plugins/ecmascript/runtime/js_set_iterator.h" +#include "plugins/ecmascript/runtime/js_tagged_value.h" +#include "plugins/ecmascript/runtime/js_thread.h" +#include "plugins/ecmascript/runtime/object_factory.h" +#include "plugins/ecmascript/tests/runtime/common/test_helper.h" +#include "utils/bit_utils.h" + +using namespace panda::ecmascript; +using namespace panda::ecmascript::builtins; + +namespace panda::test { +using BuiltinsSet = ecmascript::builtins::BuiltinsSet; +using JSSet = ecmascript::JSSet; + +class BuiltinsSetTest : public testing::Test { +public: + static void SetUpTestCase() + { + GTEST_LOG_(INFO) << "SetUpTestCase"; + } + + static void TearDownTestCase() + { + GTEST_LOG_(INFO) << "TearDownCase"; + } + + void SetUp() override + { + TestHelper::CreateEcmaVMWithScope(instance, thread, scope); + } + + void TearDown() override + { + TestHelper::DestroyEcmaVMWithScope(instance, scope); + } + + PandaVM *instance {nullptr}; + EcmaHandleScope *scope {nullptr}; + JSThread *thread {nullptr}; + + class TestClass : public base::BuiltinsBase { + public: + static JSTaggedValue TestFunc(EcmaRuntimeCallInfo *argv) + { + JSTaggedValue key = GetCallArg(argv, 0).GetTaggedValue(); + if (key.IsUndefined()) { + return JSTaggedValue::Undefined(); + } + JSArray *jsArray = JSArray::Cast(GetThis(argv)->GetTaggedObject()); + int length = jsArray->GetArrayLength() + 1; + jsArray->SetArrayLength(argv->GetThread(), length); + return JSTaggedValue::Undefined(); + } + }; +}; + +JSSet *CreateBuiltinsSet(JSThread *thread) +{ + JSHandle env = thread->GetEcmaVM()->GetGlobalEnv(); + JSHandle newTarget(env->GetBuiltinsSetFunction()); + // 4 : test case + auto ecmaRuntimeCallInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread, JSTaggedValue(*newTarget), 4); + ecmaRuntimeCallInfo->SetFunction(newTarget.GetTaggedValue()); + ecmaRuntimeCallInfo->SetThis(JSTaggedValue::Undefined()); + + [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread, ecmaRuntimeCallInfo.get()); + JSTaggedValue result = BuiltinsSet::SetConstructor(ecmaRuntimeCallInfo.get()); + + EXPECT_TRUE(result.IsECMAObject()); + return JSSet::Cast(reinterpret_cast(result.GetRawData())); +} +// new Set("abrupt").toString() +TEST_F(BuiltinsSetTest, CreateAndGetSize) +{ + ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); + JSHandle env = thread->GetEcmaVM()->GetGlobalEnv(); + JSHandle newTarget(env->GetBuiltinsSetFunction()); + JSHandle set(thread, CreateBuiltinsSet(thread)); + + auto ecmaRuntimeCallInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread, JSTaggedValue::Undefined(), 6); + ecmaRuntimeCallInfo->SetFunction(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetThis(set.GetTaggedValue()); + ecmaRuntimeCallInfo->SetCallArg(0, JSTaggedValue::Undefined()); + { + [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread, ecmaRuntimeCallInfo.get()); + JSTaggedValue result = BuiltinsSet::GetSize(ecmaRuntimeCallInfo.get()); + + EXPECT_EQ(result.GetRawData(), JSTaggedValue(0).GetRawData()); + } + + JSHandle array(factory->NewTaggedArray(5)); + for (int i = 0; i < 5; i++) { + array->Set(thread, i, JSTaggedValue(i)); + } + + JSHandle values = JSArray::CreateArrayFromList(thread, array); + auto ecmaRuntimeCallInfo1 = TestHelper::CreateEcmaRuntimeCallInfo(thread, JSTaggedValue::Undefined(), 6); + ecmaRuntimeCallInfo1->SetFunction(newTarget.GetTaggedValue()); + ecmaRuntimeCallInfo1->SetThis(set.GetTaggedValue()); + ecmaRuntimeCallInfo1->SetCallArg(0, values.GetTaggedValue()); + ecmaRuntimeCallInfo1->SetNewTarget(newTarget.GetTaggedValue()); + { + [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread, ecmaRuntimeCallInfo1.get()); + JSTaggedValue result1 = BuiltinsSet::SetConstructor(ecmaRuntimeCallInfo1.get()); + + EXPECT_EQ(JSSet::Cast(reinterpret_cast(result1.GetRawData()))->GetSize(), 5); + } +} + +TEST_F(BuiltinsSetTest, AddAndHas) +{ + ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); + // create jsSet + JSHandle set(thread, CreateBuiltinsSet(thread)); + JSHandle key(thread, factory->NewFromCanBeCompressString("key").GetTaggedValue()); + + auto ecmaRuntimeCallInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread, JSTaggedValue::Undefined(), 6); + ecmaRuntimeCallInfo->SetFunction(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetThis(set.GetTaggedValue()); + ecmaRuntimeCallInfo->SetCallArg(0, key.GetTaggedValue()); + + [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread, ecmaRuntimeCallInfo.get()); + JSTaggedValue result1 = BuiltinsSet::Has(ecmaRuntimeCallInfo.get()); + + EXPECT_EQ(result1.GetRawData(), JSTaggedValue::False().GetRawData()); + + // test Add() + JSTaggedValue result2 = BuiltinsSet::Add(ecmaRuntimeCallInfo.get()); + EXPECT_TRUE(result2.IsECMAObject()); + JSSet *jsSet = JSSet::Cast(reinterpret_cast(result2.GetRawData())); + EXPECT_EQ(jsSet->GetSize(), 1); + + // test Has() + auto ecmaRuntimeCallInfo1 = TestHelper::CreateEcmaRuntimeCallInfo(thread, JSTaggedValue::Undefined(), 6); + ecmaRuntimeCallInfo1->SetFunction(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo1->SetThis(JSTaggedValue(jsSet)); + ecmaRuntimeCallInfo1->SetCallArg(0, key.GetTaggedValue()); + { + [[maybe_unused]] auto prevLocal = TestHelper::SetupFrame(thread, ecmaRuntimeCallInfo1.get()); + JSTaggedValue result3 = BuiltinsSet::Has(ecmaRuntimeCallInfo1.get()); + + EXPECT_EQ(result3.GetRawData(), JSTaggedValue::True().GetRawData()); + } + + // test -0.0 + JSHandle negativeZero(thread, JSTaggedValue(-0.0)); + JSHandle positiveZero(thread, JSTaggedValue(+0.0)); + auto ecmaRuntimeCallInfo2 = TestHelper::CreateEcmaRuntimeCallInfo(thread, JSTaggedValue::Undefined(), 6); + ecmaRuntimeCallInfo2->SetFunction(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo2->SetThis(JSTaggedValue(jsSet)); + ecmaRuntimeCallInfo2->SetCallArg(0, negativeZero.GetTaggedValue()); + { + [[maybe_unused]] auto prevLocal = TestHelper::SetupFrame(thread, ecmaRuntimeCallInfo2.get()); + BuiltinsSet::Add(ecmaRuntimeCallInfo.get()); + } + + auto ecmaRuntimeCallInfo3 = TestHelper::CreateEcmaRuntimeCallInfo(thread, JSTaggedValue::Undefined(), 6); + ecmaRuntimeCallInfo3->SetFunction(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo3->SetThis(JSTaggedValue(jsSet)); + ecmaRuntimeCallInfo3->SetCallArg(0, positiveZero.GetTaggedValue()); + { + [[maybe_unused]] auto prevLocal = TestHelper::SetupFrame(thread, ecmaRuntimeCallInfo3.get()); + JSTaggedValue result4 = BuiltinsSet::Has(ecmaRuntimeCallInfo3.get()); + + EXPECT_EQ(result4.GetRawData(), JSTaggedValue::False().GetRawData()); + } +} + +TEST_F(BuiltinsSetTest, ForEach) +{ + // generate a set has 5 entry{key1,key2,key3,key4,key5} + ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); + JSHandle set(thread, CreateBuiltinsSet(thread)); + char keyArray[] = "key0"; + for (int i = 0; i < 5; i++) { + keyArray[3] = '1' + i; + JSHandle key(factory->NewFromCanBeCompressString(keyArray)); + auto ecmaRuntimeCallInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread, JSTaggedValue::Undefined(), 6); + ecmaRuntimeCallInfo->SetFunction(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetThis(set.GetTaggedValue()); + ecmaRuntimeCallInfo->SetCallArg(0, key.GetTaggedValue()); + + [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread, ecmaRuntimeCallInfo.get()); + JSTaggedValue result1 = BuiltinsSet::Add(ecmaRuntimeCallInfo.get()); + + EXPECT_TRUE(result1.IsECMAObject()); + JSSet *jsSet = JSSet::Cast(reinterpret_cast(result1.GetRawData())); + EXPECT_EQ(jsSet->GetSize(), i + 1); + } + // test foreach; + JSHandle jsArray(JSArray::ArrayCreate(thread, JSTaggedNumber(0))); + JSHandle func = + factory->NewJSFunction(thread->GetEcmaVM()->GetGlobalEnv(), reinterpret_cast(TestClass::TestFunc)); + + auto ecmaRuntimeCallInfo1 = TestHelper::CreateEcmaRuntimeCallInfo(thread, JSTaggedValue::Undefined(), 8); + ecmaRuntimeCallInfo1->SetFunction(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo1->SetThis(set.GetTaggedValue()); + ecmaRuntimeCallInfo1->SetCallArg(0, func.GetTaggedValue()); + ecmaRuntimeCallInfo1->SetCallArg(1, jsArray.GetTaggedValue()); + + [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread, ecmaRuntimeCallInfo1.get()); + JSTaggedValue result2 = BuiltinsSet::ForEach(ecmaRuntimeCallInfo1.get()); + + EXPECT_EQ(result2.GetRawData(), JSTaggedValue::VALUE_UNDEFINED); + EXPECT_EQ(jsArray->GetArrayLength(), 5); +} + +TEST_F(BuiltinsSetTest, DeleteAndRemove) +{ + ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); + // create jsSet + JSHandle set(thread, CreateBuiltinsSet(thread)); + + // add 40 keys + char keyArray[] = "key0"; + for (int i = 0; i < 40; i++) { + keyArray[3] = '1' + i; + JSHandle key(factory->NewFromCanBeCompressString(keyArray)); + + auto ecmaRuntimeCallInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread, JSTaggedValue::Undefined(), 6); + ecmaRuntimeCallInfo->SetFunction(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetThis(set.GetTaggedValue()); + ecmaRuntimeCallInfo->SetCallArg(0, key.GetTaggedValue()); + + [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread, ecmaRuntimeCallInfo.get()); + JSTaggedValue result1 = BuiltinsSet::Add(ecmaRuntimeCallInfo.get()); + + EXPECT_TRUE(result1.IsECMAObject()); + JSSet *jsSet = JSSet::Cast(reinterpret_cast(result1.GetRawData())); + EXPECT_EQ(jsSet->GetSize(), i + 1); + } + // whether jsSet has delete key + keyArray[3] = '1' + 8; + JSHandle deleteKey(factory->NewFromCanBeCompressString(keyArray)); + + auto ecmaRuntimeCallInfo1 = TestHelper::CreateEcmaRuntimeCallInfo(thread, JSTaggedValue::Undefined(), 6); + ecmaRuntimeCallInfo1->SetFunction(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo1->SetThis(set.GetTaggedValue()); + ecmaRuntimeCallInfo1->SetCallArg(0, deleteKey.GetTaggedValue()); + + [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread, ecmaRuntimeCallInfo1.get()); + JSTaggedValue result2 = BuiltinsSet::Has(ecmaRuntimeCallInfo1.get()); + + EXPECT_EQ(result2.GetRawData(), JSTaggedValue::True().GetRawData()); + + // delete + JSTaggedValue result3 = BuiltinsSet::Delete(ecmaRuntimeCallInfo1.get()); + + EXPECT_EQ(result3.GetRawData(), JSTaggedValue::True().GetRawData()); + + // check deleteKey is deleted + JSTaggedValue result4 = BuiltinsSet::Has(ecmaRuntimeCallInfo1.get()); + + EXPECT_EQ(result4.GetRawData(), JSTaggedValue::False().GetRawData()); + + JSTaggedValue result5 = BuiltinsSet::GetSize(ecmaRuntimeCallInfo1.get()); + + EXPECT_EQ(result5.GetRawData(), JSTaggedValue(39).GetRawData()); + + // clear + JSTaggedValue result6 = BuiltinsSet::Clear(ecmaRuntimeCallInfo1.get()); + EXPECT_EQ(result6.GetRawData(), JSTaggedValue::VALUE_UNDEFINED); + EXPECT_EQ(set->GetSize(), 0); +} + +TEST_F(BuiltinsSetTest, Species) +{ + ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); + JSHandle env = thread->GetEcmaVM()->GetGlobalEnv(); + JSHandle set(thread, CreateBuiltinsSet(thread)); + JSHandle speciesSymbol = env->GetSpeciesSymbol(); + EXPECT_TRUE(!speciesSymbol->IsUndefined()); + + JSHandle newTarget(env->GetBuiltinsSetFunction()); + + JSTaggedValue value = + JSObject::GetProperty(thread, JSHandle(newTarget), speciesSymbol).GetValue().GetTaggedValue(); + JSHandle valueHandle(thread, value); + EXPECT_EQ(value, newTarget.GetTaggedValue()); + + // to string tag + JSHandle toStringTagSymbol = env->GetToStringTagSymbol(); + JSHandle stringTag(JSObject::GetProperty(thread, set, toStringTagSymbol).GetValue()); + JSHandle str = factory->NewFromCanBeCompressString("Set"); + EXPECT_TRUE(!stringTag.GetTaggedValue().IsUndefined()); + EXPECT_TRUE(EcmaString::StringsAreEqual(*str, *stringTag)); + + JSHandle constructor = JSHandle::Cast(JSTaggedValue::ToObject(thread, valueHandle)); + EXPECT_EQ(JSHandle(set)->GetPrototype(thread), constructor->GetFunctionPrototype()); + + JSHandle key1(factory->NewFromCanBeCompressString("add")); + JSTaggedValue value1 = JSObject::GetProperty(thread, set, key1).GetValue().GetTaggedValue(); + EXPECT_FALSE(value1.IsUndefined()); + + JSHandle key2(factory->NewFromCanBeCompressString("has")); + JSTaggedValue value2 = JSObject::GetProperty(thread, set, key1).GetValue().GetTaggedValue(); + EXPECT_FALSE(value2.IsUndefined()); + + JSHandle key3(factory->NewFromCanBeCompressString("clear")); + JSTaggedValue value3 = JSObject::GetProperty(thread, set, key1).GetValue().GetTaggedValue(); + EXPECT_FALSE(value3.IsUndefined()); + + JSHandle key4(factory->NewFromCanBeCompressString("size")); + JSTaggedValue value4 = JSObject::GetProperty(thread, set, key1).GetValue().GetTaggedValue(); + EXPECT_FALSE(value4.IsUndefined()); + + JSHandle key5(factory->NewFromCanBeCompressString("delete")); + JSTaggedValue value5 = JSObject::GetProperty(thread, set, key1).GetValue().GetTaggedValue(); + EXPECT_FALSE(value5.IsUndefined()); + + JSHandle key6(factory->NewFromCanBeCompressString("forEach")); + JSTaggedValue value6 = JSObject::GetProperty(thread, set, key1).GetValue().GetTaggedValue(); + EXPECT_FALSE(value6.IsUndefined()); +} + +TEST_F(BuiltinsSetTest, GetIterator) +{ + JSHandle set(thread, CreateBuiltinsSet(thread)); + + auto ecmaRuntimeCallInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread, JSTaggedValue::Undefined(), 4); + ecmaRuntimeCallInfo->SetFunction(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetThis(set.GetTaggedValue()); + [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread, ecmaRuntimeCallInfo.get()); + + // test Values() + JSTaggedValue result = BuiltinsSet::Values(ecmaRuntimeCallInfo.get()); + JSHandle iter(thread, result); + EXPECT_TRUE(iter->IsJSSetIterator()); + EXPECT_EQ(IterationKind::VALUE, IterationKind(iter->GetIterationKind().GetInt())); + EXPECT_EQ(JSSet::Cast(set.GetTaggedValue().GetTaggedObject())->GetLinkedSet(), iter->GetIteratedSet()); + + // test entrys() + JSTaggedValue result2 = BuiltinsSet::Entries(ecmaRuntimeCallInfo.get()); + JSHandle iter2(thread, result2); + EXPECT_TRUE(iter2->IsJSSetIterator()); + EXPECT_EQ(IterationKind::KEY_AND_VALUE, IterationKind(iter2->GetIterationKind().GetInt())); +} +} // namespace panda::test diff --git a/tests/runtime/builtins/builtins_string_test.cpp b/tests/runtime/builtins/builtins_string_test.cpp new file mode 100644 index 000000000..1ab23e738 --- /dev/null +++ b/tests/runtime/builtins/builtins_string_test.cpp @@ -0,0 +1,1461 @@ +/* + * Copyright (c) 2021-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 "plugins/ecmascript/runtime/base/builtins_base.h" +#include "plugins/ecmascript/runtime/builtins/builtins_regexp.h" +#include "plugins/ecmascript/runtime/builtins/builtins_string.h" +#include "plugins/ecmascript/runtime/ecma_runtime_call_info.h" +#include "plugins/ecmascript/runtime/ecma_string.h" +#include "plugins/ecmascript/runtime/ecma_vm.h" +#include "plugins/ecmascript/runtime/global_env.h" + +#include "plugins/ecmascript/runtime/js_array.h" +#include "plugins/ecmascript/runtime/js_primitive_ref.h" +#include "plugins/ecmascript/runtime/js_regexp.h" +#include "plugins/ecmascript/runtime/js_tagged_value.h" +#include "plugins/ecmascript/runtime/object_factory.h" +#include "plugins/ecmascript/tests/runtime/common/test_helper.h" + +using namespace panda::ecmascript; +using namespace panda::ecmascript::builtins; + +namespace panda::test { +class BuiltinsStringTest : public testing::Test { +public: + static void SetUpTestCase() + { + GTEST_LOG_(INFO) << "SetUpTestCase"; + } + + static void TearDownTestCase() + { + GTEST_LOG_(INFO) << "TearDownCase"; + } + + void SetUp() override + { + TestHelper::CreateEcmaVMWithScope(instance, thread, scope); + } + + void TearDown() override + { + TestHelper::DestroyEcmaVMWithScope(instance, scope); + } + + PandaVM *instance {nullptr}; + EcmaHandleScope *scope {nullptr}; + JSThread *thread {nullptr}; +}; + +JSTaggedValue CreateRegExpObjByPatternAndFlags(JSThread *thread, const JSHandle &pattern, + const JSHandle &flags) +{ + JSHandle env = thread->GetEcmaVM()->GetGlobalEnv(); + JSHandle regexp(env->GetRegExpFunction()); + JSHandle globalObject(thread, env->GetGlobalObject()); + + // 8 : test case + auto ecmaRuntimeCallInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread, regexp.GetTaggedValue(), 8); + ecmaRuntimeCallInfo->SetFunction(regexp.GetTaggedValue()); + ecmaRuntimeCallInfo->SetThis(globalObject.GetTaggedValue()); + ecmaRuntimeCallInfo->SetCallArg(0, pattern.GetTaggedValue()); + ecmaRuntimeCallInfo->SetCallArg(1, flags.GetTaggedValue()); + + [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread, ecmaRuntimeCallInfo.get()); + JSTaggedValue result = BuiltinsRegExp::RegExpConstructor(ecmaRuntimeCallInfo.get()); + return result; +} + +TEST_F(BuiltinsStringTest, StringConstructor1) +{ + JSHandle env = thread->GetEcmaVM()->GetGlobalEnv(); + + ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); + JSHandle string(env->GetStringFunction()); + JSHandle globalObject(thread, env->GetGlobalObject()); + JSHandle string2 = factory->NewFromCanBeCompressString("ABC"); + + auto ecmaRuntimeCallInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread, string.GetTaggedValue(), 6); + ecmaRuntimeCallInfo->SetFunction(string.GetTaggedValue()); + ecmaRuntimeCallInfo->SetThis(globalObject.GetTaggedValue()); + ecmaRuntimeCallInfo->SetCallArg(0, string2.GetTaggedValue()); + + [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread, ecmaRuntimeCallInfo.get()); + JSTaggedValue result = BuiltinsString::StringConstructor(ecmaRuntimeCallInfo.get()); + JSTaggedValue value(static_cast(result.GetRawData())); + ASSERT_TRUE(value.IsECMAObject()); + JSHandle ref(thread, JSPrimitiveRef::Cast(value.GetTaggedObject())); + JSTaggedValue test = factory->NewFromCanBeCompressString("ABC").GetTaggedValue(); + ASSERT_EQ( + EcmaString::Cast(ref->GetValue().GetTaggedObject())->Compare(reinterpret_cast(test.GetRawData())), + 0); +} + +// String.fromCharCode(65, 66, 67) +TEST_F(BuiltinsStringTest, fromCharCode1) +{ + ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); + const double arg1 = 65; + const double arg2 = 66; + const double arg3 = 67; + + auto ecmaRuntimeCallInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread, JSTaggedValue::Undefined(), 10); + ecmaRuntimeCallInfo->SetFunction(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetThis(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetCallArg(0, JSTaggedValue(arg1)); + ecmaRuntimeCallInfo->SetCallArg(1, JSTaggedValue(arg2)); + ecmaRuntimeCallInfo->SetCallArg(2, JSTaggedValue(arg3)); + + [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread, ecmaRuntimeCallInfo.get()); + JSTaggedValue result = BuiltinsString::FromCharCode(ecmaRuntimeCallInfo.get()); + ASSERT_TRUE(result.IsString()); + JSTaggedValue value(static_cast(result.GetRawData())); + JSHandle valueHandle(thread, JSTaggedValue(value.GetTaggedObject())); + JSTaggedValue test = factory->NewFromCanBeCompressString("ABC").GetTaggedValue(); + ASSERT_EQ( + EcmaString::Cast(valueHandle->GetTaggedObject())->Compare(reinterpret_cast(test.GetRawData())), + 0); +} + +// String.fromCodePoint(65, 66, 67) +TEST_F(BuiltinsStringTest, fromCodePoint1) +{ + ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); + const double arg1 = 65; + const double arg2 = 66; + const double arg3 = 67; + + auto ecmaRuntimeCallInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread, JSTaggedValue::Undefined(), 10); + ecmaRuntimeCallInfo->SetFunction(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetThis(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetCallArg(0, JSTaggedValue(arg1)); + ecmaRuntimeCallInfo->SetCallArg(1, JSTaggedValue(arg2)); + ecmaRuntimeCallInfo->SetCallArg(2, JSTaggedValue(arg3)); + + [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread, ecmaRuntimeCallInfo.get()); + JSTaggedValue result = BuiltinsString::FromCodePoint(ecmaRuntimeCallInfo.get()); + ASSERT_TRUE(result.IsString()); + JSHandle resultHandle(thread, reinterpret_cast(result.GetRawData())); + JSTaggedValue test = factory->NewFromCanBeCompressString("ABC").GetTaggedValue(); + ASSERT_EQ(resultHandle->Compare(reinterpret_cast(test.GetRawData())), 0); +} + +// "abcabcabc".charAt(5) +TEST_F(BuiltinsStringTest, charAt1) +{ + ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); + JSHandle thisVal = factory->NewFromCanBeCompressString("abcabcabc"); + + auto ecmaRuntimeCallInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread, JSTaggedValue::Undefined(), 6); + ecmaRuntimeCallInfo->SetFunction(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetThis(thisVal.GetTaggedValue()); + ecmaRuntimeCallInfo->SetCallArg(0, JSTaggedValue(static_cast(5))); + + [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread, ecmaRuntimeCallInfo.get()); + JSTaggedValue result = BuiltinsString::CharAt(ecmaRuntimeCallInfo.get()); + ASSERT_TRUE(result.IsString()); + JSHandle resultHandle(thread, reinterpret_cast(result.GetRawData())); + JSTaggedValue test = factory->NewFromCanBeCompressString("c").GetTaggedValue(); + ASSERT_EQ(resultHandle->Compare(reinterpret_cast(test.GetRawData())), 0); +} + +// "一二三四".charAt(2) +TEST_F(BuiltinsStringTest, charAt2) +{ + ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); + JSHandle thisVal = factory->NewFromString("一二三四"); + + auto ecmaRuntimeCallInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread, JSTaggedValue::Undefined(), 6); + ecmaRuntimeCallInfo->SetFunction(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetThis(thisVal.GetTaggedValue()); + ecmaRuntimeCallInfo->SetCallArg(0, JSTaggedValue(static_cast(2))); + + [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread, ecmaRuntimeCallInfo.get()); + JSTaggedValue result = BuiltinsString::CharAt(ecmaRuntimeCallInfo.get()); + ASSERT_TRUE(result.IsString()); + JSHandle resultHandle(thread, reinterpret_cast(result.GetRawData())); + JSTaggedValue test = factory->NewFromString("三").GetTaggedValue(); + ASSERT_EQ(resultHandle->Compare(reinterpret_cast(test.GetRawData())), 0); +} + +// "abcabcabc".charAt(-1) +TEST_F(BuiltinsStringTest, charAt3) +{ + ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); + JSHandle thisVal = factory->NewFromCanBeCompressString("abcabcabc"); + + auto ecmaRuntimeCallInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread, JSTaggedValue::Undefined(), 6); + ecmaRuntimeCallInfo->SetFunction(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetThis(thisVal.GetTaggedValue()); + ecmaRuntimeCallInfo->SetCallArg(0, JSTaggedValue(static_cast(-1))); + + [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread, ecmaRuntimeCallInfo.get()); + JSTaggedValue result = BuiltinsString::CharAt(ecmaRuntimeCallInfo.get()); + ASSERT_TRUE(result.IsString()); + JSHandle resultHandle(thread, reinterpret_cast(result.GetRawData())); + JSTaggedValue test = factory->GetEmptyString().GetTaggedValue(); + ASSERT_EQ(resultHandle->Compare(reinterpret_cast(test.GetRawData())), 0); +} + +// "ABC".charCodeAt(0) +TEST_F(BuiltinsStringTest, charCodeAt1) +{ + ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); + JSHandle thisVal = factory->NewFromCanBeCompressString("ABC"); + + auto ecmaRuntimeCallInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread, JSTaggedValue::Undefined(), 6); + ecmaRuntimeCallInfo->SetFunction(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetThis(thisVal.GetTaggedValue()); + ecmaRuntimeCallInfo->SetCallArg(0, JSTaggedValue(static_cast(0))); + + [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread, ecmaRuntimeCallInfo.get()); + JSTaggedValue result = BuiltinsString::CharCodeAt(ecmaRuntimeCallInfo.get()); + + ASSERT_EQ(result.GetRawData(), JSTaggedValue(65).GetRawData()); +} + +// "ABC".charCodeAt(-1) +TEST_F(BuiltinsStringTest, charCodeAt2) +{ + ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); + JSHandle thisVal = factory->NewFromCanBeCompressString("ABC"); + + auto ecmaRuntimeCallInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread, JSTaggedValue::Undefined(), 6); + ecmaRuntimeCallInfo->SetFunction(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetThis(thisVal.GetTaggedValue()); + ecmaRuntimeCallInfo->SetCallArg(0, JSTaggedValue(static_cast(-1))); + + [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread, ecmaRuntimeCallInfo.get()); + JSTaggedValue result = BuiltinsString::CharCodeAt(ecmaRuntimeCallInfo.get()); + + JSTaggedValue test = BuiltinsString::GetTaggedDouble(base::NAN_VALUE); + ASSERT_EQ(result.GetRawData(), test.GetRawData()); +} + +// "ABC".codePointAt(1) +TEST_F(BuiltinsStringTest, codePointAt1) +{ + ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); + JSHandle thisVal = factory->NewFromCanBeCompressString("ABC"); + + auto ecmaRuntimeCallInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread, JSTaggedValue::Undefined(), 6); + ecmaRuntimeCallInfo->SetFunction(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetThis(thisVal.GetTaggedValue()); + ecmaRuntimeCallInfo->SetCallArg(0, JSTaggedValue(static_cast(1))); + + [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread, ecmaRuntimeCallInfo.get()); + JSTaggedValue result = BuiltinsString::CodePointAt(ecmaRuntimeCallInfo.get()); + + ASSERT_EQ(result.GetRawData(), JSTaggedValue(66).GetRawData()); +} + +// 'a'.concat('b', 'c', 'd') +TEST_F(BuiltinsStringTest, concat1) +{ + ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); + JSHandle thisStr = factory->NewFromCanBeCompressString("a"); + JSHandle val1 = factory->NewFromCanBeCompressString("b"); + JSHandle val2 = factory->NewFromCanBeCompressString("c"); + JSHandle val3 = factory->NewFromCanBeCompressString("d"); + + auto ecmaRuntimeCallInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread, JSTaggedValue::Undefined(), 10); + ecmaRuntimeCallInfo->SetFunction(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetThis(thisStr.GetTaggedValue()); + ecmaRuntimeCallInfo->SetCallArg(0, val1.GetTaggedValue()); + ecmaRuntimeCallInfo->SetCallArg(1, val2.GetTaggedValue()); + ecmaRuntimeCallInfo->SetCallArg(2, val3.GetTaggedValue()); + + [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread, ecmaRuntimeCallInfo.get()); + JSTaggedValue result = BuiltinsString::Concat(ecmaRuntimeCallInfo.get()); + ASSERT_TRUE(result.IsString()); + JSHandle resultHandle(thread, reinterpret_cast(result.GetRawData())); + JSTaggedValue test = factory->NewFromCanBeCompressString("abcd").GetTaggedValue(); + ASSERT_EQ(resultHandle->Compare(reinterpret_cast(test.GetRawData())), 0); +} + +// "abcabcabc".indexof('b') +TEST_F(BuiltinsStringTest, indexof1) +{ + ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); + JSHandle thisStr = factory->NewFromCanBeCompressString("abcabcabc"); + JSHandle val = factory->NewFromCanBeCompressString("b"); + + auto ecmaRuntimeCallInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread, JSTaggedValue::Undefined(), 6); + ecmaRuntimeCallInfo->SetFunction(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetThis(thisStr.GetTaggedValue()); + ecmaRuntimeCallInfo->SetCallArg(0, val.GetTaggedValue()); + + [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread, ecmaRuntimeCallInfo.get()); + JSTaggedValue result = BuiltinsString::IndexOf(ecmaRuntimeCallInfo.get()); + + ASSERT_EQ(result.GetRawData(), JSTaggedValue(1).GetRawData()); +} + +// "abcabcabc".indexof('b', 2) +TEST_F(BuiltinsStringTest, indexof2) +{ + ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); + JSHandle thisStr = factory->NewFromCanBeCompressString("abcabcabc"); + JSHandle val = factory->NewFromCanBeCompressString("b"); + + auto ecmaRuntimeCallInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread, JSTaggedValue::Undefined(), 8); + ecmaRuntimeCallInfo->SetFunction(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetThis(thisStr.GetTaggedValue()); + ecmaRuntimeCallInfo->SetCallArg(0, val.GetTaggedValue()); + ecmaRuntimeCallInfo->SetCallArg(1, JSTaggedValue(static_cast(2))); + + [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread, ecmaRuntimeCallInfo.get()); + JSTaggedValue result = BuiltinsString::IndexOf(ecmaRuntimeCallInfo.get()); + + ASSERT_EQ(result.GetRawData(), JSTaggedValue(4).GetRawData()); +} + +// "abcabcabc".indexof('d') +TEST_F(BuiltinsStringTest, indexof3) +{ + ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); + JSHandle thisStr = factory->NewFromCanBeCompressString("abcabcabc"); + JSHandle val = factory->NewFromCanBeCompressString("d"); + + auto ecmaRuntimeCallInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread, JSTaggedValue::Undefined(), 6); + ecmaRuntimeCallInfo->SetFunction(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetThis(thisStr.GetTaggedValue()); + ecmaRuntimeCallInfo->SetCallArg(0, val.GetTaggedValue()); + + [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread, ecmaRuntimeCallInfo.get()); + JSTaggedValue result = BuiltinsString::IndexOf(ecmaRuntimeCallInfo.get()); + + ASSERT_EQ(result.GetRawData(), JSTaggedValue(-1).GetRawData()); +} + +// "abcabcabc".lastIndexOf('b') +TEST_F(BuiltinsStringTest, lastIndexOf1) +{ + ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); + JSHandle thisStr = factory->NewFromCanBeCompressString("abcabcabc"); + JSHandle val = factory->NewFromCanBeCompressString("b"); + + auto ecmaRuntimeCallInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread, JSTaggedValue::Undefined(), 6); + ecmaRuntimeCallInfo->SetFunction(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetThis(thisStr.GetTaggedValue()); + ecmaRuntimeCallInfo->SetCallArg(0, val.GetTaggedValue()); + + [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread, ecmaRuntimeCallInfo.get()); + JSTaggedValue result = BuiltinsString::LastIndexOf(ecmaRuntimeCallInfo.get()); + + ASSERT_EQ(result.GetRawData(), JSTaggedValue(7).GetRawData()); +} +// "abcabcabc".lastIndexOf('b', 2) +TEST_F(BuiltinsStringTest, lastIndexOf2) +{ + ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); + JSHandle thisStr = factory->NewFromCanBeCompressString("abcabcabc"); + JSHandle val = factory->NewFromCanBeCompressString("b"); + + auto ecmaRuntimeCallInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread, JSTaggedValue::Undefined(), 8); + ecmaRuntimeCallInfo->SetFunction(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetThis(thisStr.GetTaggedValue()); + ecmaRuntimeCallInfo->SetCallArg(0, val.GetTaggedValue()); + ecmaRuntimeCallInfo->SetCallArg(1, JSTaggedValue(static_cast(2))); + + [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread, ecmaRuntimeCallInfo.get()); + JSTaggedValue result = BuiltinsString::LastIndexOf(ecmaRuntimeCallInfo.get()); + + ASSERT_EQ(result.GetRawData(), JSTaggedValue(1).GetRawData()); +} + +// "abcabcabc".lastIndexOf('d') +TEST_F(BuiltinsStringTest, lastIndexOf3) +{ + ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); + JSHandle thisStr = factory->NewFromCanBeCompressString("abcabcabc"); + JSHandle val = factory->NewFromCanBeCompressString("d"); + + auto ecmaRuntimeCallInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread, JSTaggedValue::Undefined(), 6); + ecmaRuntimeCallInfo->SetFunction(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetThis(thisStr.GetTaggedValue()); + ecmaRuntimeCallInfo->SetCallArg(0, val.GetTaggedValue()); + + [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread, ecmaRuntimeCallInfo.get()); + JSTaggedValue result = BuiltinsString::LastIndexOf(ecmaRuntimeCallInfo.get()); + + ASSERT_EQ(result.GetRawData(), JSTaggedValue(-1).GetRawData()); +} + +// "abcabcabc".includes('b') +TEST_F(BuiltinsStringTest, Includes2) +{ + ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); + JSHandle thisStr = factory->NewFromCanBeCompressString("abcabcabc"); + JSHandle val = factory->NewFromCanBeCompressString("b"); + + auto ecmaRuntimeCallInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread, JSTaggedValue::Undefined(), 6); + ecmaRuntimeCallInfo->SetFunction(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetThis(thisStr.GetTaggedValue()); + ecmaRuntimeCallInfo->SetCallArg(0, val.GetTaggedValue()); + + [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread, ecmaRuntimeCallInfo.get()); + JSTaggedValue result = BuiltinsString::Includes(ecmaRuntimeCallInfo.get()); + + ASSERT_EQ(result.GetRawData(), JSTaggedValue::True().GetRawData()); +} + +// "abccccccc".includes('b',2) +TEST_F(BuiltinsStringTest, Includes3) +{ + ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); + JSHandle thisStr = factory->NewFromCanBeCompressString("abccccccc"); + JSHandle val = factory->NewFromCanBeCompressString("b"); + + auto ecmaRuntimeCallInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread, JSTaggedValue::Undefined(), 8); + ecmaRuntimeCallInfo->SetFunction(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetThis(thisStr.GetTaggedValue()); + ecmaRuntimeCallInfo->SetCallArg(0, val.GetTaggedValue()); + ecmaRuntimeCallInfo->SetCallArg(1, JSTaggedValue(static_cast(2))); + + [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread, ecmaRuntimeCallInfo.get()); + JSTaggedValue result = BuiltinsString::Includes(ecmaRuntimeCallInfo.get()); + + ASSERT_EQ(result.GetRawData(), JSTaggedValue::False().GetRawData()); +} + +// "一二三四".includes('二') +TEST_F(BuiltinsStringTest, Includes4) +{ + ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); + JSHandle thisStr = factory->NewFromString("一二三四"); + JSHandle val = factory->NewFromString("二"); + + auto ecmaRuntimeCallInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread, JSTaggedValue::Undefined(), 6); + ecmaRuntimeCallInfo->SetFunction(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetThis(thisStr.GetTaggedValue()); + ecmaRuntimeCallInfo->SetCallArg(0, val.GetTaggedValue()); + + [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread, ecmaRuntimeCallInfo.get()); + JSTaggedValue result = BuiltinsString::Includes(ecmaRuntimeCallInfo.get()); + + ASSERT_EQ(result.GetRawData(), JSTaggedValue::True().GetRawData()); +} + +// "To be, or not to be, that is the question.".startsWith('To be') +TEST_F(BuiltinsStringTest, startsWith1) +{ + ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); + JSHandle thisStr = factory->NewFromCanBeCompressString("To be, or not to be, that is the question."); + JSHandle val = factory->NewFromCanBeCompressString("To be"); + + auto ecmaRuntimeCallInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread, JSTaggedValue::Undefined(), 6); + ecmaRuntimeCallInfo->SetFunction(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetThis(thisStr.GetTaggedValue()); + ecmaRuntimeCallInfo->SetCallArg(0, val.GetTaggedValue()); + + [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread, ecmaRuntimeCallInfo.get()); + JSTaggedValue result = BuiltinsString::StartsWith(ecmaRuntimeCallInfo.get()); + + ASSERT_EQ(result.GetRawData(), JSTaggedValue::True().GetRawData()); +} + +// "To be, or not to be, that is the question.".startsWith('not to be') +TEST_F(BuiltinsStringTest, startsWith2) +{ + ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); + JSHandle thisStr = factory->NewFromCanBeCompressString("To be, or not to be, that is the question."); + JSHandle val = factory->NewFromCanBeCompressString("not to be"); + + auto ecmaRuntimeCallInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread, JSTaggedValue::Undefined(), 6); + ecmaRuntimeCallInfo->SetFunction(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetThis(thisStr.GetTaggedValue()); + ecmaRuntimeCallInfo->SetCallArg(0, val.GetTaggedValue()); + + [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread, ecmaRuntimeCallInfo.get()); + JSTaggedValue result = BuiltinsString::StartsWith(ecmaRuntimeCallInfo.get()); + + ASSERT_EQ(result.GetRawData(), JSTaggedValue::False().GetRawData()); +} + +// "To be, or not to be, that is the question.".startsWith('not to be', 10) +TEST_F(BuiltinsStringTest, startsWith3) +{ + ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); + JSHandle thisStr = factory->NewFromCanBeCompressString("To be, or not to be, that is the question."); + JSHandle val = factory->NewFromCanBeCompressString("not to be"); + + auto ecmaRuntimeCallInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread, JSTaggedValue::Undefined(), 8); + ecmaRuntimeCallInfo->SetFunction(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetThis(thisStr.GetTaggedValue()); + ecmaRuntimeCallInfo->SetCallArg(0, val.GetTaggedValue()); + ecmaRuntimeCallInfo->SetCallArg(1, JSTaggedValue(static_cast(10))); + + [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread, ecmaRuntimeCallInfo.get()); + JSTaggedValue result = BuiltinsString::StartsWith(ecmaRuntimeCallInfo.get()); + + ASSERT_EQ(result.GetRawData(), JSTaggedValue::True().GetRawData()); +} + +// "To be, or not to be, that is the question.".endsWith('question.') +TEST_F(BuiltinsStringTest, endsWith1) +{ + ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); + JSHandle thisStr = factory->NewFromCanBeCompressString("To be, or not to be, that is the question."); + JSHandle val = factory->NewFromCanBeCompressString("question."); + + auto ecmaRuntimeCallInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread, JSTaggedValue::Undefined(), 6); + ecmaRuntimeCallInfo->SetFunction(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetThis(thisStr.GetTaggedValue()); + ecmaRuntimeCallInfo->SetCallArg(0, val.GetTaggedValue()); + + [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread, ecmaRuntimeCallInfo.get()); + JSTaggedValue result = BuiltinsString::EndsWith(ecmaRuntimeCallInfo.get()); + + ASSERT_EQ(result.GetRawData(), JSTaggedValue::True().GetRawData()); +} + +// "To be, or not to be, that is the question.".endsWith('to be') +TEST_F(BuiltinsStringTest, endsWith2) +{ + ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); + JSHandle thisStr = factory->NewFromCanBeCompressString("To be, or not to be, that is the question."); + JSHandle val = factory->NewFromCanBeCompressString("to be"); + + auto ecmaRuntimeCallInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread, JSTaggedValue::Undefined(), 6); + ecmaRuntimeCallInfo->SetFunction(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetThis(thisStr.GetTaggedValue()); + ecmaRuntimeCallInfo->SetCallArg(0, val.GetTaggedValue()); + + [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread, ecmaRuntimeCallInfo.get()); + JSTaggedValue result = BuiltinsString::EndsWith(ecmaRuntimeCallInfo.get()); + + ASSERT_EQ(result.GetRawData(), JSTaggedValue::False().GetRawData()); +} + +// "To be, or not to be, that is the question.".endsWith('to be', 19) +TEST_F(BuiltinsStringTest, endsWith3) +{ + ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); + JSHandle thisStr = factory->NewFromCanBeCompressString("To be, or not to be, that is the question."); + JSHandle val = factory->NewFromCanBeCompressString("to be"); + + auto ecmaRuntimeCallInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread, JSTaggedValue::Undefined(), 8); + ecmaRuntimeCallInfo->SetFunction(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetThis(thisStr.GetTaggedValue()); + ecmaRuntimeCallInfo->SetCallArg(0, val.GetTaggedValue()); + ecmaRuntimeCallInfo->SetCallArg(1, JSTaggedValue(static_cast(19))); + + [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread, ecmaRuntimeCallInfo.get()); + JSTaggedValue result = BuiltinsString::EndsWith(ecmaRuntimeCallInfo.get()); + + ASSERT_EQ(result.GetRawData(), JSTaggedValue::True().GetRawData()); +} + +// "有ABC".toLocaleLowerCase() +TEST_F(BuiltinsStringTest, toLocaleLowerCase2) +{ + ASSERT_NE(thread, nullptr); + ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); + JSHandle this_str = factory->NewFromString("有ABC"); + + auto ecmaRuntimeCallInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread, JSTaggedValue::Undefined(), 4); + ecmaRuntimeCallInfo->SetFunction(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetThis(this_str.GetTaggedValue()); + + [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread, ecmaRuntimeCallInfo.get()); + JSTaggedValue result = BuiltinsString::ToLocaleLowerCase(ecmaRuntimeCallInfo.get()); + ASSERT_TRUE(result.IsString()); + JSHandle result_handle(thread, reinterpret_cast(result.GetRawData())); + JSTaggedValue test = factory->NewFromString("有abc").GetTaggedValue(); + ASSERT_EQ(result_handle->Compare(reinterpret_cast(test.GetRawData())), 0); +} + +// "ABC".toLowerCase() +TEST_F(BuiltinsStringTest, toLowerCase1) +{ + ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); + JSHandle thisStr = factory->NewFromCanBeCompressString("ABC"); + + auto ecmaRuntimeCallInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread, JSTaggedValue::Undefined(), 4); + ecmaRuntimeCallInfo->SetFunction(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetThis(thisStr.GetTaggedValue()); + + [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread, ecmaRuntimeCallInfo.get()); + JSTaggedValue result = BuiltinsString::ToLowerCase(ecmaRuntimeCallInfo.get()); + ASSERT_TRUE(result.IsString()); + JSHandle resultHandle(thread, reinterpret_cast(result.GetRawData())); + JSHandle test = factory->NewFromCanBeCompressString("abc"); + ASSERT_TRUE(JSTaggedValue::SameValue(resultHandle.GetTaggedValue(), test.GetTaggedValue())); +} + +// "abc".toUpperCase() +TEST_F(BuiltinsStringTest, toUpperCase1) +{ + ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); + JSHandle thisStr = factory->NewFromCanBeCompressString("abc"); + + auto ecmaRuntimeCallInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread, JSTaggedValue::Undefined(), 4); + ecmaRuntimeCallInfo->SetFunction(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetThis(thisStr.GetTaggedValue()); + + [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread, ecmaRuntimeCallInfo.get()); + JSTaggedValue result = BuiltinsString::ToUpperCase(ecmaRuntimeCallInfo.get()); + ASSERT_TRUE(result.IsString()); + JSHandle resultHandle(thread, reinterpret_cast(result.GetRawData())); + JSTaggedValue test = factory->NewFromCanBeCompressString("ABC").GetTaggedValue(); + ASSERT_EQ(resultHandle->Compare(reinterpret_cast(test.GetRawData())), 0); +} + +// "abc".localecompare('b') +TEST_F(BuiltinsStringTest, localecompare1) +{ + ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); + JSHandle thisStr = factory->NewFromCanBeCompressString("abc"); + JSHandle val = factory->NewFromCanBeCompressString("b"); + + auto ecmaRuntimeCallInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread, JSTaggedValue::Undefined(), 6); + ecmaRuntimeCallInfo->SetFunction(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetThis(thisStr.GetTaggedValue()); + ecmaRuntimeCallInfo->SetCallArg(0, val.GetTaggedValue()); + + [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread, ecmaRuntimeCallInfo.get()); + JSTaggedValue result = BuiltinsString::LocaleCompare(ecmaRuntimeCallInfo.get()); + + ASSERT_EQ(result.GetRawData(), JSTaggedValue(-1).GetRawData()); +} + +// "abc".localecompare('abc') +TEST_F(BuiltinsStringTest, localecompare2) +{ + ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); + JSHandle thisStr = factory->NewFromCanBeCompressString("abc"); + JSHandle val = factory->NewFromCanBeCompressString("abc"); + + auto ecmaRuntimeCallInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread, JSTaggedValue::Undefined(), 6); + ecmaRuntimeCallInfo->SetFunction(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetThis(thisStr.GetTaggedValue()); + ecmaRuntimeCallInfo->SetCallArg(0, val.GetTaggedValue()); + + [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread, ecmaRuntimeCallInfo.get()); + JSTaggedValue result = BuiltinsString::LocaleCompare(ecmaRuntimeCallInfo.get()); + + ASSERT_EQ(result.GetRawData(), JSTaggedValue(0).GetRawData()); +} + +// "abc".localecompare('aa') +TEST_F(BuiltinsStringTest, localecompare3) +{ + ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); + JSHandle thisStr = factory->NewFromCanBeCompressString("abc"); + JSHandle val = factory->NewFromCanBeCompressString("aa"); + + auto ecmaRuntimeCallInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread, JSTaggedValue::Undefined(), 6); + ecmaRuntimeCallInfo->SetFunction(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetThis(thisStr.GetTaggedValue()); + ecmaRuntimeCallInfo->SetCallArg(0, val.GetTaggedValue()); + + [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread, ecmaRuntimeCallInfo.get()); + JSTaggedValue result = BuiltinsString::LocaleCompare(ecmaRuntimeCallInfo.get()); + + ASSERT_EQ(result.GetRawData(), JSTaggedValue(1).GetRawData()); +} + +// "abc".normalize('NFC') +TEST_F(BuiltinsStringTest, normalize1) +{ + ASSERT_NE(thread, nullptr); + ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); + JSHandle this_str = factory->NewFromString("abc"); + JSHandle val = factory->NewFromString("NFC"); + + auto ecmaRuntimeCallInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread, JSTaggedValue::Undefined(), 6); + ecmaRuntimeCallInfo->SetFunction(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetThis(this_str.GetTaggedValue()); + ecmaRuntimeCallInfo->SetCallArg(0, val.GetTaggedValue()); + + [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread, ecmaRuntimeCallInfo.get()); + JSTaggedValue result = BuiltinsString::Normalize(ecmaRuntimeCallInfo.get()); + ASSERT_TRUE(result.IsString()); + JSHandle result_handle(thread, reinterpret_cast(result.GetRawData())); + JSTaggedValue test = factory->NewFromString("abc").GetTaggedValue(); + ASSERT_EQ(result_handle->Compare(reinterpret_cast(test.GetRawData())), 0); +} + +// "hello".padEnd(10) +TEST_F(BuiltinsStringTest, padEnd1) +{ + ASSERT_NE(thread, nullptr); + + ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); + JSHandle this_str = factory->NewFromString("hello"); + + auto ecmaRuntimeCallInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread, JSTaggedValue::Undefined(), 6); + ecmaRuntimeCallInfo->SetFunction(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetThis(this_str.GetTaggedValue()); + ecmaRuntimeCallInfo->SetCallArg(0, JSTaggedValue(static_cast(10))); + + [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread, ecmaRuntimeCallInfo.get()); + JSTaggedValue result = BuiltinsString::padEnd(ecmaRuntimeCallInfo.get()); + ASSERT_TRUE(result.IsString()); + JSHandle result_handle(thread, reinterpret_cast(result.GetRawData())); + JSTaggedValue test = factory->NewFromString("hello ").GetTaggedValue(); + ASSERT_EQ(result_handle->Compare(reinterpret_cast(test.GetRawData())), 0); +} + +// "hello".padEnd(11, world) +TEST_F(BuiltinsStringTest, padEnd2) +{ + ASSERT_NE(thread, nullptr); + + ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); + JSHandle this_str = factory->NewFromString("hello"); + JSHandle fillString = factory->NewFromString("world"); + + auto ecmaRuntimeCallInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread, JSTaggedValue::Undefined(), 8); + ecmaRuntimeCallInfo->SetFunction(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetThis(this_str.GetTaggedValue()); + ecmaRuntimeCallInfo->SetCallArg(0, JSTaggedValue(static_cast(11))); + ecmaRuntimeCallInfo->SetCallArg(1, fillString.GetTaggedValue()); + + [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread, ecmaRuntimeCallInfo.get()); + JSTaggedValue result = BuiltinsString::padEnd(ecmaRuntimeCallInfo.get()); + ASSERT_TRUE(result.IsString()); + JSHandle result_handle(thread, reinterpret_cast(result.GetRawData())); + JSTaggedValue test = factory->NewFromString("helloworldw").GetTaggedValue(); + ASSERT_EQ(result_handle->Compare(reinterpret_cast(test.GetRawData())), 0); +} + +// "world".padStart(10) +TEST_F(BuiltinsStringTest, padStart1) +{ + ASSERT_NE(thread, nullptr); + + ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); + JSHandle this_str = factory->NewFromString("world"); + + auto ecmaRuntimeCallInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread, JSTaggedValue::Undefined(), 6); + ecmaRuntimeCallInfo->SetFunction(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetThis(this_str.GetTaggedValue()); + ecmaRuntimeCallInfo->SetCallArg(0, JSTaggedValue(static_cast(10))); + + [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread, ecmaRuntimeCallInfo.get()); + JSTaggedValue result = BuiltinsString::padStart(ecmaRuntimeCallInfo.get()); + ASSERT_TRUE(result.IsString()); + JSHandle result_handle(thread, reinterpret_cast(result.GetRawData())); + JSTaggedValue test = factory->NewFromString(" world").GetTaggedValue(); + ASSERT_EQ(result_handle->Compare(reinterpret_cast(test.GetRawData())), 0); +} + +// "world".padStart(11, "hello") +TEST_F(BuiltinsStringTest, padStart2) +{ + ASSERT_NE(thread, nullptr); + + ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); + JSHandle this_str = factory->NewFromString("world"); + JSHandle fillString = factory->NewFromString("hello"); + + auto ecmaRuntimeCallInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread, JSTaggedValue::Undefined(), 8); + ecmaRuntimeCallInfo->SetFunction(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetThis(this_str.GetTaggedValue()); + ecmaRuntimeCallInfo->SetCallArg(0, JSTaggedValue(static_cast(11))); + ecmaRuntimeCallInfo->SetCallArg(1, fillString.GetTaggedValue()); + + [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread, ecmaRuntimeCallInfo.get()); + JSTaggedValue result = BuiltinsString::padStart(ecmaRuntimeCallInfo.get()); + ASSERT_TRUE(result.IsString()); + JSHandle result_handle(thread, reinterpret_cast(result.GetRawData())); + JSTaggedValue test = factory->NewFromString("hellohworld").GetTaggedValue(); + ASSERT_EQ(result_handle->Compare(reinterpret_cast(test.GetRawData())), 0); +} + +// "abc".repeat(5) +TEST_F(BuiltinsStringTest, repeat1) +{ + ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); + JSHandle thisStr = factory->NewFromCanBeCompressString("abc"); + + auto ecmaRuntimeCallInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread, JSTaggedValue::Undefined(), 6); + ecmaRuntimeCallInfo->SetFunction(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetThis(thisStr.GetTaggedValue()); + ecmaRuntimeCallInfo->SetCallArg(0, JSTaggedValue(static_cast(5))); + + [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread, ecmaRuntimeCallInfo.get()); + JSTaggedValue result = BuiltinsString::Repeat(ecmaRuntimeCallInfo.get()); + ASSERT_TRUE(result.IsString()); + JSHandle resultHandle(thread, reinterpret_cast(result.GetRawData())); + JSTaggedValue test = factory->NewFromCanBeCompressString("abcabcabcabcabc").GetTaggedValue(); + ASSERT_EQ(resultHandle->Compare(reinterpret_cast(test.GetRawData())), 0); +} + +// 'The morning is upon us.'.slice(4, -2) +TEST_F(BuiltinsStringTest, slice1) +{ + ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); + JSHandle thisStr = factory->NewFromCanBeCompressString("The morning is upon us."); + + auto ecmaRuntimeCallInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread, JSTaggedValue::Undefined(), 8); + ecmaRuntimeCallInfo->SetFunction(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetThis(thisStr.GetTaggedValue()); + ecmaRuntimeCallInfo->SetCallArg(0, JSTaggedValue(static_cast(4))); + ecmaRuntimeCallInfo->SetCallArg(1, JSTaggedValue(static_cast(-2))); + + [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread, ecmaRuntimeCallInfo.get()); + JSTaggedValue result = BuiltinsString::Slice(ecmaRuntimeCallInfo.get()); + ASSERT_TRUE(result.IsString()); + JSHandle resultHandle(thread, reinterpret_cast(result.GetRawData())); + JSTaggedValue test = factory->NewFromCanBeCompressString("morning is upon u").GetTaggedValue(); + ASSERT_EQ(resultHandle->Compare(reinterpret_cast(test.GetRawData())), 0); +} + +// 'The morning is upon us.'.slice(12) +TEST_F(BuiltinsStringTest, slice2) +{ + ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); + JSHandle thisStr = factory->NewFromCanBeCompressString("The morning is upon us."); + + auto ecmaRuntimeCallInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread, JSTaggedValue::Undefined(), 6); + ecmaRuntimeCallInfo->SetFunction(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetThis(thisStr.GetTaggedValue()); + ecmaRuntimeCallInfo->SetCallArg(0, JSTaggedValue(static_cast(12))); + + [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread, ecmaRuntimeCallInfo.get()); + JSTaggedValue result = BuiltinsString::Slice(ecmaRuntimeCallInfo.get()); + ASSERT_TRUE(result.IsString()); + JSHandle resultHandle(thread, reinterpret_cast(result.GetRawData())); + JSTaggedValue test = factory->NewFromCanBeCompressString("is upon us.").GetTaggedValue(); + ASSERT_EQ(resultHandle->Compare(reinterpret_cast(test.GetRawData())), 0); +} + +// 'Mozilla'.substring(3, -3) +TEST_F(BuiltinsStringTest, substring1) +{ + ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); + JSHandle thisStr = factory->NewFromCanBeCompressString("Mozilla"); + + auto ecmaRuntimeCallInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread, JSTaggedValue::Undefined(), 8); + ecmaRuntimeCallInfo->SetFunction(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetThis(thisStr.GetTaggedValue()); + ecmaRuntimeCallInfo->SetCallArg(0, JSTaggedValue(static_cast(3))); + ecmaRuntimeCallInfo->SetCallArg(1, JSTaggedValue(static_cast(-3))); + + [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread, ecmaRuntimeCallInfo.get()); + JSTaggedValue result = BuiltinsString::Substring(ecmaRuntimeCallInfo.get()); + ASSERT_TRUE(result.IsString()); + JSHandle resultHandle(thread, reinterpret_cast(result.GetRawData())); + JSTaggedValue test = factory->NewFromCanBeCompressString("Moz").GetTaggedValue(); + ASSERT_EQ(resultHandle->Compare(reinterpret_cast(test.GetRawData())), 0); +} + +// 'Mozilla'.substring(7, 4) +TEST_F(BuiltinsStringTest, substring2) +{ + ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); + JSHandle thisStr = factory->NewFromCanBeCompressString("Mozilla"); + + auto ecmaRuntimeCallInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread, JSTaggedValue::Undefined(), 8); + ecmaRuntimeCallInfo->SetFunction(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetThis(thisStr.GetTaggedValue()); + ecmaRuntimeCallInfo->SetCallArg(0, JSTaggedValue(static_cast(7))); + ecmaRuntimeCallInfo->SetCallArg(1, JSTaggedValue(static_cast(4))); + + [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread, ecmaRuntimeCallInfo.get()); + JSTaggedValue result = BuiltinsString::Substring(ecmaRuntimeCallInfo.get()); + ASSERT_TRUE(result.IsString()); + JSHandle resultHandle(thread, reinterpret_cast(result.GetRawData())); + JSTaggedValue test = factory->NewFromCanBeCompressString("lla").GetTaggedValue(); + ASSERT_EQ(resultHandle->Compare(reinterpret_cast(test.GetRawData())), 0); +} + +// " Hello world! ".trim() +TEST_F(BuiltinsStringTest, trim1) +{ + ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); + JSHandle thisStr = factory->NewFromCanBeCompressString(" Hello world! "); + + auto ecmaRuntimeCallInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread, JSTaggedValue::Undefined(), 4); + ecmaRuntimeCallInfo->SetFunction(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetThis(thisStr.GetTaggedValue()); + + [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread, ecmaRuntimeCallInfo.get()); + JSTaggedValue result = BuiltinsString::Trim(ecmaRuntimeCallInfo.get()); + ASSERT_TRUE(result.IsString()); + JSHandle resultHandle(thread, reinterpret_cast(result.GetRawData())); + JSTaggedValue test = factory->NewFromCanBeCompressString("Hello world!").GetTaggedValue(); + ASSERT_EQ(resultHandle->Compare(reinterpret_cast(test.GetRawData())), 0); +} + +TEST_F(BuiltinsStringTest, trim2) +{ + auto ecmaVM = thread->GetEcmaVM(); + JSHandle env = ecmaVM->GetGlobalEnv(); + + ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); + JSHandle thisStr = factory->NewFromCanBeCompressString(" Hello world! "); + JSHandle stringObject(env->GetStringFunction()); + JSHandle value(thread, JSTaggedValue(thisStr.GetTaggedValue().GetTaggedObject())); + JSHandle str = factory->NewJSPrimitiveRef(stringObject, value); + + auto ecmaRuntimeCallInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread, JSTaggedValue::Undefined(), 4); + ecmaRuntimeCallInfo->SetFunction(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetThis(str.GetTaggedValue()); + + [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread, ecmaRuntimeCallInfo.get()); + JSTaggedValue result = BuiltinsString::Trim(ecmaRuntimeCallInfo.get()); + ASSERT_TRUE(result.IsString()); + JSHandle resultHandle(thread, reinterpret_cast(result.GetRawData())); + JSTaggedValue test = factory->NewFromCanBeCompressString("Hello world!").GetTaggedValue(); + ASSERT_EQ(resultHandle->Compare(reinterpret_cast(test.GetRawData())), 0); +} + +// " Hello world! ".trimEnd() +TEST_F(BuiltinsStringTest, trimEnd1) +{ + ASSERT_NE(thread, nullptr); + + ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); + JSHandle this_str = factory->NewFromString(" Hello world! "); + + auto ecmaRuntimeCallInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread, JSTaggedValue::Undefined(), 4); + ecmaRuntimeCallInfo->SetFunction(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetThis(this_str.GetTaggedValue()); + + [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread, ecmaRuntimeCallInfo.get()); + JSTaggedValue result = BuiltinsString::TrimEnd(ecmaRuntimeCallInfo.get()); + ASSERT_TRUE(result.IsString()); + JSHandle result_handle(thread, reinterpret_cast(result.GetRawData())); + JSTaggedValue test = factory->NewFromString(" Hello world!").GetTaggedValue(); + ASSERT_EQ(result_handle->Compare(reinterpret_cast(test.GetRawData())), 0); +} + +// new String(" Hello world! ").trimEnd() +TEST_F(BuiltinsStringTest, trimEnd2) +{ + ASSERT_NE(thread, nullptr); + auto ecmaVm = thread->GetEcmaVM(); + JSHandle env = ecmaVm->GetGlobalEnv(); + + ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); + JSHandle this_str = factory->NewFromString(" Hello world! "); + JSHandle string_object(env->GetStringFunction()); + JSHandle value(thread, JSTaggedValue(this_str.GetTaggedValue().GetHeapObject())); + JSHandle str = factory->NewJSPrimitiveRef(string_object, value); + + auto ecmaRuntimeCallInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread, JSTaggedValue::Undefined(), 4); + ecmaRuntimeCallInfo->SetFunction(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetThis(str.GetTaggedValue()); + + [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread, ecmaRuntimeCallInfo.get()); + JSTaggedValue result = BuiltinsString::TrimEnd(ecmaRuntimeCallInfo.get()); + ASSERT_TRUE(result.IsString()); + JSHandle result_handle(thread, reinterpret_cast(result.GetRawData())); + JSTaggedValue test = factory->NewFromString(" Hello world!").GetTaggedValue(); + ASSERT_EQ(result_handle->Compare(reinterpret_cast(test.GetRawData())), 0); +} + +// " Hello world! ".trimStart() +TEST_F(BuiltinsStringTest, trimStart1) +{ + ASSERT_NE(thread, nullptr); + + ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); + JSHandle this_str = factory->NewFromString(" Hello world! "); + + auto ecmaRuntimeCallInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread, JSTaggedValue::Undefined(), 4); + ecmaRuntimeCallInfo->SetFunction(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetThis(this_str.GetTaggedValue()); + + [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread, ecmaRuntimeCallInfo.get()); + JSTaggedValue result = BuiltinsString::TrimStart(ecmaRuntimeCallInfo.get()); + ASSERT_TRUE(result.IsString()); + JSHandle result_handle(thread, reinterpret_cast(result.GetRawData())); + JSTaggedValue test = factory->NewFromString("Hello world! ").GetTaggedValue(); + ASSERT_EQ(result_handle->Compare(reinterpret_cast(test.GetRawData())), 0); +} + +// new String(" Hello world! ").trimStart() +TEST_F(BuiltinsStringTest, trimStart2) +{ + ASSERT_NE(thread, nullptr); + auto ecmaVm = thread->GetEcmaVM(); + JSHandle env = ecmaVm->GetGlobalEnv(); + + ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); + JSHandle this_str = factory->NewFromString(" Hello world! "); + JSHandle string_object(env->GetStringFunction()); + JSHandle value(thread, JSTaggedValue(this_str.GetTaggedValue().GetHeapObject())); + JSHandle str = factory->NewJSPrimitiveRef(string_object, value); + + auto ecmaRuntimeCallInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread, JSTaggedValue::Undefined(), 4); + ecmaRuntimeCallInfo->SetFunction(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetThis(str.GetTaggedValue()); + + [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread, ecmaRuntimeCallInfo.get()); + JSTaggedValue result = BuiltinsString::TrimStart(ecmaRuntimeCallInfo.get()); + ASSERT_TRUE(result.IsString()); + JSHandle result_handle(thread, reinterpret_cast(result.GetRawData())); + JSTaggedValue test = factory->NewFromString("Hello world! ").GetTaggedValue(); + ASSERT_EQ(result_handle->Compare(reinterpret_cast(test.GetRawData())), 0); +} + +// new String("abcabcabc").toString(); +TEST_F(BuiltinsStringTest, ToString) +{ + auto ecmaVM = thread->GetEcmaVM(); + JSHandle env = ecmaVM->GetGlobalEnv(); + + ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); + JSHandle thisStr = factory->NewFromCanBeCompressString("abcabcabc"); + JSHandle stringObject(env->GetStringFunction()); + JSHandle value(thread, JSTaggedValue(thisStr.GetTaggedValue().GetTaggedObject())); + JSHandle str = factory->NewJSPrimitiveRef(stringObject, value); + + auto ecmaRuntimeCallInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread, JSTaggedValue::Undefined(), 4); + ecmaRuntimeCallInfo->SetFunction(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetThis(str.GetTaggedValue()); + + [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread, ecmaRuntimeCallInfo.get()); + JSTaggedValue result = BuiltinsString::ToString(ecmaRuntimeCallInfo.get()); + ASSERT_TRUE(result.IsString()); + JSHandle resultHandle(thread, reinterpret_cast(result.GetRawData())); + JSTaggedValue test = JSTaggedValue(*thisStr); + ASSERT_EQ(result.GetRawData(), test.GetRawData()); +} + +TEST_F(BuiltinsStringTest, ValueOf) +{ + auto ecmaVM = thread->GetEcmaVM(); + JSHandle env = ecmaVM->GetGlobalEnv(); + + ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); + JSHandle thisStr = factory->NewFromCanBeCompressString("abcabcabc"); + JSHandle stringObject(env->GetStringFunction()); + JSHandle value(thread, JSTaggedValue(thisStr.GetTaggedValue().GetTaggedObject())); + JSHandle str = factory->NewJSPrimitiveRef(stringObject, value); + + auto ecmaRuntimeCallInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread, JSTaggedValue::Undefined(), 4); + ecmaRuntimeCallInfo->SetFunction(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetThis(str.GetTaggedValue()); + + [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread, ecmaRuntimeCallInfo.get()); + JSTaggedValue result = BuiltinsString::ValueOf(ecmaRuntimeCallInfo.get()); + ASSERT_TRUE(result.IsString()); + JSHandle resultHandle(thread, reinterpret_cast(result.GetRawData())); + JSTaggedValue test = JSTaggedValue(*thisStr); + ASSERT_EQ(result.GetRawData(), test.GetRawData()); +} + +static inline JSFunction *BuiltinsStringTestCreate(JSThread *thread) +{ + EcmaVM *ecmaVM = thread->GetEcmaVM(); + JSHandle globalEnv = ecmaVM->GetGlobalEnv(); + return globalEnv->GetObjectFunction().GetObject(); +} + +TEST_F(BuiltinsStringTest, Raw) +{ + ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); + JSHandle foo(factory->NewFromCanBeCompressString("foo")); + JSHandle bar(factory->NewFromCanBeCompressString("bar")); + JSHandle baz(factory->NewFromCanBeCompressString("baz")); + JSHandle rawArray = JSHandle::Cast(JSArray::ArrayCreate(thread, JSTaggedNumber(0))); + JSHandle obj(rawArray); + JSHandle key0(thread, JSTaggedValue(0)); + PropertyDescriptor desc0(thread, foo); + JSArray::DefineOwnProperty(thread, obj, key0, desc0); + JSHandle key1(thread, JSTaggedValue(1)); + PropertyDescriptor desc1(thread, bar); + JSArray::DefineOwnProperty(thread, obj, key1, desc1); + JSHandle key2(thread, JSTaggedValue(2)); + PropertyDescriptor desc2(thread, baz); + JSArray::DefineOwnProperty(thread, obj, key2, desc2); + + JSHandle constructor(thread, BuiltinsStringTestCreate(thread)); + JSHandle templateString( + factory->NewJSObjectByConstructor(JSHandle(constructor), constructor)); + JSHandle rawKey(factory->NewFromCanBeCompressString("raw")); + JSObject::SetProperty(thread, templateString, rawKey, rawArray); + JSHandle test = factory->NewFromCanBeCompressString("foo5barJavaScriptbaz"); + + JSHandle javascript = factory->NewFromCanBeCompressString("JavaScript"); + + auto ecmaRuntimeCallInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread, JSTaggedValue::Undefined(), 10); + ecmaRuntimeCallInfo->SetFunction(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetThis(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetCallArg(0, JSTaggedValue(templateString.GetObject())); + ecmaRuntimeCallInfo->SetCallArg(1, JSTaggedValue(static_cast(5))); + ecmaRuntimeCallInfo->SetCallArg(2, javascript.GetTaggedValue()); + + [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread, ecmaRuntimeCallInfo.get()); + JSTaggedValue result = BuiltinsString::Raw(ecmaRuntimeCallInfo.get()); + ASSERT_TRUE(result.IsString()); + ASSERT_TRUE(EcmaString::StringsAreEqual(reinterpret_cast(result.GetRawData()), *test)); +} + +TEST_F(BuiltinsStringTest, Replace) +{ + ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); + JSHandle thisStr = factory->NewFromCanBeCompressString("Twas the night before Xmas..."); + JSHandle searchStr = factory->NewFromCanBeCompressString("Xmas"); + JSHandle replaceStr = factory->NewFromCanBeCompressString("Christmas"); + JSHandle expected = factory->NewFromCanBeCompressString("Twas the night before Christmas..."); + + auto ecmaRuntimeCallInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread, JSTaggedValue::Undefined(), 8); + ecmaRuntimeCallInfo->SetFunction(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetThis(thisStr.GetTaggedValue()); + ecmaRuntimeCallInfo->SetCallArg(0, searchStr.GetTaggedValue()); + ecmaRuntimeCallInfo->SetCallArg(1, replaceStr.GetTaggedValue()); + + [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread, ecmaRuntimeCallInfo.get()); + JSTaggedValue result = BuiltinsString::Replace(ecmaRuntimeCallInfo.get()); + TestHelper::TearDownFrame(thread, prev); + + ASSERT_TRUE(result.IsString()); + ASSERT_TRUE(EcmaString::StringsAreEqual(reinterpret_cast(result.GetRawData()), *expected)); + + JSHandle replaceStr1 = factory->NewFromCanBeCompressString("abc$$"); + JSHandle expected1 = factory->NewFromCanBeCompressString("Twas the night before abc$..."); + + auto ecmaRuntimeCallInfo1 = TestHelper::CreateEcmaRuntimeCallInfo(thread, JSTaggedValue::Undefined(), 8); + ecmaRuntimeCallInfo1->SetFunction(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo1->SetThis(thisStr.GetTaggedValue()); + ecmaRuntimeCallInfo1->SetCallArg(0, searchStr.GetTaggedValue()); + ecmaRuntimeCallInfo1->SetCallArg(1, replaceStr1.GetTaggedValue()); + + prev = TestHelper::SetupFrame(thread, ecmaRuntimeCallInfo1.get()); + JSTaggedValue result1 = BuiltinsString::Replace(ecmaRuntimeCallInfo1.get()); + TestHelper::TearDownFrame(thread, prev); + + JSHandle resultString1(thread, result1); + ASSERT_TRUE(result1.IsString()); + ASSERT_TRUE(EcmaString::StringsAreEqual(*resultString1, *expected1)); + + JSHandle replaceStr2 = factory->NewFromCanBeCompressString("abc$$dd"); + JSHandle expected2 = factory->NewFromCanBeCompressString("Twas the night before abc$dd..."); + + auto ecmaRuntimeCallInfo2 = TestHelper::CreateEcmaRuntimeCallInfo(thread, JSTaggedValue::Undefined(), 8); + ecmaRuntimeCallInfo2->SetFunction(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo2->SetThis(thisStr.GetTaggedValue()); + ecmaRuntimeCallInfo2->SetCallArg(0, searchStr.GetTaggedValue()); + ecmaRuntimeCallInfo2->SetCallArg(1, replaceStr2.GetTaggedValue()); + + prev = TestHelper::SetupFrame(thread, ecmaRuntimeCallInfo2.get()); + JSTaggedValue result2 = BuiltinsString::Replace(ecmaRuntimeCallInfo2.get()); + TestHelper::TearDownFrame(thread, prev); + + JSHandle resultString2(thread, result2); + ASSERT_TRUE(result2.IsString()); + ASSERT_TRUE(EcmaString::StringsAreEqual(*resultString2, *expected2)); + + JSHandle replaceStr3 = factory->NewFromCanBeCompressString("abc$&dd"); + JSHandle expected3 = factory->NewFromCanBeCompressString("Twas the night before abcXmasdd..."); + + auto ecmaRuntimeCallInfo3 = TestHelper::CreateEcmaRuntimeCallInfo(thread, JSTaggedValue::Undefined(), 8); + ecmaRuntimeCallInfo3->SetFunction(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo3->SetThis(thisStr.GetTaggedValue()); + ecmaRuntimeCallInfo3->SetCallArg(0, searchStr.GetTaggedValue()); + ecmaRuntimeCallInfo3->SetCallArg(1, replaceStr3.GetTaggedValue()); + + prev = TestHelper::SetupFrame(thread, ecmaRuntimeCallInfo3.get()); + JSTaggedValue result3 = BuiltinsString::Replace(ecmaRuntimeCallInfo3.get()); + TestHelper::TearDownFrame(thread, prev); + + JSHandle resultString3(thread, result3); + ASSERT_TRUE(result3.IsString()); + ASSERT_TRUE(EcmaString::StringsAreEqual(*resultString3, *expected3)); + + JSHandle replaceStr4 = factory->NewFromCanBeCompressString("abc$`dd"); + JSHandle expected4 = + factory->NewFromCanBeCompressString("Twas the night before abcTwas the night before dd..."); + + auto ecmaRuntimeCallInfo4 = TestHelper::CreateEcmaRuntimeCallInfo(thread, JSTaggedValue::Undefined(), 8); + ecmaRuntimeCallInfo4->SetFunction(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo4->SetThis(thisStr.GetTaggedValue()); + ecmaRuntimeCallInfo4->SetCallArg(0, searchStr.GetTaggedValue()); + ecmaRuntimeCallInfo4->SetCallArg(1, replaceStr4.GetTaggedValue()); + + prev = TestHelper::SetupFrame(thread, ecmaRuntimeCallInfo4.get()); + JSTaggedValue result4 = BuiltinsString::Replace(ecmaRuntimeCallInfo4.get()); + TestHelper::TearDownFrame(thread, prev); + + JSHandle resultString4(thread, result4); + ASSERT_TRUE(result4.IsString()); + ASSERT_TRUE(EcmaString::StringsAreEqual(*resultString4, *expected4)); +} + +TEST_F(BuiltinsStringTest, Replace2) +{ + ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); + JSHandle thisStr = factory->NewFromCanBeCompressString("Twas the night before Xmas..."); + JSHandle searchStr = factory->NewFromCanBeCompressString("Xmas"); + JSHandle replaceStr = factory->NewFromCanBeCompressString("abc$\'dd"); + JSHandle expected = factory->NewFromCanBeCompressString("Twas the night before abc...dd..."); + + auto ecmaRuntimeCallInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread, JSTaggedValue::Undefined(), 8); + ecmaRuntimeCallInfo->SetFunction(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetThis(thisStr.GetTaggedValue()); + ecmaRuntimeCallInfo->SetCallArg(0, searchStr.GetTaggedValue()); + ecmaRuntimeCallInfo->SetCallArg(1, replaceStr.GetTaggedValue()); + + [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread, ecmaRuntimeCallInfo.get()); + JSTaggedValue result = BuiltinsString::Replace(ecmaRuntimeCallInfo.get()); + TestHelper::TearDownFrame(thread, prev); + + ASSERT_TRUE(result.IsString()); + ASSERT_TRUE(EcmaString::StringsAreEqual(reinterpret_cast(result.GetRawData()), *expected)); + + JSHandle replaceStr2 = factory->NewFromCanBeCompressString("abc$`dd$\'$ff"); + JSHandle expected2 = + factory->NewFromCanBeCompressString("Twas the night before abcTwas the night before dd...$ff..."); + + auto ecmaRuntimeCallInfo2 = TestHelper::CreateEcmaRuntimeCallInfo(thread, JSTaggedValue::Undefined(), 8); + ecmaRuntimeCallInfo2->SetFunction(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo2->SetThis(thisStr.GetTaggedValue()); + ecmaRuntimeCallInfo2->SetCallArg(0, searchStr.GetTaggedValue()); + ecmaRuntimeCallInfo2->SetCallArg(1, replaceStr2.GetTaggedValue()); + + prev = TestHelper::SetupFrame(thread, ecmaRuntimeCallInfo2.get()); + JSTaggedValue result2 = BuiltinsString::Replace(ecmaRuntimeCallInfo2.get()); + TestHelper::TearDownFrame(thread, prev); + + JSHandle resultString2(thread, result2); + ASSERT_TRUE(result2.IsString()); + ASSERT_TRUE(EcmaString::StringsAreEqual(*resultString2, *expected2)); + + JSHandle replaceStr3 = factory->NewFromCanBeCompressString("abc$`dd$\'$"); + JSHandle expected3 = + factory->NewFromCanBeCompressString("Twas the night before abcTwas the night before dd...$..."); + + auto ecmaRuntimeCallInfo3 = TestHelper::CreateEcmaRuntimeCallInfo(thread, JSTaggedValue::Undefined(), 8); + ecmaRuntimeCallInfo3->SetFunction(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo3->SetThis(thisStr.GetTaggedValue()); + ecmaRuntimeCallInfo3->SetCallArg(0, searchStr.GetTaggedValue()); + ecmaRuntimeCallInfo3->SetCallArg(1, replaceStr3.GetTaggedValue()); + + prev = TestHelper::SetupFrame(thread, ecmaRuntimeCallInfo3.get()); + JSTaggedValue result3 = BuiltinsString::Replace(ecmaRuntimeCallInfo3.get()); + TestHelper::TearDownFrame(thread, prev); + + JSHandle resultString3(thread, result3); + ASSERT_TRUE(result3.IsString()); + ASSERT_TRUE(EcmaString::StringsAreEqual(*resultString3, *expected3)); + + JSHandle replaceStr4 = factory->NewFromCanBeCompressString("abc$`dd$$"); + JSHandle expected4 = + factory->NewFromCanBeCompressString("Twas the night before abcTwas the night before dd$..."); + + auto ecmaRuntimeCallInfo4 = TestHelper::CreateEcmaRuntimeCallInfo(thread, JSTaggedValue::Undefined(), 8); + ecmaRuntimeCallInfo4->SetFunction(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo4->SetThis(thisStr.GetTaggedValue()); + ecmaRuntimeCallInfo4->SetCallArg(0, searchStr.GetTaggedValue()); + ecmaRuntimeCallInfo4->SetCallArg(1, replaceStr4.GetTaggedValue()); + + prev = TestHelper::SetupFrame(thread, ecmaRuntimeCallInfo4.get()); + JSTaggedValue result4 = BuiltinsString::Replace(ecmaRuntimeCallInfo4.get()); + TestHelper::TearDownFrame(thread, prev); + + ASSERT_TRUE(result4.IsString()); + JSHandle resultString4(thread, result4); + ASSERT_TRUE(EcmaString::StringsAreEqual(*resultString4, *expected4)); +} + +TEST_F(BuiltinsStringTest, Replace3) +{ + ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); + JSHandle thisStr = factory->NewFromCanBeCompressString("Twas the night before Xmas..."); + JSHandle searchStr = factory->NewFromCanBeCompressString("Xmas"); + JSHandle replaceStr = factory->NewFromCanBeCompressString("$&a $` $\' $2 $01 $$1 $21 $32 a"); + JSHandle expected = factory->NewFromCanBeCompressString( + "Twas the night before Xmasa Twas the night before ... $2 $01 $1 $21 $32 a..."); + + auto ecmaRuntimeCallInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread, JSTaggedValue::Undefined(), 8); + ecmaRuntimeCallInfo->SetFunction(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetThis(thisStr.GetTaggedValue()); + ecmaRuntimeCallInfo->SetCallArg(0, searchStr.GetTaggedValue()); + ecmaRuntimeCallInfo->SetCallArg(1, replaceStr.GetTaggedValue()); + + [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread, ecmaRuntimeCallInfo.get()); + JSTaggedValue result = BuiltinsString::Replace(ecmaRuntimeCallInfo.get()); + + ASSERT_TRUE(result.IsString()); + ASSERT_TRUE(EcmaString::StringsAreEqual(reinterpret_cast(result.GetRawData()), *expected)); +} + +TEST_F(BuiltinsStringTest, Replace4) +{ + // invoke RegExpConstructor method + JSHandle pattern1 = + thread->GetEcmaVM()->GetFactory()->NewFromCanBeCompressString("quick\\s(brown).+?(jumps)"); + JSHandle flags1 = thread->GetEcmaVM()->GetFactory()->NewFromCanBeCompressString("iug"); + JSTaggedValue result1 = CreateRegExpObjByPatternAndFlags(thread, pattern1, flags1); + JSHandle searchStr(thread, reinterpret_cast(result1.GetRawData())); + JSHandle expected = thread->GetEcmaVM()->GetFactory()->NewFromCanBeCompressString( + "The Quick Brown Fox Jumpsa The Over The Lazy Dog Jumps Brown $1 Jumps1 $32 a Over The Lazy Dog"); + + // make dyn_runtime_call_info2 + JSHandle thisStr = + thread->GetEcmaVM()->GetFactory()->NewFromCanBeCompressString("The Quick Brown Fox Jumps Over The Lazy Dog"); + JSHandle replaceStr = + thread->GetEcmaVM()->GetFactory()->NewFromCanBeCompressString("$&a $` $\' $2 $01 $$1 $21 $32 a"); + + auto ecmaRuntimeCallInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread, JSTaggedValue::Undefined(), 8); + ecmaRuntimeCallInfo->SetFunction(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetThis(thisStr.GetTaggedValue()); + ecmaRuntimeCallInfo->SetCallArg(0, searchStr.GetTaggedValue()); + ecmaRuntimeCallInfo->SetCallArg(1, replaceStr.GetTaggedValue()); + + [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread, ecmaRuntimeCallInfo.get()); + JSTaggedValue result = BuiltinsString::Replace(ecmaRuntimeCallInfo.get()); + + ASSERT_TRUE(result.IsString()); + ASSERT_TRUE(EcmaString::StringsAreEqual(reinterpret_cast(result.GetRawData()), *expected)); +} + +TEST_F(BuiltinsStringTest, ReplaceAll1) +{ + ASSERT_NE(thread, nullptr); + ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); + JSHandle this_str = factory->NewFromString("The Quick Brown Fox Jumps Over The Lazy Dog"); + JSHandle search_str = factory->NewFromString("o"); + JSHandle replace_str = factory->NewFromString("a"); + JSHandle expected = factory->NewFromString("The Quick Brawn Fax Jumps Over The Lazy Dag"); + + auto ecmaRuntimeCallInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread, JSTaggedValue::Undefined(), 8); + ecmaRuntimeCallInfo->SetFunction(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetThis(this_str.GetTaggedValue()); + ecmaRuntimeCallInfo->SetCallArg(0, search_str.GetTaggedValue()); + ecmaRuntimeCallInfo->SetCallArg(1, replace_str.GetTaggedValue()); + + [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread, ecmaRuntimeCallInfo.get()); + JSTaggedValue result = BuiltinsString::ReplaceAll(ecmaRuntimeCallInfo.get()); + + ASSERT_TRUE(result.IsString()); + ASSERT_TRUE(EcmaString::StringsAreEqual(reinterpret_cast(result.GetRawData()), *expected)); +} + +TEST_F(BuiltinsStringTest, ReplaceAll2) +{ + ASSERT_NE(thread, nullptr); + ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); + JSHandle this_str = factory->NewFromString("xxx"); + JSHandle search_str = factory->NewFromString(""); + JSHandle replace_str = factory->NewFromString("_"); + JSHandle expected = factory->NewFromString("_x_x_x_"); + + auto ecmaRuntimeCallInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread, JSTaggedValue::Undefined(), 8); + ecmaRuntimeCallInfo->SetFunction(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetThis(this_str.GetTaggedValue()); + ecmaRuntimeCallInfo->SetCallArg(0, search_str.GetTaggedValue()); + ecmaRuntimeCallInfo->SetCallArg(1, replace_str.GetTaggedValue()); + + [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread, ecmaRuntimeCallInfo.get()); + JSTaggedValue result = BuiltinsString::ReplaceAll(ecmaRuntimeCallInfo.get()); + + ASSERT_TRUE(result.IsString()); + ASSERT_TRUE(EcmaString::StringsAreEqual(reinterpret_cast(result.GetRawData()), *expected)); +} + +TEST_F(BuiltinsStringTest, ReplaceAll3) +{ + ASSERT_NE(thread, nullptr); + ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); + JSHandle this_str = factory->NewFromString("xxx"); + JSHandle search_str = factory->NewFromString("x"); + JSHandle replace_str = factory->NewFromString(""); + JSHandle expected = factory->NewFromString(""); + + auto ecmaRuntimeCallInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread, JSTaggedValue::Undefined(), 8); + ecmaRuntimeCallInfo->SetFunction(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetThis(this_str.GetTaggedValue()); + ecmaRuntimeCallInfo->SetCallArg(0, search_str.GetTaggedValue()); + ecmaRuntimeCallInfo->SetCallArg(1, replace_str.GetTaggedValue()); + + [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread, ecmaRuntimeCallInfo.get()); + JSTaggedValue result = BuiltinsString::ReplaceAll(ecmaRuntimeCallInfo.get()); + + ASSERT_TRUE(result.IsString()); + ASSERT_TRUE(EcmaString::StringsAreEqual(reinterpret_cast(result.GetRawData()), *expected)); +} + +TEST_F(BuiltinsStringTest, Split) +{ + // invoke RegExpConstructor method + ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); + JSHandle thisStr = factory->NewFromCanBeCompressString("Hello World. How are you doing?"); + JSHandle separatorStr = factory->NewFromCanBeCompressString(" "); + JSHandle limit(thread, JSTaggedValue(3)); + JSHandle expected1 = factory->NewFromCanBeCompressString("Hello"); + JSHandle expected2 = factory->NewFromCanBeCompressString("World."); + JSHandle expected3 = factory->NewFromCanBeCompressString("How"); + + auto ecmaRuntimeCallInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread, JSTaggedValue::Undefined(), 8); + ecmaRuntimeCallInfo->SetFunction(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetThis(thisStr.GetTaggedValue()); + ecmaRuntimeCallInfo->SetCallArg(0, separatorStr.GetTaggedValue()); + ecmaRuntimeCallInfo->SetCallArg(1, JSTaggedValue(static_cast(3))); + + [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread, ecmaRuntimeCallInfo.get()); + JSTaggedValue result = BuiltinsString::Split(ecmaRuntimeCallInfo.get()); + + ASSERT_TRUE(result.IsECMAObject()); + JSHandle resultArray(thread, reinterpret_cast(result.GetRawData())); + ASSERT_TRUE(resultArray->IsJSArray()); + JSHandle resultObj(resultArray); + JSHandle string1( + JSObject::GetProperty(thread, resultObj, JSHandle(thread, JSTaggedValue(0))).GetValue()); + JSHandle string2( + JSObject::GetProperty(thread, resultObj, JSHandle(thread, JSTaggedValue(1))).GetValue()); + JSHandle string3( + JSObject::GetProperty(thread, resultObj, JSHandle(thread, JSTaggedValue(2))).GetValue()); + ASSERT_TRUE(EcmaString::StringsAreEqual(*string1, *expected1)); + ASSERT_TRUE(EcmaString::StringsAreEqual(*string2, *expected2)); + ASSERT_TRUE(EcmaString::StringsAreEqual(*string3, *expected3)); +} + +TEST_F(BuiltinsStringTest, Split2) +{ + // invoke RegExpConstructor method + ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); + JSHandle thisStr = factory->NewFromCanBeCompressString("a-b-c"); + JSHandle pattern1 = factory->NewFromCanBeCompressString("-"); + JSHandle flags1 = factory->NewFromCanBeCompressString("iug"); + JSTaggedValue result1 = CreateRegExpObjByPatternAndFlags(thread, pattern1, flags1); + JSHandle separatorObj(thread, result1); + + JSHandle limit(thread, JSTaggedValue(3)); + JSHandle expected1 = factory->NewFromCanBeCompressString("a"); + JSHandle expected2 = factory->NewFromCanBeCompressString("b"); + JSHandle expected3 = factory->NewFromCanBeCompressString("c"); + + auto ecmaRuntimeCallInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread, JSTaggedValue::Undefined(), 8); + ecmaRuntimeCallInfo->SetFunction(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetThis(thisStr.GetTaggedValue()); + ecmaRuntimeCallInfo->SetCallArg(0, separatorObj.GetTaggedValue()); + ecmaRuntimeCallInfo->SetCallArg(1, JSTaggedValue(static_cast(3))); + + [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread, ecmaRuntimeCallInfo.get()); + JSTaggedValue result = BuiltinsString::Split(ecmaRuntimeCallInfo.get()); + + ASSERT_TRUE(result.IsECMAObject()); + JSHandle resultArray(thread, result); + ASSERT_TRUE(resultArray->IsJSArray()); + JSHandle resultObj(resultArray); + JSHandle string1( + JSObject::GetProperty(thread, resultObj, JSHandle(thread, JSTaggedValue(0))).GetValue()); + JSHandle string2( + JSObject::GetProperty(thread, resultObj, JSHandle(thread, JSTaggedValue(1))).GetValue()); + JSHandle string3( + JSObject::GetProperty(thread, resultObj, JSHandle(thread, JSTaggedValue(2))).GetValue()); + ASSERT_TRUE(EcmaString::StringsAreEqual(*string1, *expected1)); + ASSERT_TRUE(EcmaString::StringsAreEqual(*string2, *expected2)); + ASSERT_TRUE(EcmaString::StringsAreEqual(*string3, *expected3)); +} +} // namespace panda::test diff --git a/tests/runtime/builtins/builtins_symbol_test.cpp b/tests/runtime/builtins/builtins_symbol_test.cpp new file mode 100644 index 000000000..d1a4b068f --- /dev/null +++ b/tests/runtime/builtins/builtins_symbol_test.cpp @@ -0,0 +1,331 @@ +/* + * Copyright (c) 2021-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 "plugins/ecmascript/runtime/builtins/builtins_symbol.h" +#include "plugins/ecmascript/runtime/ecma_runtime_call_info.h" +#include "plugins/ecmascript/runtime/ecma_string.h" +#include "plugins/ecmascript/runtime/ecma_vm.h" +#include "plugins/ecmascript/runtime/global_env.h" +#include "plugins/ecmascript/runtime/js_function.h" +#include "plugins/ecmascript/runtime/js_handle.h" +#include "plugins/ecmascript/runtime/js_hclass.h" +#include "plugins/ecmascript/runtime/js_object-inl.h" +#include "plugins/ecmascript/runtime/js_primitive_ref.h" +#include "plugins/ecmascript/runtime/js_tagged_value-inl.h" +#include "plugins/ecmascript/runtime/js_thread.h" +#include "plugins/ecmascript/runtime/object_factory.h" +#include "plugins/ecmascript/runtime/symbol_table-inl.h" +#include "plugins/ecmascript/tests/runtime/common/test_helper.h" +#include "utils/bit_utils.h" + +using namespace panda::ecmascript; +using namespace panda::ecmascript::builtins; + +namespace panda::test { +using Symbol = ecmascript::builtins::BuiltinsSymbol; +using BuiltinsBase = panda::ecmascript::base::BuiltinsBase; + +class BuiltinsSymbolTest : public testing::Test { +public: + static void SetUpTestCase() + { + GTEST_LOG_(INFO) << "SetUpTestCase"; + } + + static void TearDownTestCase() + { + GTEST_LOG_(INFO) << "TearDownCase"; + } + + void SetUp() override + { + TestHelper::CreateEcmaVMWithScope(instance, thread, scope); + } + + void TearDown() override + { + TestHelper::DestroyEcmaVMWithScope(instance, scope); + } + + PandaVM *instance {nullptr}; + EcmaHandleScope *scope {nullptr}; + JSThread *thread {nullptr}; +}; + +// new Symbol.toString() +TEST_F(BuiltinsSymbolTest, SymbolNoParameterToString) +{ + auto ecmaVM = thread->GetEcmaVM(); + + JSHandle symbol = ecmaVM->GetFactory()->NewJSSymbol(); + + auto ecmaRuntimeCallInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread, JSTaggedValue::Undefined(), 4); + ecmaRuntimeCallInfo->SetFunction(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetThis(symbol.GetTaggedValue()); + + [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread, ecmaRuntimeCallInfo.get()); + JSTaggedValue result = Symbol::ToString(ecmaRuntimeCallInfo.get()); + JSHandle resultHandle(thread, reinterpret_cast(result.GetRawData())); + ASSERT_TRUE(result.IsString()); + + auto symbolValue = ecmascript::base::BuiltinsBase::GetTaggedString(thread, "Symbol()"); + ASSERT_EQ(reinterpret_cast(symbolValue.GetRawData())->Compare(*resultHandle), 0); +} + +// new Symbol("aaa").toString() +TEST_F(BuiltinsSymbolTest, SymbolWithParameterToString) +{ + auto ecmaVM = thread->GetEcmaVM(); + + JSHandle symbol = ecmaVM->GetFactory()->NewPublicSymbolWithChar("aaa"); + + auto ecmaRuntimeCallInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread, JSTaggedValue::Undefined(), 4); + ecmaRuntimeCallInfo->SetFunction(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetThis(symbol.GetTaggedValue()); + + [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread, ecmaRuntimeCallInfo.get()); + JSTaggedValue result = Symbol::ToString(ecmaRuntimeCallInfo.get()); + JSHandle resultHandle(thread, reinterpret_cast(result.GetRawData())); + ASSERT_TRUE(result.IsString()); + + auto symbolValue = ecmascript::base::BuiltinsBase::GetTaggedString(thread, "Symbol(aaa)"); + ASSERT_EQ(reinterpret_cast(symbolValue.GetRawData())->Compare(*resultHandle), 0); +} + +// new Symbol().valueOf() +TEST_F(BuiltinsSymbolTest, SymbolNoParameterValueOf) +{ + auto ecmaVM = thread->GetEcmaVM(); + JSHandle env = ecmaVM->GetGlobalEnv(); + + JSHandle symbol = ecmaVM->GetFactory()->NewJSSymbol(); + + auto ecmaRuntimeCallInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread, JSTaggedValue::Undefined(), 4); + ecmaRuntimeCallInfo->SetFunction(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetThis(symbol.GetTaggedValue()); + + [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread, ecmaRuntimeCallInfo.get()); + JSTaggedValue result = BuiltinsSymbol::ValueOf(ecmaRuntimeCallInfo.get()); + TestHelper::TearDownFrame(thread, prev); + EXPECT_TRUE(result.IsSymbol()); + ASSERT_EQ(result.GetRawData() == (JSTaggedValue(*symbol)).GetRawData(), true); + + JSHandle symbolObject(env->GetSymbolFunction()); + JSHandle symbolValue(symbol); + JSHandle symbolRef = ecmaVM->GetFactory()->NewJSPrimitiveRef(symbolObject, symbolValue); + + auto otherEcmaRuntimeCallInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread, JSTaggedValue::Undefined(), 4); + otherEcmaRuntimeCallInfo->SetFunction(JSTaggedValue::Undefined()); + otherEcmaRuntimeCallInfo->SetThis(symbolRef.GetTaggedValue()); + + prev = TestHelper::SetupFrame(thread, otherEcmaRuntimeCallInfo.get()); + JSTaggedValue otherResult = BuiltinsSymbol::ValueOf(otherEcmaRuntimeCallInfo.get()); + TestHelper::TearDownFrame(thread, prev); + EXPECT_TRUE(otherResult.IsSymbol()); + ASSERT_EQ(otherResult.GetRawData() == (JSTaggedValue(*symbol)).GetRawData(), true); +} + +// new Symbol("bbb").valueOf() +TEST_F(BuiltinsSymbolTest, SymbolWithParameterValueOf) +{ + auto ecmaVM = thread->GetEcmaVM(); + JSHandle env = ecmaVM->GetGlobalEnv(); + + JSHandle symbol = ecmaVM->GetFactory()->NewPublicSymbolWithChar("bbb"); + + auto ecmaRuntimeCallInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread, JSTaggedValue::Undefined(), 4); + ecmaRuntimeCallInfo->SetFunction(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetThis(symbol.GetTaggedValue()); + + [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread, ecmaRuntimeCallInfo.get()); + JSTaggedValue result = BuiltinsSymbol::ValueOf(ecmaRuntimeCallInfo.get()); + TestHelper::TearDownFrame(thread, prev); + EXPECT_TRUE(result.IsSymbol()); + ASSERT_EQ(result.GetRawData() == (JSTaggedValue(*symbol)).GetRawData(), true); + + JSHandle symbolObject(env->GetSymbolFunction()); + JSHandle symbolValue(symbol); + JSHandle symbolRef = ecmaVM->GetFactory()->NewJSPrimitiveRef(symbolObject, symbolValue); + + auto otherEcmaRuntimeCallInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread, JSTaggedValue::Undefined(), 4); + otherEcmaRuntimeCallInfo->SetFunction(JSTaggedValue::Undefined()); + otherEcmaRuntimeCallInfo->SetThis(symbolRef.GetTaggedValue()); + + prev = TestHelper::SetupFrame(thread, otherEcmaRuntimeCallInfo.get()); + JSTaggedValue otherResult = BuiltinsSymbol::ValueOf(otherEcmaRuntimeCallInfo.get()); + TestHelper::TearDownFrame(thread, prev); + EXPECT_TRUE(otherResult.IsSymbol()); + ASSERT_EQ(otherResult.GetRawData() == (JSTaggedValue(*symbol)).GetRawData(), true); +} + +// new Symbol().for +TEST_F(BuiltinsSymbolTest, SymbolWithParameterFor) +{ + auto ecmaVM = thread->GetEcmaVM(); + JSHandle env = ecmaVM->GetGlobalEnv(); + + JSHandle tableHandle(env->GetRegisterSymbols()); + + JSHandle string = ecmaVM->GetFactory()->NewFromCanBeCompressString("ccc"); + ASSERT_EQ(string->GetLength(), 3); + JSHandle string_handle(string); + ASSERT_EQ(tableHandle->ContainsKey(thread, string_handle.GetTaggedValue()), false); + + JSHandle symbol = ecmaVM->GetFactory()->NewSymbolWithTableWithChar("ccc"); + + auto ecmaRuntimeCallInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread, JSTaggedValue::Undefined(), 6); + ecmaRuntimeCallInfo->SetFunction(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetThis(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetCallArg(0, string.GetTaggedValue()); + + [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread, ecmaRuntimeCallInfo.get()); + JSTaggedValue result = BuiltinsSymbol::For(ecmaRuntimeCallInfo.get()); + TestHelper::TearDownFrame(thread, prev); + ASSERT_EQ(tableHandle->ContainsKey(thread, string_handle.GetTaggedValue()), true); + + JSTaggedValue target(*symbol); + ASSERT_EQ(result.GetRawData() == target.GetRawData(), true); +} + +// Symbol.keyFor (sym) +TEST_F(BuiltinsSymbolTest, SymbolKeyFor) +{ + auto ecmaVM = thread->GetEcmaVM(); + JSHandle env = ecmaVM->GetGlobalEnv(); + + JSHandle symbol = ecmaVM->GetFactory()->NewPublicSymbolWithChar("bbb"); + + auto ecmaRuntimeCallInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread, JSTaggedValue::Undefined(), 6); + ecmaRuntimeCallInfo->SetFunction(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetThis(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetCallArg(0, symbol.GetTaggedValue()); + + [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread, ecmaRuntimeCallInfo.get()); + JSTaggedValue result = BuiltinsSymbol::KeyFor(ecmaRuntimeCallInfo.get()); + TestHelper::TearDownFrame(thread, prev); + ASSERT_EQ(result.GetRawData(), JSTaggedValue::VALUE_UNDEFINED); + + JSHandle string = ecmaVM->GetFactory()->NewFromCanBeCompressString("ccc"); + ASSERT_EQ(string->GetLength(), 3); + + auto ecmaRuntimeCallInfo1 = TestHelper::CreateEcmaRuntimeCallInfo(thread, JSTaggedValue::Undefined(), 6); + ecmaRuntimeCallInfo1->SetFunction(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo1->SetThis(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo1->SetCallArg(0, string.GetTaggedValue()); + + prev = TestHelper::SetupFrame(thread, ecmaRuntimeCallInfo1.get()); + BuiltinsSymbol::For(ecmaRuntimeCallInfo1.get()); + TestHelper::TearDownFrame(thread, prev); + + JSHandle otherSymbol = ecmaVM->GetFactory()->NewPublicSymbolWithChar("ccc"); + auto ecmaRuntimeCallInfo2 = TestHelper::CreateEcmaRuntimeCallInfo(thread, JSTaggedValue::Undefined(), 6); + ecmaRuntimeCallInfo2->SetFunction(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo2->SetThis(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo2->SetCallArg(0, otherSymbol.GetTaggedValue()); + + prev = TestHelper::SetupFrame(thread, ecmaRuntimeCallInfo2.get()); + JSTaggedValue otherResult = BuiltinsSymbol::KeyFor(ecmaRuntimeCallInfo2.get()); + TestHelper::TearDownFrame(thread, prev); + ASSERT_TRUE(otherResult.IsString()); + JSHandle tableHandle(env->GetRegisterSymbols()); + JSTaggedValue stringValue(*string); + ASSERT_EQ(tableHandle->ContainsKey(thread, stringValue), true); +} + +// Symbol.ToPrimitive() +TEST_F(BuiltinsSymbolTest, SymbolToPrimitive) +{ + auto ecmaVM = thread->GetEcmaVM(); + JSHandle env = ecmaVM->GetGlobalEnv(); + + JSHandle symbol = ecmaVM->GetFactory()->NewJSSymbol(); + + auto ecmaRuntimeCallInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread, JSTaggedValue::Undefined(), 4); + ecmaRuntimeCallInfo->SetFunction(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetThis(symbol.GetTaggedValue()); + + [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread, ecmaRuntimeCallInfo.get()); + JSTaggedValue result = BuiltinsSymbol::ToPrimitive(ecmaRuntimeCallInfo.get()); + TestHelper::TearDownFrame(thread, prev); + EXPECT_TRUE(result.IsSymbol()); + ASSERT_EQ(result.GetRawData() == (JSTaggedValue(*symbol)).GetRawData(), true); + + JSHandle symbolObject(env->GetSymbolFunction()); + JSHandle symbolValue(symbol); + JSHandle symbolRef = ecmaVM->GetFactory()->NewJSPrimitiveRef(symbolObject, symbolValue); + + auto otherEcmaRuntimeCallInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread, JSTaggedValue::Undefined(), 4); + otherEcmaRuntimeCallInfo->SetFunction(JSTaggedValue::Undefined()); + otherEcmaRuntimeCallInfo->SetThis(symbolRef.GetTaggedValue()); + + prev = TestHelper::SetupFrame(thread, otherEcmaRuntimeCallInfo.get()); + JSTaggedValue otherResult = BuiltinsSymbol::ToPrimitive(otherEcmaRuntimeCallInfo.get()); + TestHelper::TearDownFrame(thread, prev); + EXPECT_TRUE(otherResult.IsSymbol()); + ASSERT_EQ(otherResult.GetRawData() == (JSTaggedValue(*symbol)).GetRawData(), true); +} + +// constructor +TEST_F(BuiltinsSymbolTest, SymbolConstructor) +{ + auto ecmaVM = thread->GetEcmaVM(); + + auto ecmaRuntimeCallInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread, JSTaggedValue::Undefined(), 6); + ecmaRuntimeCallInfo->SetFunction(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetThis(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetCallArg(0, JSTaggedValue::Undefined()); + + [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread, ecmaRuntimeCallInfo.get()); + JSTaggedValue result = BuiltinsSymbol::SymbolConstructor(ecmaRuntimeCallInfo.get()); + TestHelper::TearDownFrame(thread, prev); + EXPECT_TRUE(result.IsSymbol()); + JSSymbol *sym = reinterpret_cast(result.GetRawData()); + ASSERT_EQ(sym->GetDescription().IsUndefined(), true); + + JSHandle string = ecmaVM->GetFactory()->NewFromCanBeCompressString("ddd"); + + auto otherEcmaRuntimeCallInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread, JSTaggedValue::Undefined(), 6); + otherEcmaRuntimeCallInfo->SetFunction(JSTaggedValue::Undefined()); + otherEcmaRuntimeCallInfo->SetThis(JSTaggedValue::Undefined()); + otherEcmaRuntimeCallInfo->SetCallArg(0, string.GetTaggedValue()); + + prev = TestHelper::SetupFrame(thread, otherEcmaRuntimeCallInfo.get()); + JSTaggedValue result1 = BuiltinsSymbol::SymbolConstructor(otherEcmaRuntimeCallInfo.get()); + TestHelper::TearDownFrame(thread, prev); + JSHandle resultString = JSTaggedValue::ToString( + thread, JSHandle(thread, reinterpret_cast(result1.GetRawData())->GetDescription())); + ASSERT_EQ(resultString->Compare(*string), 0); +} + +TEST_F(BuiltinsSymbolTest, SymbolGetter) +{ + auto ecmaVM = thread->GetEcmaVM(); + + JSHandle symbol = ecmaVM->GetFactory()->NewPublicSymbolWithChar(""); + JSHandle string = ecmaVM->GetFactory()->NewFromCanBeCompressString(""); + + auto ecmaRuntimeCallInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread, JSTaggedValue::Undefined(), 4); + ecmaRuntimeCallInfo->SetFunction(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetThis(symbol.GetTaggedValue()); + + [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread, ecmaRuntimeCallInfo.get()); + JSTaggedValue result = BuiltinsSymbol::DescriptionGetter(ecmaRuntimeCallInfo.get()); + TestHelper::TearDownFrame(thread, prev); + ASSERT_TRUE(result.IsString()); + EcmaString *resString = reinterpret_cast(result.GetRawData()); + ASSERT_EQ(resString->GetLength(), 0); + ASSERT_EQ(EcmaString::StringsAreEqual(resString, *string), true); +} +} // namespace panda::test diff --git a/tests/runtime/builtins/builtins_typedarray_test.cpp b/tests/runtime/builtins/builtins_typedarray_test.cpp new file mode 100644 index 000000000..ea776764a --- /dev/null +++ b/tests/runtime/builtins/builtins_typedarray_test.cpp @@ -0,0 +1,367 @@ +/* + * Copyright (c) 2021-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 "plugins/ecmascript/runtime/base/typed_array_helper-inl.h" +#include "plugins/ecmascript/runtime/base/typed_array_helper.h" + +#include "plugins/ecmascript/runtime/builtins/builtins_array.h" +#include "plugins/ecmascript/runtime/builtins/builtins_object.h" +#include "plugins/ecmascript/runtime/builtins/builtins_typedarray.h" +#include "plugins/ecmascript/runtime/ecma_runtime_call_info.h" +#include "plugins/ecmascript/runtime/ecma_string.h" +#include "plugins/ecmascript/runtime/ecma_vm.h" +#include "plugins/ecmascript/runtime/global_env.h" +#include "plugins/ecmascript/runtime/js_array.h" +#include "plugins/ecmascript/runtime/js_array_iterator.h" +#include "plugins/ecmascript/runtime/js_handle.h" +#include "plugins/ecmascript/runtime/js_hclass.h" +#include "plugins/ecmascript/runtime/js_object-inl.h" +#include "plugins/ecmascript/runtime/js_tagged_value-inl.h" +#include "plugins/ecmascript/runtime/js_tagged_value.h" +#include "plugins/ecmascript/runtime/js_thread.h" +#include "plugins/ecmascript/runtime/js_typed_array.h" +#include "plugins/ecmascript/runtime/object_factory.h" +#include "plugins/ecmascript/runtime/object_operator.h" + +#include "plugins/ecmascript/tests/runtime/common/test_helper.h" +#include "utils/bit_utils.h" + +using namespace panda::ecmascript; +using namespace panda::ecmascript::builtins; +using namespace panda::ecmascript::base; + +namespace panda::test { +using Array = ecmascript::builtins::BuiltinsArray; +using TypedArray = ecmascript::builtins::BuiltinsTypedArray; +using TypedArrayHelper = ecmascript::base::TypedArrayHelper; + +class BuiltinsTypedArrayTest : public testing::Test { +public: + static void SetUpTestCase() + { + GTEST_LOG_(INFO) << "SetUpTestCase"; + } + + static void TearDownTestCase() + { + GTEST_LOG_(INFO) << "TearDownCase"; + } + + void SetUp() override + { + TestHelper::CreateEcmaVMWithScope(instance, thread, scope); + } + + void TearDown() override + { + TestHelper::DestroyEcmaVMWithScope(instance, scope); + } + +protected: + PandaVM *instance {nullptr}; + EcmaHandleScope *scope {nullptr}; + JSThread *thread {nullptr}; + + class TestClass : public base::BuiltinsBase { + public: + static JSTaggedValue TestForEachFunc(EcmaRuntimeCallInfo *argv) + { + JSHandle key = GetCallArg(argv, 0); + if (key->IsUndefined()) { + return JSTaggedValue::Undefined(); + } + JSArray *jsArray = JSArray::Cast(GetThis(argv)->GetTaggedObject()); + int length = jsArray->GetArrayLength() + 1; + jsArray->SetArrayLength(argv->GetThread(), length); + return JSTaggedValue::Undefined(); + } + + static JSTaggedValue TestEveryFunc(EcmaRuntimeCallInfo *argv) + { + uint32_t argc = argv->GetArgsNumber(); + if (argc > 0) { + [[maybe_unused]] int aaa = GetCallArg(argv, 0)->GetInt(); + // 10 : test case + if (GetCallArg(argv, 0)->GetInt() > 10) { + return GetTaggedBoolean(true); + } + } + return GetTaggedBoolean(false); + } + + static JSTaggedValue TestFilterFunc(EcmaRuntimeCallInfo *argv) + { + ASSERT(argv); + uint32_t argc = argv->GetArgsNumber(); + if (argc > 0) { + // 10 : test case + if (GetCallArg(argv, 0)->GetInt() > 10) { + return GetTaggedBoolean(true); + } + } + return GetTaggedBoolean(false); + } + + static JSTaggedValue TestMapFunc(EcmaRuntimeCallInfo *argv) + { + int accumulator = GetCallArg(argv, 0)->GetInt(); + accumulator = accumulator * 2; // 2 : mapped to 2 times the original value + return BuiltinsBase::GetTaggedInt(accumulator); + } + + static JSTaggedValue TestFindFunc(EcmaRuntimeCallInfo *argv) + { + uint32_t argc = argv->GetArgsNumber(); + if (argc > 0) { + // 10 : test case + if (GetCallArg(argv, 0)->GetInt() > 10) { + return GetTaggedBoolean(true); + } + } + return GetTaggedBoolean(false); + } + + static JSTaggedValue TestFindIndexFunc(EcmaRuntimeCallInfo *argv) + { + uint32_t argc = argv->GetArgsNumber(); + if (argc > 0) { + // 10 : test case + if (GetCallArg(argv, 0)->GetInt() > 10) { + return GetTaggedBoolean(true); + } + } + return GetTaggedBoolean(false); + } + + static JSTaggedValue TestReduceFunc(EcmaRuntimeCallInfo *argv) + { + int accumulator = GetCallArg(argv, 0)->GetInt(); + accumulator = accumulator + GetCallArg(argv, 1)->GetInt(); + return BuiltinsBase::GetTaggedInt(accumulator); + } + + static JSTaggedValue TestReduceRightFunc(EcmaRuntimeCallInfo *argv) + { + int accumulator = GetCallArg(argv, 0)->GetInt(); + accumulator = accumulator + GetCallArg(argv, 1)->GetInt(); + return BuiltinsBase::GetTaggedInt(accumulator); + } + + static JSTaggedValue TestSomeFunc(EcmaRuntimeCallInfo *argv) + { + uint32_t argc = argv->GetArgsNumber(); + if (argc > 0) { + // 10 : test case + if (GetCallArg(argv, 0)->GetInt() > 10) { + return GetTaggedBoolean(true); + } + } + return GetTaggedBoolean(false); + } + }; +}; + +JSTaggedValue CreateBuiltinsJSObject(JSThread *thread, const CString keyCStr) +{ + auto ecmaVM = thread->GetEcmaVM(); + JSHandle env = ecmaVM->GetGlobalEnv(); + JSHandle dynclass = env->GetObjectFunction(); + ObjectFactory *factory = ecmaVM->GetFactory(); + + JSHandle obj(factory->NewJSObjectByConstructor(JSHandle(dynclass), dynclass)); + JSHandle key(factory->NewFromCanBeCompressString(&keyCStr[0])); + JSHandle value(thread, JSTaggedValue(1)); + JSObject::SetProperty(thread, obj, key, value); + return obj.GetTaggedValue(); +} + +JSTypedArray *CreateTypedArrayFromList(JSThread *thread, const JSHandle &array) +{ + auto ecmaVM = thread->GetEcmaVM(); + JSHandle env = ecmaVM->GetGlobalEnv(); + + JSHandle jsarray(JSArray::CreateArrayFromList(thread, array)); + JSHandle int8_array(env->GetInt8ArrayFunction()); + JSHandle globalObject(thread, env->GetGlobalObject()); + // 6 : test case + auto ecmaRuntimeCallInfo1 = TestHelper::CreateEcmaRuntimeCallInfo(thread, JSTaggedValue::Undefined(), 6); + ecmaRuntimeCallInfo1->SetNewTarget(JSTaggedValue(*int8_array)); + ecmaRuntimeCallInfo1->SetThis(JSTaggedValue(*globalObject)); + ecmaRuntimeCallInfo1->SetCallArg(0, jsarray.GetTaggedValue()); + + [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread, ecmaRuntimeCallInfo1.get()); + JSTaggedValue result = TypedArray::Int8ArrayConstructor(ecmaRuntimeCallInfo1.get()); + + EXPECT_TRUE(result.IsECMAObject()); + JSTypedArray *int8arr = JSTypedArray::Cast(reinterpret_cast(result.GetRawData())); + return int8arr; +} + + +TEST_F(BuiltinsTypedArrayTest, Species) +{ + auto ecmaVM = thread->GetEcmaVM(); + JSHandle env = ecmaVM->GetGlobalEnv(); + JSHandle array(env->GetArrayFunction()); + JSHandle globalObject(thread, env->GetGlobalObject()); + + auto ecmaRuntimeCallInfo1 = TestHelper::CreateEcmaRuntimeCallInfo(thread, JSTaggedValue::Undefined(), 4); + ecmaRuntimeCallInfo1->SetFunction(array.GetTaggedValue()); + ecmaRuntimeCallInfo1->SetThis(globalObject.GetTaggedValue()); + + [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread, ecmaRuntimeCallInfo1.get()); + JSTaggedValue result = TypedArray::Species(ecmaRuntimeCallInfo1.get()); + ASSERT_TRUE(result.IsECMAObject()); +} + +// ES2021 23.2.3.13 new Array(1,2,3,4,3).includes(searchElement [ , fromIndex ]) +TEST_F(BuiltinsTypedArrayTest, Includes1) +{ + ASSERT_NE(thread, nullptr); + + ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); + + JSHandle array(factory->NewTaggedArray(5)); + array->Set(thread, 0, JSTaggedValue(1)); + array->Set(thread, 1, JSTaggedValue(2)); + array->Set(thread, 2, JSTaggedValue(3)); + array->Set(thread, 3, JSTaggedValue(4)); + array->Set(thread, 4, JSTaggedValue(3)); + JSHandle obj(thread, CreateTypedArrayFromList(thread, array)); + + + // new Array(1,2,3,4,3).includes(1,0) + auto ecmaRuntimeCallInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread, JSTaggedValue::Undefined(), 8); + ecmaRuntimeCallInfo->SetFunction(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetThis(obj.GetTaggedValue()); + ecmaRuntimeCallInfo->SetCallArg(0, JSTaggedValue(static_cast(1))); + ecmaRuntimeCallInfo->SetCallArg(1, JSTaggedValue(static_cast(0))); + + [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread, ecmaRuntimeCallInfo.get()); + JSTaggedValue result = TypedArray::Includes(ecmaRuntimeCallInfo.get()); + TestHelper::TearDownFrame(thread, prev); + ASSERT_EQ(result.GetRawData(), JSTaggedValue(true).GetRawData()); +} + +// ES2021 23.2.3.13 new Array(1,2,3,4,3).includes(searchElement [ , fromIndex ]) +TEST_F(BuiltinsTypedArrayTest, Includes2) +{ + ASSERT_NE(thread, nullptr); + + ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); + + JSHandle array(factory->NewTaggedArray(5)); + array->Set(thread, 0, JSTaggedValue(1)); + array->Set(thread, 1, JSTaggedValue(2)); + array->Set(thread, 2, JSTaggedValue(3)); + array->Set(thread, 3, JSTaggedValue(4)); + array->Set(thread, 4, JSTaggedValue(3)); + JSHandle obj(thread, CreateTypedArrayFromList(thread, array)); + + // new Array(1,2,3,4,3).includes(1,3) + auto ecmaRuntimeCallInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread, JSTaggedValue::Undefined(), 8); + ecmaRuntimeCallInfo->SetFunction(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetThis(obj.GetTaggedValue()); + ecmaRuntimeCallInfo->SetCallArg(0, JSTaggedValue(static_cast(1))); + ecmaRuntimeCallInfo->SetCallArg(1, JSTaggedValue(static_cast(3))); + + [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread, ecmaRuntimeCallInfo.get()); + JSTaggedValue result = TypedArray::Includes(ecmaRuntimeCallInfo.get()); + TestHelper::TearDownFrame(thread, prev); + ASSERT_EQ(result.GetRawData(), JSTaggedValue(false).GetRawData()); +} + +// ES2021 23.2.3.13 new Array(1,2,3,4,3).includes(searchElement [ , fromIndex ]) +TEST_F(BuiltinsTypedArrayTest, Includes3) +{ + ASSERT_NE(thread, nullptr); + + ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); + + JSHandle array(factory->NewTaggedArray(5)); + array->Set(thread, 0, JSTaggedValue(1)); + array->Set(thread, 1, JSTaggedValue(2)); + array->Set(thread, 2, JSTaggedValue(3)); + array->Set(thread, 3, JSTaggedValue(4)); + array->Set(thread, 4, JSTaggedValue(3)); + JSHandle obj(thread, CreateTypedArrayFromList(thread, array)); + + // new Array(1,2,3,4,3).includes(5,0) + auto ecmaRuntimeCallInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread, JSTaggedValue::Undefined(), 8); + ecmaRuntimeCallInfo->SetFunction(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetThis(obj.GetTaggedValue()); + ecmaRuntimeCallInfo->SetCallArg(0, JSTaggedValue(static_cast(5))); + ecmaRuntimeCallInfo->SetCallArg(1, JSTaggedValue(static_cast(0))); + + [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread, ecmaRuntimeCallInfo.get()); + JSTaggedValue result = TypedArray::Includes(ecmaRuntimeCallInfo.get()); + TestHelper::TearDownFrame(thread, prev); + ASSERT_EQ(result.GetRawData(), JSTaggedValue(false).GetRawData()); +} + +// ES2021 23.2.3.13 new Array(1,2,3,4,3).includes(searchElement [ , fromIndex ]) +TEST_F(BuiltinsTypedArrayTest, Includes4) +{ + ASSERT_NE(thread, nullptr); + + ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); + + JSHandle array(factory->NewTaggedArray(5)); + array->Set(thread, 0, JSTaggedValue(1)); + array->Set(thread, 1, JSTaggedValue(2)); + array->Set(thread, 2, JSTaggedValue(3)); + array->Set(thread, 3, JSTaggedValue(4)); + array->Set(thread, 4, JSTaggedValue(3)); + JSHandle obj(thread, CreateTypedArrayFromList(thread, array)); + + // new Array(1,2,3,4,3).includes(1) + auto ecmaRuntimeCallInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread, JSTaggedValue::Undefined(), 6); + ecmaRuntimeCallInfo->SetFunction(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetThis(obj.GetTaggedValue()); + ecmaRuntimeCallInfo->SetCallArg(0, JSTaggedValue(static_cast(1))); + + [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread, ecmaRuntimeCallInfo.get()); + JSTaggedValue result = TypedArray::Includes(ecmaRuntimeCallInfo.get()); + TestHelper::TearDownFrame(thread, prev); + ASSERT_EQ(result.GetRawData(), JSTaggedValue(true).GetRawData()); +} + +// ES2021 23.2.3.13 new Array(1,2,3,4,3).includes(searchElement [ , fromIndex ]) +TEST_F(BuiltinsTypedArrayTest, Includes5) +{ + ASSERT_NE(thread, nullptr); + + ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); + + JSHandle array(factory->NewTaggedArray(5)); + array->Set(thread, 0, JSTaggedValue(1)); + array->Set(thread, 1, JSTaggedValue(2)); + array->Set(thread, 2, JSTaggedValue(3)); + array->Set(thread, 3, JSTaggedValue(4)); + array->Set(thread, 4, JSTaggedValue(3)); + JSHandle obj(thread, CreateTypedArrayFromList(thread, array)); + + // new Array(1,2,3,4,3).includes(5) + auto ecmaRuntimeCallInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread, JSTaggedValue::Undefined(), 6); + ecmaRuntimeCallInfo->SetFunction(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetThis(obj.GetTaggedValue()); + ecmaRuntimeCallInfo->SetCallArg(0, JSTaggedValue(static_cast(5))); + + [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread, ecmaRuntimeCallInfo.get()); + JSTaggedValue result = TypedArray::Includes(ecmaRuntimeCallInfo.get()); + TestHelper::TearDownFrame(thread, prev); + ASSERT_EQ(result.GetRawData(), JSTaggedValue(false).GetRawData()); +} +} // namespace panda::test diff --git a/tests/runtime/builtins/builtins_weak_map_test.cpp b/tests/runtime/builtins/builtins_weak_map_test.cpp new file mode 100644 index 000000000..2af7d22bd --- /dev/null +++ b/tests/runtime/builtins/builtins_weak_map_test.cpp @@ -0,0 +1,211 @@ +/* + * Copyright (c) 2021-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 "plugins/ecmascript/runtime/base/builtins_base.h" +#include "plugins/ecmascript/runtime/builtins/builtins_weak_map.h" +#include "plugins/ecmascript/runtime/ecma_runtime_call_info.h" +#include "plugins/ecmascript/runtime/ecma_string.h" +#include "plugins/ecmascript/runtime/ecma_vm.h" +#include "plugins/ecmascript/runtime/global_env.h" +#include "plugins/ecmascript/runtime/js_array.h" +#include "plugins/ecmascript/runtime/js_handle.h" +#include "plugins/ecmascript/runtime/js_hclass.h" +#include "plugins/ecmascript/runtime/js_map_iterator.h" +#include "plugins/ecmascript/runtime/js_object-inl.h" +#include "plugins/ecmascript/runtime/js_tagged_value.h" +#include "plugins/ecmascript/runtime/js_thread.h" +#include "plugins/ecmascript/runtime/js_weak_container.h" +#include "plugins/ecmascript/runtime/object_factory.h" +#include "plugins/ecmascript/runtime/tagged_array-inl.h" +#include "plugins/ecmascript/tests/runtime/common/test_helper.h" +#include "utils/bit_utils.h" + +using namespace panda::ecmascript; +using namespace panda::ecmascript::builtins; + +namespace panda::test { +using BuiltinsWeakMap = ecmascript::builtins::BuiltinsWeakMap; +using JSWeakMap = ecmascript::JSWeakMap; + +class BuiltinsWeakMapTest : public testing::Test { +public: + static void SetUpTestCase() + { + GTEST_LOG_(INFO) << "SetUpTestCase"; + } + + static void TearDownTestCase() + { + GTEST_LOG_(INFO) << "TearDownCase"; + } + + void SetUp() override + { + TestHelper::CreateEcmaVMWithScope(instance, thread, scope); + } + + void TearDown() override + { + TestHelper::DestroyEcmaVMWithScope(instance, scope); + } + + PandaVM *instance {nullptr}; + EcmaHandleScope *scope {nullptr}; + JSThread *thread {nullptr}; +}; + +static JSObject *JSObjectTestCreate(JSThread *thread) +{ + EcmaVM *ecmaVM = thread->GetEcmaVM(); + ObjectFactory *factory = ecmaVM->GetFactory(); + [[maybe_unused]] EcmaHandleScope scope(thread); + JSHandle globalEnv = ecmaVM->GetGlobalEnv(); + JSHandle jsFunc = globalEnv->GetObjectFunction(); + return *factory->NewJSObjectByConstructor(JSHandle(jsFunc), jsFunc); +} + +JSWeakMap *CreateBuiltinsWeakMap(JSThread *thread) +{ + JSHandle env = thread->GetEcmaVM()->GetGlobalEnv(); + JSHandle newTarget(env->GetBuiltinsWeakMapFunction()); + // 4 : test case + auto ecmaRuntimeCallInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread, newTarget.GetTaggedValue(), 4); + ecmaRuntimeCallInfo->SetFunction(newTarget.GetTaggedValue()); + ecmaRuntimeCallInfo->SetThis(JSTaggedValue::Undefined()); + + [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread, ecmaRuntimeCallInfo.get()); + JSTaggedValue result = BuiltinsWeakMap::WeakMapConstructor(ecmaRuntimeCallInfo.get()); + + EXPECT_TRUE(result.IsECMAObject()); + return JSWeakMap::Cast(reinterpret_cast(result.GetRawData())); +} + +// new Map("abrupt").toString() +TEST_F(BuiltinsWeakMapTest, CreateAndGetSize) +{ + ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); + JSHandle env = thread->GetEcmaVM()->GetGlobalEnv(); + JSHandle newTarget(env->GetBuiltinsWeakMapFunction()); + JSHandle map(thread, CreateBuiltinsWeakMap(thread)); + + JSHandle array(factory->NewTaggedArray(1)); + JSHandle internal_array(factory->NewTaggedArray(2)); + JSTaggedValue value(JSObjectTestCreate(thread)); + internal_array->Set(thread, 0, value); + internal_array->Set(thread, 1, JSTaggedValue(0)); + auto result = JSArray::CreateArrayFromList(thread, internal_array); + array->Set(thread, 0, result); + + JSHandle values = JSArray::CreateArrayFromList(thread, array); + + auto ecmaRuntimeCallInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread, JSTaggedValue::Undefined(), 6); + ecmaRuntimeCallInfo->SetFunction(newTarget.GetTaggedValue()); + ecmaRuntimeCallInfo->SetThis(map.GetTaggedValue()); + ecmaRuntimeCallInfo->SetCallArg(0, values.GetTaggedValue()); + ecmaRuntimeCallInfo->SetNewTarget(newTarget.GetTaggedValue()); + + [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread, ecmaRuntimeCallInfo.get()); + + JSTaggedValue result1 = BuiltinsWeakMap::WeakMapConstructor(ecmaRuntimeCallInfo.get()); + JSHandle weakMap(thread, JSWeakMap::Cast(reinterpret_cast(result1.GetRawData()))); + EXPECT_EQ(weakMap->GetSize(), 1); +} + +TEST_F(BuiltinsWeakMapTest, SetAndHas) +{ + // create jsWeakMap + JSHandle weakMap(thread, CreateBuiltinsWeakMap(thread)); + JSHandle key(thread, JSObjectTestCreate(thread)); + + auto ecmaRuntimeCallInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread, JSTaggedValue::Undefined(), 8); + ecmaRuntimeCallInfo->SetFunction(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetThis(weakMap.GetTaggedValue()); + ecmaRuntimeCallInfo->SetCallArg(0, key.GetTaggedValue()); + ecmaRuntimeCallInfo->SetCallArg(1, JSTaggedValue(static_cast(1))); + + { + [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread, ecmaRuntimeCallInfo.get()); + JSTaggedValue result1 = BuiltinsWeakMap::Has(ecmaRuntimeCallInfo.get()); + + EXPECT_EQ(result1.GetRawData(), JSTaggedValue::False().GetRawData()); + } + + // test Set() + JSTaggedValue result2 = BuiltinsWeakMap::Set(ecmaRuntimeCallInfo.get()); + EXPECT_TRUE(result2.IsECMAObject()); + JSWeakMap *jsWeakMap = JSWeakMap::Cast(reinterpret_cast(result2.GetRawData())); + EXPECT_EQ(jsWeakMap->GetSize(), 1); + + // test Has() + auto ecmaRuntimeCallInfo1 = TestHelper::CreateEcmaRuntimeCallInfo(thread, JSTaggedValue::Undefined(), 8); + ecmaRuntimeCallInfo1->SetFunction(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo1->SetCallArg(0, key.GetTaggedValue()); + ecmaRuntimeCallInfo1->SetCallArg(1, JSTaggedValue(static_cast(1))); + ecmaRuntimeCallInfo1->SetThis(JSTaggedValue(jsWeakMap)); + { + [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread, ecmaRuntimeCallInfo1.get()); + JSTaggedValue result3 = BuiltinsWeakMap::Has(ecmaRuntimeCallInfo1.get()); + + EXPECT_EQ(result3.GetRawData(), JSTaggedValue::True().GetRawData()); + } +} + +TEST_F(BuiltinsWeakMapTest, DeleteAndRemove) +{ + // create jsWeakMap + JSHandle weakMap(thread, CreateBuiltinsWeakMap(thread)); + + // add 40 keys + JSTaggedValue lastKey(JSTaggedValue::Undefined()); + for (int i = 0; i < 40; i++) { + JSHandle key(thread, JSObjectTestCreate(thread)); + auto ecmaRuntimeCallInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread, JSTaggedValue::Undefined(), 8); + ecmaRuntimeCallInfo->SetFunction(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetThis(weakMap.GetTaggedValue()); + ecmaRuntimeCallInfo->SetCallArg(0, key.GetTaggedValue()); + ecmaRuntimeCallInfo->SetCallArg(1, JSTaggedValue(static_cast(i))); + + [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread, ecmaRuntimeCallInfo.get()); + JSTaggedValue result1 = BuiltinsWeakMap::Set(ecmaRuntimeCallInfo.get()); + + EXPECT_TRUE(result1.IsECMAObject()); + JSWeakMap *jsWeakMap = JSWeakMap::Cast(reinterpret_cast(result1.GetRawData())); + EXPECT_EQ(jsWeakMap->GetSize(), i + 1); + lastKey = key.GetTaggedValue(); + } + + // whether jsWeakMap has delete lastKey + + auto ecmaRuntimeCallInfo1 = TestHelper::CreateEcmaRuntimeCallInfo(thread, JSTaggedValue::Undefined(), 6); + ecmaRuntimeCallInfo1->SetFunction(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo1->SetThis(weakMap.GetTaggedValue()); + ecmaRuntimeCallInfo1->SetCallArg(0, lastKey); + + [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread, ecmaRuntimeCallInfo1.get()); + JSTaggedValue result2 = BuiltinsWeakMap::Has(ecmaRuntimeCallInfo1.get()); + + EXPECT_EQ(result2.GetRawData(), JSTaggedValue::True().GetRawData()); + + // delete + JSTaggedValue result3 = BuiltinsWeakMap::Delete(ecmaRuntimeCallInfo1.get()); + + EXPECT_EQ(result3.GetRawData(), JSTaggedValue::True().GetRawData()); + + // check deleteKey is deleted + JSTaggedValue result4 = BuiltinsWeakMap::Has(ecmaRuntimeCallInfo1.get()); + + EXPECT_EQ(result4.GetRawData(), JSTaggedValue::False().GetRawData()); +} +} // namespace panda::test diff --git a/tests/runtime/builtins/builtins_weak_set_test.cpp b/tests/runtime/builtins/builtins_weak_set_test.cpp new file mode 100644 index 000000000..efa59e77b --- /dev/null +++ b/tests/runtime/builtins/builtins_weak_set_test.cpp @@ -0,0 +1,205 @@ +/* + * Copyright (c) 2021-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 "plugins/ecmascript/runtime/builtins/builtins_weak_set.h" +#include "plugins/ecmascript/runtime/ecma_string.h" +#include "plugins/ecmascript/runtime/ecma_vm.h" +#include "plugins/ecmascript/runtime/global_env.h" +#include "plugins/ecmascript/runtime/js_array.h" +#include "plugins/ecmascript/runtime/js_handle.h" +#include "plugins/ecmascript/runtime/js_hclass.h" +#include "plugins/ecmascript/runtime/js_object-inl.h" +#include "plugins/ecmascript/runtime/js_set_iterator.h" +#include "plugins/ecmascript/runtime/js_tagged_value.h" +#include "plugins/ecmascript/runtime/js_thread.h" + +#include "plugins/ecmascript/runtime/js_weak_container.h" +#include "plugins/ecmascript/runtime/object_factory.h" +#include "plugins/ecmascript/tests/runtime/common/test_helper.h" +#include "utils/bit_utils.h" + +using namespace panda::ecmascript; +using namespace panda::ecmascript::builtins; + +namespace panda::test { +using BuiltinsWeakSet = ecmascript::builtins::BuiltinsWeakSet; +using JSWeakSet = ecmascript::JSWeakSet; + +class BuiltinsWeakSetTest : public testing::Test { +public: + static void SetUpTestCase() + { + GTEST_LOG_(INFO) << "SetUpTestCase"; + } + + static void TearDownTestCase() + { + GTEST_LOG_(INFO) << "TearDownCase"; + } + + void SetUp() override + { + TestHelper::CreateEcmaVMWithScope(instance, thread, scope); + } + + void TearDown() override + { + TestHelper::DestroyEcmaVMWithScope(instance, scope); + } + + PandaVM *instance {nullptr}; + EcmaHandleScope *scope {nullptr}; + JSThread *thread {nullptr}; +}; + +static JSObject *JSObjectTestCreate(JSThread *thread) +{ + [[maybe_unused]] EcmaHandleScope scope(thread); + EcmaVM *ecmaVM = thread->GetEcmaVM(); + JSHandle globalEnv = ecmaVM->GetGlobalEnv(); + JSHandle jsFunc = globalEnv->GetObjectFunction(); + JSHandle newObj = + thread->GetEcmaVM()->GetFactory()->NewJSObjectByConstructor(JSHandle(jsFunc), jsFunc); + return *newObj; +} + +JSWeakSet *CreateBuiltinsWeakSet(JSThread *thread) +{ + JSHandle env = thread->GetEcmaVM()->GetGlobalEnv(); + JSHandle newTarget(env->GetBuiltinsWeakSetFunction()); + + // 4 : test case + auto ecmaRuntimeCallInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread, JSTaggedValue(*newTarget), 4); + ecmaRuntimeCallInfo->SetFunction(newTarget.GetTaggedValue()); + ecmaRuntimeCallInfo->SetThis(JSTaggedValue::Undefined()); + [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread, ecmaRuntimeCallInfo.get()); + JSTaggedValue result = BuiltinsWeakSet::WeakSetConstructor(ecmaRuntimeCallInfo.get()); + + EXPECT_TRUE(result.IsECMAObject()); + JSWeakSet *jsWeakSet = JSWeakSet::Cast(reinterpret_cast(result.GetRawData())); + return jsWeakSet; +} + +TEST_F(BuiltinsWeakSetTest, CreateAndGetSize) +{ + ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); + JSHandle env = thread->GetEcmaVM()->GetGlobalEnv(); + JSHandle newTarget(env->GetBuiltinsWeakSetFunction()); + JSHandle weakSet(thread, CreateBuiltinsWeakSet(thread)); + + JSHandle array(factory->NewTaggedArray(5)); + for (int i = 0; i < 5; i++) { + JSHandle key(thread, JSObjectTestCreate(thread)); + array->Set(thread, i, key.GetTaggedValue()); + } + + JSHandle values = JSArray::CreateArrayFromList(thread, array); + auto ecmaRuntimeCallInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread, JSTaggedValue::Undefined(), 6); + ecmaRuntimeCallInfo->SetFunction(newTarget.GetTaggedValue()); + ecmaRuntimeCallInfo->SetThis(weakSet.GetTaggedValue()); + ecmaRuntimeCallInfo->SetCallArg(0, values.GetTaggedValue()); + ecmaRuntimeCallInfo->SetNewTarget(newTarget.GetTaggedValue()); + + [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread, ecmaRuntimeCallInfo.get()); + + JSTaggedValue result1 = BuiltinsWeakSet::WeakSetConstructor(ecmaRuntimeCallInfo.get()); + JSHandle weakSetResult(thread, + JSWeakSet::Cast(reinterpret_cast(result1.GetRawData()))); + EXPECT_EQ(weakSetResult->GetSize(), 5); +} + +TEST_F(BuiltinsWeakSetTest, AddAndHas) +{ + // create jsWeakSet + JSHandle weakSet(thread, CreateBuiltinsWeakSet(thread)); + JSHandle key(thread, JSObjectTestCreate(thread)); + auto ecmaRuntimeCallInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread, JSTaggedValue::Undefined(), 6); + ecmaRuntimeCallInfo->SetFunction(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetThis(weakSet.GetTaggedValue()); + ecmaRuntimeCallInfo->SetCallArg(0, key.GetTaggedValue()); + + JSWeakSet *jsWeakSet; + { + [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread, ecmaRuntimeCallInfo.get()); + JSTaggedValue result1 = BuiltinsWeakSet::Has(ecmaRuntimeCallInfo.get()); + + EXPECT_EQ(result1.GetRawData(), JSTaggedValue::False().GetRawData()); + + // test Add() + JSTaggedValue result2 = BuiltinsWeakSet::Add(ecmaRuntimeCallInfo.get()); + EXPECT_TRUE(result2.IsECMAObject()); + jsWeakSet = JSWeakSet::Cast(reinterpret_cast(result2.GetRawData())); + EXPECT_EQ(jsWeakSet->GetSize(), 1); + } + + // test Has() + auto ecmaRuntimeCallInfo1 = TestHelper::CreateEcmaRuntimeCallInfo(thread, JSTaggedValue::Undefined(), 6); + ecmaRuntimeCallInfo1->SetFunction(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo1->SetThis(JSTaggedValue(jsWeakSet)); + ecmaRuntimeCallInfo1->SetCallArg(0, key.GetTaggedValue()); + { + [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread, ecmaRuntimeCallInfo1.get()); + JSTaggedValue result3 = BuiltinsWeakSet::Has(ecmaRuntimeCallInfo1.get()); + + EXPECT_EQ(result3.GetRawData(), JSTaggedValue::True().GetRawData()); + } +} + +TEST_F(BuiltinsWeakSetTest, DeleteAndRemove) +{ + // create jsSet + JSHandle weakSet(thread, CreateBuiltinsWeakSet(thread)); + + // add 40 keys + JSTaggedValue lastKey(JSTaggedValue::Undefined()); + for (int i = 0; i < 40; i++) { + JSHandle key(thread, JSObjectTestCreate(thread)); + + auto ecmaRuntimeCallInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread, JSTaggedValue::Undefined(), 6); + ecmaRuntimeCallInfo->SetFunction(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetThis(weakSet.GetTaggedValue()); + ecmaRuntimeCallInfo->SetCallArg(0, key.GetTaggedValue()); + + [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread, ecmaRuntimeCallInfo.get()); + JSTaggedValue result1 = BuiltinsWeakSet::Add(ecmaRuntimeCallInfo.get()); + + EXPECT_TRUE(result1.IsECMAObject()); + JSWeakSet *jsWeakSet = JSWeakSet::Cast(reinterpret_cast(result1.GetRawData())); + EXPECT_EQ(jsWeakSet->GetSize(), i + 1); + lastKey = key.GetTaggedValue(); + } + // whether jsWeakSet has delete lastKey + + auto ecmaRuntimeCallInfo1 = TestHelper::CreateEcmaRuntimeCallInfo(thread, JSTaggedValue::Undefined(), 6); + ecmaRuntimeCallInfo1->SetFunction(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo1->SetThis(weakSet.GetTaggedValue()); + ecmaRuntimeCallInfo1->SetCallArg(0, lastKey); + + [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread, ecmaRuntimeCallInfo1.get()); + JSTaggedValue result2 = BuiltinsWeakSet::Has(ecmaRuntimeCallInfo1.get()); + + EXPECT_EQ(result2.GetRawData(), JSTaggedValue::True().GetRawData()); + + // delete + JSTaggedValue result3 = BuiltinsWeakSet::Delete(ecmaRuntimeCallInfo1.get()); + + EXPECT_EQ(result3.GetRawData(), JSTaggedValue::True().GetRawData()); + + // check deleteKey is deleted + JSTaggedValue result4 = BuiltinsWeakSet::Has(ecmaRuntimeCallInfo1.get()); + + EXPECT_EQ(result4.GetRawData(), JSTaggedValue::False().GetRawData()); +} +} // namespace panda::test diff --git a/tests/runtime/common/CMakeLists.txt b/tests/runtime/common/CMakeLists.txt new file mode 100644 index 000000000..213d66288 --- /dev/null +++ b/tests/runtime/common/CMakeLists.txt @@ -0,0 +1,26 @@ +add_custom_target(ecmascript_common_tests) +add_dependencies(tests ecmascript_common_tests) + +add_subdirectory(helloworld) +add_subdirectory(newobjdynrange) +add_subdirectory(bitwiseop) +add_subdirectory(big_file) +add_subdirectory(dyninstruction) +add_subdirectory(throwdyn) +add_subdirectory(getunmappedargs) +add_subdirectory(lexicalenv) +add_subdirectory(missingargs) +add_subdirectory(module) +add_subdirectory(generator) +add_subdirectory(async) +add_subdirectory(promise) +add_subdirectory(yieldstar) +add_subdirectory(class) +add_subdirectory(multiargs) +add_subdirectory(fortest) +add_subdirectory(returnundefined) +add_subdirectory(sieve) +add_subdirectory(strictequal) +add_subdirectory(restargs) +add_subdirectory(native_methods_api_no_crash) +add_subdirectory(ecma_empty_class_check) diff --git a/tests/runtime/common/assert_scope_test.cpp b/tests/runtime/common/assert_scope_test.cpp new file mode 100644 index 000000000..9f984fe7b --- /dev/null +++ b/tests/runtime/common/assert_scope_test.cpp @@ -0,0 +1,59 @@ +/* + * Copyright (c) 2021-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 "gtest/gtest.h" +#include "plugins/ecmascript/runtime/mem/assert_scope-inl.h" + +using namespace panda::ecmascript; + +namespace panda::test { +class AssertScopeTest : public testing::Test { +public: + void SetUp() override {} + + void TearDown() override {} +}; + +TEST_F(AssertScopeTest, AssertScopeChecker) +{ +#if !defined(NDEBUG) + { + EXPECT_TRUE(AllowHeapAlloc::IsAllowed()); + EXPECT_TRUE(AllowGarbageCollection::IsAllowed()); + [[maybe_unused]] DisallowGarbageCollection no_gc; // set dis-allow gc + { + [[maybe_unused]] AllowGarbageCollection allow_gc; // set allow gc + EXPECT_TRUE(AllowGarbageCollection::IsAllowed()); + + [[maybe_unused]] DisAllowHeapAlloc no_heap_alloc; // set dis-allow alloc + { + [[maybe_unused]] AllowHeapAlloc heap_alloc; // set allow alloc + EXPECT_TRUE(AllowHeapAlloc::IsAllowed()); + } + EXPECT_FALSE(AllowHeapAlloc::IsAllowed()); + } + // allow alloc + EXPECT_TRUE(AllowHeapAlloc::IsAllowed()); + // dis-allow gc + EXPECT_FALSE(AllowGarbageCollection::IsAllowed()); + } + // allow gc + EXPECT_TRUE(AllowGarbageCollection::IsAllowed()); +#endif + EXPECT_TRUE(AllowHeapAlloc::IsAllowed()); + EXPECT_TRUE(AllowGarbageCollection::IsAllowed()); +} + +} // namespace panda::test diff --git a/tests/runtime/common/async/CMakeLists.txt b/tests/runtime/common/async/CMakeLists.txt new file mode 100644 index 000000000..411c530ca --- /dev/null +++ b/tests/runtime/common/async/CMakeLists.txt @@ -0,0 +1,22 @@ +# Huawei Technologies Co.,Ltd. + +set(ASYNC_OUTPUT ${CMAKE_CURRENT_BINARY_DIR}/async.txt) +set(ASYNC_BIN ${CMAKE_CURRENT_BINARY_DIR}/async.abc) +set(ASYNC_JS ${CMAKE_CURRENT_SOURCE_DIR}/async.js) +set(ASYNC_VERIFY ${CMAKE_CURRENT_SOURCE_DIR}/verify.sh) + +set(RUNTIME_ARGUMENTS --boot-panda-files=${PANDA_BINARY_ROOT}/pandastdlib/arkstdlib.abc --load-runtimes=\"ecmascript\" --compiler-enable-jit=false --gc-type=stw --run-gc-in-place ${ASYNC_BIN} _GLOBAL::func_main_0) + +add_custom_command( + OUTPUT ${ASYNC_OUTPUT} + COMMENT "running javascript async testcase" + COMMAND ${PANDA_RUN_PREFIX} $ ${ASYNC_JS} --output ${ASYNC_BIN} + COMMAND rm -f ${ASYNC_OUTPUT} + COMMAND ${PANDA_RUN_PREFIX} $ ${RUNTIME_ARGUMENTS} 2>&1 > ${ASYNC_OUTPUT} + COMMAND bash ${ASYNC_VERIFY} ${ASYNC_OUTPUT} +) +add_custom_target(async + DEPENDS ${ASYNC_OUTPUT} ${ASYNC_VERIFY} +) +add_dependencies(async ark_asm ark) +add_dependencies(ecmascript_common_tests async) diff --git a/tests/runtime/common/async/async.js b/tests/runtime/common/async/async.js new file mode 100644 index 000000000..9c53b6823 --- /dev/null +++ b/tests/runtime/common/async/async.js @@ -0,0 +1,10 @@ +async function foo() { + var a = await 1 + print(a) +} + +var s = foo() +s.then(msg=>{ + print(msg) +}) +print("main over"); \ No newline at end of file diff --git a/tests/runtime/common/async/verify.sh b/tests/runtime/common/async/verify.sh new file mode 100755 index 000000000..989431359 --- /dev/null +++ b/tests/runtime/common/async/verify.sh @@ -0,0 +1,17 @@ +#!/bin/bash +# Copyright (c) Huawei Technologies Co., Ltd. 2020-2021. All rights reserved. + +set -eo pipefail + +expected="main over +1 +undefined" +actual=$(cat "$1") +if [[ "$actual" == "$expected" ]];then + exit 0; +else + echo -e "expected:"$expected + echo -e "actual:"$actual + echo -e "\033[31masync test failed\033[0m" + exit 1; +fi diff --git a/tests/runtime/common/big_file/CMakeLists.txt b/tests/runtime/common/big_file/CMakeLists.txt new file mode 100644 index 000000000..9c2e9d665 --- /dev/null +++ b/tests/runtime/common/big_file/CMakeLists.txt @@ -0,0 +1,26 @@ +# Copyright (c) 2021-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. + +set(TEST_JS "${CMAKE_CURRENT_SOURCE_DIR}/big_file.js") +set(TEST_BIN "${CMAKE_CURRENT_BINARY_DIR}/big_file.abc") +set(TEST_MARKER "${CMAKE_CURRENT_BINARY_DIR}/big_file.ok") + +add_custom_command(OUTPUT ${TEST_MARKER} + COMMENT "Running javascript big_file testcase" + COMMAND ${PANDA_RUN_PREFIX} $ ${TEST_JS} --opt-level=2 --output ${TEST_BIN} + COMMAND ${CMAKE_COMMAND} -E touch ${TEST_MARKER} + DEPENDS ${TEST_JS} +) +add_custom_target(ecmascript_big_file_test DEPENDS ${TEST_MARKER}) +add_dependencies(ecmascript_big_file_test es2panda) +add_dependencies(ecmascript_common_tests ecmascript_big_file_test) diff --git a/tests/runtime/common/big_file/big_file.js b/tests/runtime/common/big_file/big_file.js new file mode 100644 index 000000000..8e7499a99 --- /dev/null +++ b/tests/runtime/common/big_file/big_file.js @@ -0,0 +1,2097 @@ +/* + * Copyright (c) 2021-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. + */ + +/** + * Generated with ruby script: + * + * require 'securerandom' + * + * N = 2048 + * LEN = 64 + * random_strings = N.times.map { |_| SecureRandom.hex(LEN / 2) } + * + * code = < { print(sum)})() + * HEREDOC + * + * puts code + */ + +const arr = [] +arr.push("e078658c28a5dcce28f62f886f80644c26c7eda07c4a7ddfbfc6c2804b02101d") +arr.push("7cd131eba1d29b272991660a0883f04d77fba9976a304bcf0c01b1adc48edee9") +arr.push("424a395328de95b928930edc7c4302276af17c25186823226311e60e1eafe216") +arr.push("50e9f4c60a5dafe349b750df75e469df8e8fae5ff4dccaa7639e6ba359a959c1") +arr.push("0d737b2322e91747d80303669de175b32e9e4d100e5ad4d4b5ce73b5379b9ff8") +arr.push("a84cbffbe8cde3dde89d3cebde5da141a2416669658a5354782d21e923a21ff8") +arr.push("f439da8fc21e8428d67b7d0719334b7c27b97d64c957d8945f7fa7db019bb1e0") +arr.push("f397eb3d34fbb6b61373dda5c92eeb47a2c31c3ac89c155e3ed44202099aca1a") +arr.push("0c4c9971554f098b5f473cbe5915b9698755c1fd858512f57f04513ac2f1c060") +arr.push("30cbcdb435b6d2132ac0ca14b9c7192987f669d77690985fc45543dbc09d6663") +arr.push("a16e0298759afe938817fe14bd0b55dcbb5aa5211a4dc489c2d5745b738ff255") +arr.push("38a2523d6f1f95e827f697ee2f2927846ade210920257ddec4dfc2b9153fe6b0") +arr.push("6c97ef72547d4663865e43e5b78de0064167a4bd15b9d466287bc48ffbcde58d") +arr.push("037afcac89c66fe3afd1396c52b9967d983a0666bc50e850f466092b66b5d9d3") +arr.push("fb5200c9130afa6adbd44d39cc02b14e5a93c571c2f557615d7eb178f10fee75") +arr.push("5c260a83fad2104ca19c1ac781d42d4733f232074845cd326273790b77a5d6a7") +arr.push("e75e7577f2809aaf456e64cc22036238f5548bfaf3f44ae6f7ac92517fddfb5a") +arr.push("df1a771cff5ca409c5d56b99d59e8af018213ccc2964c5a19ecc613dcd9f3746") +arr.push("33dbd068b1ea8374fecbd5089359db103bff783f285e863248589416383361f8") +arr.push("69f13777367263ce992b07ddc20aaba1d9f377ea4d8655bcd840f3ee072e8390") +arr.push("897772b06b41cd3d1c27d78dd28932c27fcf2c0984d4340524ecf09544078017") +arr.push("54273cee86f86a02cda34df63e59a57d3291e07faf24bf627eabb9ff49b1fea3") +arr.push("0f8ca348fa82dfad2c9c46594c7e3e9dda9e0df738a4a64155a8f55c9f95fba1") +arr.push("f23d293cf5d733832d797876b57aed8289aa854238cae3a363a76808119a5f47") +arr.push("5bad10152a5333197e898eaa4181ae9bfc164ba02eb16f028705efabf02f5ced") +arr.push("eae6a2c8b25b513532b48a54e6ff76cc03a697e510a467702b2b9fef85f16cea") +arr.push("a1c61ce7e52470de3e214e51161d0caaebc553a7a33185b2b81f0aea3724abdf") +arr.push("853b897aab7b5ff40dd196d3a11325f7c25698248834469fbfd18e33ff8e3f18") +arr.push("b63673fd271376cf677ac7dc6e4b979803b0c87f969ff843ffa4e4396ac210e1") +arr.push("a0bc55e366b23e819af797c5951ebd18d259f8bcff2c42a4128a12631538dae4") +arr.push("d8029469d170957b6e9e559b77bda0f418617a3bd27c805182a3a21ca2c31732") +arr.push("a5f6b9cec0b3ccb656f4a3a4c31775c8341c56c04d50b38baf0ad4e13b4f62c4") +arr.push("ff22958d54f6f3df4ffcbf9a7a288b2ee8aca6d5473b93dfcb1fd3d0c32f6aec") +arr.push("c694d09a2549204e88bf6866de69f5a346791c7436ab24bfcfd078fade46bac8") +arr.push("ef7d175ead20e33b2e3f0c35a01acc654ec8fc2737fc414286a0b8036008ce11") +arr.push("33451b1a79cbc968b61adbc5b3c4144f2896ff9eb2822f3ba338f22c7f974040") +arr.push("4d48719d5b1dc1cf81d9bca5a3b53261eaa1ba036e4b32672dffc7b968044039") +arr.push("3ce3cfa7f894d5a05d5bcb024a602aeb3dc2ef1c96bc0fc91258ee37daa106f3") +arr.push("c7211d651dacede90e4d5f73968d7fcd451b0cdea593548f6b54c4eda477eed2") +arr.push("0d9dfdc9e2925ec7c39086f35bf5dbbbccf4908ec4c8c14a01368e37505cc230") +arr.push("66d8f178c10975dc25a18dfcebe699d6538bcfe065928f96e0c0c907cbd6db5d") +arr.push("b45dc54343d92144b5726a7f685a80678e2a14434021f9980e96bd8e465bebfc") +arr.push("d05039866c8fd3e7936976da08141c58750014109d5ff2a879a89df893656bdc") +arr.push("923e8550f949f05dadcb1c4685e4c3cb9f6e580fc46459f98af9d7ea6cd6c444") +arr.push("d8fd568fc3fd28a22ed7f2f037bd56333855b505db75f6442c4e2268bc0c850f") +arr.push("ff1400320ab6626b6580d09c5f2fc2ccd1c9827f8fae0ee2699fc6555bb400ba") +arr.push("17e498aef93ad497da43bf7c12c8aaeecce087903ff16b8f3881390277d5827f") +arr.push("2131a68652b8957f32fc1968778ab58ff242e166b630a8bb6505d9c49527f93f") +arr.push("ec33e649e4d762c507aef50aff383d45297564817d5f4c2cc28e7f5e70fbc6d7") +arr.push("95abf83cd66781fa0a6b4589c24fb283b1b0606ea743acd5c987b3c0b9ed5450") +arr.push("2843a78368d24d2a181457bc49afb94a38762030685785f7edf4d18addc55069") +arr.push("6e656249833edb61f11b73c31cdc03a52a02925241ba3db68438091302579dc3") +arr.push("ec14a6df57377b5d1f202c2b0bb800bb237957bae0d382e0b8fcfddc723a35d9") +arr.push("0e176101f6a24b6e46800b47c4ffb0bcb4df5f44802d62c16d446577ac8ae6fc") +arr.push("2f596feac39d21b6d339872893d4da22b5a65d70443a778e4f7601d91640b3d8") +arr.push("cefe9afb9e2cc3d6af34dc007ef28e37e57d99659622b990475c4bf092e22c15") +arr.push("319ce240f2f4b275a9e2a91fea5ba44df70423569962813147a47d89e3ae2519") +arr.push("050a54398df3b17cb6f2172d8b5c731d4bc942d9c0ae547798cf1e75794ba4c9") +arr.push("94a4954585b5dc73594161e988ef839959c16259fce5f9b2d0cbae3671e4c365") +arr.push("1a8c62f5af4094dfdd100383929ec3b99c2afc5861ac3ed561cb2fdfac7ab701") +arr.push("6d8139d24dffab3de1ee1c498612a5b54190f57ba7a48d7922ab8b7f0adbb642") +arr.push("410d06b44ae2bf42836945b5820916c9af9b6bca153077eca8eb09c77570ab0b") +arr.push("017b0d1f30c34657adc07c7727a1d71ff2ab6c6d6b454101677c53e79d02c273") +arr.push("589051c98ed0a8bcc51e9148181667eed2e0b00df148e7184efea3cc0e927180") +arr.push("5ee62e05c31339486d97112acb2255909d90d5aa0eedb21525f8454111fdd3b8") +arr.push("d9e9a0f47c7196a94fa31553c2fce5fe5d3fd2d44ed9e2e11fe88c1c98994a6d") +arr.push("e2a5c1bd7482b3ec5b083de779578219a4012ff434d814a5d6e276ddf7458b49") +arr.push("2ea01176bcb398d2a108a21bd7f192188a973c7f0d97274f6cdcda55be3f4ff7") +arr.push("97762a602cfddba7f453c68a92e3c05aee92e8caca688db5ab1508bccbe19f3a") +arr.push("45d2ca5d8b0b86cd39d290ab6fa508770c631c623cc31fd1a75c7497457df6b1") +arr.push("1f9d6bad02ffc097019bc067b24972e29f7011735d48fb8cff5bac890e3452da") +arr.push("9c37f0b5179efd9dd781138b9579343db64b2a9e392204f69ba2b767e3b2a165") +arr.push("c78fe1588681f1782d0530ba3e94ff88d1a028bf9541cd8f5a3c21eea33ee1f0") +arr.push("0a44e53efd20c58dab5c659a30c2fc6a69e0768117607e37004c650f231f9461") +arr.push("87f3159f8f0d249748907e86c7a2a520fe7e127e20c471ea50fc836c121f2654") +arr.push("14ae8f900e92f41974b82fda838dfb4137d36ddba20a0629d64f0323dc37dfc1") +arr.push("44999a5c6ee1cd2b80a75feb0468d6efdaab1a8a1846d29699f49d3caee2bc77") +arr.push("850092ad2a2bf4157e8d58e1d5dee74d319448c13448aa0466e67d8294e9c541") +arr.push("3a8d2c43c7286dc36d90a845189b9fc1a3395f1d235ad3902400d09aafbbe79c") +arr.push("eaeeeecc2c594c8f66b3700c2037fccd2c7c9be8ca875f37ba75b23e2df123be") +arr.push("ea7752d34191804b5037f516f02ed7774815192882707223fccec4fa37207370") +arr.push("95cbb9ace62f7b30436813feb69bcfd2ba7f09599845186c45764fbb4cc357f6") +arr.push("27d796c7fa0241204f761732df70cb5d666905cb90d6a16324cd89d93127f62d") +arr.push("f3d29a629d2051d9185b41062efa79c2c5348eeb793a3fdebe1ca169f81e33bb") +arr.push("b4e94cfe6b912af10763156f98d2db1a84c1342c7793e9ee0df044246431d280") +arr.push("139e257e25bac908ed49b93291c7b1b66aa73f0fa60d92e3c2ec09a2816cf3a4") +arr.push("97530dbd82daafef92ffe2b2517591574293e1c2c849b0da769e9227e73b3673") +arr.push("85f854edc9db74fe6e0615382c6f544746c5cf470e78e8a34eaad56883634e3a") +arr.push("dfdf2bd96d3881f4fd01a935a83488c811621867e01a08aa1a89131d176c0f5b") +arr.push("12b24f1954e674d9b022697f154c7bb48b6fb9b3fbfc813dac3f9984aaa374ca") +arr.push("2ae33282740a7320e95ffe3596bf3b6769c24b03198a0c00f8989f063249338b") +arr.push("409efcc902a02d4bcb446fb5895ccf5cc1687a6f16e9046d525ae1f56ed19570") +arr.push("8dfedbe44fd6aaa2495ff0e9cd7cd02387231fd6ab1d62eb60b255fb57c21530") +arr.push("6ed914eabf0385761db124de5735e5f61ba06ee64d2223f554a09404edad799e") +arr.push("2f68cf84903c744fcc058ab80c7a84319e8c283c03eacdc6a6fd2f037720df9d") +arr.push("99229e38a970d3b0a5a7945d54c8ff84f4e15976787d0b4adaa379d5256b5299") +arr.push("70d552f3b51d5aa7de22666cbe8d36dc6a57859b50df5c07a62e972e2f02cc3b") +arr.push("8ac1dc28c01b4621f05f2833f4640970747de0492b19d91f806177d7b7e0839a") +arr.push("ca3a12d5ec909ca9e1c1a32ec11bf0c9e2f7797fff487ce965103fbaeaedf5a4") +arr.push("c50a86fc95241f6bfe5d8a2c04346e9a71a9c3b216160af0ac8c577aeb252fb4") +arr.push("200e5ed1e5bfd430cbfa03d36af2e9e4d0ad442f11f4d3f159ee8a40d8e83bb5") +arr.push("9d9056b501d1e244ecea9f2f455f8c437758a1df0f534816a6d799f7920fd719") +arr.push("d054072a2ce4a3fd310e5b79c642da99b6a857e6d58d112bd802ff8fd43baa1c") +arr.push("2709864573c4f173f9eb669e3645b6b46aa59f2bab53d904571e8de7fb1af4f4") +arr.push("95838034d44dde7904e7dbb0c3830a45c003875ada3f5d8eed84a9de9dd167e1") +arr.push("adbe335abbbd97d4d7a12be7e8abc406c36b512f39d27b7c06a815980c43bb21") +arr.push("4bdbdc2b2400c5df5da726ea5c45b690d5fb7da3ff735805fad29c7a68d3d4e2") +arr.push("8e6efdc228a75c95d119326fa0728aac68e7ee79d210a179723207fc42b8480c") +arr.push("cfbc7055e55da5f6300fb31c9c51d47be5deb9d28f894346857639431c53fa40") +arr.push("d92d3fa8a2a06e24afc73cc97d7659a9f10d9800e366abd3d4bbae16cc0deb1b") +arr.push("d0dfc1930eb2d675bf1b7a0ba26d35c4d786d064dc4933ed7226047c1f9883ef") +arr.push("8caef6851020b4e03e53c2b54ca12a59a32287cebcd83a8cc1a6cda2fdac1d22") +arr.push("51f1ca4dab1f4d9406b8fa173ebb094cfb2c298d5f81c71dccea9a53852b5250") +arr.push("08b43055d85ba612a2e70a0c7e946e0302bd780810d37e2f86484028589bf00d") +arr.push("e20b92eb26558cd8a7f7e78f2c1ea46984eddcffcf9af2afb8f457cec6517df4") +arr.push("db1d13997fa3a58f0f73fea8390cdb18096f181dc002bbff04728bddb4bfe211") +arr.push("0da6f5005b5e60e258cb33a35c2b357f500a05f8d1fe36353fb0a716cb6e6e64") +arr.push("cfd791931667c01f666a5a560209900def669222aa7a97a2b8ff054c456440a8") +arr.push("557fef7c55ff484dea3a3ff16d1ed756bb46ed223bbd27a1b8854d9a67198ded") +arr.push("f07bb85d14c420201dfacef19b268bc72e71ae6fc443af0ff8499a39b8fe478f") +arr.push("dde644461b4d9e62df301302346634b42fe3dfb5aef2e2c811697025f0c42f3e") +arr.push("ec00e868d5997e3b921af1dcea05ccd49ef595c7a4d2fce3510b5793c5bf7aae") +arr.push("523375055110cd4dba9b626d075b10a4cabb1ecaaba235db6988ef726611cb4a") +arr.push("fff7d8303aade0d3439c2a208ac0e2d073443b1e7490e9287e72f598a9c6fdfa") +arr.push("0b31b2aaa229e437f1c5c70ef75450a6cd720565ef9f39cb2f21f3e305d5449a") +arr.push("f38f0eb9207a35bb3376344ee9839dccfc9c7ebfba9ed799cf5d8f4ef9819fc2") +arr.push("a9923300b9bfdd4e3e25c26bcbb4e7ab46b9d53eef8b607c39b7bd86b7d3ebe3") +arr.push("37730c49c8a6459bda7e754f9aad8a9fb93f4af299666adea170c70080db5d3e") +arr.push("552055ea6685f1a002feb12770196bb4bed50aa3b0dd1c931894adbc0080ce15") +arr.push("ac7b4ed40e513d2af44159961cf866cff26fb41ae71be5f98807a9f567df7895") +arr.push("0057475162cf3ee6fe7c66da7b82301eb60e91fc3617a6e8a460e55a1b66572b") +arr.push("5d34e0eff7e58fbccd219e39e36e36b2446eb22dd465a825492a9e884cfae8cb") +arr.push("1ba5c66739a4d5409b64149c04e2c3e8e8defbf4630e36b8e3f7bacd35e7267d") +arr.push("188e3234cae1462d4fdcaf3acbc06d1a53e55d498ea8455e29b63e4211cc6da2") +arr.push("502e54e47579024d387183964360500421b24c9626d2ce771fe61f83b79a1645") +arr.push("87e946f612fe891aae9b561f96a4707eea4a973561cfdd53d70d37e6c8455388") +arr.push("630b39ea329984c876bfbac4367f1230d7161779915e9885504bfcad0934904f") +arr.push("c8a753ec1e135cbee6b7654139df14e856a329d3afd392aeccbd30b279abb2c5") +arr.push("6fdb161a5431d1ac30d405e44ee016e9699d7d12d647a48fe2bf7d3e621698b6") +arr.push("97e7eb901da55c0e08163e5af3700396263ca2785ffaf32a6dbdbeb18d60fe51") +arr.push("96943d0b05683eb7871543775109830b681fdd37826fe0fdb702268974605dc5") +arr.push("030ae16905bddc6e5d894289c993ef01c61621474d37599d1f62a7f001765f56") +arr.push("fe57d89e78daec424f4f3113c742e4c740032e1b394f07fd1ef2daa060542aaf") +arr.push("c24dd1b0c5d95b17225fcf00af1f6e3df62b102bd7b7f39d297ed5df73316ef4") +arr.push("d011633fa3e62b40f1c7501c7d2653790d267305b3e861257b1148bf0fa4d768") +arr.push("1a39a6ec972891a549dc4fdf03f5a3bb7809fe8c18047641f59fc612d73d884f") +arr.push("7ceca6841320cb1c460c2fdc67c9a2a4d7fa388589bc455c8aa7b7864858e134") +arr.push("ba4abd456ed18e492a7f7874375552ed7c3b4d871f223a65a914567d1cf23250") +arr.push("56694cb4dcaba7ff2e672edca87b5e183b00ce3d23d27e8ae515bc56707faa4e") +arr.push("1c8c35b664642a1811b6ac85d940d0062f5458ff78289a9fb308a6a3958e27a8") +arr.push("790d6391c3dddad0cc354a4e7a588db33c2e8dd45d61ef390ca1660a46acaba2") +arr.push("febbcffbb3bb8a74e519da1ed249239fc84876386e175cd91969c0324a505f5b") +arr.push("208c9b7ab62c8a347b204096484a4bcf5be2ab7e3590d3c83dbf6d7e4e7d056d") +arr.push("1ed031f456b0051a30a714fbbf12fa61b62106d8b48ea8e3d7b3552b18fed01d") +arr.push("34ea7ddfbfa686eb3ea337b0dc8f932db5691900d0e053ce142d0e1aea9a30e2") +arr.push("4d84bf4bec2ee7d79e35ef8faf6ef36b77a0063c5756e2bcc16fd6406412fd3f") +arr.push("5d9487a89241f2141e6ecf81f256c2d5582d5e633d2612cde86d5418f0bb14c5") +arr.push("70fcedd34d40df78fdfa109b33539e653c2577d53197b433bebab43bc5c84b14") +arr.push("f7011349a6bc3b4a6692e18aa1b74f1f45e6e7e6446324dd08370b17a4f4566f") +arr.push("9e28e50d8f6cf93286cf994be8b9f1127035e714af72c7d53ae73355780402ef") +arr.push("1b57dedceb9e73174a57e4d2db17e8a226189fcb216b0c59e2dc46c298c59aa1") +arr.push("558985165b56f67be45559ba90990f2f507c7fa3ac0532ecb495949f778729d7") +arr.push("e80f9885b2992a9b33181e96ff3c6361b2a55ff74cd16ac988f3a21a391f902e") +arr.push("d1a67e0a12136c51ee55d344a7ca791f2c5f68f616b0c8970f8bb2306ddb7c55") +arr.push("0817a1cddbe1fc0092c3224e88cd170322c266ef60d643335a86b6be027fb8a3") +arr.push("0038bce627ed509650afc699eebcc2e98242cd4735037bfbe7899c72da299685") +arr.push("db30db0012b19ad64825ccbc1279b87b30f6d6adc54e0e29c07220ef2d35353a") +arr.push("2341bef69fde3f99ca6866086110d0cea0fef675d4a6a6420b2d8e29ca7fbfde") +arr.push("ef6960e5887ada7135dc68722b5ce40f6b56a65b5d1bbf042b8081f5dd1fb4da") +arr.push("90bf180c6518de91933ec6c2bf979a832a631dab6eeb22bc996cdf4942d6a7d1") +arr.push("6173566e82ae640f9fb94fc491462278a5da706c482410c7c66d8ec5e02906a4") +arr.push("ac1761106c9a4215f83cf0c6c3f3c385e332909f25dd17bfdf2730f621c6d9da") +arr.push("e170c1e09e9e7d11f314d03b3bd3e1a463c65257c9e29d01202eb1c50499c72b") +arr.push("a9131eddf07331c38695e9bd26ceea32025c83cd121883105642244f148059a2") +arr.push("91c094b4e1f0cc77ea5282d907a4922db1dbe7deaaba81e05bd12245d6551d7c") +arr.push("5f79a418029ae389521e64957a691cd982e731401e44752084313ef9b6f3f989") +arr.push("c0713dc07577cb9452270ae9e4eb56a0ef25a0b943d9a62a203782dbaaebbac2") +arr.push("a8400de359539ebcf451ef35c6d959fbce0bdf0d5e3f3dfdc67003934c655435") +arr.push("d0f537d98d626faa0b832d62a714152932fb61e3ce046ea29d7c0558d86c9862") +arr.push("a25fc34c5471e95e99169cc9f30bf890b4f9d4742fbad89e6916332b17b8d554") +arr.push("96bda683aac5377334ed323f89e1f4fb7d35b7dd60de68b669daec5880cb95ed") +arr.push("f069371bf5827ebaf137896c13cda475f7981cbf85996c1a127a0c4b08053432") +arr.push("7cb684f99326907386062f29c3f55fac794b5628634faa85c19e366a4868a42c") +arr.push("b1e2588d38b4cedf4941830d55589ee757bae9f821749d4b071ac8812ddf1620") +arr.push("8a2dcab264a77f9f060ab6c6545201f6240d1f8a966bc4a7e1b96630611da237") +arr.push("f98e07552581a01f53a41dabc2b75e68cfa84402eac072d7340ad05dcaec6bd2") +arr.push("504e274fae86e2450d209cb388848d0eff8d4c82743a681a72bc828ceef90510") +arr.push("07542af852951d61895115674a55b15e4f1be581384fcd4d53fadf6cabb4a72f") +arr.push("cde7e87797fa19e95c9b186e9569bbea7b23b06f9bf23db1f2bcee03ae0cc3ac") +arr.push("771528bfef2416e00f895968439795a41c1ac0831396d1d472b5e0a45ef2db46") +arr.push("5c70fd445c6f5ccf970b7cdf9f0eeb2b28b9aa16f2e03646132f606842865073") +arr.push("57709698b60c60d26f357238a25b30812e55d1d2dcd9cdaefacdf6d7aa7b4164") +arr.push("ac5794db69d6e13a99f211100c54eda54c519c9fdba60e8a01bed6b3cf3b43a8") +arr.push("09dbe877d7ddebcd71c5f920b331d33f4980f0c02ddf9173078d882f6884f8e1") +arr.push("ccc5e7527dd5dda9a1f1e7a8168e5a7af1d4ede5c092e1edd20244502e2706c2") +arr.push("136c0dcf1e9583aeeb3a7052ea300a9a90d9bea8d57608efc58c70793e1c1524") +arr.push("e1dda6218ed725bb0ee8e9b2c76357eec3947b4867e4bb0d9d860e811314dca4") +arr.push("8756f7d0c5dad1b2a58fd536b22c6374ed574fd070639930e0c2c6c54d76e47a") +arr.push("9c663aff8e1bc8b72381a92d883a7d1ceda51b2fbc0e4823a0c94210abad5bc3") +arr.push("4e235f34b11407fd9ae071a29f80d7f2ca86210dae8d13f434c763d0d9fea43b") +arr.push("982ee8277e193643129e18838256fa4d8f59dbaf4965bb51d0d74124a9573efb") +arr.push("44f536cfa295526b36ed02bf7cbefb91ea3e8c1a9a1501c60729b5e82b8f0866") +arr.push("b266c52cc9f97058cadb9cb6d462160fa2caeea7f50649fc64a2bed94714e6f1") +arr.push("3c52881e9e82efbd0317339c2c860ccb79e7cbec3810a460b6cafdbc28e1b412") +arr.push("196c286fca9e0ff80ad5d0e30dd6b7e56ffda0d8f5dce8e199889b4b9fa3d50e") +arr.push("0bf325b56eedf20637326377e6659f505e324089801d3e07131dd54749745cfc") +arr.push("5808fe14adc2dc684be2a409b2141e3cf92e3a0699ff4d3232d13c1f465b746b") +arr.push("857ceed3dced3458719c589bf89b257127fc8a1cf64d4344202dd226168dafc8") +arr.push("865150c630b67a934ddb7257d3d3ea47053649b189e468c3606f45a665af8eaf") +arr.push("be93561ce4140446a14736d66d90c48d421121026ef7366c7e375562699671f0") +arr.push("194f8b8a2612ff5e76c1359fe6bb5e74e715a25fb04bd35d6a148c3bede88e0d") +arr.push("c9f204105b1181c248e9de5ceba061b1cb6309b8906c6591dce3f2a8a854f0f4") +arr.push("0e273bfa8149ec07265bd56c2996b85d2ca65fe423f045185f3f9aec7f74bc42") +arr.push("402429bf9b26c89949a3bcaccfb4b44de8e203b3c3de5f90b2214a838afcdbad") +arr.push("b2caf541476a5b4ca07ecf5a7e52654e4a318e5ccf6ebcbff07ffb40926c6dfa") +arr.push("99430cca37e9a5e4acf2828072b2472667906701f2fcff77fd3747612926bff2") +arr.push("8a0114894e3d6b4b2f87ede0370ecf80e8122b52976bd494bd66fc1522aa82fa") +arr.push("749cba5c6ef9620bf2b406f1008e7eeaac193c1cb9f14773d1b5fedb67353058") +arr.push("1b4746e2991cd0d75f599c571d17ae6ec0f92d533cb319308a3c6d5dcdfa4dba") +arr.push("2a5e2c2c4e59e7cd33222c67a00fdd96587fcfedfcf94e52f06f595f7b924ff7") +arr.push("ad70dede26d010c6de8b7daf303b1f9d8608df377480665bde62e49fa283e9a0") +arr.push("3dbcbe612b69c2b5c89461b1f68de2ff17a6a6c5d74ad428241174488a02333d") +arr.push("9698cb4e91bc556e4b783b89987e78172bd29ee54aa5b0e45d9e64929a486a3e") +arr.push("64468fbb669db1cbede3081cef4bb822c1a3e1820d1ed909065d3a08315922e2") +arr.push("e67556221897b654d85c44c6f4a14edcc9ba895c2e168a318d2953389d9d4b7a") +arr.push("1c5e5d1c64a4f441ea8195e8d7455a2da51733272424ffcc57d56552228c1452") +arr.push("909f70595f7fee7b8232aaa8dac06a38a0cf5c7395378e88e7c639ff55a86219") +arr.push("745145adfcf4bb37c6f043f1db74bf6b5acdf4df26a1fa073ebed50510d60db4") +arr.push("54a0928175447addca13f77724424dd2563e93b9b260f418961a42b2a45c54e9") +arr.push("b17244c25e7911aeb794838d89fbc652e09fcbe473475887e91a706b91fce202") +arr.push("3762093b8f97dc1b761b67ec64b10e837921831b6b655059ab80cdfb01bc7d46") +arr.push("a1193a8ad702e94fae88f00c2f884247c6bf666912987e6ac0b34af52cc4cfb7") +arr.push("785b889d5e248011c7ebd03cd48fc32c16cd2ad326d4c7ee795ea072cc50e3df") +arr.push("0c65019d9124eafb756d8d67e13e56dd6e0a750ff2af49fae10c33f9adeb9894") +arr.push("5226ce98332607b4d03d1c1df3808b80305d2dfc20a61c09dc32abbe841cc053") +arr.push("69c678b547067d0bd6a669a9449ce896095b8c88a96af27e6f0eaa4dcd116e06") +arr.push("b1e9627137d1e11ffd3b09833a7233ae57faf6e38d69110cb3058d1edcbb76df") +arr.push("542fe9f6ca3f6520bf306fc70d12e8f9d7d232c9658992a0a7849b78df4536fc") +arr.push("af251802f6e3d982f08794f9601bb8623dcbb3c513dc167898d05692ccd64b44") +arr.push("55ea40dc67523d794af7a6b2555f0c3c661ce0f87e55bd55b99c56f175da84c6") +arr.push("efc7f430797fa0f807d5a005dcecd56c77f11f4750c31e33fa3308a8ba88192b") +arr.push("81650b93f4bda2f41caadc35f34285319d7f24e92ad1c8abe341b4c123e537a5") +arr.push("676968ce7b378f96f86aeac1322c63644bda6eafc825c576f601d356e24c9028") +arr.push("f85c6f691c71888f51ccaf41bf897b72ca55b80088c1511c58a45dc4972454a5") +arr.push("1dab04295fb953e4d8454338fb83682d8878d31d459f48825a193fa88475b327") +arr.push("d19f3677f020c8d815908618a0f8148aee9da1b212ac5bea18777b8f764df304") +arr.push("451be65487a525296b177ec8712958875c654ac5203399ccc0e0986a838989e5") +arr.push("b3427e3b3957380d68632be1e89904d39f9cbe5375613558008789d246928972") +arr.push("25c431cbf2f30cd42c8e1fbbb5927c38c7bdbe4f44c5fc22815488bb9ddb5167") +arr.push("576611e628a030609733333dfc7d1f390042f92b28bc30f69cb5e162e0129b9e") +arr.push("30562ba46c6b8126009d0f4f910eb573a8609efde6bc735c33d5aa7ad2feb404") +arr.push("a8ad85feaabcde39ebd2806aab08a5c9c4b81ceb87eba21406f0fee7ee110cd3") +arr.push("90448b5693051d4516e71dfdc221c066e2659f07039950886279f18bd6bc6ee8") +arr.push("7c99512011d8a669fb39a5439f9f6b3514230c56b06127441339d74968f362d1") +arr.push("395cda0aaf76f5ec13a719e7ce0e00899218c9618d997a56dcd64b0b73e825ed") +arr.push("a1c79a721728d48529af5f4ede43ac2d52647d63b49517e630bd7f444745f38a") +arr.push("8703e987710d12d0942d6b652052e16f0aaa3200196f59cf0a27baa5170c0ee2") +arr.push("287861c46a53d02e45edbc46433ffd8a8235ddc6a9c78f03b750af74441c3916") +arr.push("6d6501eb0ef496155555493f39b0d8ce0bdb972cc4c9fb4b807db995060315dd") +arr.push("4a6198d187316eb3ffe996af5911d26286de612b5f7aaa617ca216a1309b6e64") +arr.push("3278e624bccf2a911873b189e0542af85491dbc546ad68b0b792bef0ffba0c2e") +arr.push("55ddecdd6b74ef8ed0b83f94969b3a7276229a1a21e1bc0a2108075ba27f05a8") +arr.push("9b933539f83607d9c30e8171a00c90e524c3514684d81faa8517418edfd7dc71") +arr.push("72e8fd40222c883fe39d90f3eab82cf7007570d76833e965d5f4677afe9328d8") +arr.push("368c38e7ccf535340dc7ee1ca7ce0b19d25ab55569cc165c47a390880a8107ef") +arr.push("71a9ff37e52aed50f95d5eedb0ce18569e1343686b3f27919d3b7f79ab94c171") +arr.push("050903875f5d9dc1dae5a00749839ba60633cc380e72806a7f42be9d6e8f74af") +arr.push("5441e5adf73498e1242ffb3803a7bc663036090e622bba670987530526b3f47b") +arr.push("ca047ba34673c046966080767e4bc02bf3f4f72f06e66d438e425eecc8cb85f0") +arr.push("8b78cf9298bceece05d4869c5bf4ceb5f59bfd777bc3d8c82b2acadb2a838471") +arr.push("1483a8147c66184c789bba041a7e17fafcbbaba34770e14f63a05d89e3a94c7f") +arr.push("a4f49941c263b268008a82e0539c2688f72a2004b00907b844521901af5d3ec9") +arr.push("1bcc56420c564ce76435a8f56c9086cde771e91d8fc6f2f82c7768e2dde51245") +arr.push("d733aedfde0735ef0f00ca2ded71f0e8941f29340e5448d2fa8f992cd9e99053") +arr.push("243c81ab4d48c79f32057e25cfd2077ecd94e0b8f3f8f0788e864b9068939043") +arr.push("a01e5e7f3ab9aab83d1da682fd9543b6a4231069e7038f43ae7e661d4a5237a9") +arr.push("b85c2942e1853b421ff5197522d84f59518bcbc749a44d3da8b6c2af4b924960") +arr.push("f6b91e7acf9a4dc60e70056201c5ef0c959e0a326e1c5f0ee16d0dd15713d7bc") +arr.push("251399823457828b738d9996a124b4894971815ad9d791d2d5db2238db1b9b49") +arr.push("c885f24bc54e9ad40c095fd8a3dd3c027b03b0db51147fa4e2d865904b3f2dad") +arr.push("b2f1073a4504f18dfe727e9e63dd02d4e111910236855cdf0de741b32b0d08d8") +arr.push("208d08c280709adce0c7b3bea862915192ef8ea2d722f558dc626676ce0fb38a") +arr.push("8b2781acd0a9fa02b9a421da9c0d3d50a99c20b005013f330379b421f5adf9cf") +arr.push("5a7c31458e2954760fc59e812ac8eae04bd7208a8030e5b0ecc438afcca5acb7") +arr.push("4c669f22ad288dc91e24d561b4e72b5e60e85565bf75204361fe9c71007c9be6") +arr.push("af6ed0feda581e3d965b1e18a4909eb94a48d593ba98ea6798b6ed394561ca8c") +arr.push("da24352334761d0c0fa91763eff903871b6f038ffcc315775afaf7a311b7f021") +arr.push("7a86caedc8df7af2ec2f659c711a326694d5d53ca4c01cd73d04e6a800ec8d71") +arr.push("63f83b62786514a09747173077fbff9cd063fa4373fbd5598f874d257684b189") +arr.push("5f98746c2b9c40a79cf756f2ac9e34fea83fdb23f62ac85b3ded949e3494df63") +arr.push("b56fb42c0715beb1d3a8e3c1640c7b3a833afb6868fe93381e976510668fc6f1") +arr.push("96998dcc5e051473f1950ec1bdfe86aca9aefcc4c2b3ec420d19eac2de92a6f7") +arr.push("f91e7a53236fcf455c6f0d966474f296951d27ca8625ebc522451782045881b4") +arr.push("a81e3e377a4b8f133becaa84ec31cd20116dfe0ef08683b9a28af5bb78fc67a0") +arr.push("1bd552d0e96c5e560c521968bf7e511ae65f2c252e97a952cf3b7ff8c455d937") +arr.push("b97f2baac4f1e85e1133f16ba9b29ca4e00ac9ab2c4dc8555de3a3a851838f18") +arr.push("1768243b110815c4b567458ae725581459a53b6cbbe655d0212a36ff18d015de") +arr.push("82dbaf71f19599f1ddb1cc8ff911f8aacb8cd318b24b5d03bb6e50086ad146ff") +arr.push("a8026f62020de4a2b9d8544ecca7ecb21f5296c812ac3c3244f10d9c52b0fd1f") +arr.push("6fe10d1a89a73d4d0e85df98e58250a44da5e866dc78c3c0e9193ab010c6b9d4") +arr.push("ba797a4c7d10a4c3347b3fc42e223683f9f19dfa7bd79d4641b0195cf846708e") +arr.push("bb33a5dbbd0779b55ec9a1c7a28c7b9e0bbb4e48408f160be0c57b63605fa311") +arr.push("6c28afe709d4eddea8995fac6334ec64d7cf6192a3906f5ae46a775f378efd7a") +arr.push("e82cdee2098f9967e0521531be0ba611a8c23e2d6034a8b0385c47af7f33452a") +arr.push("9554a3462a1303a2278559aec26042dd97da5fa21f42f5291099c61ce41a4ee7") +arr.push("9e2c46633d739ac48a1138b350a8ed0bb0c402fa9da2a76a1a79c2fada828fa1") +arr.push("51e76932e72c85d68c7d0b1babacb896a7fb5120798d012a03d280e3fb40e2d0") +arr.push("3d079001e37eb6b38d9054abef7765b5b77a4706c337902b8c3914bac209a607") +arr.push("72f421188de948fa22df79a6b197e4f3ff0b0ec866490090398c0d9554660615") +arr.push("09e9480f0b0a9ef58e14ced5a4b189df6208ca8c439221f7e0a6056494a4978c") +arr.push("4bd19cff5ff91950fe5fca80046406283062e8e6546cc150b494414413465f76") +arr.push("c79d0d0d45bbabc12109fed38be6e0dd20808cc50c744f3df385ac2639d85292") +arr.push("e0e56746136fb5ed64e36750d064c640de10e7196100d450024752c0b17a44c3") +arr.push("5431ee862239583d7399d3651dfb83bca0280ee08157487c93d8db9ba3cd1f6e") +arr.push("c482bb2c5448352626a801702182eb0187e6bcedcf6cf5927f17e327e3bed346") +arr.push("097cb49f59cde863372ac2f2d123ea8527ec3b1eb3389cdff68f291e2994b0e6") +arr.push("e936fb029f702d0d4302ad0a87b1860ff651bcb01f5dd430558021c451083309") +arr.push("0a6a795fa18be7e13ee3fc16ac3fe975c10b4330614103d8ae91d9f98807649f") +arr.push("09ac1db34caf318d97eacf752f7f89a23b37275c64cb8ee8613b16314183ed66") +arr.push("ee38704765230d69c54d0703ba5ae75a144b9f6cf3c1f88090fd9c75211f9a99") +arr.push("62c1d3acb220bfd48d1fd8c7a63e6554a17be8c63bb8f711f814cc40ce663243") +arr.push("87ff6ea1312709718e39e27ad3304d6a976dce28616ed7f2df4501beaab0f72a") +arr.push("8fb49321eb0d294e362a9850043f5a6ee72194f872bbdc5da45b33af480a1905") +arr.push("112654c5b9dee5841dc8e9e36a67a2227dedfca103d1ace750bf9a3c58617077") +arr.push("83d3a9fb6f1d70683544feb39751a39b05f41b6290829390bf28427b37787fea") +arr.push("63db2e06ce813a4054cd11d8ddefe33979d851947ad8e7756b2a7370847a8f79") +arr.push("e4b06774afb7c78db164f85df89ab43d105a7e0a01758d6b5a4456b54cb49679") +arr.push("6a309149bbfe0bdb351b74cb4123f1fcf8d8bfa30e37b469ea2261b667305e23") +arr.push("799fad7268d426bd524660403d7d5b02f90b3185908d79ec3b245685e77d25cc") +arr.push("45c1111a785ef062ed093d5566483166bee7cb9ec5fc9642d91735d535ca8cce") +arr.push("b1b02bcdb24ae6ac19c052a3601bc84fcc28973175754c951dfe8ca961b9b4c9") +arr.push("34a5ba76144b7124eba61e8ffafc2014aacb44d0d900d9c5f7b3460247654db0") +arr.push("9bdc85795a130a72e86603f360b12f30d5f3f7491b6ddb8ddaa4085db66771e9") +arr.push("6fb23267402a3b07b897f5cf46273d99b9931f6e8ffb1e667168f8420f315046") +arr.push("41641f88f5d2740e06c3b7ae5e04c00f3b478e9b4d58ec62f8ee7b0743778893") +arr.push("72d62041e21ffdd5652a45c4c25604cb08e5fdac0e5bcc270f9744d43ab51fdd") +arr.push("c22049250578359c2b6b67ad830c6584c1eb98b1f3db0eaccb484322dd4b4c15") +arr.push("dd648933e7f61ab91a896b2af1d864945c12b9adba97dbbabd3fd7ca7c3845bd") +arr.push("1eed6d0f6ea5a813f000a448043325d8c17c83ff3c8b9cc2e9c36961a1210efb") +arr.push("ec7bad890989f3997d71718e13cfa36c95c812cb0952082f6acad0e2732a6829") +arr.push("48186758b3722e846f1c34f4d339b9ecb5a8e26a22b1e2eb52b7437db154c1dd") +arr.push("a648f493577be07a3c80cec1783a900dacec34c868105de5ae27ca704078d645") +arr.push("8b200a377f266ad181ae5720460a7b805ff90391158ffbd052085c773c07c330") +arr.push("e829ab9446c53667b8332feb69e2464c84966ef03e46546dc4f503190da61c4f") +arr.push("c9289b342a10f1a348cbf006c4ca5ece636ffcd1b69941afb78d242ca910737f") +arr.push("e8a178a999762ff414bd9779275a7ec3c7d75424990598ac104bb463f3ab766b") +arr.push("6be005369987877ddbdd5aeb27f0c23969c17696b6ae22c4fde3a47f689a19d5") +arr.push("a285e2ee9d927296321a78874b9d908c2243183737a2a9cce6bb7bf32ffc61d5") +arr.push("e4a3aeecff56c90f17037523d6d8c0a3d597e17522a307ff80eafb15c55a9787") +arr.push("f90f2c331e927015eed65a5bb79825f18cfdb4574c1caee9e4ad910001bfbd24") +arr.push("7c3954e637c8b49cac5ce69df888e502e78cec69c2aac6cf0a5e02b84eeec845") +arr.push("a59880dae1ef3e33424c5f4841154a5c2d2be0570627db5293786a673f14f011") +arr.push("04c0e6689cf164ac298a5b3967af3b76e811d083a1991ffa4fa3d4359859dd9d") +arr.push("a824af39f1b2e2e014cbda1e7fd4c11a6c59445673da85a8058b3172f8ad18b4") +arr.push("94779ed4540af40b3f7e8858e02177f3a1da629087a8d51481876bd47b0b68e8") +arr.push("e5b279dac5e1fa063fdc364281b2d1fb63cfa47c057841bf05f7661fb60f5d09") +arr.push("7d1d00191d75d61b3f588a3ae0c0e5a235437cf9aad1c9472cab454c6176b2b4") +arr.push("65c7d8112ae65b2f23d39a2e93afb03d466565b607afae8a71d54d0bf995b3e6") +arr.push("7811a39d25076fc60645902eb47473e51536d867974e61dc355e13383686d78d") +arr.push("bb9869d824eef9c393c49190aeec9250307a2abafeba49f9faf7a8747fc28e65") +arr.push("fbc3a752f9242c2c5f43065230a59ce2e514009199673abca3a62d9ae60323ee") +arr.push("4e13be9fa8458b482d59838cb83062fb1e101fe068b011b258a6ea43e8529d51") +arr.push("2921ab3a44a261eee64008c8e99e1426210c67057031d69367e52431abfaf83b") +arr.push("99043b5ceabc83fa25993ec2482883ba73dd7361cc8616c4ecac9aaf7b8b3810") +arr.push("41f45cfdfdb032e469845e03c1f5e678f05e4497992d9dc52e3b1254ac517df3") +arr.push("15e1b8d1bf44a841176dc6b0ff34401f67507209ad4ea07df7b79001cd9f76cb") +arr.push("2b5f4597875caa8a54f34a3bec2cc5ff807a97ffe8bacc82ed1e301ab2c4183d") +arr.push("e1bd2795d26b05e5793e3c5170b858ae3f626b1a9daa05363c5bb694b99efb8e") +arr.push("89b4c24aa45977c5047a18ae9bcccf31c9a01bc56a2e635628feb621801552ba") +arr.push("600bc418856694e8f48363856221f05a81a70ad441ee427b6bdf3b2b34741112") +arr.push("8b64bae49d8f51dc288d527d8656201b013ded7945b2f3e77cf84bda618ac0b4") +arr.push("73c3357b3094d5749e163e8ee523933aeca8c6147ad259609f9c8dbe1da22241") +arr.push("4e9751164cc9284e09a0caff37b83580edfb427f5096c110f381b54f44f3d425") +arr.push("1d44b7241c46bbbd8509cff780136b94ae95ca44fa44ecae55ac03b0ecc15bc3") +arr.push("df1978a6f27e4f589d08cd2d0565002b444d436378271f3bc0c245289d3f68ad") +arr.push("3c677fa05be30943ac6156553a0e51f23a5bef78827c4a332f98d91558867ce7") +arr.push("56e5d29485b46f3d43863138036e1814a9fbbaa059820f129e2e655e165336e7") +arr.push("3ff555f02eb54170e653059bfb59aef0fd2adee6bf94a091d7084f299492a4f1") +arr.push("1c96873332a350748987f73ab8b17edf2a79e3ba4fcd16151136ff2ecd737bd9") +arr.push("586744f74fe23f0d23f9809ac79da9ae5dabc14b751cc7720eeea92fa4fc2c19") +arr.push("a529a64eeb0dad17c4511c9c875adeb6bc7cf71246d26f18a17fa7e21c68cc15") +arr.push("1a0511a579952d2e1c0508c542b4cb411581be76b6ffc9c5a6d2a03b14cb60d4") +arr.push("dda85adf9b974e673b83410c3f5f8725e3123734f01f284a2df28a19a738c097") +arr.push("5ccc7785b07d437501cdac0044d31871182cbe0f0cca7045c2def1857ac279aa") +arr.push("fbf6e7a63618ac9f64547bf14ab9e1ce699a38bb63c4a4a21f82dcf25e91566e") +arr.push("e4b60cf590d5b1db01f1c61cc8f663ccc3f3b9eb222ccb8117d7d9bf3a6d84fb") +arr.push("556c2d6c0785419e75ebd8d27fd9f88af5399c491f05332e28ac0cf0c10531a3") +arr.push("53d3e998246bdd6d317b519e1cf275e704ad484e0d9cb4e9c859d49b63b3f4b3") +arr.push("0d4508e075b85bb117d2eeb13b271586bd9fe0fa4355b2ec03d3ec249adacb18") +arr.push("e36e3249417a49497c408a5a2a1ffdeee274b51df40a891880526c67a5bb00d0") +arr.push("ad500eaa7b3ec9f3f09f31a0b34b909f1a24e0381fbc2b847eb99d172bcd23fe") +arr.push("658ce34a9e947a61693812a40dae1b6a420e55c190efa8b732eaf36d5a14955d") +arr.push("28e71bf87127edeccf59896a01905965aea376cb53bb78ecc7c31bcdfb6f99ca") +arr.push("d6d40c111242a50ecc3f5bdadb4b5c6c35b9893f96c8ad693c9d33c9658353ee") +arr.push("043698c5478e2fc6a11dd2697de8b3e50699b1ba134431f6a362945f47281401") +arr.push("09829e0559c11bc3f06eb5f2cb9a4e6eaa6acbe07297f016239b6b098744af37") +arr.push("8af52213c899774ad87d99659e62d08168693b99b7a64d23d8ddcf06110063b5") +arr.push("1dc0fb438f7b46408a1cdbf5a9c65be8b816be14755e6ce08623af13d5414955") +arr.push("0e3ddc13fbe27c0b140118e6bd455e52423e60b3a13aceebaa4e2b75eae4476b") +arr.push("dc7765cb4bc1c73ada21597d08910897672a5a2cdac76fe655cb6ab74e1f9fea") +arr.push("39e66c8e809abc6182bf1aa49bc9761f14478ef3e69645f5bf103120e70a43bc") +arr.push("8ee54faf2ff4974d9d39e89332b0908848994b20e839bc92b742b178ebbbb8fa") +arr.push("9e7a0770a51d84440911ef3237511e16816e63f5355d2f81674e94e7f87366b3") +arr.push("323a3ee430b7589c49622ad7a67b6de4d68395b1cfbb77d34db5e3c5bb9abab8") +arr.push("cc2b6acdbece0be36da9cb3194d4d3172b4b3ed8ad9ab48964334f31c46de9c2") +arr.push("e06b15d3e3a55f83b7ee23545a856bc0ad3fa7425c0ae07e5b3c5721b04d59ce") +arr.push("dc95fd554f40e96b4de869ab34ebf5eb8fa935b5f3e58b003563963e2208d7c0") +arr.push("3396a0b61b91f2a1bb01b6fef1daf0df228a264e5605fca3eeb220b634625dc9") +arr.push("f5f744ac564604d9132c0f8ef86f059186397dac7ec521d2eb14a3170d00a366") +arr.push("54a71d9f1a1e16d8d50992e4b4a04360984a245b780ab8a6d7dd5f5e52fa284e") +arr.push("ac9cc04d044c6046ec63c7b1b2ab54e6871e7f5f43ce86c573016da5746f523e") +arr.push("72e5cc1b69a21d7fd4201f1c32672e280786b2dc29130361632d3c3143d7191c") +arr.push("0f333abd261909087d7dc67ab5678553b447b09a135981c5053e53d796eb71ae") +arr.push("9d5e92507f8435382912d77251aeac644cfeeb0fd9591c40b421c5f2318df889") +arr.push("3a13ce51d49f06c2550ffd661ee4dbd7f6db79214052fb4e233123db442f47ce") +arr.push("48deb248dd30c3c454fca445a95710cc57e1d1ce8c7b13fecd1fdf79fa79a5ea") +arr.push("c7053f69249c65f3912849361a5ee824712732ff179bbe89dc4dbb421477abe6") +arr.push("a0365082e89beeadcf463a7d4e4d686a569b0dacb8dc59e3213efbb58ca681d5") +arr.push("3337b713f499365dca0b3070971c925b80d3859ecc1cef23feb8d83a15bc5561") +arr.push("f431e37e0086621863b465aacffbd9d2d321009d99fe2ad304072f6adb63429e") +arr.push("6edcb9423236304c7af69f33fb201491c2f22d594cb9552f3bd02d737199dae3") +arr.push("ce7f197837959eba6cf6a9bf5f62b499e1c8b61cee4c415d2dc00a144c16056f") +arr.push("ecaf63923e24fc428aaa9348f22f40a93df92fa33d07998ad2b1dcd7f1ff65d4") +arr.push("6164e28d0d4dc6946932d3ff303bd8d0fd1ba9d0e60099fad1c63e559bd42769") +arr.push("16815e6083cb6eb6cc5813780ec40008f4373eba70e1003969dda253addc0bd7") +arr.push("2b069b86acdf3b19262f97c9a0513e40397ce904cd079ba812cf98e64d998876") +arr.push("be78fada4d3e42fa1f0fc04a4f119bcb038b1c59f4ca2597185c5d3f712bb645") +arr.push("bb38ab668e3dbe97e56d125d58536c85f6b13e31dd9a653026f1ca45f71f1187") +arr.push("bd5bd9cc376b8f36bf36d53bd5573c335fc71a7bb0b39882af0abb6dbb756c35") +arr.push("af3fab108af31ae04753a7bb666395a1b02d6a728a5eab64056cd32377dd60c1") +arr.push("81a8ae97a6721500ae9170dddd52119e7cc24626d9b0d021488d5c7c0a72f23d") +arr.push("17c9970158eb0e75b48dfcee043a26215c326b16040b1935208a39b090f12348") +arr.push("c8c702a63b3783339d4c3dc3e42dea09dc8384c1113541e7e371570efb9f2461") +arr.push("20256f27f766454c33e0f8cb92fe51cd97767f7a60879724c3c46766fd5cca2c") +arr.push("6b847ad2ac48ad0cdc136ffb88f3529f9c78116fda4bfef4e08a14c477b366b8") +arr.push("fb829b418309fb3b2e8bc15567f53aa7b7c7ba18c32d05db9cf655487f4ce7e1") +arr.push("af6eaa61a40ec3cd894663fa5db54ccc726884450d172023065110450cb0c1c0") +arr.push("3cd76222293ee0a9e96b43e2517f40b39ebcac17e098d4a4242bfabd7ae12544") +arr.push("926f992da6357b642ec0ba209760591d10a7a1395dce870e99579c8e5dc8b745") +arr.push("6f46f9dc01d1f20b63a84686abb038faae6c139d3c19702fae6e5f0187d735f6") +arr.push("1f466cee8d7c8d6a1e6f3206a86bd93b8cae7c17bd4bfbb96f93a3ea57db58e6") +arr.push("d28c43a6add8c786b150bd35193efb8bd41463ead93221a7b1e5892dbc4f57c8") +arr.push("2614a82b8391c0c1c6f7970dfab64a1570cb825a93b8f65dd42471a0aa1e883c") +arr.push("d04710edf0690736c7a8e00e5e71816eb9525d4f9fcaee111c3045bd7196d800") +arr.push("7cf1a78de4cea8186786f4137ae1abc2be9fcae8d1a004a4a9c6c7c7cf6a2b8b") +arr.push("be0ab8a64a16f0ace9127a68d833b284457f71e46a254a1e6a2f54d6eb59ba67") +arr.push("6d9cdded009d08a98a62b4f853b96037949c779f7d42d34a3fff9a94b123aa6f") +arr.push("85584ed0de9e812c31729dd6fd39703466bacbb16a51d094aff6b34f5d4aff3f") +arr.push("39b9168d1e6bde8ad2ce9a09e2db552055bf70f4ae2150b4f9f2bf5daa07aa2d") +arr.push("abc414f4dc30803ecfdc7e65847b8bb60526a645f0b8b80a1a9d6c5ece66968e") +arr.push("f50db2672ed5ced79e81813d5c098b15a9ae94de3337ea8b9a1b4becf5785c7f") +arr.push("8c82a446e5e3e7f793b8e142f389fbb87ddce191c518abcd831e180172e780c7") +arr.push("f8c316e754c3ffa60d490d73b0121f5900e07b3ee7b4cbd44b25f3a46ec843e6") +arr.push("f7e69d4b03ddba2261b009156f67bc394d42d66370db351c1e575c7de493b156") +arr.push("55d7054fb20c63b091af96c004638897ea3f09b7e2537a262d8677b1bccf09c1") +arr.push("cfca378e0467c0439afd671f410d8a4e3b0e038eba00655f97d1b1e50a96e15c") +arr.push("e9cd3131af2865689ff54a37829ac91a738eec3e068203de86d3ffbce269d736") +arr.push("32dc60c47d8bed5e55da2fd908cc1112fd8c150c98bbb25a80868cc2cc9c30eb") +arr.push("394075b605644bbc853b18a6ab66427800a140d5a1f62c7a6ecea138aa9c2e5b") +arr.push("b4bc928ee266fe5f73b8ab1604e0a02ec136377707af96c06ac9fbb105eb8ab2") +arr.push("fbdb50655bb92febeee322bb669e75f08bf7fc21a615631e4b90f9246424d1d3") +arr.push("bafc91a3c5a98c5bdd9bd6f911573fc97c8aa128321a4712060835336608977b") +arr.push("218653d19843300e918d2d5f97e3a8d2c779c9fd1732b479654fa650e865d04e") +arr.push("565e3b258293cea5045f5ee2a2b6bcc399a2b64c2f208b879cc6be9f2eb1229a") +arr.push("46434cac28c766869fa87573bbb1502fa05ea372c5f2977bc74093b84423a853") +arr.push("3294ad63d9a6cdb04f3f36a188748b1efda3af9b00c45f07c7e45c6de7c39571") +arr.push("c04e0c06e6f4e86cba3930f8d42a3c52977b60798407a61f114e6f530b59e953") +arr.push("a53e77aeede23d68ef366e7592261ff9a9f7bbb8691210a1691c663c3541dcf0") +arr.push("e91f0b580ef4884359b17240cf46b832b0d4af771a9a03d0bd469db9adb9d756") +arr.push("259f806559015da4928d332907a7e1c8f37bd6e1c55c6a5442fcb8e84f8d590d") +arr.push("a7584c2bc7d89fb7866e66cff5d74deb114abc13dd18c1399dd21b9c1a52bd70") +arr.push("16c675322dee245e54be93694fc846d035ad81c467f3c9bf97accf6051652986") +arr.push("f1de29e0be6ffda50c60651cc30983e945db2c0ddb2f4e221aba8b6a8674be99") +arr.push("43cd67da96b5d068e785024a50ae62bfd4bfcc51c7cd227e53ad92daea7eacb1") +arr.push("061c28157def70180fa231ce8d965ed405b55409d382a507add2aef1aed16ea9") +arr.push("58a9f07e1ee917c5015184290ab6ea6ff726d1cab4cb7a2ad5f9ec3ba224b363") +arr.push("80322b5048c58c0501dc8a669ffb099b2ab867945541501ab86155aba8647b4a") +arr.push("3db596b2a51663358988e2fda392305a713448d75d70a86fce52e31a0eb82db6") +arr.push("59a7c06fd1a021b09504c7d97262a307c20281677bc4b875af08e69603b767f2") +arr.push("8a76d7ae806140742bd3ce4ce9e984984537e8159f397428f3c57cd0a6d8b9f1") +arr.push("59f78dfa3ab2f4a6c78c9c3bbd34de1e89be528492eec55400a5804709ebec1a") +arr.push("40ed0db5220e17c4ea108206521a348ee7150740bd71480a16de7f48e46938bb") +arr.push("1c1da13369c37b3b0794d23da8e21216769a2f26175c125f1ad5ac5288f9415d") +arr.push("ad58b127cee78b3f70bed95393809133fd8d355cf6e9a5262e287cbbc956c3eb") +arr.push("56005aab42eecc1beee6277856bcba61d16bc28909dfe86917a421852cc19f26") +arr.push("916dd0ac351b8e0f70e12a58e28867a95b4b183c10d4c2d56e810b3679a2d146") +arr.push("db8247b3f2c04390fdff83ff11c0eb837e4bc1ddf5b3b38178f8b992c9e58af4") +arr.push("4818ad82e218b5d6f12532fde178a0181bcb90bfe835e3752175910b451a0c2d") +arr.push("842529b4f08168bcf16b5bcf5c825fa5c901a4efd2e3ca9730d2ae0d5a19b5cf") +arr.push("2a9102efb3add97439c0e525ad4ae671d11424a305848181aa2d3feda398cec6") +arr.push("0571ed9e97fb4f7c91017fa2046c04e9f57a2d161e17ef16eb185168effb902e") +arr.push("6aca2bc058df8160d666b07304c6daf65a0002d17a8abef9a0c05536c4992b80") +arr.push("6487f472a7270a90cbfae2f36ba07467ea0dc7082b208025a111e1595d44addb") +arr.push("f940879137a980f73f59ce9bacb6d0772987185b1f11e1f2f649a01c7cf96747") +arr.push("083e41695a23e7d9e3dcbe5ed4c2f8f03ed90956f7a78da77fcdf06bda6eb34f") +arr.push("560f0224a6003563e9cb1b8a280a8a78ffdd2c9799c44a082e3a81a65e4442a6") +arr.push("d0b8d41dd938317df252001a8722bddf962d5078c49724d383db4b96027e426d") +arr.push("4a042e2a6764d27d65dde0edfdcb09337dadb4c22f138e0f9713436589222188") +arr.push("307ea37cf21be28b2d834a3c802e54ea208065907166f4103ca802968afd30fa") +arr.push("7df70a37b085f1f1b08d3cddb1dd242911593f8d21689c8ebf8c0a20f488c6bc") +arr.push("a57f4a689a2d2aa200f424219d597ccdd45c575f885b30e29d7ce771c9dae3ed") +arr.push("64ad59adb24d2ba7eed5c01b8fb53f8ed0a91b9f3eb4605c3b8f2c91bb1cf942") +arr.push("c539a629928db120379918e8829ff2862c7f6bedc48f232aaeda6d120b9b9798") +arr.push("aee5a31d4bcc3195fbd9c56435be2838bde3c93a51b56e4618ed6af8d4128f4f") +arr.push("10e1f3f46f98f3a106efffecc6e33a88400a90d4a5c32f7d35afc542b5f2cb98") +arr.push("d6b3df6af2b32932cc2f7bf213035c3390a87d6ee3525945f23ebd9b1b25c397") +arr.push("0b5890858e3d5e8ab823f745746d3c0c382fa7f67b0810a734b79aed9ca19f58") +arr.push("99b4ac6f72baf5c89b1395829330679564423f84089503e2bd8c075a41107c55") +arr.push("2b5a30bc56e076d4442a25d660e3a667191bbfd0769a54359fc9e452df14f1c1") +arr.push("0823e45e61a686136bcedb9c5838c5150ce12d205e698eec1b6326c87bb8bc7d") +arr.push("9edc729cbe199d382278499ee4082eb5bd6df96e49615cfbc5db49d3b3da4614") +arr.push("b03023f4b1a066bfadaae1608153263ce800c5769eb23e3af6d8e608c0c67b70") +arr.push("e50559ebe3a2edf5f227774b813df34b00985bf09708635f36bc6cf3cfacb384") +arr.push("891aa75e6b5041bc3907a3a6b5825b1356638c382d394be910be270eaeb4e11a") +arr.push("d6ecb8212c832f03145666a1ab1ec28b74749afb08748e71a1f50f39f3a17d16") +arr.push("941939ab1b924306cabe8ab9fc9e2ab1a3d4a7330762c47c66345d281b0951bf") +arr.push("284d671fe3cd710191b2701a5b6c0fe4ae7332aadde6bb298fcfd2a22ef4e535") +arr.push("57d7471fe8e1a6f58dc6cc867fe8f235ecb6d80600e8ecc4e42587fd59dabcc0") +arr.push("99ac639f8ed0f70df83211b6cc650f192d866719ba6c6ea67ceb312ed888ff7d") +arr.push("3bc9d57f3b3c874372a11094ed4edd60b01d4a2345f125b8239c62c4cb5ff71c") +arr.push("6f2942af90d28478b013371b15f839a1aa0e8ddc01ea5269166ef95b2e1da25a") +arr.push("2a9cc0dfe52b5d2a0763e2900f1f336c8a7970eeb3123bb1f374e888a3cc8670") +arr.push("e75e3da7f0ebcbc086faddd0a5648595510a494c23d22c061bd082fe22b687a0") +arr.push("76e467cd36374a80e5944709dbe1cbd166310dae181c1dfa7803f80e2ef0e49b") +arr.push("35cb73fd98f50d86ec805f22900cacc33d94460e5f146a9c99d2839617be2b92") +arr.push("4b53db79a41b8f2cbb84d1f55e105a7be1ee79b18a67ab3c6265333afb4ba279") +arr.push("59f09e7d59712752a9c207084ff519443227df054b39c93fc4473dec1e10d006") +arr.push("b87bf4d04731e7507600c052b974098dba100bceffd8f2c8dd968615d19030ff") +arr.push("dae7a9df61106e982a704b6ef3582ae32e04da4aca7296113a695a55158d60a0") +arr.push("a12505f9df2df175d05d774dcd358e4b74304123fb0e12a6f4b53b296033eb1b") +arr.push("443602c7cfcaec395f443584c6351790975e2fc1d8a41726d7ea24f069fdda0a") +arr.push("3ea23ea31c9a00590f33ab23a833ac2f41afd73d39bfdc274bd363633da69403") +arr.push("a375eef618afa482430ade462ca61779a3de56d3022a260b6ddac8e4650957e4") +arr.push("c0d31c1a3d4290752723d60e81a4c797d90c89c38024646316aadf100df87095") +arr.push("108b47aea8e96ac5557c05f0721c51cb409f7256e4b48e72462f3223949b683e") +arr.push("2f0ca638aae23a96454c873191d09da4e77f5aa12bf45aed0dc34429e721f3d1") +arr.push("631578b06d9c71217ca7ebcce273a27ca72d4126051e2e99fa3637433f35400a") +arr.push("0ea2d71e7145cba52a4078318d0f3add16198e8856224b571878bb1dac5730e8") +arr.push("2f74f92caa9a5d3f8c8a386457bb22a11aeed511939b1f2e92b300e3aa7ecf14") +arr.push("e6f4ec647a63bec32604431de747a9636d2022f97e9dc094cf8fd590dda5d613") +arr.push("09c10b3661323f73d51843bed443e80833b689d65385bff14a9d0fce8b9c811f") +arr.push("ace9c4dbe78c974584b71a9653eb969f01e59f2fa59eda636a0601d4ea8fb0ac") +arr.push("44458a32884b5c3f8eeb54007c3fe25bfb0c31088a64960e70f30f97714ab82b") +arr.push("a4d967d1a636eaed7294d910c0e28e6f45ec23879f88885f4a701503b29f2194") +arr.push("8f38ca68cc79b915a29baa42b77306cb5e7114eb9ba6ecfccd73aed88857eba2") +arr.push("f6cd29ee46bbdfa7101ebc43edd573d85266fe79eaac36a068fbe25864de36f0") +arr.push("f17c07490432e202253975c88ba65300cefed300ddff9f964a7266bfe6025b85") +arr.push("9ecebf76f043c341225b6ac3d48bcfae90f4eaa4be5a4dbfbdf216f3445332ac") +arr.push("2078a2a437293d4e006ff333f7d25ec8c069b274f101d9e2679b2c293440d43c") +arr.push("25d8ce622c552ae05fc74bcbd93d7de82322f573447aaa33ec986ac830e9e48b") +arr.push("812f0b727e785502114a9a620ddeb98d0bd0f191b8f41796e8081074c461c175") +arr.push("88153a0f8512e7cfdbdde2e52f2718cd1fb5a538ccdfe19ffe2473c6f3256188") +arr.push("7c01c962771891b8296b247a427dd8be6db1335b0a3b670d053ed6258d644e82") +arr.push("7a5b3eafbce877039f00ce9780b53a60ed6ec690bd4d7afa29f91a2702e81da3") +arr.push("a56f7a05fd38cbb73b762d6f88c1af6a1c9fa0636990dded374d61b0cd223993") +arr.push("0ec4e27941862cc2865e4404f71353eea9eddfcbef39cf7f4eba82ce968191b3") +arr.push("1819172231c61b8756b11721b684c69b6a6b818d63d4c34657084faa0bf649d4") +arr.push("58195a7e1ae9f62320c1b0440f7ed205ba4ae1fcf955a368758666ddb5f56979") +arr.push("1b03f5cf1ec5294c5ba3afc9f30db4a6ddaff40d7dbe6bf54b13d9d20bd5664f") +arr.push("6538caa726aa346358da19ea88aaecda1cd7a60a5a766a0aca66051a6ea5d32c") +arr.push("4a233baf4296ef9b674e8a6ed83cff7fa96366dfee58a89a7f2c20d43bca3a1c") +arr.push("47a1904ac39f8a96d0dd3c5aca17ffbf585db3588234098572c18bb5e535c0f1") +arr.push("9bec10cf7d68dc1581e0f19e5e544846435dc6e729b34b127bf192ce9e26062c") +arr.push("35507986cc262258db2cc05ad9c23a68f680ec5ca8a7e6db70921373ff21f987") +arr.push("7e632be6647676fd58e92105d2e0ef38262c0e0b45b3d5ca406faf4887776585") +arr.push("37e8f699cabe90d722658151cf1d87114be981ba730616d331acdf85ec0c084c") +arr.push("e49cbb698b77b24ba575688508c57a70c83415b2496e06e9bbcfd742ddac9ecf") +arr.push("5823e0e0c7a5db8131ef0c6ceb01a79ac6a09c5d47430f822a63cc5954efc699") +arr.push("dd6423ac71cd0bb82b5e4fc4a0991ce5813ec95633e95fb88ed98219dbe10eae") +arr.push("be13fa4350f7513e54490838e0cf49af104b14574a6b31a3ed365c6575b777a0") +arr.push("f1ad7370bbdda464497d8da5c1ea627aba5df2733fbe24e17d7d8d7a49b8f361") +arr.push("688b0cce684e126229072b77f248bae31f25304daf05e811e162ca1d9c9d4ff6") +arr.push("2c27669d26eeed825f3709706a3076eb84b90e0d6446e75dfa84299863ef910c") +arr.push("97bc79882be660f870fbaf04ebb5f3eee8fe70b19db2d0e45f93a3e959b53ac9") +arr.push("5131fde23d025a4c243e401fcf1eb3a21bc4088bbf74ae58cb747f59632f43e2") +arr.push("f6140a10d2e0f038d06a17b8692a2e6cf90bbc8244180b2d9ed7a0f032231826") +arr.push("3ddc6b34c12a921b48aa8010f088f0a19cb9505de1e6c3eb621364014cc8d4df") +arr.push("9ff2b3684caf8098082049a87101fd056ae450d5aba2f46f3db9797e3bc08afa") +arr.push("ccc01899e5b8d7c3279eb61167527a163a6f201e4576be59a620db7fdb1b14fd") +arr.push("2c5ec90fa6fe991bf8646b68281663b33dbd18362dffc9ad7f91227417155fe4") +arr.push("6ea3a7a0a131ae75a96cfc22d58c0f20094b7db75f1c98ceec72833d85dc8fc0") +arr.push("9e37b4c8bb7fa344d5bf7d6225a601109823cd1c852afad3e45de6c2cc3c95ba") +arr.push("e4ca8a50183121b9b2eb2063176f348bbac4d8c3b3d7415d11f0ff5c842f2876") +arr.push("268114f03af6f96d5d12ad62ed832a71b04932275c4b63487860897e3c1f31d4") +arr.push("5cbf3a4029a178fc5f4cfb19a57538103c556a95db90b5164ae5a1b1434a26f0") +arr.push("c19923218ea75eb8d6c7436e83ff88fae1e7736d0df16c52a4d4cbffda620b73") +arr.push("54f8734866cb5003b67448f56664a9dd9c1a604b295af8ed52c6d379a914a1fb") +arr.push("e63ce693a9df46b951dd4a870d8d0223874839976d31fc0138f6a636bda574f7") +arr.push("db485563c39c4392a78cf6dd607ce098a0e76ac230f32152ba1258e872ed2554") +arr.push("6d05c194df292baeddb2f008fa2c3a941b2131708c82d684e5e893fc16014cd7") +arr.push("16b9aba00a6171e3ba565014d255a038f45ecedf6fc2a33712fddd9e562bf92f") +arr.push("1130ecee98af9259b7d5e32e327f5585018a01bbdcf79e00ddcdc926ec22a478") +arr.push("f78141cdcff711e4907614f6816743abdf86da86564bf293306e491f1116bb1a") +arr.push("6e5bdaf4e8f18a86abded7baf892147a3a62ca01e4a75972a672ca7ba44b416b") +arr.push("c037dfa9ea2974fc0b05aa0aca4b7727ec7d4a4dd55d6307d6a0ef9d6bd31667") +arr.push("7d11e3de2d3dcad97ae939cffd022a1e4d0d0830efea349070c75cc457cb12ef") +arr.push("65506e80f552c2389d63dd0b53bb4f9a489fb8d3ed67dff7db6f6263bea93c30") +arr.push("480dd757b3b1859d2101573a4a4dd1688ed7daad69d191a1b8872c785997a860") +arr.push("324a4a8bbac2367f4125242c05815bdb4f7034c3e35e18f791fc664c4bde326f") +arr.push("43a9feec4e08fce088fe77d30793cc737dc907b9ffcf3e91a180374d25b6fc7f") +arr.push("cf9447729171d2febafd62d9ac3f66566a3057b5f27ac92857e71d0b8c821697") +arr.push("641a39578297405067bfa9a3794cb832beb29a6d29fb7cce75998cacb2cdb091") +arr.push("c14a71132ebbf4ea31fdafcdcdd296156f6f02c622c8155a0ed18f563f99c8ae") +arr.push("d52397431c8a58518f05c022564d506fbd87950892c712f581f1d12c6e227b2c") +arr.push("d65e07737ce80fed77bf530618ce28a5a9612971d5e61ec7bf729b54df302e8a") +arr.push("71c28e0f462497f105ced87b7fbff30e3e60b03adfa618a22930f999622daed9") +arr.push("d91fe13ae291f925b660a5d43f039e404ad22667003b71c356aad90ffaa43f71") +arr.push("e62b4ba3bd78e0cd9a4a808b3f4c1a3fa74db11bb90d303c4fd8a2125797672e") +arr.push("5a27f9c5c90e9c9d5b25d544a9c4bd22ca9d118f8888467446b4e1a96c631887") +arr.push("aa982e039eee22d4e9b0a6bd3a94f4f2133ce9edac3a292cdeb2e260bb698619") +arr.push("8c2d80b17e8f2feae446237c0873dcff6383fcaa33e869c9f639faf70cdfaa95") +arr.push("e615a608e1bfe035e27a5695f230c1ac0df18646a1ead556d769b369862e5f2e") +arr.push("3aa5256d07f8be3eedf679c3e3d6c7cb8200e1e586e076480e0e129dd45487c6") +arr.push("3b9cf09204982d2911ac025e3d77e9bfba9e869c731b59d463478bace181e96b") +arr.push("8650cc0be88dcf146317233e1497b02feb48bad3f9c9663babe453dcb5e0b6d6") +arr.push("8a0005cbacbef2aa121863825d4bd707fb375695a64fe7271078e70fd15b3786") +arr.push("9836273f595b46e632648754146b0f79ea80d7daddc36433291c89df2d517b85") +arr.push("b9de43e62a95553b2b0662029baa3de7023b25eb98ec0f7a9034d9e0bb54db11") +arr.push("5250969aa65ea868f1dc4e919b30c1a375f9ab14a7be4473cdc5e1f5c9cd8abd") +arr.push("481a22a8f35bf2de46d1178b0eb3d6112741372fe5c7402d1230d4f38cdd785f") +arr.push("e14dbdd19767910a15099248978046be3da04046e9cc4086f6796589fbb03870") +arr.push("03e9d929c65560019b2ddd3842b3de6f0133f1eb0702cec7b5ac0d31125d8f8d") +arr.push("86a62d6b1d60afd990e29bdb39fd8911f03d3c2c7e0987a276eca03e89d80a0d") +arr.push("583174e6b534fd400a3e7f63d2b714a7bdf989eb2e7f6cd708bdbe11391878a7") +arr.push("a636d3477f0b692c93e418e27b9c6001a6413114413b126ce58d93d128dfe0ce") +arr.push("fd828a41126180850a5bf815e59a5b2ebcce3bc38a73cba1b836c290a9513f86") +arr.push("b1b7134784ef80892fa93c1ba5814e18f7f962f539d6ce322955b5b58c76dfb0") +arr.push("1adb2aaf18e4a8994f0462c7a4a2e0e9ce630ab2cbbae8681c1c792ccb2b5540") +arr.push("68fd7ef8d3ccd393014084ccfbcb09b09da3a226ed8b73fb5a38c54f5d4138f3") +arr.push("89fef12a3ed4182367504f8b16772bffb87b0127b4663853e5d09b9af2373762") +arr.push("255ed88ac5d9ff5305022fa134ca16cc4659140dcc858a6815907967083d1fa1") +arr.push("9bfed7cf117db319969dc70b791b2a0eaec60d1b174a82f2bd0ae7daf46c516f") +arr.push("3a1a8090ec578728bdbbc54f987fed80bbfc28c666dbeb9b14bbeb4e17a0d8f7") +arr.push("e91bce69ec8912f3ff52d1141fc55a7f217c3a6047623395ebce9c617ad9fe3e") +arr.push("a56241e96a532a560d604c979b26e29dacbfc1fbcc1407c646a1801b43e4927b") +arr.push("29a73423f8da5af5440f5451916b81214247c01bfc223cfbe7c1f9e873ae1478") +arr.push("c26b3f81cfb8f6198aedcefe25ead2f81268d04f8cfd7d633e182284d6aa8329") +arr.push("0026087e50e1ead83ee1b92c5d80bb1318e276a6ff72e58152951b8ecb2d33c5") +arr.push("20aba517c8c989058271b648738268544a64197350aa0c2c107fff610abfeb01") +arr.push("d3f3d16d9f708916dc521e1dddb03f73f31a269ba17e82c283c829eeb7d7870d") +arr.push("73015bc32bb97ed9937ee8b085b0bbc1b2374c8e5b862194351ff99aee080cfa") +arr.push("41ee130ebc28f1524e0493807697f41af4f17144eebda98e88cefa34ba5d9c6e") +arr.push("9eb517e0e111aee342f484d8fdff5fc5c420321e69a5d7e0b5b4dc058924621a") +arr.push("550ee669c2a27e6a31560d20f528ee105a0e1a9ad804ecf7f034d5fb174093db") +arr.push("fe90af6b7046f2bc03dd93989748da5a8a09a1815a9430d5431e8a63185b7beb") +arr.push("59e1ca61801fdb62bdedac687c3c4073fc19d53d2c647865df54a32a55cd8a91") +arr.push("f6092db0092098f7524115eec2ee2f2cec6ea0d4f411b5557b0a6efa3c044f90") +arr.push("1ddf8607568b162ed336a17b5947748856c7a109998ae591944323377648720e") +arr.push("2e8b037efae7a215c584e4a75e445782d9cff160f89a749c843c369f2dbf3cb4") +arr.push("a8dd0ad59dfeac15be8b4fe81fb9e94391ccdca349700f0bc7bbf6f14e220144") +arr.push("65e242449c38a46d33aed9f89b921eba194970ba28672a89221b17f1d14b396b") +arr.push("77b7d2361ebe4a4f6b3a22c12a74c348f453f28f03f4296f10ee35041e033d11") +arr.push("13e079959d7c014765f6a02a860376f60159cf1e728e9ef7fa99ea06c6b56c4f") +arr.push("b358e5c3817774b7de19ce270d388cad65b6f7bc7845f79aebbee5039fcd0fb9") +arr.push("2be8a830b55de5bfcb81c70979580b39ee22181300481f51be627c27c54d5405") +arr.push("da13f7bd30ded46e4f359a3f0e878eeb6c746314881d191646ad5722f82281e3") +arr.push("f7ac7c5e971bdff8cb84ad48142081ec249047a3fb6dc97bf99bbb9524b52c6e") +arr.push("4184e7208e671104035827e4642b8d0e6a020203acb492e743b0957287ed2219") +arr.push("1b137e973bc519f02f85545fa6baa19e7b4c941d835591df7a5bb2ebeef84280") +arr.push("7c414da9e9953ef8e42ce148e4f4000674e55b3354fce6117da7aac3b9a4810a") +arr.push("b137755b9aae98d6d324a8a39f043d24ec1c1297c9e8ff8580f88dea11a424e4") +arr.push("05fc432c8d7a7bcab2ae151013b433682731e7d5e8e83532e50ccff4971e968f") +arr.push("4d5a9623688fb3613fe94d0f0f169a3de96bd437c816463bde44a4ed1c95e997") +arr.push("2d75df3056c1513b3ed4a60196cf7d05a70b3b8f119347ac7e6b9c9dfb31f43c") +arr.push("6148881d4e7326d53b4852a8dedb7855f541ed8046187f76f9a716e5792d7cd3") +arr.push("1a23121a98be3d23943479f01d43ebc36907c144bcfd78f862d4642407bfd3cb") +arr.push("83375f2d8d8d6500b715ac4d67a14ab121504d1c840a0f4e3ec2cc0588a7beb8") +arr.push("89d257e3ba2f69f008bc36e7d66af4f8f0f00c9b42702a81772608d4b862eace") +arr.push("6f37511c0f125aa5a7efbcc599e4470fb1035e51f946109ba4a89989888720b4") +arr.push("b99621e60b237ba6a6651b40f25b59a152332519f6aa3c3ec59e6a771f05ff9e") +arr.push("c193764e6303c5b7deb1968674e15db78abd8b65c49fb17c6af2bd2eefe47dd8") +arr.push("69a413f09adfde47212ccb4cfe2db6354b14e50978c36965b02cb0cf6a2d339b") +arr.push("24eb0b49b0c8dac33e2642bbdb8e418f1ba428da1b9ae1fbb2ba5c4c33d1ff6b") +arr.push("95f657bfe1a863beefdde121e45fe98201db0f77895a0513b21b3ae2d719bb24") +arr.push("35c27542b5042616748b5c29bafee4158561dd4bc0758c03683d4a9d492dc11a") +arr.push("1ddbf1725be11d6d85845824a74bf46aadd971cddd18454992bb164887811882") +arr.push("1dc1b538a921090a16393f6c551ca1120340f0dc8d31baebbbb362e95b1bfdb7") +arr.push("c772d445ffeba80519c203d26b3a9ba964402d1b1c421e35b5091ce8a9027e83") +arr.push("a8c5e99779c253d98a467a9b35387167e967ff6ce82540bfbdf78a9f7a5fac84") +arr.push("b9f61d80e10e9fd124ad897b5a7765b2b91ebcddc1af3fba38d9481db50cacd5") +arr.push("42e68c9a5c3e73a3e26f64e92b60088e1816059238f6e450f089cfefe3fe68a2") +arr.push("312f8cf84717ac0d7505a1a98bf48fa199f7f61d44d812676f50364d7fdabe2a") +arr.push("c1cc9537967e035872ae93e9a3d28c1a8398d09e976d92b2517e91ea4dc45d29") +arr.push("a6058473c67b01013909360e3e18a5fdd7e40015b884fb4293a90267ca1b39c7") +arr.push("278c5bf49ef146cf04127690e7fd251967ef83f8c8adf91e52f77cf1fe56a23d") +arr.push("8278a63246f588f2fc9657d3ef486b5e5a635bbbb7da000d795afc860521c1c6") +arr.push("afb6b6304f0d59665dd59ed868ca233230f50a7446d37039da70035737f3af3b") +arr.push("129e7dc3627cd8cac96ed7b0f5bf8099fb093461753646873ea81a5fbb28247b") +arr.push("80d03a9ef236e8e08fcb9c0fbf8f9c09d1c7b0932c70320a5d1b3a0abeeed494") +arr.push("24376bef9baafb28c6e1ede42d23ab80c17f709cac08129b415f3ac677d9df33") +arr.push("38671fe373ef4b1590624d9bc8eb2c3edd533aedd5af0e1fd2f0fa4b806b0146") +arr.push("6b69bceaecc0015bb7cce5f59f30afa4f7b18d8ddedf00490e3424be644f58ca") +arr.push("06c9790d2659963fef776f5d764bbfaaec550f642c8097edf8926b4d3f136a20") +arr.push("4e2d23a103a99eb426b7f095006f97c14fb9d3fb222173f54571d745669b173a") +arr.push("1d2a188f328d3ce714bcfdc6d18605c652df21352471cd30ba077f2135ce8c97") +arr.push("7884eaaf49b0cd311685cb3b833acf17187a8da22cb8c921ff04bd31ab16adc6") +arr.push("5ade431bba3db1dae6d083057d4f5029ab0504272833a3b1ebe9e97076cd8a1b") +arr.push("3d3393fa73e3b5e236281bb15e1efe1fc7a90095b7891f273428ce4a3efd3182") +arr.push("d78b6b8ed331f82e3464c211c01f0487c30f9db6e5dbf7911cca033d131ee2f0") +arr.push("04b6ddd46864fa3a30fedd212a967629ddcb520650117e6ae5c9d5e688b0ff3f") +arr.push("2caea2947539385fc003f69f465147f82e14dd9e617f5aa902e1670d16786b70") +arr.push("dfdf02ff39dac472eac978aa65d8e9572e031d7c5f2b78c36e2d016648c71af3") +arr.push("b9abc083bec9e4c4e69f8ee93859b459959ae2df22758db45405228487e90b37") +arr.push("9bf23e0d049e0913ac2104436589063d86540902c76a30ff7563172f838fb1ec") +arr.push("a3c8e7a694e2c90f69233d5144c321e48ef443092acee03603afc1bdefe3abb5") +arr.push("2d1f9cadd94ae0a675b79fc2e2f3c1f38e69b1320413df931b57cac4f55bd172") +arr.push("7585906920b787cce7542d043196b52baaf9453198d436f2913cc2560a814f96") +arr.push("3f4e963428fa1b45aa0aa7f972b234080de1cd6ce129c71efee7ae518514e2e4") +arr.push("46e45ec8697e46b14b2687f81155dab791263b6416a14da15f0c951ee8c9238c") +arr.push("ff04b11d56cd5d422f923103ecfb4e369b69a8dd349436c4eb3088f8053b7fe1") +arr.push("00a03cc6e072186d563692a25a4cc0794e42c4d98f6a03275440cb9dd878080c") +arr.push("8c53ff99479d282c5e2724dc9c82945b40611608302bfe9541262a7ecc373fd6") +arr.push("0b104b794b249db3179c8831d7f99f198a416b22e77458a57cbfdeeeb6ff0688") +arr.push("2533cade76bb0ed3ab2f8c5fece07c44d39516d946b80b07cfc3310b0a4b98eb") +arr.push("170c04862ffaacaa9fef02706070985bae93675401efe2a8905cf0b51c3aced4") +arr.push("ec7c6ff8e0d2f8c011b267e964dab158903bfffa6613ad10ef88d1c9da180519") +arr.push("389042eda4d14c167a8f137dd47b67a80b49e2208162a61b2f26f7471de9ffde") +arr.push("13e1b175d9801ad1a17a7f532f9ba1e8ecfb867aa3d9037a2f5a50f6d35d47c5") +arr.push("2f0ce60ac7c9e131689db1fe1cda8d697956ff091674a536bd895f4f43c60c74") +arr.push("ddcd14c2170399a4701f2eb985af99f2cc5705619c226d595b16fbf24edda08a") +arr.push("e766b96d12bb1b7d93b4d67fcd3c55283fb170445a01eba8492ce4ba6891a29c") +arr.push("f4efaff899b88ff30500eb6fbb4f27179451fb89e53687af1648e8d771c013e1") +arr.push("e68c92824e4ef3f97e1ea3cd01ba9b6d6b54cc3c76d130bdb5703dd02f4797cc") +arr.push("2ae2017299150a9072596d487abf1be767ecd4948af9b51eaeab4f6e62afe45d") +arr.push("e7d5a968eb1019c7f1505b8e444086df94e9f4798e130afab9bae49d5ae8136e") +arr.push("82b373621c2a0e95b29a18098fee451cab19d6b6ed56b8ddb50db1ad64c83008") +arr.push("9e0391404fbed321461fa60b12426e12c96a7ba40193209275ab4cdcbb9611ae") +arr.push("9e57fffa1291f816a0ac37010f9cbfa8cc70442772b827e78c918dc53ae01a45") +arr.push("1d90e0d356d346b840f705dd3278b02f0b099512e9c60f5cc90fe4210db5db72") +arr.push("9f289a660c681ff12514378f831aeaec6ce017cc51f462d5393bec296b19d599") +arr.push("73179859691ab0bed17519c1a3761556e8c66f052fc17e85dffc02e60043ba69") +arr.push("532b71b6f95c5c4aa3b2f7a5807928f258d4047152bc616871c3d91431b6ade1") +arr.push("3c8d7ca2e8428a27ee85d6379fbafa3ba623737f2e6c623fe132b22a62e5b0c0") +arr.push("59b8b4f1fc149eadbca7611e071b92ac18487a821ed35589eb8b9a1314a2b28a") +arr.push("b9eecbcaa8d9795e1b3c5dcbd7838acbd6e8b71d151fc8fb9780cd77a8004b6c") +arr.push("62257976a042a77ae648da3a02dbaae65c4232f68e4d23ab76aefc92dce52619") +arr.push("81b6c65747bfc5a8bc12a2fa85ba23a3fc6355d6828f1f295e0233ea12f13b34") +arr.push("1f8f4de7b2df2e57012f970324194c786a1b7f58652e4f4f92e029f8563f4b3c") +arr.push("d800f9a1397f5e2b7d60d3fff21ec5bf66089b4de41ceb516964e2a061d6665f") +arr.push("fb2a0295d1b97e270cb16e88dfc7331bddad67158eaad1c089d75a0f5866a426") +arr.push("c624f5541611caa84843f42c6ae7a315f1b6bc4e0d58cff02f2c407120792408") +arr.push("0d9911df53891da047f79a48baa69a81a59837f4054d513c16d029538c23bc6f") +arr.push("f033d699e635ff8561ff45ca324884913296b008b96ff9b83a1e5ca886b377fe") +arr.push("1fc5fac904298972a980ca83386f4d8a2ef87a7da774fe6179823e2e8422952b") +arr.push("ea85c171f68759603a9b5175d4d479d810e4304adbb40a07133043935d7ad4eb") +arr.push("67ce716a5e03228ee8c355051037c90e9dff698a8d60cfd3cf2b06981ca5e947") +arr.push("3a8d013c2f3c446d65654666ff87d00cab106d505114a2fe953907f0178c7001") +arr.push("e361d1380ef3c3dcc5384f1cd4fcf1e1365fda6e0d4e4f589ce2bc2da69245d1") +arr.push("5b2b9bd2cd5db61160ef612adf4f519dced09f794583dd4751c4d359f9f5b3e6") +arr.push("0f6bfdca334938784803b4213e85521a03036d357cdd3087e127820c0ef902ae") +arr.push("2e923390c7953c6ede99c758cbde4d0166db31da275b944b7f825f7ff2514fc5") +arr.push("3db9b536005d014ac034084251826bf098fc73ccd957bab167ab01ce30d47e6c") +arr.push("badb85b4f4b64e9f2f313ae54b794ed0b02c6a76319adeadfe85ef422c3b5e0a") +arr.push("c0c41eed45b10ca9e0cfcb51ceecad910eaf7622ff51126df3b695e7ccc0a53e") +arr.push("018e87c5c0ab862c4dfabbb7bcea68bb63f2a4b62aab432d2ae12971061560e6") +arr.push("83bde50b860a12d62ef48555e8c807ccd76a4aa9477bbd9e7f4ab09b2673df5b") +arr.push("098733d4e84ab1a614f22c26a284653c6ab5de37abd8d93103b1a757eecf11bd") +arr.push("9c6f8791ac266a86a31eb93acb24f1878eda4366a5cdeb4c53061d9c3497f067") +arr.push("c9d02f1169d62db820a92b55676c99d8273a2b2c4698029d80d8f0ad9415e55a") +arr.push("2403a25ed8ea602732075420ceb59246f436db3523e05c0eec10fa2fce126a60") +arr.push("8959385ceb58b8ee6b3fe9aa433765e7384bedb7c3dd3b385d3a3fed9c89f065") +arr.push("4979351e19b1b2643653abef3304a361c8d63becede5eeda38f5c5e1869d449b") +arr.push("9d4411b4d120dc8359658f10eb3bd512f6704e91369c33d87b473e350b333092") +arr.push("d24a565dd2279a944735e99811e5a6470c06f14a0eaeb295f58027f9700f1680") +arr.push("2ce0e2cde989ba50d8d512bc9709f71de750f88a6a741e1a9cc4c2a694e77fc3") +arr.push("88bfa3918570f84a64ab7dca559c0c44c8018a30c754bc2dd0293a480e5a1a84") +arr.push("9c185d3b9c0b7e957eff34af373f323248c712f64fba2717884d2a54c31795f8") +arr.push("4ef0f6f071325f7aab509ec2c902f19db513b43c7ba0e998727cef6e42bdf4fd") +arr.push("c1b403d6c1be17f51e4bbcf8758daa4ec4fd50133025a5f35dc71716e1eb8b84") +arr.push("cf3b44638f58b2906c29e22d8d9b1265249db058457ef6f9a27f5d3fc83a69a0") +arr.push("01f1f36d1a9bf5d7441e222bb722aaf4480fbc3e3533e531e5359754400decb6") +arr.push("f571e0f99c0a110f80ef6ac2f6fc44cb76f2b5faf3f3c00f10185eda66a3eb86") +arr.push("fd065651e15b0842b61857f59f01c52da9f629fc075c81f9affc9471aca2701f") +arr.push("3c68b9a38962946d605d08216fd8cd4e13d78115ae956a9b5a395f124b8aab50") +arr.push("60f74c243e1a4615655f058a9db17503cc5aa296b32e1d1729af3f9e0f21618a") +arr.push("9e07c0c1c0b8963158052d3249153c02b2a5f33899158dcd07ca5a26b3319f94") +arr.push("6047e203d5f10d7cd4de836ce731ca96ed72014b050615cdb531097f5c7654d2") +arr.push("1406afcad1e9491f29fe085baa84d9976f41e0183f2e519b8f49466afe22b5b9") +arr.push("c11686394e07dbb523425f25fefc742d9f9e5a9e7c8fccab23238764f1ec1b1d") +arr.push("c1c60762cf710753b35af210dd43ccc6f0b47a436e0594c07c63bb09069636dc") +arr.push("275564ff809828e27c0b7948391975e516bb5df7a05e9d69de6f206b42d96c51") +arr.push("4d7e6c79170283e4ec88ce06ab0ea23dc516917f16db66d0f4d7e4c5eb31e7bd") +arr.push("fb19087831b6cf783f086a9548c524d59cdaec46068b02469f28f8862aac0749") +arr.push("036e5b365ba93211e7c2337ac245d6af8ed06d431236994adf1ac74db453bbfa") +arr.push("50f9f264b9428b03928a2b357fe70b3077b9d09475e1362ce68205ad430a53ee") +arr.push("74d4b4091492b4d7948be1bcbba96ae11a5e53f98bf404daafad19da6af83353") +arr.push("1c268656afd6b8a08d20f045badbccfc305907d81ce12973761614f3d05c81e8") +arr.push("e0916e705a2e7d0907db894613cefb92d3f6987b36c6b350fd2df4e76cdbc354") +arr.push("fcb972f5ffe6b6caf2f19ea8e79862e9e736003269cb2dbae208a43cefe0c2e7") +arr.push("be7ee9b13b26e2074e9e8a465076d7c174dc334762557fce5a8977fe207b0c63") +arr.push("a9551b6c5f7a02e3ac2307638f6f4a6272a47ee7cff3ebbd0ab86e8b97fe57ea") +arr.push("c6f1cc92f99e70435135cf36d8bad7744dd139529f6129f9e1c95d7ad4cb9f29") +arr.push("f7da3bff427f05b2ee98101b194d4b6e4874958b0330678e39921ec576e1061b") +arr.push("47f19a25044e995598b59b0c0c8de43fc00f60bac98bac2498f8d37d4c5094a5") +arr.push("0afaf4c47a1e629524762a877bc5c4b3830f02491b093786c1880e29e7c9a898") +arr.push("df8053f8741d7eea4cd7672f33e8ee8fe9a47ed01fe5c54cdaf35f3598f2e1aa") +arr.push("e7ef214933b619671db0e8f8e426b7298fb207e5ca2b21c521e846073cb84b21") +arr.push("f6de5a1ee81d3f26dd8c60b57ab6b85864156442aa1e082c30966e61fc587288") +arr.push("5a61d795cd1563b42b966fd30f40d330408a6c6eae851d166751eeed72a426d5") +arr.push("fbb1ebc4eae40757e21e3d5db931bb64408d1e074a2fb7712ac6e1ca5602fae0") +arr.push("7e9255e594a6ffb5b6d2cd2fcf4fb51b94ab5ddadce0a93e79d78c38378a50c2") +arr.push("ff5fe313aa63a76a14a45f9352dcb8b37e23e9ccf5d1907ae636007961ea2b4e") +arr.push("09f93c323af9891da3b50e66907df9df7f08f5baf3a770462c9869a56f894ef3") +arr.push("8b8091b9fbea9db9b616aa3b53e820c1db556e47688296f94cd3ab13dfe70407") +arr.push("f4ba3cb65af6e72d160fa9abfc4bf2c6bfa6dae74321c0ff4c90e306d60a5a94") +arr.push("e516875492252f30513c05ea5ebb8dd4168dae58d60b7808e31765fc3db03d87") +arr.push("ab74f2dd8129e5679daa120d1fd26a5f5511e02f658928565fc05972889a5016") +arr.push("f2fbafdd63934f404ab95a6873ab3fffe5573e9321d10c96c368bf6e586714c7") +arr.push("4e17735a3fc5f6696433008cd150625d7eb0c79f5602005d450e7cb13db1fe60") +arr.push("ba19aa85984e851a0a19c0c473d5eb2a8ad318fc1e8dab4bb78d47fc2290423a") +arr.push("97135ae619a8e36ac488c5bfd3647eb0a81345f617de2c29dffc6f19771b2db2") +arr.push("e568dcba13d5fc8345a7c6f8d499272744c726c3c3800356199395d681b157a8") +arr.push("2f3880110f5a50609648c16d7d32a17ba8e90d355f6a88e8f92b8f023174b30c") +arr.push("c4c8d29e17a67a4ac2bd02c0a018052ed66250a76fa2a6045900643704b7e9a3") +arr.push("eafa2509c9bbd134bd029e5e45157e63b0df551f6763ecf966e52cbf4d80d002") +arr.push("9771bf0176036223aac07ff3f76ab39ae44455af77e55e524e5be8fd61ebcd69") +arr.push("a9c67d36dbeb6bf16bd3dbb11a8561a65123877c03f4ac4253c1a825f2320f31") +arr.push("9c5f32ef74e5ed7ff95e824d8197d272ca39b89dc0287f1b5d61dbd53745d50a") +arr.push("f48fac1b60a63029751d75200bb1cde160cb51afe7abbc6506800419b83791df") +arr.push("0ece9392c2fa3a25494e0ef662c52dd3fdc67a3c24d8032ea105eaded4b67c77") +arr.push("cd45d90689e93c59e4eaad7cd819d5e8ce75d9d5347febd0f622657e7b1f0911") +arr.push("0a9d8544d8d203170f63073ccc1cc77999855aa6c02ecd9180c28eac1dbc2d16") +arr.push("f580a128771e3920e93102be5f20b455e4a14fff17509ed20cf633ab8d919b61") +arr.push("fe8ce5e741cb32085599b411b8a710d408fd5c8407dfc67581299bc8d4698acc") +arr.push("fa818c299792a1f14a0d688dcd36a0ca5f94ab88097c06f8dc40faa55d225ab9") +arr.push("a5d1c37dce3fe0510727cdef64cd45f372391fd4f42ad44f32ad62fd7bf19986") +arr.push("473aa72eef9edcd366b008623c53610a6b7a65e0fa8e5f02267d5bea5a12fd62") +arr.push("5c64966be06f0266f48a44d85e13016015b4fe0cc5bb773ef2de04abd6f85e7b") +arr.push("bf891486658dfb6ed3ce917084268f454e8f733f48164fbbf941c34014bd01b8") +arr.push("6d3c70d16a15d1e7d7c0726e057d5bd97929994ea78e5237be40b4007b4b05ed") +arr.push("4e56a00c98edcf23d740200ab81f80b5fa936486524d25afab97314143ae3c66") +arr.push("b7237f5db1bdb267a617802eb4008f97abf6110387f9dfb4e18b22f59181cec5") +arr.push("97d28a7ac2f0e1acc654d5d0c74142e541b0046aeb5a77db042fa85e62b3e316") +arr.push("cacd2766112bb19e439ba3ceee8b73767258d41eb6e07d090772033f65c540ec") +arr.push("796c96d864ced93715a8f853a01f86f3ae8e562769faf3a06ea06e4d906b7f98") +arr.push("5018a9974e384016ae27ea527e4205050b2f9bc6456a7144c5063eea45957c30") +arr.push("745dc90e86fcbeb6cfbbff1ef151465292c5d9c2511c911f94401adcceb22de5") +arr.push("314aa289920d7e4f5d62147718aa5806ebd8846ce926c17acf4f006c1a02dcf7") +arr.push("55256d0dbd17fd26fd3bc247a7af0cf7896795445c9d70f939f4eb850f80ed90") +arr.push("0e9d937b8f9c9cb65252a7cd9590a9cb4117937e7788c0b62883e63297bb6ed9") +arr.push("721de30853318a2b3e284e8cd67ed17315a90e35248cd53f847ebe25a4ae1004") +arr.push("7b59b33957ae9555c3bb8e9fe5a7fbd0c0f8847cedcdb627183baf9e5f7d7ecb") +arr.push("07d42a4570d9756f27a666fadc86eb648ba1ca317ca9c4fcebc4799fca089297") +arr.push("5edfc81e3394178e562461b24dc3a7918fd0c0ebb56dafc4ba1fb4d1aad49a1c") +arr.push("9ed502bd9c40a89502bff90514d3420260a7f89b285dc3db3eabb6e4d7c33e8a") +arr.push("f5054d2cea039e5fc49d238e324e5c7b051748e213585b37031acfe37c0631b4") +arr.push("8a46fcf826ede7ef009d1a84a7408464401f18ba1ea9318410a33f618727852c") +arr.push("ebae00aef3999e2622adf9349675246ab391f3f1aee2872da31eac63b8eaa74b") +arr.push("9877700467f3ae17db6439ca15a22cb065fd1bed9996b9ef3954ce799a23b299") +arr.push("25cbbdf1f273ea8277931e0fcd4219ff58b843ac040c934114fd22f5072ea012") +arr.push("41da8fcca9a0d14b0039b93fa8823121f53b2053d515aa317853eb35a25bfa7e") +arr.push("b7cd4836db87d078031894bdbefdff2284af6e9bce52701fea6fcc347c604475") +arr.push("52637ecb485bf17cee90170fb4db56aa5b5972dfff5777718c44ce9173659a16") +arr.push("d8bfc9cc722a12c866d7ddb1b18746af2d127c7e19fdb7d906aa30ee235ae246") +arr.push("f4711f287d4a3afeb3750d8ec190e9e75fe383193e6e7d003fef58db80a3eb27") +arr.push("4f2a778e9b88e4f448715daf0ccc7fa258f2ce47dff39a917d931d14c42639cc") +arr.push("0807f0ade0c156de3ffb9241639d0e30119d295ddfe9ace6a12422de2e25e287") +arr.push("a991945a035c3c7ae37b5b4de72b85a59c578f504d8b48dd5767f0528cbc4081") +arr.push("0f3bcce96200066fb132c77314520c0a5aae2eafbca83be8a806d81c25276e64") +arr.push("6ee3ffa6e18cfbcfd00061ccf93aaed47b3696d4289538ae858d6aa4952b6cb4") +arr.push("3a807295c5f51a89883264ff4519f1d644f25126702fe13120dafdf0d97092e3") +arr.push("b565bc480f8ed2825b228067c0c93df0f893901c59409f593e9bf7a6f6e99984") +arr.push("3bf2ddccfa8b58cbe15194e4aa7216799742184b33c069dba472819077ecb5e6") +arr.push("75c174c0fa5af2c284f860ab464ad1ef2165317c5d4de76d58a7ae913c53abc1") +arr.push("ce4678099e12466e1b2ddc29c6571ce4b15e20eff18e3d027e0ec215216e7bed") +arr.push("1d8fea08a3e8e89cf9e945b993c53c98048f750a3e91fe3fecc899cfd929d438") +arr.push("518ec9faaab66e4b6ce9442713c0be03f9665525102db0b887d3b5628282dcb3") +arr.push("643596697af7fad0f978710925ac5ba5da96a5e3cfa5ec4c49b8721ba149bbd8") +arr.push("e47b564367e26a97b9be1678d22e98baf00b90de866b7fe416bce74265c18fea") +arr.push("4cba52672a26460f2dbfc2af2923bfdb5c7cb1fa5e9556d30b07e95a80ce126c") +arr.push("b95558087ff88ee66d45f96fefc6386f02a41f954429a2803157091fc7cba8a9") +arr.push("4388bf0d41a6b4d5170ff6773d204b3cf05f2b3ddfa188c2c0562fc91d9453ce") +arr.push("81e247995026cfb6b4a1bbc98e2646071a555b1c64bcc15e1efbf0d80a76a442") +arr.push("35e5e068dc0cc57380f84e42826363ca747794580318bef74d862c7e85fa0996") +arr.push("6f105489f39e5c00262bbf68a62598d139857fd1c80ab00580ff046c46494e8b") +arr.push("17f2a299fc7672c772bb581c86ad1736b476bce9b23440d024d165fd3dced1ae") +arr.push("689bd9e8b55c1cd100ea464a49bed3e6322461b85a5d65bc41afdb863d0688ee") +arr.push("c1226418673823611ff75d5ab7f3e87fecaf52f662da21057ad5bc254caf5013") +arr.push("8ceb06dade987b45dfb73d1e31a8700c919073847b1a433558648bd7b8bd1b36") +arr.push("63bb6e626e512ee2417be53083b214d0a0ec0591053fd32a1464b677db69a0e7") +arr.push("142e8681220fb391f98bd983d5002f05b6d1e509d4ea994dd3ca35532869a04a") +arr.push("78091f3f5eb0b30f14a5e895e6042ba60f85e7a975f602b5480ac54d950dfab7") +arr.push("312d1072c3758a24bea54a4f5f33ce591f606ded30db5719df622a293cf504e7") +arr.push("8fe4e1b14b5f8acae8689b8184e726010aef524d94a0973ce99ebb7b761877ec") +arr.push("eaf52943093a4cfe2b6297ab28d0d7b1b87550e27f3ec37de3c9130ee555d81c") +arr.push("c047c77a81d3ffbbf28ba990ea899fb458314836e9988c3c6748fc6c488382a2") +arr.push("93e159c9310c593714100dade4d9d91cca45812f406ff77950052cd90f7bacb5") +arr.push("3064c0a12a9fdb43eefd9974178d4750dbffce467cbc893aa346e38fe3a2cd9e") +arr.push("3df2c28b8462cafc7ffd8838bc6f43717638c2280dbde2e40eec48001b049cda") +arr.push("7b3723187bb9e3da479154f0f9d6dacf91f213105aaaf61ad4f46d708c6afa63") +arr.push("4eff320f00cc85a38be7d6ac9e1642aac7ad7a9cae0c9dc1ed0221b1335a9bf2") +arr.push("e87e879cb8166e7de97557501daca46c9a0755986a6d12462e901da568127770") +arr.push("04ac3718296b08bda460ac2c29ecda760a57d31d9fcbb1527a7cae344b568f4d") +arr.push("8cf36e41bd31b67538b64f80ff8842e3ba276e171c622a02d994a3ddee8348ca") +arr.push("f6870a8942b8a35aff1d5e40f5052cc3cb4386b7cbd8d55c5a2723646d2758ee") +arr.push("e9fbe4f24eefbb930c86b281adbd798596f4825664fa87c52f1fa4770a05fea5") +arr.push("96defa170ef5782d63f76d5f0f9540dc9938cc642c7f1af1236afa39d18f32f9") +arr.push("db179e05fd54afa77f5e51ec2a13c41752370b3fce941aa19dbb0e17b87bf1d3") +arr.push("3de03e22311c1a34ba98bef8fca354442efaf81d4cd0e3671cad2c4846d62ecd") +arr.push("8dd2535f9e05c80a9bf1eff2d76edab86ad8141457f6e04dde8b8fd784294aef") +arr.push("796dbe4159ba59b47675cf1d1e483fef39f74d382638f31f98a06420f165a20f") +arr.push("4a7942750b2322b8b1ca8ec3b78dd1e97e9ec9c5a6e61c9fd4b04dff90bd81d2") +arr.push("358f5cadf3f8181effc7f7fc2924668aab929c5a3aa2361236205fe4147afdbd") +arr.push("068b58fe5ea4da38aede78f386f63bfe242e2aa6b851d1d02b90fc8f4fc0fa77") +arr.push("0fe14390c9689b9b6f8cde3beb2d0c3395ea4e7a8adbe4f45f0a382f585bf7cd") +arr.push("5afe727ea672fe6010c37546812ac01248a059a4f4a40f4b9d1fc5842687ce8b") +arr.push("67c52dcaf58c7e189f8d7e2a43050cbf837086056b352b791ec6d70604d508bc") +arr.push("a4c62dd1643e3ae032f465b1fa1c9dc915996356659708d6f0e0fa190caaab73") +arr.push("a07ec14622424c9ddf8042c462e01d754ebe8944783ec39819b27537c343fddc") +arr.push("d5b416eba7898863479f34f4040be8ce755ed111a547cc2fff448007668f2117") +arr.push("3ce2ffcaae459ee4d04ee663fa2a9ea19f928be3ee452dcc40cb56570dbcd230") +arr.push("a5aacbce4204aea75b52fede9e847205046c28685fbd4af05fc4ac1ea7666b4e") +arr.push("9ce2f49dbb362a5c43d97e38d568042bc6437e17962c0bb270222e514cd01d72") +arr.push("3296cea2eefea74f548bd180b2fad0b528e05d7264de03b9fb5c2cbc2b3e8cab") +arr.push("f56b6874d41a0ad3805c0b5d2521117da5dc64e7272d247d3e6d524b0bc27f19") +arr.push("367d0738f811ff4a8647e84b3967d746593499d88ff6c82efea21381967e9f98") +arr.push("15002d83d2cfa3c9d5f7d6b5d3518effa89553ae866cbc1666a4ca1e47718531") +arr.push("d047ae84ea6dc3cfa60a80d1eecf62d00caa1dcab8f35d85cca6aa15384bee3e") +arr.push("9bb28a48fe7034df1236e1b4d9d362288f19959011f3e1f86cd254403b5c71c2") +arr.push("26c4553d6f79c4e265fd363298833eaa5c20dbe6c5a63efabe20419a50185061") +arr.push("1f818d21e5a3eff53de117ae1576e2ee2743f0e020ce057ddfc70df566eaf107") +arr.push("553ad158ebc0ae9ce7894b21277e398d4126675c9bdf05a9168239f87ab277c1") +arr.push("d5f882f52e61e80025d7bb4bfb7bea7baca61d8b8f09f1c9b0542c8e5cda43b8") +arr.push("76a515195cb4cfb6e9a0b43f4d44f13c7647ac669fe78f199f317c335a5e1c8d") +arr.push("e266068298c9ecb2231ed4033652aa0221806d327ae8d4699cbda91a81ae187d") +arr.push("ccffaa44ef1fa5d4da1ad22c24b2e68cca53df4c70d357e12770825916b3b906") +arr.push("763b61b828e510b31b03ca8a4509c237b5805d2be61336d20d7e7059d1b3c906") +arr.push("0b8d855c37f31f56e337df92c1dd8a9b343f25ad8c238e2266bace12e1c5ed2f") +arr.push("a4358fd288109d0885cc6d3fd50e5b6b7741d9c922054172187e23d317ce1af5") +arr.push("dbe7a890bf6772b61d0cabc7c362b77fee60cbcfab93575c16b8a0b88b1326b1") +arr.push("d1ad2f1ad98d18d08e30ae8d9e2fd4c3670d9ba089d72aaaa4e69d4c8eba2847") +arr.push("51f340dc9c6394b64ad7bd962a32cb92c00d6d626cf8732e06b3b37dfb322893") +arr.push("281f6cfa3ea359e69a789fa865cbf386450cd4b63e7c57f6faf667a78275a985") +arr.push("274de01a2fc2b9572cc976403301c48d25ac02b82aa21261b8760df136cff912") +arr.push("5c0216698fce8ba796ccdcf764068e05a88a4cc3b4b77ccfb77a12c8da92328e") +arr.push("93507f7be0d28feef75d597c6105fbb1489e95a345cf7be7aecb498d001db0ad") +arr.push("3b082feec2a2d6247bacd1ad53238d862078981feb5d14abbdfe316c76822524") +arr.push("4b441e4767f6d740eed0d3fc43b841c481a781982e8a80be9301391743eb1859") +arr.push("604053e8f6f0a4822f9077b7053f9fb1c45bf133fd69ff38bf72ed3ff0e727a9") +arr.push("80773e5921f386fee45211b7e000f3e701f84cdc9e56f2622e08cdec16a750a4") +arr.push("307850c3147d0c491a7ee14609cff2e0dc811aa3be23ad576311ef4b7dc02fca") +arr.push("feae12b5dde467c616ce1cf3fb4dd4dcc172b85472e468bbf5207e7b620d0a11") +arr.push("4209bf8169a73ae70d675cc37f9e00aaef71ddb9fbf1b5428603c365b2d5db45") +arr.push("243a86b3f258eea28ef514dfcf0b6c58f220edf993477077a39f7edf20764b56") +arr.push("980076659535f4ed966f73abf0b317424ff3bebb85b2c486d0605f60847458dc") +arr.push("c0ef3f9c8a57bbbb3930efccc82e009869a4eb1a23346d183d3f26e7933de75f") +arr.push("7f9319b2ee326e3fec01b381a900a01e0c6f83115531a336abb92b0f33ef4719") +arr.push("38f614353f67440ce04339b0e39268d0039877d0e7a1c14f1e739655366f29bf") +arr.push("d6828cb8597949b5e8294141602bb22869a088ab3e4e447d20d72359525f9262") +arr.push("fbf1f1e98a5e2ca74b50fdb9be18a80177dd64d199bfe44a9b140567c4802d85") +arr.push("81ab08a8aae5a36894db2ea7b22a79399cf1601744d48cb821cc137abf5b484d") +arr.push("4c8f5452724b5feab6f6cbe2ba93089856ebbf81d6373d5da55be9329fd77381") +arr.push("58d4f7fb8d00fa2975e46497627a843eb035a08ba7fc557aade4d3368dc40389") +arr.push("ee7864322af3fc0ce7987f5340b099ffedafaac1f07b4d9bd2590f60d514b23d") +arr.push("4d9bb9accbdc277471f0065ab5ea52882b0d6d309ae099b2ea2c629fa6c2ad24") +arr.push("770b2f423e95c20a5f5b3c68ff40dfccf5b23940547a6f0f4376ae3ef216e02f") +arr.push("1935e15cce40915da4707b7219148a78646b70c83c2ff22b5f86c090e91adb08") +arr.push("e6dd025196bb134f564a094cf1c7814fad84d927335e6132cb164e4929e20ee1") +arr.push("2bb96e3f55446adced56503ca894ff069b34324b34e07bff48b0576d7322ddca") +arr.push("aa0adc40aef20eefe38f44fba6cb333525d71530dc9be32ed7a6f41dd751fe71") +arr.push("b43831f86fe9a6823d65622da4c25368e587b865c8893399edc3da5d6d1a70e9") +arr.push("94ee448f9ce2c66965e58a373a2b3f88a4490b0cc69ca0c3b17cc60d610baa2c") +arr.push("1ad7d66b432d423a8f6bd937a1ea381076e43189574c2cd3b6eea7d9054ab785") +arr.push("6a285fd220e153308f38900d84d50f20574f10228989f7dad09815406064dd1b") +arr.push("b00f2f04e3f0c81b74a24e16487676f53156385265cd3ba4cb2c76eee9e8184c") +arr.push("f5c46a1ae71cf5fc6ceef0faeb41c41879537a8ec43ad6c09312812cb34e5353") +arr.push("de0e2478676ad6ee01eb2039d706c5d29cb35c5d5f76dcf8db886f61d4ea6f1d") +arr.push("b6b3b5645e8b99dbcefdba10a10a9770e695a4cf800aea1a5ad2e50d731d25a3") +arr.push("d2d00f4b59ae807adc60cfb3d08bdda79b673817ea1f4878d35f6ca042d25d4f") +arr.push("fb8d6056363ffcc6bfe1fdca0325175c958686bca05d9f808ef713f36ed03f24") +arr.push("1a7fbbe4cbb14890f63af8e842d7a17dea3d4704a259a1fee7d1a674730d5574") +arr.push("0487bebb6e0d122af7ab1dfab20323426e1d2ae61eceb03ae92fb491e18489d0") +arr.push("6a4f93791600b6600b1b45ac08a4dfd9128abcc6a3031de0fedfe7174a06ba10") +arr.push("44ac65a93ca33343609c538d8ad003f8bd354aa3a5bc09ce0d91e62397fcce3a") +arr.push("fc46dbec9910ef47446ca751911476956d1dd52946ffc36af24fdd87db13dc75") +arr.push("88e85c8f5fdf03c1c7ec63f2bd3d5dd2969da6245d51c4fc2bd93537d72dd123") +arr.push("810ff681c7e8d741ba75adc32128300d2da7cc7909885e2ba60ac1289075351e") +arr.push("78c3ff3655a04c8d0d7629effff23689501ec761d8da0599bf6649a42e63a9df") +arr.push("90fb2763e54131f972d96af07da20e4ba1bacd73d5a35cd9b67220026b958c3a") +arr.push("c4b2041a63de7f240771f4cfc22ac7d7a93be7c7eb2b77500e2c44a747beca75") +arr.push("c32fde6e9199e1c7e4c79daf11047867051908b558ff4bfb50370e6bf919a856") +arr.push("af26b6e6a6353b150d6bea6843ebdd47cc7ed3d842487a045d69264ebb5634e8") +arr.push("b3d1b02b784b839aea04178b8ed5425101bcdd97957ccdae33f7ac24d2f8d8f0") +arr.push("161dcb33cb98bece55f276c6a1054013a4780843467d4336db68feef688344df") +arr.push("4c6b0ad07d7bf6b0ba92e0c0e803db2ed1e92143a377ddc578f49a266ee72f8e") +arr.push("c81d0f0d876dc1de9562e586b76d1a64e54a044ccacf3c0f9c02f1f68f436786") +arr.push("40e3a96413e31aa409d5cc3aa9929ad59cf2253390f7b7c35797662919926d8c") +arr.push("c84f6f5ae833f55c246c99b43a6bfcb29e4b66accb50c39461356fbb186010d9") +arr.push("ba47686d44296b1cdfc133dd06193acdaa0a1997785ea6ec85af1e607f0414e3") +arr.push("df6701a1d3c660768f871c1b360721727407e128cce500eae8327a1d29baec6c") +arr.push("aea8b12d20a25950b80c0c96e5df8e5596a8f37c375c21c867539b05236eece7") +arr.push("2d4d58aa11b00f1b4f590afc7ecbf0fe831d9cdb24cba00b8afba18c61e864a9") +arr.push("5a7a7b478fe87506f22f98b8e0a8f46b201e812fc12c821efb3400984c1db429") +arr.push("8a87ea9fe4508fef21780cfa6affe99d60075d8a33704fbcd766f4c807b0d73c") +arr.push("f9deb2a4a85e24ebc4c0a1d1446d5c3619afa2d202bf2ecc8b4baef32e35f049") +arr.push("e343614ac2c3e5f490e5c7b46b50a0778d5e729244ebe112f80040b5b680917e") +arr.push("f9408f5bfae068672c9064f16782619a4be4f03592667759365adcaa067a1496") +arr.push("a042251ab50f06a6dc207c2b40927aab7ce19d11f9f1364746f65aa5ad2a1d35") +arr.push("49c3b2ea279c8ed108335567399067a22ab7d93c645bdae4256b425683cd810d") +arr.push("e4d60304bfb4158abaf1f68e543a3785a65bcad6fea60d0e1d61b2a072b1795e") +arr.push("41799966c0d6b0f6f28e71ab94e9166445ae736a4057f676e6c5e1fcd5d2561c") +arr.push("8244cf05b1eb35a07268ea5a716d6e80ab75e1192688a2e26e547f87bfc99b8f") +arr.push("e40f247680725141cdcaa1c95d6890047af20379d1259527fa1a72e4142b35e6") +arr.push("c95c44f62ef7cafbf4050d83879992aa8d4c5fb39219f3a3205fe0caa1c242dd") +arr.push("1c192e90f934f4313ccb49134026ddbe6f77e3f2f04da76e45d182e56dab6a6e") +arr.push("8766d2a938e6f5ae61a2cf5f470463de61bf9273fd25aecd0821394bc61fdbe3") +arr.push("fbd4dbafd42dea15b04e1766c5491f2d1917b1663efdddf372292aa68e4e39c1") +arr.push("1654f858e7d70997ffd95e2c2f510608aaed99f1fb0992e6caf733dcb289f3a6") +arr.push("099ee6f32ed2d2216cceb7f6c6a75fb6e10885ab3f66dc6d86bafa1a270e22ff") +arr.push("6a31959874eb4ff7cf7f7be95ea07559fe743a8ec812d6701e4dc44c8d505269") +arr.push("55d7ba128f964b44028cb642c0fe89c802ed0ce835d1c6eb6e95a2e174e2b635") +arr.push("8eed2097abebadd4bbbafda059b116b15398ab24f51d302e0804a18c9968e102") +arr.push("b3c15c33878ee455607a37eb60c870ad13e6e0c9bae7029bd29725b4a3d67bed") +arr.push("65b618636323b5ddf2ed8913cde78896ec658b1b3ea5060dcb4dcfe3ced560be") +arr.push("c136a93afd9502e679fc55e11bcf7e362d070f3f54551cf0bc3af7954e3181e2") +arr.push("20572021ac3bffd257208cffa56fd0b0af348a26fae99ff60b383918471084e2") +arr.push("75c12b011bf0d55154eef21d3c81206fe72bc41553b6c3ec24e2be4759908caa") +arr.push("bd4f7c21090ad2cec776a5f1f641d3f84007951d413c0ed05a55d4804057c05b") +arr.push("85bea3d48d812ddd59f0ee8026009bd0117b65ebc6a1a5f7bf51f1661833565c") +arr.push("50e6699e2a2fe67b4eeeeacc43b256abbce8be3256f80056eb4d15f279a997dc") +arr.push("51883951b4c9526f5d28a334a12938bce1e1ab24fa00a2e02b76f84363de5903") +arr.push("51747af631f5d4be2aa4bdc200bce708b6078ddf509ca35ce3f4f0fb63ff873b") +arr.push("113a2d7533623ec844a03f80757d941678d4dbd3c1fd76997371812b84ad5624") +arr.push("3f69b1df011504abaffde4ce9c600dbcc1ccae645b1efb1ea49946331b52f5cd") +arr.push("34ecbfdfe782e70a307001d2dba977d939a34ea820e38d854e102d1072808165") +arr.push("0afe48e1c40487b8aa57cb4c589b2671bb4d5440e21ca9d0fb876630d0f6ba83") +arr.push("a835f3011dfb43f5a9fd8ed4043729a85514f9be16835601eaaa7a7f03767cdc") +arr.push("34f7928916927e88d8aa459291e1d9fd1d735a515d96e74107345ea90809ceac") +arr.push("5d62d4cd9a2e3ebc53c8e70eff6f859ba009c4924b3af33792666cec4cc95c70") +arr.push("1732d70e00b80167c5755fb0fd2067e5a3e28b41db56c9fdc683f77ff90bb1b7") +arr.push("fd7eb895808036d8be0136d9f341bed0049a8d268023d80ec6c1257745ecc963") +arr.push("c7ef4c6c05019e99d320458254a0232c0f91546a68d75547e7da528605c36924") +arr.push("2acda3ed74a81149cad3e9ea27e92cd2f670cd2d5a238209641b34911184a42f") +arr.push("363d02d18a3325f7715d430cd6b9c85b4f5794c8db7fe755c9688633cc8c5f6f") +arr.push("2af352f02f35bff9e36d07137cb0c22dd4ca6ff1715d23ade93e0e1c2f8394a7") +arr.push("1c1f84b97849d901c6d11e17579d5e15c94a61835a52a2088e3e8a4e6b99d06f") +arr.push("9b0a386c8d3763ad0e5fbf5ef4b96af84d26106455d2d0fd456cc340c5e486d8") +arr.push("41361d58ddc88b9d887291e8ebe0a33f0848d566b316d41233ce899ea4bf5811") +arr.push("c3ddd6706dcc9b83cb8d22f7fb51f0a44fe9d19ea4c12e40008d1127f021e3d0") +arr.push("085f5df6a7cf51f883e5b5a39bd7108c9db2ff7b025ddc8bb0422f95ca3c1d63") +arr.push("f8ef4f51e440a735b33b32a8306abad89b29060777189f047dd3094360ec84a2") +arr.push("84cbf15e40b97b1c03fa9307a5d7b89669cbd4d899de3a689ac26710fb15bf7d") +arr.push("4663c43dbf13556c22c56472c653198682cb322bf6f3dad410e7fee8ce8e5c5b") +arr.push("811aa61e3a5c45b99ab9ba7ff253cb358d59a8fce1c57b2e583b34205487c439") +arr.push("3d4c184f11fadbf74ba04cf3e8e98ffa8e321573ad3d011dc201541210a3ce8b") +arr.push("f0ba3351706b68abf08941c2ba2031e06ba0f86b5bcb79e77c3b99caeccd6ac2") +arr.push("1554a93dfbd6c2bea33366de213994340328d3b60a0b00538723866cf8bde118") +arr.push("7ceac3478f633e2c21beb7c68242a8e73e10cd5ee47d32cad69b9e049836f7fe") +arr.push("90bafb97c64c204bce9679989ed8429558d5b5ab71761646f80f54d809492a42") +arr.push("e042b0ed17190908b54d2586a9ececfefe99e381a0521dd578bfe61bcb1343c3") +arr.push("752f473f74e30cc57ee096ba557cac67b81df5f3d8fa354382122b448db911e7") +arr.push("288936f2d794ff1a3a044c9beee43db02daa1d861c8514b146cb976495584a9a") +arr.push("51861a95f3ef75ba2a735b108043fb73b633ea1c9fb38aa047cce7c2462056a1") +arr.push("07d13618ed8c05332383ea1cbf7c72efd5a104c1b31025681f248d733e1c11a0") +arr.push("95c23bd5618100a5bdd9cf3a2b2d59fb02385d6dcc48ce3b8a644885deb1be34") +arr.push("922ece73e8520c8d323c6fa540e562e1bfc65e88fe9e2c2a0ca34d9baf2a1ecf") +arr.push("a538a0c46d0da018c06fcd74ba90dcd98fc3f4b289122fc311fcd77169ef42f3") +arr.push("61666bf7eb7316afbd67fc6f2b6e897b88401e6e5f790c74f3927ed8bde338cf") +arr.push("cc005c4771d6b4da28802c8ccb05f056b959d56612124c864f01cdceee6c5f20") +arr.push("8894222d9373e54d26ad9235be4ae6249f3c933d572f904465eca0bce2e82a79") +arr.push("b524196a366c814aa6c9cd933bcc75fb017dd7fb9762e3e9dc6143c8f56e441d") +arr.push("75f3782d0bd345922cdcb0da2a4267b3befef5dc0ffb7ed8cca05fba072c818e") +arr.push("606fafb4ad0b0f12192cc802906246c9129315bf69e8d20279df855fe7afc824") +arr.push("8a9d0115f1076dd749aa53183250e8ce30bb8fa77d39ddd56b6bc3fb54203064") +arr.push("89eec853caa238958ab4b0f486eccc507993162c79c772b2f034e9328f74f18f") +arr.push("e13c9d949914890fe662e41cfaca988c226af41a6e5ea3f7c6e4979d8c30a4ba") +arr.push("aa77905a37bda52244a400c9ec99bd7a538e1b602dfef1c6d52d33c004987f3c") +arr.push("7a1577d4be14b06e66d9b3f6caca5f7427c5345516839876ac733df1ff906767") +arr.push("879fad57a5cb4c82502dab733be94a9b16f669011063a2c82708dafeda5b1999") +arr.push("26247155e41267af239e3610de32ce5cde4ca860152995be70d5ec688511969a") +arr.push("bf25430fc57a94694d896803ad0641cb4ed8226e6dfe73b5b88fd3091e474083") +arr.push("1b2338ffb1bacbd95da0a1ec6eb027382841dcba9de210fbefdaf5d60c861db2") +arr.push("9afa0e52f02670dcb1cd16d5d1b859fa962d4423c08bdb9b469372c0675549e0") +arr.push("b2af9ab5d98b5c65b81ac2563a0a8f77aca52db4b3d80d230b6badf435f89789") +arr.push("357f6a1ab679fccfdbc37f3cc7c153903b289c70d64445e205aeb59556a006e4") +arr.push("60cea3da5e5ec53a2375e11207cd34387e40d66715c45f398de3e2d13a179eff") +arr.push("d103ad98dfd4f91de52c41fc16ab2df5d5841b06174faa9e01fa714ebfad2441") +arr.push("5d39adf9299e617c843dfdc7918c9b9e6d3e04339de0552d62f41b6430633d5e") +arr.push("4a22101e22173d55a9eecee9f0cd6c8fe0d15699ac1a0ad3dd1d2857c66cf616") +arr.push("737086a5ef20c91210c944d4ff653f6747d75237e20889a934b0bf239a41ea6f") +arr.push("e870e144248f731e776451356034e3709a6bb57c7f43777c0e662bb24c208d67") +arr.push("574c0722dfeff83a4199c2cde61e430521da764b6fd64177fbb02ed96055a6e0") +arr.push("2986102b0a309e224ba7e1dc43a898ccb5a6e1640a00f76925eaf5eaa2a86f76") +arr.push("33f75e42b1b1d861fe70170d1066605038a38e680ff249eea0890bedd93a7350") +arr.push("56c99f35356c2a64a30283fa5e6e037e5a61536d17a6cf2e11883bd9b19ea021") +arr.push("cb62be2a047ea5ea3d0387529099f853200b5fec4be28ca7c7eae70e5869d391") +arr.push("10615725d7d9bfb16988c4e151e06a5ca753c2699f2316012213920550d6993b") +arr.push("5a25b3715efd584e638b93cc244ad4cb22e41a49be07b781a4937e9e9e4a3310") +arr.push("2615cb8423362cceb8980267190c433a4cf9dc1183bf05e16261336b3a9c1f75") +arr.push("b2c32c07bd6096b8fb7fb4a9cd45dd34f70b472e7763d9d638d8f68a202cf84c") +arr.push("a5e8f21411bf393f6a4e9967cca754292fb17834b0a4f256045cf0d61ef56990") +arr.push("3c346e30776255ad989d25ff6dab3fb90376f56a953ac8c8745febb2741f1b3b") +arr.push("6533901e447002c7eb587b32d2f28ac05e70e45df134c9fd76e9c76cb8c8435e") +arr.push("c2a2b93e7f39f194d3ec7f49c5048cab35a07cfcab7c7b87d757779802881ade") +arr.push("e0baed20d771c7dfd8ca76494362fd686864281748530da66b35da43fe31b815") +arr.push("2c9e7b52972faf8bbc7ae42808553e8eceea79e42825e577868192c2520c8703") +arr.push("474f759c48464a2bb2c67b494c057c19c3a7e7642457b144692ff6a4c8408245") +arr.push("8358f547adf67d09405aa2f5da766d3fb79b2b14f554773ecbfb5bc93e30668e") +arr.push("489122809efe3148b65c756315100f17b4f04d078886d4e1522f03c48bc552fa") +arr.push("66ae3b0270f9de15190feb6be4bace33369d8fb051c7e75023fb418bfe4ddf42") +arr.push("c3706778c9ee93151ef44808e2401390f28ef26fec22b17300befa0463b3f7c3") +arr.push("99e05b4138bf5498f378c9ce3ae7d1cdd613e01db406cde1cf2add5d64ebc0a9") +arr.push("4230756f3ffbe486b67a69a1bad43d2392f6e1c690dfe730db491c1911ad28ca") +arr.push("be1222cf8f27d383483c8d03e725416f616024732e9ec178eabb13755a6592af") +arr.push("80105b914feed7133f04a8a032adf6a1f553bad6f3ad66f2e18e08741de17f98") +arr.push("05b549576005b343b3462adb851b9da05db319301f44cc3983535a4530dcd84c") +arr.push("2a34524f24d2dd1bc9ff5c413aab145600829503c5e2fa1b5fa8d1bb1bdc7604") +arr.push("9a32e1a42ab60427e0011dbbd923e16b96f5e6f11d0487c07107f4c1744b0414") +arr.push("66af056a6247053d468dae0826186480b4b8e3059c49e919115721b492a55b05") +arr.push("60cd2dfb59b71a851e471f8c580deb4fb0038e70190e6c45d07fc4a3c9ab870e") +arr.push("261dc450c57c83016b9bbdd27c74588972705a17c33be85e5e1d02ce03ea07f3") +arr.push("b68338655588700a11dbc9219ff7166193f31d0c733eaa1d3cc5069e835983bc") +arr.push("b85546b3028d351136bc44a6dc0b0641bf9a8a06dec13899474eaf8ce179fa41") +arr.push("d8d7fa191a1a61fbdc8df24d2169780b1ef3b041cbacd0f8e1c986e6dae9d14e") +arr.push("91cfd30917dc7ca3c445757bd79b3d0095f6a21b4e99069dcfd88ee29ad80698") +arr.push("bd5d4392434905cdccdab8971db918316d487c66b4fbad07b8f1c08ca87c7b18") +arr.push("1556c011fb86c962e546dfa99293b75a376457a0a16fa1fd254f9284cbc0dee9") +arr.push("980448015e130a0d588436419315e7fefa60d1726d6cebd293f6c499155ab168") +arr.push("56b95dd21f5eabda9d477f4a0df991fa402bdea13adacf9d05482341f6f5ccc2") +arr.push("4c76056773d8ab59cc01931085048a705ca667e0bfaf3cea9995d7565b937c4c") +arr.push("44f88b2daebadf3aeffcfad38a5ee5b8850b5df580a6f6aa3534177f87b50d4b") +arr.push("95bd799e850a2cf309645f3f9bfca53d57ef3a71e4cbe91ed9aa6f7528295094") +arr.push("6de2f833f67990b77934b80471a48725e3968fdecc7da241b476b880e40d5686") +arr.push("2f3bfb7a82d3f318921ff0090c368019bc8c011ff69d87efabb369ce317b53e0") +arr.push("943217f0a9d3328f25359fd2e838d447f3cfa3acb3972397cd873e299efc5d5c") +arr.push("e20095bc14b62e955418bdddc8e538eab74a509ed829a93d2df8a63767f84ad1") +arr.push("c39f761d6bbf4353998a5d8ebf3ea4ae5668eebd24dbd6c758a921e100a5df05") +arr.push("29f914c4ecf1182949954bfc85eb5a7f0fb771dc2a438d0acf271c9029a1bbbc") +arr.push("ea298a66d1c62e9f4073c29336089cd84d39131820658bd1c5e6c836259960ba") +arr.push("f6518f16625ec360e666656f7591f8f13d434897e4b574d5cd094b2c94cb188c") +arr.push("18ca3ddf74cb7e4c0f35f9ea377a2a37eea37fe9679fa78d5270ffe8a03b290d") +arr.push("e0dab795285ba69a69edfcfe07a7c410e76fff515bec426c8c49aa3c47ef93a6") +arr.push("b40d878bc2b644ae1eda86d86cc6993925db36ae6f61528503e94d8ed9100bb9") +arr.push("d7226c3a4e5a4a548ad8550cf1e9568f476f6269f6804a9e756c9e109e6ed063") +arr.push("017b514eb789745865fe2a731bfaca022557cbe4c668b2290f8e148945cfcf93") +arr.push("2071b5471037ccdaefa5736d839f31def7bdf5f4e8010eaf5da907629ddb1bda") +arr.push("0995038fe2b49a45ebcae22376337d621cbbc4a7bee03107a76abf4d46e2e7ac") +arr.push("b0b84e8201d62a487c4bc8eb8e33bf67a841b765a1e2b18c5631cbcc8abfc88b") +arr.push("9d57a1bef9333669c538483b5234fd08abb6a8b768f7cba23066c0efd164ad3b") +arr.push("7d6823c58ffbfde1693afd7dc207ed7e33538a11e0dd0930b7e55efa832bc9d6") +arr.push("630ea9478ced32a930fe55ecd7b3fecc76217b9ecb7862c39a7c444460219691") +arr.push("dadeddcb4640f25e8ea2c17446926e52ce0566f5ee1fc180983f04cc7fb83e30") +arr.push("2bca36af443ca3a4af186e06e9591ea5b43aa96ebcbdd78aca3b01866975629f") +arr.push("34d6ab93c19893b0b5a10c915cb088acffd2440021ddfc20ac391302cdd35b7d") +arr.push("e83230c4cc349b61368ca16c9b5592b28fc816829d00cb362c20a5628840c459") +arr.push("e410ac5f3f660deb0a72647e26333eea3b28688229603affae58ec894594cd81") +arr.push("f848100e3c176d5841dfacff36805c390b8f335f58a0da909f221e5fc8e9eb75") +arr.push("4e0ccdb6be0563525e949f8fcbcd39040b74c268ca22dc393c60efc1d6b25fc6") +arr.push("36b02c39087299055d354d5b9985b1978add2b3b726a0882b89727f23675f654") +arr.push("d5a69bb918abeca13df71ff8d824a2e4945169dfabc55c41e3b90132f2e7323d") +arr.push("a6829c188a14ddf4c7e309a1492f5259c89ba55841ebe3664a43931f125097c7") +arr.push("04f4b8e20f95d9421092cff6fefce5427d1dbc9c21f613d2589fed6ab737a88c") +arr.push("b1c1842a5f7254732c993354493428f2c4513cd9ec2963a12a4349f9de54dee3") +arr.push("659fdfd9fee7b9d4feb749c6307ed338724bdc8e512c30cd4ca65fc62f79f81b") +arr.push("d5da13addaf695292aabc24dbaf566a0070d2c80a3c945fc0c7190d9f7bdbf59") +arr.push("a0c9db658c53ece4e589eab457c3457487e31922cfaab298350630ebdf8596b3") +arr.push("04449ca8f5527f437b5b1bfe169b2b4dc5319e166381a5469be22174c2930a13") +arr.push("cab38978c85dc650264e431ad3d7067087086dd8652ae4219face082f8e9b5e0") +arr.push("c130a0bd8badb897bbb8c3d613043409230e1e4204c0c9ee19ba51aa1555792a") +arr.push("50e3a91fa42ef845096af8bee9328db5ed1155862227889d59d154562bb312e5") +arr.push("c62cfee00007968ace2ad19f6f206dfcad47f37f0af47be713e1d89c9ded0907") +arr.push("028169f1522f9affa20057476c45e4e28abceca0f2d8e4a36ccdbe99f87acf35") +arr.push("a768ca3a34de705e1ed5290522c85ea8d4e2cbc4a488932a8d9515be4b08cf1b") +arr.push("78dccaf9db07f68190b1e4efc72b6ead55b16469b8fa99b8b3e544d567cab046") +arr.push("100538008ed6f40c44afd4dc15815700d52fcef4aea62c594ae9f8dabb9b58ec") +arr.push("540613de4156b1c7d798503f473f555713dcf6a6107dd260935cceb5736a0c20") +arr.push("98d39c9cd1ea899867be93fb649e1782ccf3ad8945aad39c25273e92d3146717") +arr.push("46c71f86fd0815e30d7fb4b0dc5366c0faf56a6b68885a71b46f149cc0525f13") +arr.push("b43da034c2f082e40b74535c9c54447fcb905548fa904b967c8f5ac4d33dc1dc") +arr.push("bd532735b43754e4f3806bc80e3a294d511654514f29d5c50895ec65bf5a3a48") +arr.push("e3b911df5b2bb03f68388fccb8674324c6c2509033d9b82cf01885a5cf0cd9f4") +arr.push("048461ef9c558c533c237effdbf5e7b92ed50577ef3ecbee4b2dc18f2f6835c0") +arr.push("f10de4b0c1ae2efb73444ade6e4db2af3b2f7e7a59403376832071aaf8a30fcf") +arr.push("476b47948f194bc47e17ebcaf2a15f9421b77e21a0e487a520649bd6e35f64af") +arr.push("ba81bf0454690d7162b7b46e77476f66523589a34b6e19529918c800e5a6bd5e") +arr.push("e08fc5f7f7e0f825d88246cb0d6412047fe5ccdada4232b98e02a890fb719979") +arr.push("2eb8122b808fbff2a9fdc98e4698bbb17c08b787a021b029738c36357bc00774") +arr.push("1152245a97c37f94cf3eb30a1ac0a98110eb4678b1db1558207763db8f0c4d46") +arr.push("a13ebd019f156a87d88208aceecd8290ceddf9acc3418153a8892c451b9401ec") +arr.push("1d35101835a023997ac51df361b53460cf42da96e39989068fe984d15518ec19") +arr.push("a1893f81a5c8dac7152a277849c7c34c492749e05e86fa9277027c4276b8cf6a") +arr.push("b64876b322f4e096b5a30d527d0e4d5ca82e9444a1808ad4bc314bcffb9950d7") +arr.push("e3f55cb142e51b41569bdaabb542f1751fe11c49dc78d92d7615f46d6f7e5e72") +arr.push("f0623f22fed12b44968981c477041f19f87f951fc171adc31509170a9e6ff163") +arr.push("1997d19de678dc85571e8581e788434db433835c4bc5ae08c50761952a7f64fd") +arr.push("22468a9f59c53398b3b37c18143b0f6d2ccb244366d3033d4a76fcac1e865316") +arr.push("94f0ad65e46414cce4eb3f899065f9bd824cff6557041c09d04aa51b171cb83e") +arr.push("8297da81ffe01f0ddeba92182d3cb88490190a6b82c228f824efb5e1e933d5ae") +arr.push("ce24d33f1bd387971926c25d56eab71a057d100898ab77073c9bb6dc3df9ae8e") +arr.push("694361fafc4ab684f9a235b2508b282d3c723b3355ed7d5c4a99368cde6475af") +arr.push("19d268b7ce4c0ac91d153a939f3e04a0c134c06d7cd1a2ccd203cb6ef662aa57") +arr.push("e7a5e1fc272e6245764bea9245a55f1540c43ae01ee1d0602a13f22477f2bcf2") +arr.push("8670b7d9b3803b3f0c6d6b08e741068dc359d3a1b83e9037e40c98d9cf09489b") +arr.push("9b37ed75d6d1bc86158184297c727b8a1f1360809ead8f1d877e3962c20d3096") +arr.push("418394a0a3d0118da8d15c178e5a33b0428d07272943a3058121664ef0813f38") +arr.push("cccd8315ce661cb24fcb3cc49f6996fd93d4b2f2356a06aee2fa03108c433ebd") +arr.push("ff93917734fa6999e0de11e97af77b98194fb2171cacdbd46af026d2357ebf52") +arr.push("0fd31aebd7514e5f6184a2bfc56b9dd5077a0bbd4deb2006c9350b7e69f3378c") +arr.push("8d18b75013e428cfde5fbb26a3005b46400e2c34a6230aa92054e85f95db075a") +arr.push("989b058c5bac70bfc47c1f6763882196dc5b7169043083dedc50ce3237283421") +arr.push("4029249d83788ac824a7254e53c0fec07d24f2f9b8ab9065e98dba34761e7a8c") +arr.push("0439dedc634052fa11bb9bc9a19a4c4f952b0f61a0706fbd86468219f696a90a") +arr.push("5f7a2c3bd061dfcc41b8ad7df04add6385c3c74878fd663070e13ee21e61743b") +arr.push("6402a4056dd00fa6749fb8582891766aba75095f8659536f9b32c9885ca1fc5f") +arr.push("5acf148413ec73d4d0fe22b82dd4ea79e180ce044de5a798696646f09657d40e") +arr.push("68992c0125586a9a477bd1f977928d2fc03ec043e0496e1d28e7130f29d3e96d") +arr.push("36d3eab26556d87fdd04f78ad66de3aa415d0936bd4c9d93832866b214fe889c") +arr.push("e6177e16132fbc25b8958a48b3786c1ad4fb36c0a9870f5a71929565f0fdfd25") +arr.push("21d04d0f4b29550904b0ba49adea186744616099e450445bd8c3837950c01e09") +arr.push("95cbdc78acf5f80c9120d3234ec7db4f6209830587079010e508e26954c4a19a") +arr.push("dd4a48beb73c2dbbf7416d691b2c0a6032492849209bab30b71438899ae19439") +arr.push("65227cfa4bf84e87a6010ffcf7a2db371497f0b872da5f4b3de1eb03b9124099") +arr.push("28ab7b82d0d1e91fb9c69429c7ffa143619e04d991629f98380935da4bb21cdc") +arr.push("4fc34e2ca970d4994fed5f398b01d9a2daafa93f4f985a1092332d3dba82d36e") +arr.push("ed910459d87282addc9287e8a246fbb64d1002c435720f61b0aebf049e2b7e8c") +arr.push("52cfd5c8e50a02e47d6fafa39a6e873350cb76b2147decd0b51653dc23d93764") +arr.push("f3a413ced3821c9140ce5eeebf4753a5fea883bb492a6b254b2848f45470d12e") +arr.push("24f731898e897c92922ff960dde97ef32fa1b14c9f8b364e2c5c26642e69dd3d") +arr.push("fb912f1af7dcaa1b9dc681c5f96bbdff4a567331fab8c6997cf77524c7857ecf") +arr.push("a07c034b1c274497770f463f0f69276788d00d81d81bc2bb622f07cd5499721b") +arr.push("167808a427c40f2118bbcc42618c7c34b6a4508308afa44ac0994b07ba79a3d1") +arr.push("01160cc61586a0eedc62d706d5f18caddb0ce23240757ce30c1311358e82fb45") +arr.push("95bb87b1d4abd829e420b33f477b88176865e8714ca467eb396fbe856e129749") +arr.push("ff08c86b0e0431f7c8b3abd02a6502e17a68e92c31fadc3a6e8cf64c2bc71f46") +arr.push("7467de20a4ec1283e13bbcb0a026662e6ecd0660590ffbeb1da08f89dd06e267") +arr.push("2b0967d453b4eeff7c6ea5de2c1437e5eb191553e2bfae613a0183a589755fa4") +arr.push("345c9975a0dcbbe3939cf52854b1729a2e074bc7cd5e4419816b3b7affddf2f8") +arr.push("cd679c38a4f6ca577740673267515a248dfc780380cddfaa64367e21464b130e") +arr.push("c361f60469c7f2cb69f237af804608786e86c87502b6a6a28af9626259b2894b") +arr.push("e74405ee4dcceae35b77aa4fb5b536ea82b7e94192f4339dee7b65009ab5c64b") +arr.push("54837ca2bc239003a114fbc85019e702fd9963ef320cfafce14c2edc61cb4a63") +arr.push("4d04883ed3aa5b3426a016c3c563c938f638ff910e0ba3e7514d093fa82c083e") +arr.push("34273ee33dc26ecf617c01016c79dceb567cf47f7c9af3e8a794adfbba2c693a") +arr.push("2a4f7be27282d6b2b742425a1faefd12e02396c78fe2a684f5be1b71ed8cd3e6") +arr.push("e6dadf7d4dff382da213cebeb8c6297a9f996dc22c509a255f35d4dd2e0b5d1d") +arr.push("e9137a75d350b9923a7376aea78a5330c3dddb0cf5921ff16fee2944d36bdee0") +arr.push("d93256e19c8eb74484c69b32ab2d523979d7ecc1cdcdea1ead53442c894ec399") +arr.push("78cfd5104fea293ed9f3eb3211556eb3c7d7d39e350a557a44445834c506316c") +arr.push("1c3bc52b9706b08957fb9aee2aa385534d4922ee6c290f7432bc8aa16522a46e") +arr.push("70b3a0b8374ee6e67739a10000f6abd6fc8a506bf1002ba1e8d8f95720f6c1f8") +arr.push("e06d54cb34a771299f9757cad9ec02793ced621fda19febc43d613135d55b6f0") +arr.push("f456af4839be211087f344328f0a35fa309833d3b3fdfc5c8337214d0bb8361b") +arr.push("985b19c1968e9c33eea6eecb10803f66c83852005ce23b54581bb4e44268875a") +arr.push("0581201aa31952ade14715377eaed1f4e757c1435a98e0ae4a3b873ac7805207") +arr.push("3bc1bffb31ba12f63308de13747dac324542f5542841da59ad57a53ce8b699fe") +arr.push("4ff9af03b4174967c40aceb5dbcab3572ef1380bbae29f874422f97be899dfb6") +arr.push("0ae9ca58d3b114f90e4a3954cf8f2dbf5b8b48a3e168e74feed742df7a3dfdc5") +arr.push("15b639c665d6eae6b3957397d1158817567f38ec4e84e48beed10badcd364681") +arr.push("d00deccd416aa4817c6a99343e973c907a34a87212144d521ba8ad6e7bc4f0ed") +arr.push("6c8027a1d90e582f77081c53a80a67c4d13ea696cac331f4473ceb2c42e9ff5b") +arr.push("a8361077ca5aedd74d3d5da830c5df227d70629b211899bc39f6d34ffc884528") +arr.push("6d423ed2e5aa18d9643156d5dc28cc0b9db12d13597e0b095d08c9f618243c6c") +arr.push("ec05ee7e8a8c6614f49acd11a3053abc45ff6641a6e21e5bdf1a4c075eeb5202") +arr.push("90ec08509f52397b28e356aca52008fffa98cae565d18d0de6418f5aa22877cd") +arr.push("cfeca97f9968d389d7c53f83ce1b659d2954137e70a407fe9c0c4469f85b59b8") +arr.push("44681fc1281ae4f850bc2f109bd4ccbeae07c7fbd45c6d852d6e6e963f38d8c4") +arr.push("399a9026622c90ff94b5dde8a280d87964c6214d95e2d0ee5a87ec7567c94453") +arr.push("57f94e784620055d2462247137b458d4401f38b160dbc9b6d394484211dd0d91") +arr.push("d311586954e58af9156b0d17a7127f485bcccbd8d57a28bc882608b3a1402633") +arr.push("1b8d614cbe9a11340d8b6ac0b607077dbe17baba661a1f02c2f262cf87d29525") +arr.push("2c205092874831a8eaff8075377afa58f9e5ef2d1295048edc997f0f68cde13d") +arr.push("3bafe232d0a6f5f7a6fc3a43b8be44d9635ff62fc683ece43f3656bdaec018cb") +arr.push("61cda68377a6d894ca27dcec2b6794d3e027366c18f7839d9f80ac4fdc93b1e6") +arr.push("df1317726fd14d9f7b19de419f2c14527576e507a97a85da2c236cfbdd73d364") +arr.push("57ad69d28719543e8518893a7badc784d04844c33f08d2f6f5e5aa53a9e603a2") +arr.push("a4d18e0ae811f17dcda231fda6390517458436670904922a0aa4cdbffb45447a") +arr.push("8744a9726a502cb1a303f5292bdc3b1ee5ce82c693d80cf7d12dc4580a5c8af6") +arr.push("89fa48fb7e9f93fc0f207a8a02f403b3fabd1fa658a7da12e502d8966155695a") +arr.push("b052d2a8e969214d6fdafcb9ce0e3424ba322fe8c7af887de3e14d1d93e7114a") +arr.push("9bf9fa524d64c8ef207ebe160b0efa0c876651b7953a67bfc018476e2603a4a3") +arr.push("5a6fcde871e69e93b335ee7d5dfd969a3bfa933b0de56ec24d4221ad3457e386") +arr.push("2d86457165bbb1670d22ea1ad8381d393ae4caeb9e1cd7103f53b2590f7d9570") +arr.push("868b4ac0a0a576d70c2b70f4d2f3ef9f2c2149c636991128451af73a1df6d5ac") +arr.push("1d20367a29b6c2bb4245233c99cac63ee562963042f72aecd5a77b7cd9a73eba") +arr.push("0fd44917b806518dca7711459a088db9c791d63881f42813db534289a07052c0") +arr.push("9d4fa661b26c3fa8a34947ce72aa3381a640b9c2665f14352b4fea4793fb2667") +arr.push("beca1b651a9c253dc25b50b1dd535140bda8a9584ff9d078002352ae88be6cb8") +arr.push("03f2c5f22807b9f9fbbaccd6cef628668dbe6af13256d4cb6c65c071c2a2b13e") +arr.push("4c3a0bf5601f0acfd70c96f3490e06cd2733709acd0ca861ff1571279a9d335b") +arr.push("f610ae48676334c799edc669a2a9266d50a070e2c3d6f5431a0547da4e68b1ac") +arr.push("c90cc4ebb61906819ec73342687a288aae78259383dcfca9db4a136fb6ecdab2") +arr.push("fd84fc1b10b2d9719e0234109a422e437026881b096868f143cc0e5cd9ada4c2") +arr.push("781295fbdfa257d0e8724248cb07c7301a511c8c73651306d07c30bfe51e9f58") +arr.push("09588431d963de05ae3e12f60fbae6225cb0297e482745f9394c05ebdab40977") +arr.push("53660ec1d1b8e20bd5b9024e1b38805e94c3655a56facddbfb3fae0174f59928") +arr.push("f6aa0ad032c477e8fd0444dc6db24bebe86632c2ba3f0545cd5589a655e200fe") +arr.push("5f95d01594311afd97a39fb631663418d92b590b6e1929bd6fdd1e324f409553") +arr.push("d8eabf2f854193b50495e44f2c911bd546f67ef6733a4e8cf5f611646ee06efd") +arr.push("016909c141967f5a816605a87a6bff605c552a86ccc430753c3f2dc579032423") +arr.push("bff2fd8963fd1e8f4138459276ea82b97c2892346319bde487c03f57fab21ee5") +arr.push("a372b81ebaae1fbd1dfce4f89d406663e65f733bda2490d9a68bf439ecb5bc23") +arr.push("e046df6b111c4afd2c2209234712b9d75909739ce470fb2a0f73b2d0b2a4659c") +arr.push("5ff3434ddbfd745763e9beb18b095e58e2502431347e985d4b8fd149ecbb99b1") +arr.push("4b43a1f0caf6ecbd6cc7a6660c48a28cbebadc78ab2ab80e67e06fbae933b703") +arr.push("1510304f7bcde4d34c108aef2319ed2b0e72104dbd83a355fc73f7be93fa5f99") +arr.push("a2e0bc48c73641a6d415970dced62da7e91a86e6f58e6ff581745f0f38ce907e") +arr.push("b4f5c46a6808d69e0b8f487c5e983d6b366c3ababf1f69fc66bb9704c1bd90ca") +arr.push("2c9c9f2fe38baff62fa5d97c1e93fe7f4298ed7d01c5859d24904608bcd580d9") +arr.push("984155102a33e61032d9f3642041755859b192071b37e1ae42b70e9db50069fa") +arr.push("56bcc7ff0677a575e54b7fa597f049c2a4eb9b802989df3760768fa3152f2a68") +arr.push("a7fe903a4db6224a9b03d37f543f13e2b285cc21f14dfde2c9a6ad5009859faa") +arr.push("4c43a9d5ca9c53251a91ce5d81f9519dbcf2a6527b057c0475928dea700ea525") +arr.push("d72b5305f9236704a570948fc881a467c83d2455a43295ca98083ddf4a549d25") +arr.push("c7b02e932f42bc8c10cdd0e30d5f278a33a67677782babe3d7bd9b18e6c37a0f") +arr.push("1e481c29fb66e04bab89eb6a07dbaf768bc7ad48cc7f7a6821344b4835158713") +arr.push("d9e1e507bcdbfa1e9542f4f04f0fe912764434b2fde4b09d91cb55651aa2af5d") +arr.push("f236d5a7e549fcbaa14316b508235500fb4d7a0d57c23523df5c836b710b9d6b") +arr.push("0e917ef388a82b4a16f6cde083575aad097bf05d62c0e85b9f300eff4bbff237") +arr.push("a763696599ad66419d5bf29bf744d87c7a9d22b8f0c4b722cc70892be6ccce23") +arr.push("fdb500f76d938074d1535bdeef8b85f1f24a5cd0682eaa2e698b370ad49f7a96") +arr.push("ccf1b42cd23b2bc7f754524cd6679f58d8549650a4f1059fce6771754dc69b1e") +arr.push("6bfdc7e316583204715201dfcbd7a008836c81cc6bf4697f5e7c1c9fb8a38656") +arr.push("bcac282c5274a7202fadfb386fb68586972f2ad6772b0154d2728e99d00e7dac") +arr.push("ace85366d26a1900207b55d6f4164420b8f869702652b9b532fd45e094327a3b") +arr.push("03ee4582e5d8ceb07ddf35862f4bf114474c20f38ab7c4ad81db1e21d80ce0b5") +arr.push("eab4d270d4604e975d412570b973e73b53fab58ff88b39de2bab7485863b4b47") +arr.push("00def3c562dff921456cd145aaaef7e0ba2d657540f58efa1ee1efadfac35f49") +arr.push("68ccab9ccf442093ef6693288258a69ff84aa158855fb6ac29b927b34e02f4de") +arr.push("08a2d8d27c56d511500b21f5e6e859e1f6aea3dbab173930470397514dfd25ad") +arr.push("ab82222cac59247f3c1efd08e2320560f34c989628708df37d6a675c923163c5") +arr.push("7501fb5ebd79b040fc8cd54842907832a4e5dc19ff10f3203277c617c3756c91") +arr.push("ad0b3c7b4591faa8238876f74dc84bdd86c02c4881f9733a2b2061185606d69d") +arr.push("cceb00bfc0dccbbf5ebaa48019ba792f9c486548ee3248754d3c0f450ed26ba9") +arr.push("4266bc616e7f6302ee978979f6e8b2c06b4fa1d2ae6be91d3fbfae64f6a4db10") +arr.push("6d0b54662e0dd5e9c08b799986cc6bca502eadc268f9cd2b7a22f74c579492ab") +arr.push("1b4d22e31c7d2a909b7ab0ed9a04f37783de623e6dc2d6c079135b48404f27d8") +arr.push("4c0aaa56d73e2218f7f946f89bff9684ae587c24eb9ddcf52d93dff94029b5b7") +arr.push("aca3e5143de2eb3181524986e024a88e10debcc7fefe615d859dadb8ddd67bc8") +arr.push("d57d35515a9ac075e9bff456fd1fa8cb6e1eb6f636d5b23c592329cba0380ebf") +arr.push("d1b6bb11a6ad135507f54e3c213cdd302c00e962d9150662590fcf83c803d113") +arr.push("0fd4c1f579095b5139b4642cccedb5592071b2fb05c7452fe6613e9faf841507") +arr.push("143605307537aa9bebc0687fb7f30e573cf065c007416a9535927727c7c042a9") +arr.push("cb6d20acbce47845592b24463d42f266501b257f5e89c527abd6099b7ca55e0b") +arr.push("9715b3488263bba0bf72c518faceef2f9a27869c511dec4428fa15d900c07e3e") +arr.push("c58da6e6071ff06da5db54bd8400889eeca984df293d000e718b0fff922ae9f7") +arr.push("fec8d8b4a3c4e63ff0b0f006b3ba13d066ccca29fcb62a39c58e2cb348487627") +arr.push("74c0d97a16b2781391cb3b2370fb4317f635074685982bda2eb4b13821e45b00") +arr.push("9a03ec6089afe06132271add866d43af81f851483af55f68881a1d71892b2dc7") +arr.push("7639b1f934d826d65c7bfbd9215b2f45f47a66ece76bc051848688e4de382bb0") +arr.push("34ebdf41b2f8371d7da3bd0c69710ab8cac73155b5b1892ee3c060786eb19ec2") +arr.push("39a8d0c644f5197b05c3e16d8708e45cc3e4c053d49c51e7d4850594cf78d868") +arr.push("b1b372aa0ac1c3881c3d5058c868cdb2a782d3d37f1a07720e12e67f292f1080") +arr.push("a697aa871e307f8feac94c343c8d6a92597521a855942e06d0766aba4bc4dccd") +arr.push("99817d72981e15af39b9980df1b5596f0784d0ac9c60b5863ab3b1bb142f79f3") +arr.push("c9f8b8512a4ff5f5fc92ed10287e4f23a121babff3d4a00e4b98fcc94b2ad016") +arr.push("19c66dae4a51bd6a5a0785522d2de34f596944946da2e17ce0f1e28ea8055ab8") +arr.push("63e842e6eb89468f9f4bee1d17a6e80c56ea1f1b3e8d1270780c2754979242a1") +arr.push("eb473cec70acff1ca2230e4188ff3f91ce1a4365ac9bb195ccc70f89df3e1496") +arr.push("128a1c33fe868fa6e1ba82ac4acde734d7ba421081f92e7de79a5dbc55313ca3") +arr.push("38224b965c43a6e5cc5790ea35cc6498f807239ceb13b9b381dc0a3ad1e8f582") +arr.push("55b33047f92f1ebe95dc0b6a20e813652105633fc1a7cf51ec416a1bf1524604") +arr.push("3f90ce570e725563cb91de8b19318b5e62cd46e6079f0985b454c6ea6be01e2d") +arr.push("c4d124ca9fea3ec82d57f1db43eea5a372f9a8e1358010dc2dc11dd175d28638") +arr.push("65b2d3b5a75c8d69a016f48cad691a7eafcb0ed18b6891013faf126d387a33c4") +arr.push("338cad756a66b34de3f5cadd035851ee8fa21f5bf026ec5320b70baf1d8952c8") +arr.push("77c6dfff6aa41aaf6fb913191fdb8731c5661b5ddd21ba8e3bac63a69aa6316d") +arr.push("5e30f936466a91e4bdadda2a743ac7622bcde10736f755a9a1fa074442bc95ba") +arr.push("f979187b3cd9be26ed761221e8d4088a70708897d918eeb8a857d53376adc7b6") +arr.push("03706ce4e70dcad92582d51f7b080f2639abf89d374fc40e85cf0d55e14b267e") +arr.push("79ee8317788c67bbb1fc416d6c256e32f9eb9c29bf494dc9f836be6d3bd443ad") +arr.push("faecefa0d718284bc544380a498693a73f9439bc29b9bb29a24437ce151a412b") +arr.push("2b6d44a9de2f127f8557c74dddc6b4c0df5f40e5f32294ac3644a8ee25d1f065") +arr.push("2c80c50aee1ac533059f528644d0db5b06eec0657983cd4a016ae0987e66ebfd") +arr.push("b4a2c23950568d70eab0ac01eb821bc2cf1149da76bbe993aa5b4e65e7409679") +arr.push("4f8a52a457185ffdf05e4262ef4313b62ea0de74ac18d76ecbe98cf29379dbb9") +arr.push("47d700f9261670fdf12d9900b0083805cfa1233c94492bd69b62159e9243eb37") +arr.push("7ab8027fd9182a50c4f18abe3a0571547d9429229505c1db91f4abb600c37c7e") +arr.push("e83c252600f786566826dff173f6d2c0f29c16f872a6fb459fc30ac36f6efada") +arr.push("71da0d6c580a0c7ed7f59e685585220c20a8eec635b4b98972ae980e4245582f") +arr.push("11e5e05cd2b666b0b74030ce6cf6188c7ca3d8d67bce2f3b9c269d8006b7a1b3") +arr.push("d0599f9c6f1f0eea797d340a92eff5529cfa8d57d8799a0cc39f37089c719c19") +arr.push("43fdbeec0d80e4823b15299abb0fa67ba73fe9987d58eb4c22b3c50593ebe412") +arr.push("b462778967df1bd01cc2915b90b6684143d0e3a30604b37cdc3bdb3d42dc3a99") +arr.push("e50afeaa017d2b5d1af0e6942fb812dc2dc4ef6ec4072a5ce4c4d8dfa16bbf66") +arr.push("6573acbbfec42d1744e17e41632e32c873ca333d8649c0f5ed21d77ec2c06f18") +arr.push("148d777b3768a92bb7d46e4885bf4724c030a53ff5b629a3e3caad57f2580096") +arr.push("7c40ba4cd6666f04f0c8584fbb43bbb1b1e303f4a7022e1fd7b89ebf4199e6f0") +arr.push("d063e017fc56afa57878b8dd15e02363d3035e844b146aabd6244cf125cf5a93") +arr.push("61e22d35ff9853e64c796f63e02f6115cc5174d0ff19414a60b9ad539428c484") +arr.push("a9eaa014b90f0f833075b001ddd75932b87dc41ac14e0ca505c450e4f76c09bb") +arr.push("d846aa7167c69fbc51580ea83d8db4da9ab18767c172f6601dd2a01ff0af97ac") +arr.push("0c7c9b70b8b1314956d52254ba4d61af6805a53ac3daa9d463fbf03605f02cca") +arr.push("5be0a4e65a03359209ea3ba243fe7c38e90d4219ea526027840fb5ecc35823ac") +arr.push("edcee698e7567c4766c99e6510dfb299ef81054ab595975a667fc4b162bcc863") +arr.push("55b97d99fcb8df69353c29fdff060f806af85455b785054e86ae98d25c88cf98") +arr.push("43985d14e12ff2959694140dcc2c2f5dd4c07f847b5d2398ce1c6e4ee0397d32") +arr.push("bb5fb280c46fd5d3201972f34f5c36e27c33206ab6f655e2b6e8cfba5c10c78c") +arr.push("b61c060b79e76a1b17a0ccf1ecc115441fa1f4a8f2d4ac125dd8711604dede6d") +arr.push("876b0970342be79dd2bcaaf26d9da8c0a9d58d2fd2914859f766869e1606f0c1") +arr.push("59d87f6a2c7490fc3dc30e1529ab7b2992d53fdf3929abd2ee40e3be51d99ddc") +arr.push("214dc0dc694b13ed7ee359e1505eab970a97cb7355496b6067e9279e9cf61bd2") +arr.push("72e5795fa70d5016c294abf9555fc1434ccba4cadfe76b061a62ae04da339bb3") +arr.push("c139504ca512181fc58a308486487573058ce99ed566a4995b0c8589fb8010c0") +arr.push("38673b2274267a7c7503bd1446626d46a8f1d71263962362053c3749765d7778") +arr.push("843fdbe143936ac59c713c2254c2ebc4379ced130f96ea7ad34e675dbeebe299") +arr.push("7f78f05803d06406d0a51fb13e1f4d14e3daca8f305a9eea1bd0a138a9816a7d") +arr.push("93e992e6d892fdd917a3d9fd145b6aead9fb3407449eab32f75beda54e45455d") +arr.push("a483113242d4c0f8ff808335c78419f8b446c01e9f1f7d9e821ae4ed9517c5e7") +arr.push("dd16e65e70445683b30dcf6a62b947447e5b1651ae5ec5950297d099482e269c") +arr.push("7edb67f8b60e1eee2718cf85469598a7b674600dd5d935fede816c1c3dfe4c2f") +arr.push("06e74b0d4d7cd2e4a1fecb79d18f0329473702c0189e7eb3ec6c21717cab5c61") +arr.push("a21db1cae2f33955b857a752b44ccb43eb24d52d1ea52e48c28db8f02eb0b18f") +arr.push("b9a8c3cec6c2a99fc11ac73d9aee3d2fd0a98bb42917c346e0876008365f1ac6") +arr.push("b0bbbbadde05dc2186a4a286a4aac02b27ed4c73bbaaf4c9cd23cd5471c2f942") +arr.push("4fdfa06b1a1323356f29e84d0c72581dcf55d3f9f8dc4e779c84970670a66ba3") +arr.push("e795501e78781a8691cff7b712f7d059ea887850fd6ef6ca6b33fa380d989b33") +arr.push("66ab565ce85e41293e2e5c303a95a5d3b6d9fb828b1d025fa1e5e4bef3e0fbbe") +arr.push("54b25fb2195f64f8a3efc4280caedf770a2b62d75c9d6e38748564c780a59049") +arr.push("fa11cb25d81c8c49e9e4a9dfd07201a9881d6a0b2e6efdfe14bc62695a8981f4") +arr.push("2c43fcf6cbe45d1d9e68256ed123a42f3b82f7d430a4b0ef437ca3a1fd6c10f6") +arr.push("6b628804ab830f20aa7d0fafcf804bbbbfb46cce4f8b41afe79df997adeb2074") +arr.push("67d12c6558f48b240c6c75c507b7dd12ea51258e343afc47e80563f7036035ed") +arr.push("91900ce747300522ca2b87db3991a4d0773dec75fe8b7936b3b9778644110a57") +arr.push("9ff51becc7db04ec7c94950f6d3c768e5c49c5f5595d4c83647c27f583e47d01") +arr.push("d9e93599f5fd48410bd9d18c7059eeff348b49176584762aa0c0346dd4ad0656") +arr.push("f4635bbffa37b60afb403e91f16e2f47832cad35ffd2c5a2a0b4a75a95bb1f30") +arr.push("a404b945411e29dbe22fc3e06ec87b09efaa9802627fb1e54cdb321018612425") +arr.push("9c1406f232dba20082ef391f8faf2cb0307db35a956d32ee1fefb40c143a5974") +arr.push("3c2f91ee7d2f60e3bfff12478df70c7278ff78cf5e0c1eed113c7e9567554c69") +arr.push("0a3f995f5d01588a5204ea5eaae476e156ba66d830c7f87fa193fa92ed726e2f") +arr.push("34620ca9d3babc3037008f11ec1a510854febb165ffdfc7c87177315b2df52ad") +arr.push("1a8ac617430e2e9e95b1fc7a946d0e32041f30f0dcb2776530060e8bed2b3c65") +arr.push("88a5c1bfe95bd8fe16c6d962838b1e540ee8d76a8f13edd2a7f9d418c8e41266") +arr.push("07cca5bfca18f249797c958c2d4a7ef9e87fe997f0cf1914895769025b29bdb2") +arr.push("29b5293672e4253f5e91abd6b001192de511b35d58323c9448ecb4be3f693085") +arr.push("fa3b332ff8fbaca16ae82d0696a8a7751f2869eb037bccc3b2bfee77be31936b") +arr.push("5be2b147b151a348c870cf2b312ef1679c80d14f980b3de7bb5f4580c3b929ed") +arr.push("1341f68b90adf5d374498a98deff99e300bd1ade4870d9278f52a40fcfbc60d9") +arr.push("d06777843a60b2f97000a73eed7c92b16da83e80c91ad69116fc78a41c3da2c7") +arr.push("cc0e51e24a4cfe8bca44b623e60d3dab485b4c45c655691ca38a0c54eecc894d") +arr.push("7a6789ab0e7929ad7833ae55aa6b2d7af3cf6ce9860a3ee811dfcf6cae5228cc") +arr.push("edae2a4ef4aaadf486f94b119bb09c399e4a51a6f741063c0ce589f121f3f125") +arr.push("fc9b6f19a3b30bc8018f7520bf56a56421897fe3e8aaa452cecfc87b65971f5d") +arr.push("b5aa5d5c2784971d7de24eb1582aac2becfeab8788e55eccb7b580289ee61b6b") +arr.push("4c5d9feb9e05c8f5a74bb3d49daed482134447df7a5dda6710dc9fe083a719b7") +arr.push("fdcd046c5b8caa771deb0a98f8725c023e36b5c127a8ca0fe649c0744b132987") +arr.push("ec740f4df6ba44ef99179028f4b54c567850eb6f78112aa4f209ef76cc71f0a6") +arr.push("b4209bbba5ea0424967007325c5736ed98760822de2a056d7acb706841d0a25f") +arr.push("c600ee32e0fa9fc527152a156685e205efdf85c4394220fbd0fdcfc939bcfd5a") +arr.push("f600a6d50cd25ca920550bd230d263827ad673abba8e324cc189ab7c94bf3b34") +arr.push("7d2bf9ee314f500508453000e6003882f5953fb4c26e08521fb01e29e12b03b0") +arr.push("7c4eab5762e4f912c9d2c97fdbf318cf770442397a7ebf18fa4d78ba78d0d512") +arr.push("4f8646c60f30ef89800fa05077e0911cd922836866efd2585a84fc84a30c3225") +arr.push("cc476a26df86738bb57030a402306b15801e24bac006c9b03d30716832908385") +arr.push("20e61fccde0ff3284df92e0ac1e91407f794df4d9c3edddd56a1ce60bd5dfad1") +arr.push("f3ad842cdba7d5ea1d80facffa6e98d336c3cdb45f243e20e1855edcfe698643") +arr.push("9e7b33215bcd1f86a9ffb6386a3654129e4c3b6c47858e2e9527101710fc5199") +arr.push("cf4b8a8a225fe5ce0d696fe7d751e93ee962530bf59cdf9b6068863e7a193129") +arr.push("571c9e4a457f019b30fc305b44f11e482ae9f2cec0ff93c81f1f200a33a3a133") +arr.push("49ef69a14dbd8eb6302b6dfd3e42e9a3fa5f8da0138de2971d9c76a6b2ef1250") +arr.push("4ea3d03827b3aba6cbbd4ade3d9dfcff1ac94ef16d9c0e87644c35c5faef1c20") +arr.push("4bf43d01f13d43306f02780b56e6825193879679c43a712a0adf25050b597b61") +arr.push("0aa5abcf12403764f24d8d21c5684d0065f82f938b901eab14879389c7d91143") +arr.push("64e9198a48b1286300288cfebfbbcd06387ae3da81cfd6189bd1f9dd56c2d378") +arr.push("8edcb192835f0d3c932764ae563cdb7df055130e78feda51612b1d56ae4c8fd4") +arr.push("10f506daeeb96dc0b54205266dd4d1c1d871ebba0a220c9d74ae52080e78c95c") +arr.push("da65d4abfe08fe7f414860625f15fd4ed1e93d1b79ce2b46f722f3a09cd60eca") +arr.push("bd2397faf7f17f195fd8d6958aebdb8e0a8ed57bfe3db8083c8876e596f6c828") +arr.push("af5ce6e54075a7932bc0dda1b0da2327377622ad295edc3daae413ac650266ab") +arr.push("dd93e4a530e6ec649f82946737fc187a0d4fb3263fa13ff561d1ffff1a3cb069") +arr.push("9a73430eec2d458fd6c030d7d22447086642900d598c3dcf2ca838633cb13282") +arr.push("c6254765eaf6569f1554c3758eccc38f0973b095945167fffebb6f7ef30eafc5") +arr.push("2fd2766621e02645aeda8f8d1ab06adde3fa1604f7ff1a30bdeb26a978b60843") +arr.push("24d02aa13aac12c170901a91468adf66b01bcd816755ea9fe835e5c8ef24d706") +arr.push("c2b081c64a55aef5f8a5af976abf937fa9f3637ff0f0648fd3c4d17de20133f4") +arr.push("182db31e7e803486b148932e41f79a03429fdbe4f09eb41e98fe277beea583a2") +arr.push("21918c3697212c72180f456af52c8409d46d170db5df1ccaf6cbbb4e16345ab4") +arr.push("6ab129c05837bdffcce1a80b878f28e844f35558639a2b037ccfd3ac464cceba") +arr.push("d3d4be329bcb1d71e08124920dc873593d2fd226d88a17a3837e6d6fc2597053") +arr.push("0c705823ff14f5d81d5446026ddebad6ef8675e01fcdcd1c5698d4db11512dd4") +arr.push("545f2c92cb3f9f9b649b53f760d371dfe535d6325a5b71828f436d4bb5f2a521") +arr.push("55f984aba6118a4232f3a279b43d622ab8a754b2270e0aa1d18f881de499ea9d") +arr.push("ba35e3b920358827ecbc75c6992067e2b1288eb248967aa1dfc69d14130a5289") +arr.push("6cf45859aaafbac32234705f32016f435a8979dcb8a93fb5eb69c53070624548") +arr.push("f2562c46d295376b03e38a186b8c31c8bb0e7b28fe0ddade6ef4c2ca67653d6a") +arr.push("658b934432762c249400c3c356ed3829db5f8899c3fcb2ee72d367ca64a6aa69") +arr.push("e82520d8790efb632cf92a76751e5ca183083bff57fbfae4790d25a7afa4baf2") +arr.push("cbd5b5e1ac1983850b2b8a79202bba8243faa0f50fe0e5c59ee0d1b303acf0c8") +arr.push("46aa37cda47afb3a369615b76882deb31d7a76edf2b618705ef4bf267b74de7a") +arr.push("999265904bbd7fe52d9590c1cbbaf9f5aeacb510347ffd599c16fd8fa510e04b") +arr.push("951088059bea2cba5e137b5315bbd9d9587161e761129c412947127912c03a5f") +arr.push("89d8ac69abfb6ebe98d3b5fe53c2561d193fa0bb05d65a37b5b34eddc5d8721e") +arr.push("9c38b10af0524272c6906984c565741b14bd2b12549a5278def4dde33962465f") +arr.push("49bc73e871c17ac0fbadeadae0f7aed78fa16eac48a62de00ffdcc1ef8476cbc") +arr.push("97aa589f7ba984e169e6e11938327ef0fa9cf28ab10cfa3cd305e3ddc74f2a3e") +arr.push("0bb3ca2c95d3deb2736dadc675bca63c6502231bd331e129833a256a94b1e2b5") +arr.push("a405baeceb95d6d9cfb2611719346189fd4916b8858a700bb84f6025f54d0fee") +arr.push("3511a52f4d9e6d1996af2c341f48d1b63f5f404c69147f84a6215a49e55879bf") +arr.push("927df94ebdead32c51beda40bd45535c1ed99b684d62e60ad6389a658d3f95c2") +arr.push("be5bc7daed7122604b0c87b6a48f5a42ce47e06471f923b612d8d6c7a5aea5f3") +arr.push("60fe6eb5a97bb974a2fd30e6ebef51b3ba7b8c8db3dfe47be10ea015bf5479c6") +arr.push("219448fc770a97afcc4931519fdac79927f0bfbe88d254a4af81768951d0ca16") +arr.push("3aaf98e18822d12eaf11c11f36d18ccd7be309f759986bc36f3c8db35e5708b2") +arr.push("8f3d13cc18345f086244d3aa165fd476b17ebb220b95907e233edce7343fe256") +arr.push("7eba424093d87a065235a756460436a4344ccaff74bd52b520bae20946b7408c") +arr.push("00c1a3f6efe6ef70f3ce69d21df809a204836fd9eb3ac5f48dea95df589a0103") +arr.push("efa92864061ec5fb8128d998e36be9eae73b711b08c5901e3541b750f6a3f75d") +arr.push("72ccbe2ab654cfb94a7681311e6cc16cc0d7dec2604ae4e9edf8c19da28d1c12") +arr.push("76219110922ed43915b43baf617a084fb4b11035bace90f523e5b0609009e8e0") +arr.push("469720aaab8205ff6ed1731d4db23e7acb110722f03aab6976fcdf1cadb05990") +arr.push("7afd0154d2696ef09e71a032308bc97d5dbe3247066ba9d188e02a71cbd2e202") +arr.push("cfcef69be531ae4329600762896ec84a299d0e4461a2a1aa5d05f339196eec19") +arr.push("d22f7e4d50b03bb8a20a9ddc9e908f7cf23799716a6cef51395027fc755db3af") +arr.push("cbc9249de4437efa50263971022f50841f6663e1e2d5d766f0157e69afa0386c") +arr.push("d8c1e57d9950f99b9c73676d3dc315b46ff6fbcc5e3403b43dabaca51c8c06c3") +arr.push("f556baff2d14eb8acc0a15eca79c52f87f9069e8d21aae9d20185a9113a298ab") +arr.push("d53fd64c17dc4c1d660983747b754d3d29f5e1ac7221d26437d64e598f3ce753") +arr.push("41418bf9853542e2308261695dc052900eedb1987a84485c157dc3cfefc8e7c1") +arr.push("81191acc88840b5194e12e99fed4a48f31f5551849176f4292fa7c623ee6c44d") +arr.push("d9d1c3521415012fc754f873ea03ff90aee6328f9f3f6a42e9fcaa0efc9b9022") +arr.push("94da1d44e02152103185f792dee047173b16747aec26ef6113cab880becd3879") +arr.push("39cc45f5ecfaa237662dfbb8a7667edb33549d289ca66440319f17f1b51e12fe") +arr.push("59592cb332795e5c6f2c5a0142c9633a5ef0d04a531296ead4f6447249060804") +arr.push("b29298164ec334b0150dec04798d526434d365ec1b6e70e8aa892316a2d4c5cc") +arr.push("019066d74134ec5067f083d36919b94274d3664a5597ece8c10c5239f26c5e14") +arr.push("ffde97827fab1c90e4721b300cb1f61c86f17959033eb2f30f1cc08ee3be1bb6") +arr.push("bacbf107aebbd9c62ccbfba0c4ba48ece02235c25383bc9019fff3919eaa22ed") +arr.push("35a92ab13963675bddbda36912a6ba9663f6a02c553d4c3e49b8b97ed2312259") +arr.push("1c9f5b86a6493fed80ff12ed265bed21ad64785b9cc5d75564040ec12388ba73") +arr.push("038115ed4605f015151a181efa141e9125c2ef6eca8aa489c3a7da93da43d577") +arr.push("fb7d89c1068ba94d0c3aea751ee3594de03e8b9fd1991c2d440f4ba639e9cc2e") +arr.push("cf05a8456b988b76d10a2319c84ecdf8bf91de402b8339b7f2d9e4a93fc1d130") +arr.push("bdeabbd52ade2c25078aa48ba9b1692ffa4952fa10122196f06f9501ef363950") +arr.push("5fa3c94e94b5f71747efcb4a26ad040efcbfd4c4a27f89ab34a355cb9e10fbd6") +arr.push("71900b2fcae96f4a48c5937b7de53f526bfc3b2695f6d255c53388c843c154f2") +arr.push("e66424cfebd8de46ae44deb9ce2457b3403b1d39abf2ef1b0ccaee7e9ce116cf") +arr.push("8d0eac7db0cbb02041eb6930adef8b23355653b06c34dd1a08f1be198f2684c3") +arr.push("6c77125f885590997fed74a8081c25cc68817150ef574f5cbf921a4d73999f25") +arr.push("a950c58b37996112ed3ed8035d1236a9edd7acfd263bd30fd0da6939429707f0") +arr.push("08f598503ce6bcb9208be23375b647f3c08c07f8bed377bb4a4a6d6ad0b945b2") +arr.push("ff8bba5358d75f3bb5c8f72a8a696d628beac8f4a812066eba6a8dbd8d33cc0d") +arr.push("0080edfdf37d1d1f759d530efa50939391d8e588d046fc47d292ad747c5b0d4c") +arr.push("590a2c87c20dfe6c26d583f0aa76c9e5f0249177f7b22921b368d582ecc25d8e") +arr.push("eddf00713401345b5d7a8fb454eb80311539d15e7ed56560a510364ce27c094a") +arr.push("11197b1e40dad83c5f5a2715ea5664e8ce37b1ddda431438243c36317d9f3d15") +arr.push("d0b34236a6246477a2b16eb50542a362ce26673438c00ffb9a6a1fbfdca836a4") +arr.push("268115ce4e486d0404564cfceb790a6f645440c61dff06767b6b721e5dd54b5f") +arr.push("9f048b6f339834ad175be6ae47fa25093c973dc43669775f57609bae6837841a") +arr.push("7f014db5eb5cca3f3db29eefd27a488de8580e558454aeb1f521c96229258e70") +arr.push("887be9e55506f6263908e99427005ec90607b09ba943e7ab59f10f61ed4d8fd1") +arr.push("773dc01b89561270061cca3d479bc4c84af9de6884180addb4af2c441a483d0b") +arr.push("c5a95cd16784158597f01df1b04166522e87c23f9153de85b7e023d1dd435fbc") +arr.push("d8e4903ce02235d9c004a941f42341b9908088b68b2f25def0cb5c20929521ea") +arr.push("9d00a83ca8e1cde79f62ac6f7ba432eefc7bd0265bb7a1b6de701961e44401b2") +arr.push("516fb4233c8e01bad79960ae5b4c1d4cadda80f2551090d8ecb0449112563663") +arr.push("fc0831e7d922797dfb04be2beb17714e4696a27cd74a74356acd5e65115a721f") +arr.push("5e6cdf2f462bc61b6d8edca7df4629fa9e937ca831f20721471f63c53fdc335a") +arr.push("896db3c3a73599f1e02045f60c423e09ac111ca376cbb92fc772d7220de61329") +arr.push("bb330b2c2fa0acd49a2f19bd9b23008147a295f85b0e048a149832d26ba8f6e6") +arr.push("058292ef31d7fea554d534660a10af8d3972eb357cabdb5592fcbd32f8526950") +arr.push("f25a1d109e8dfe3236a7fba513ce51b9e023ac8d402bd1d8e5a6df02b5a1c337") +arr.push("56a8b24d24114f13bdd32c11757a5ad070fa0683fdfafde3099740fc685fb715") +arr.push("8fb70bd132937b58ddce164db213e3bf3825121f3412e8ada3dda3e7e9f8abf5") +arr.push("944b5b609b5fbc10c8c2cfa7da6f98dba0bffa2a2e89776d680c4a1aaff51b00") +arr.push("75f9334aec3d00421c1c1adaa84f2834cb3728ec6e74f278d50b17c690d5d8da") +arr.push("1fab6ac6a3f99d7456e68083bc956d510ae7fa9f9ae41b9cbf970d82d4984ae9") +arr.push("b36d8a2154b53cbba058a4e535c7b50bde0388064ee04a05432206c7182c5bcd") +arr.push("5a470eb4dbfcdf90a89333eae66c51a991aeddaa32d0baf6fa88a6e11e080644") +arr.push("29bff23e63bee538ddceec628185fc75ca4a0a372165f2a85320be7ee81678e3") +arr.push("b9efb6bc31f121bf92f65cd1b3cc9619ca19d762844e1eefbe147580007dd5df") +arr.push("a01749451a5639a1f46c3cf8c092a8e539286a22c8bd8ba59caf198d701d23ec") +arr.push("7c839afda9a421d526dd0d5f673508746f47babd04b8a70df7104d15d099eb0e") +arr.push("ab989848f6ea86920f92210bd8919d5a776bc76238de40586507736b13ddd1e0") +arr.push("42374774245c52718ecbe755999dede6786faf7bf5b17cd0b39a0a78b35394fe") +arr.push("d4695e40f4195fc11dd6bba976a395e77d053fca88a2d68a011f60dab38ef0f9") +arr.push("dd89179fe39239e5afdd5b63ec7c21247c9a8d4eac1cc1bda8ea2f7764f6f216") +arr.push("dd7b749479a967871812de77057d5f3bff23c914d81d2632485f7eae9b72c4f3") +arr.push("2d966ffa1749328f0d2aa0eb5b35fe287089c56b40559082ca7cda013b0f0e7b") +arr.push("fbe8a327755f98bae1a0e24f3256febe7c722f5427a6f2cd538808373341810f") +arr.push("7759db787354b8df9218dfcb1ef93f6663bc0d3e0dc022a21377e8679b8028be") +arr.push("d636e6251d35497157194f13a5ce78ea6ba59e003edb9cb143e57f2e0614e09b") +arr.push("28b6fc14144865b7b8f5dec0bf4f37c9e756aa67b7eb719bd4d8cb43dc3e772e") +arr.push("23abc52a5f50da2793e9a11d5978307e63e54780e1a4ea4aecef0e4387815f1e") +arr.push("82b721843050563f685ac73212c10d37e41bf74d2debe78f41dbd2f00ee82be0") +arr.push("8de214626302a1f6b3e0de51a36e27ba2ddd25bfa6013ce24926ed0229a70939") +arr.push("ec97fa00a809e15dade5e609bf6063e438fb277a04bb981656de6bd3930a3534") +arr.push("ad629f33ad217dc4d49851514d4fcbdfd01ef826c8e7ee211f2e0176ad0ca2ec") +arr.push("dc8dc87a96269bff9206011f5f01f20e7039d7a902a23cf9ec5069b4a464f614") +arr.push("5735d34dccf7bb0e5f51dcd4742b5dce1cc911225fe6819e3e3c31c662d0eabe") +arr.push("5506c84c65e530c3c732be55d0df59470e8c7e65c9d77f17d7fb68a5d245162c") +arr.push("542e001749078783330c0705a27d9411dbab528c67b6fe1a4293c453ed268235") +arr.push("7ab9a4907f3412b8025a9a911375883517866736c6b63c0bf63ee48f5fc43fdb") +arr.push("1c5aa3753f22f3a5e4cc4e2c96b76ca885068d410387e2eaba169f369d329e95") +arr.push("fbae067ccb5606c9b0b7dec70ea7d159069f3dd0b57a2df0499052392ee437e5") +arr.push("42a4a044f740e7ea677d9be1c1523182427ae2cc877a48d40e61a358e44009c3") +arr.push("6baea9f990db100cf30959f3844a71184ba3c3b6a239490ca4cb63e9a79c392c") +arr.push("7ce46927a208179fbb12d4cb589ed13800b34954693853283e2b94e4cdba8dc8") +arr.push("3e945b433027c6ec708cdde61abd3194eaf720158b0e595a90ab81d47976eda6") +arr.push("972210d3f364ee77b3d5115a0b6145838e53c623345d4a2f9bd9e6d69579a011") +arr.push("7241f2ccaf00b8e86319a3cd32e1a10cfec696f7f79cd79b02cc30fb39f51d14") +arr.push("77a241fb46353b24cb864008bb8f7ac0ea50ffaa39e90aec85d9cb833bb3fbf2") +arr.push("4f727910b8b023f2c16de5aa6b8427fadc05b1c34d34b606b2b48278017ed5fb") +arr.push("e426589cbe31095816ac7f790ed172530543b6e5c1920256aa1a1c930c3ab048") +arr.push("f6c8c0cc58a85f3db67b5e452046c66d13b68ce49d8fdfba6d076b08335299ac") +arr.push("a21e09d1a0d0be1c1adcaa8082299ea3b29f08e66676b64ca29c5ce80f7323c2") +arr.push("0c090a16efa0962a3bed2f5a68db262a7b646d58312ebcc055ce912182bd06ba") +arr.push("03345af088273be4be2d2ea502c47c05a0658fb045a2441f9ea2ac35f00dd738") +arr.push("c71299c7f5f7bbb916a9258fbdf35b440a821cce0ed597b38ee5026fb5a31d41") +arr.push("4be5685704084887bf03fbbd10132b68c598c0bf9279e1f77bbb5c8d51255aa1") +arr.push("6b7fd63cc4a7551de969236e1a5c0d644358505095e091b2e338e6631bcfe248") +arr.push("f7ab34665b630ec30dbcf149e2eccf30371abff370d73ae49042350f0ebd97e7") +arr.push("da42a32c8cf2b66062e99868e469cee04fba952a3676af20fca47ce22542b1f2") +arr.push("5ce35bb0d77f370037c71926c30aeae484835a4b3c6e765647c7943d9afc7034") +arr.push("4fc9d1058747ad879a300266c32bb24b183beddeb4def4579e076014a11c5ff3") +arr.push("a420a754f700e2ebaad75563200a607212b1fb79e834904ce998d1336161679c") +arr.push("9653e4e38767f0d3b20ebd95331ff0190e53569979eb245295a0d8440bbba854") +arr.push("8c9a23db24318ef17b91e68c61ea6d5dbc3300ba688567e63d80f10a2b0ed3ca") +arr.push("bb7ecac6e397985c0f2e337bc09077182b6066e5a67899e6d6722e5c42f0bc30") +arr.push("9e2bfcff425d554adf456bb6051b5737ab160c6edc72cd943776a5c0d12202a6") +arr.push("86321c75097115fd490bd773c88ef791497cae6909656ab1c61b0134217cfff6") +arr.push("08e03ccd73f19212fb92c42cc6b1fa803db7df95fa5998a4b68b1fb4a1e25331") +arr.push("7545768dc1b50e2d96ffc95e5f8b2572d1e07380a80a8f1f526f6a7200cf3606") +arr.push("a497dbbb7c1028648df1c35f3660568a09a99de904c22e1c4262c96575762529") +arr.push("a708759e44a703ccd1f80fd64427f037c3239b0643a1c27c71547510e551618b") +arr.push("e65a031a709ad0363346c928f82a155d4e6a6151c0437a239e4d26be441c8bf1") +arr.push("33ab53ccaee4d2616d25090f39be78676b6a665579ffd0cdfbc9d546819d8dfa") +arr.push("fde1850cf2dce906bc7285b30c37d7baef0565a77f855ab244efca5cdfa5edab") +arr.push("a28ac445f9251272435365759bdf6b59246234315327a42f410470a178a3a2d1") +arr.push("e4fbd246611fdaab782523ebe3355126b4c1e58b60f6e3618d3005eca1657772") +arr.push("5127e3f1093fceddd518d60d370759dbb57599f5dbdfde3c913fb1f924bfebfc") +arr.push("36939214542adfc8a9fce420b20e015900c3eb5e1785ebbbdcc3d987b360f0dc") +arr.push("e44e0345f591b35229544e7e87d8b920c3cec44d1ff62661c3209fe9c684aed3") +arr.push("4a2dd28d6d22fe9ba147e219a68a97b121c6d3b00f1c93066887849c91eb3c6a") +arr.push("65c82bcd7949e5bcab628fa27f93ca3d339b1a48f3286494db854d3b6ebd2e58") +arr.push("e91d8e2a0a4266547103e4855d568c22bb2f570452bf79519119fb66074eeee2") +arr.push("6164ef8c651b43e220903bc0e32c0c7e693a80b5338e59abe13e7984be785ed6") +arr.push("eec7b4ccd0a0d675da9ecc974654dee4797247c97f55fdc8b299933bd6cd2ddc") +arr.push("54972ee54b8775b1be16cd542a96c40fc946a25a6ff7aa9b0cf824b2ebf9c1a5") +arr.push("c61162ea471b4bb1452c646b11e775eb5847f084661358aee25250fb2ca410cf") +arr.push("d0c26bd53c30cb1e0e279d72e05d6afab5ffc8ef372a41c70b7e376a6992f15b") +arr.push("dd7e862e216f64968cf132aba7e0269bfa527988a407bba006010712994a5acc") +arr.push("9bd45da4efe9cf49eef93ca1ad89da051e71be7f8f58a1e71cca59f1f4444600") +arr.push("bf0340a580779336a13933780635d9fe7f23e6cc04ee40f29a8724553d47b7e0") +arr.push("f43d3d3f631569961e089618cd0e949410a71a51b123c9dd41c4fdb861a39e64") +arr.push("20c411c51acbaf72c689966f30d38fe64a942516238a44f473b177c10b6527a8") +arr.push("5c92dfe37746f11427e31aa260f6e0ab55d4edbf89595fa2eb01ebf7a1eacec1") +arr.push("7a139db857d23d032e6734f26fe43c039c8ee837b2874e74f183d181f3cd9214") +arr.push("553b4dcd84962985adf58c3a41a3f25751ea1ebfdb2cc73493ac3dd76b81bf20") +arr.push("6e560f299da15ed2bb112ccc869e84d6f6b8ff93e671519b7bda64d9b93df395") +arr.push("c9c468f8515de1c91461cb860824cf2dcce20bb934db51ef6fd6d7dcea94b81a") +arr.push("2e1376d2152774c18bd875af8d7af0ff2c5abe3e1f3bb9bf14c2add79c4394cd") +arr.push("b1b48f15b23cf4f9950ae4604dcb1a85e1e0eb87b59cbe02fc2eeb9ff2aa8d65") +arr.push("23c783ea711f054523a1ddaf2eb0677d9f7a84ba94b94df1a10a2ad1b92e4630") +arr.push("b8e792d3b02e7e295546567eedb4e68023daf2a19f2b69fdcf3b307f78bd124a") +arr.push("d418fc21cd2b1651b84398c18ce168b0427a3c81375d1b55c4ee31999d543163") +arr.push("672ef2d5111666dfdcd52ac8941515e60953b19c9e5eb282cc03272311ea9923") +arr.push("ff030736840fc1264010db28cf13b3597a7059ef9fb1adfffefd9a2db8776e9a") +arr.push("14f59f589519b3ae3de2587d06839698a7f7469b270853f7a71f15b0b35b61f1") +arr.push("4de6737a2ebf63d0ae9c159495f38e47be5fffb6741cded7b3bafeac44a1636c") +arr.push("37b52431f949dd97b8fe891bc76e6d6bb50372a3c4e90859c12563b0b162b699") +arr.push("45b054afaca61c94ff9b265b6bd7ddf8201737348e34c1b389e148ddd3aa752b") +arr.push("52835d147013ccb5ace06edd593efdd77191d5374be980aa2fac75527f9cd169") +arr.push("c26a639434f6f239051fa1073ae35c4e64078c33e62ee9769c5173959df61658") +arr.push("7511595bd28b6a68272061a7087bb81574d7711aefa9624051546f6202d78a1d") +arr.push("75da0207f44ed9e6faf9e3f46b612e6fe17fc2519985fcaaf08b9e5187d5aad9") +arr.push("6fba3e2c6509c298fe378c65127589af8cab0159e62669f338b14564d5c8556e") +arr.push("63d20c00023f905d350aea23a6515815b33b4ca6bc1231d1d6a215eea12f9889") +arr.push("518f9cac7013fdd9d50fca2843122e7d349ffa60b8ba2b01ddbf07b6965f0210") +arr.push("2c7017794d1fd37b7ec456fe6d8f52406d892dde0472ffc2378c57e9c3494917") +arr.push("f53ba320499a938d296e77defba1d9f05002460a25d62365344ffa825d1bbb2b") +arr.push("90813d93be3769073396aa1a07d71c8456d3b8a2309959a259c2216683ed8546") +arr.push("0a802da6a28172d70cbd8867842760209cb269a63d9d46197dfd05da31b6a4b0") +arr.push("7b05654498023af54b7a58e22ab692306fb47511bf0c92c693bf66e3810066d7") +arr.push("32e62296c390471b515b4aeaa4a5bd2b24899fcf9898bd665151d5f2c20b4333") +arr.push("6eed7e5484c2da9ffbaaa429993329db8348e5daee77150e5e584282f68e130c") +arr.push("b992970c817fa7de682fded8bcd4ef152f300e6dff3bea32293eb00eef9eddf3") +arr.push("584635f1ca9ce5559b85d6337948ca04022a0e43e551533ab91ee22c67ab8911") +arr.push("9693aa83c27986f6231c666020a7bfc55f046398d0c39b324d0b1c768af402f8") +arr.push("8943f61a5f1190cec9cd1070db1d1fb4048556b1f7798ad9661234084f635f67") +arr.push("a3d62e17a7e0fb9fec981eeeed80558b2b77a2a42eebccbf5e4e65874ed73ef9") +arr.push("5eaf056b313d8fd53c55bb7833e3e51085631c67130fbf42dc6712a9c7177f81") +arr.push("2b086ac53a59fed0925b96d75beca19457773e1767f78209bbf8fc916baa5f3f") +arr.push("896cb1dbe9708f242f9b7e80b8ad625bc6010dec21ac8993748add7638da4094") +arr.push("5d32c29442ea038bcf304f0f97167de0cd8867272c5cf69badd2e24fbf280902") +arr.push("5a61981e01349af45d5c31bc18dc442c2c05cb8052d3b1c0a16e6bdd14f2915e") +arr.push("a5906a806e73e3b75033a22314c68f794a3067a5c4af89a33be38d7d48ab0a72") +arr.push("ab1006275cce015ad18f66c07d3698c5e86ee7832ee3ea48d18339573c0f3148") +arr.push("1bf371526291d18fc71994a0919247bd460d4214e9eb1d8fd58c4a3de58b9461") +arr.push("06f47bb2fca1b65845e264256d41aeb8e05295826e4c1f68893d60f56f43aad4") +arr.push("5773abc6e24335fd1c6fdfe79c56dde9b4945e51a11d0932376ab655bb478678") +arr.push("08345013f822ac0c097f621c3a077971f4d3ceabf1d7b773b7ad9c8e1a90b898") +arr.push("1e036418ea1787d044a10dc96eec322a03774db3132669f2dc582772acd8dadb") +arr.push("901d0a1bae434f6a5635bdf8256f074dbd975237635b6d25916bb4c4be4bddd8") +arr.push("f3790912d1a50e404b823ac98ab67f9c71bf2f6697854314c46dcdb029c70b5f") +arr.push("13facaecfbce5dc9314306570c4f884361ef806aae02e417e01566c0a67a3b7d") +arr.push("ca7f89b4beb0817fab70bdc06902dd1c289c11288c1e6314ab780c3620ebaa64") +arr.push("19a07b9492a6c2f49882953f696051980b3568192beab8876d814c9f7651028e") +arr.push("0deeeeff0d0e87759a5e3d632861116c375de90b868ebd2bfbc839549776a4e7") +arr.push("c7e2114481ec2aa7204426ae587ff31f28eb137edb90e5867b6a3d27c2d16daf") +arr.push("6c25343fd3e98ebd04ea6c0c19a33dfca7ef3c70b72bc14c5134265171e57fbb") +arr.push("104f660fc9800c6fec70c7dae8bdcb32e843966cb4370370d208cbbfecdf4461") +arr.push("d968721beae9250d3d6cd92e2423f3585ea696ff9308fc52f665c2267870fc67") +arr.push("6a16129ea608a7df0399f85b8a368cd86ce31d4af6924879b639a2b27c33d5a4") +arr.push("94255d4389fb26ee58a77f0ae4944eac8144a6de048460575e2b99097ed036a6") +arr.push("228b940124f0e939e639edf2e4280515836b6a35ab9185202654a2d66e26ba70") +arr.push("493d4f2f39160932d74ab39bfd04e0a0faa6db07d9a34258ba6e9167e21bdfe6") +arr.push("2d07ed4bd39d5403764b8d6fae1a5b604d344b076533acbba21c21e687a4cabb") +arr.push("c736a7a605538f75816fb0f57379ff35375f070b3330224b9bca4e0ea1071ac0") +arr.push("499c476ad6d09d0f23678b7dcba5b09599b8169786b8fec1331e5442f448a74c") +arr.push("bef72d21f971e8eedb36a746c4ee2f9e4a412e19137b1fc0670429eae0b573ba") +arr.push("f01c5d55057fb819aa4e34f4af8ddb83be386a25fbfe88efdb4634933ed4f4b9") +arr.push("ee13b837093d12596e200d8135325fef8f9ab4c04541b3b47b94f2430cef0e7a") +arr.push("26433aa805322e04f8a6318f3f1500d217348b091404ca30b41ed9270eef7c34") +arr.push("6eec556adef1b1d512582f4d0757947e9edca2e7d09da48ab74ca5eb02d0808b") +arr.push("9f0e6c368b74dd6371521eb0b1b36116f18f49a9c7d1dd7c9875aa91b200c602") +arr.push("27eecabcdcb954f9f1a45e9fd5ccbd0a7adc11d72687c599f6689010b7c4be2a") +arr.push("b773384abc530c0aca1e95968b0a478592812e997de260e3935a06eb97b83233") +arr.push("a59a03ab418475383f35614d62ec90acc63252ea9c1d65becb3adaf625f97caf") +arr.push("2d88a971df33f55935564265613967cb82e118c627560a860634508040aa736f") +arr.push("95e2e642f570e2e99665c504ac7a6c45c17a947b3fb670ba9b0baad50c52287f") +arr.push("c69cd95f07ee3ad5c619c465c071c9ea8d45a91714c2b757d5c986e0da2bf6c0") +arr.push("bcd295adfe622f4cad4949ab51a69b6cff644bd709101ca352fc26d9d64810f9") +arr.push("0205bc455d9dd842ada33267ad8becee7d20d0572086e0d95a2c8c0aa5ecf43c") +arr.push("84a4ec26872ee95b2e6dbb314a6d8637fd0cb199a488d1684b71e531be38cbf5") +arr.push("002b02846af9c0c6b2a25a9c458795d9dd31182a44ca6a159653523b7435e2a3") +arr.push("2721dea3821857c77853ebdd8a3019891073c64061644a92e3ebca4c5fe60afe") +arr.push("eb29681fa3c6265d6532358807c991a209f71cc51851edb6cba5727edca81d74") +arr.push("402ddef1d799ca64b123903afa30f997e36dc0f3fa3c1c0a06bd31f68391ba27") +arr.push("cf05607480dea488b83af28be7ed690681b7e03565010244529a08ccbb3a4903") +arr.push("9ba5aa293ecbe535f4969ddb11ea6fc366988969ba728e29dedad3a3b6e955f2") +arr.push("8fd2dada46732507e23eb20db8652e0c8ef47ea83234593dc6f7d3e8b103cdf0") +arr.push("9564d872e47e6ebd2b78c4c4b30a48e448994bd862f21e51bf5bc50c26799ba7") +arr.push("32b9f520587dfa6d37049b1103b47019d80ee1c915c34c49dabb2e732556e871") +arr.push("e44d65ff5eab25853a126b9a27c466e8b27665e4ab2d61e3d9a6e6559d88b25c") +arr.push("94fe838d782e49b1a8291ba713f6f1838a62114aa018394796cb04aa85e164c7") +arr.push("bfad2caa6bef268a2909a359ca7aefad6ea3af41210af30c6c09e105124b9d2c") +arr.push("dccebe3ca7ee67ff804827ba2e263d75315d8e97512972ffb85046009ce6f3b3") +arr.push("2118955adc18f9396189f5c6890e914c20cf3e50cc8478182ab2dbe8e2ac4457") +arr.push("ba4f1774ada144bad555cdf2358188cb14ae3e3608bb48fca6433da52024de7e") +arr.push("c45891d33a757102e43dc3773eb37fb8fd6671f0006fe2735d212a7f478d1718") +arr.push("68dcdcf329543f9bcddb532725dbfe7a2fa031fca4a0efba31cd1315bdedbd7a") +arr.push("486d6a7892c62617b36b5439400ceaa10e349111f32c6ad9bebf6dd7f3da67e5") +arr.push("0fd1cb639d2882bb2e501626282a1ac16a5899d2ed1026d69f6f0887c4f888e3") +arr.push("dbf661ecced435f78d55fa6b0bcf8cc67c644929d0ef23d77ba8f455875c689b") +arr.push("9727720b6b8fcf5d78a3c08ba399323b0598d5af44474f367b286e0290590152") +arr.push("5d6bf54e92972af7b10828831bef9d8477a152e9615ee8dc2e3ed1993ac3f796") +arr.push("3b2010ee5fa8ffbbd4bfd970e971c34eb00f5b599e22b20c6e32a1541c0713e8") +arr.push("7f04697882d32fb5ff15b1ffacd4b77162a8aa2b69ab1bd09f35cc5344579d15") +arr.push("66213ff3835f8909bd295f97e8196bce51f923ddb31bd3f247feed292fb5a2c1") +arr.push("6e21d78fe4fe76d2cf44fd2caac52558e8624d74e92acb779232c489a72655b6") +arr.push("83c902bf1cf98cc9b81b85509911713f727554444fd13404edb54ab9fe6aa9ca") +arr.push("f9bbc357f388f6912009de9a7a8684e18bc7f6db9b61c4025d74d5ea4c4c84ce") +arr.push("be56ceb6443e8d14b2a0525a9fce16507a802dfec44eea2d275bb96bbe2ec729") +arr.push("a262e9c529c59dd96a2081204d503d85f0548cd238ddd9486d640a2fe40f4134") +arr.push("0a0daa6a022bd8383c0cb2aeaf465d0b5c4a4b48dc4706461dd350656ae18302") +arr.push("8c8c3062c7802986b7cb272427d13977211db2c08fee3563c9f8b85884e9bd08") +arr.push("ed5e2eec02146bd3f5478ac05522e980517424cf840eda0070e9f8369c82390b") +arr.push("541253db2bda38cad39d15807aaa4838d11ee568241c0d933c82cce36cc122e6") +arr.push("3f205fa0508f9b7d7ea09d0453d02a8d3901663798850d92a60f647828e220d5") +arr.push("0fad7a7cf0ac8fcabe0f1aabdacef0a47f2611123ea21c1c19d47e5c528a526e") +arr.push("59c13c3deddfa6a96f125f7f6b3fe00cfffc125b379650685994878c6fc35fe9") +arr.push("68e3ec3167d7a55b38733e869b808fe98dbcf9c06bfdffcf2089043dd4126965") +arr.push("4fba6c7f45330d42413c6584cfeb8afcd9c90ab91496a470e886699740bdf88c") +arr.push("5e553031a2f9950c4b023da59070516fb3c553dfd157c8b9524c4c276aa26e07") +arr.push("2a9b9966ccadd9529630fe85bc075a39f55300e835e5b2075e01ac1b658e8c3b") +arr.push("86fa80055c48bc950b26593d53d9ec472639f8848618e7afb2147856dca522aa") +arr.push("6370ffc3d01a9de9e8220cef4897d9a9b3c592a554bb1d0e148873ad9c381503") +arr.push("be28de3b5557f9eab6f650b2cc895c2f1349a507152cce3bf3412968b7582ef6") +arr.push("df5be1d3c4deade2707ecd94ef24e585a9d9addc0d4a17665112a61895f08e81") +arr.push("52bbc37f9c665313f5efc142802443439c6bb9e100ae2695c3f67595752146ae") +arr.push("09c063492608ec66a144c95ce0f40147b3146fc6600b1b36c6661bdca299cc6f") +arr.push("28bd7cdd8223fde1e3d9bbc6e680a42f31c677e52958fed819bce4caf03ff59f") +arr.push("22ae1c0dfa8fb51072c8286b4f0fc02e62fc9f9f288dd4e2af2dacdef000c34f") +arr.push("7ef3aa7a44c05fb087760160fdb6dd6296bdd18701ff89d927d67526e401170e") +arr.push("6482f6af13b7c214f13584d3be8204b9496a0765609a163d00804c4a87755ab9") +arr.push("1b502dac089764616df6644aafa826e43747804fff90dd484144705c7722d586") +arr.push("e558ac43c0c0d9a0255eaeceacaa9da5101c8060bc820fe5ce9078e23b89f925") +arr.push("967c09ca5fa20b37a408d1210d0cc572fdc9d5dac1a194a654400bb4dd771c74") +arr.push("fd16af0fffb8591afe1e0493d149f100912d00fcca0d07acafd68e4d10aa93e8") +arr.push("0253315bea8901887bca75f18be501978a2b841403d6aca10d93addcfdccd281") +arr.push("89fb6e66f2aa6d74c4b28ba6c7a7408815dbd4d57d3972fd7f223b7b3e5f2673") +arr.push("da6bba32fabb485aba8adb507db5951ae14fd5112835c1c2ad63c04b20636b6b") +arr.push("1e2e70c3ae3bacfa14ce61b986e9f8b04bae5b8faafe6506fb0c0aea9d836980") +arr.push("fb4e9a168e958b9ff17714e67bb2adabafc26c7db77e61f5e717e29794edcdaf") +arr.push("56b71ab3b5d90ed539ee48f8a66826b450a32aee7393440e186f6d6b69d75454") +arr.push("a167a5531ff7103fb943205f25343d8aaf03a90d2d19eb5f24765c0d065bc818") +arr.push("a8c20ba22c660ba8fb2d25f5c4feeef7d9acaf8af3202f58dae7d7963f3023c0") +arr.push("e686932003dea5c332e13f80a38aefecbce95dbda381c1205f892386f0e3ff8a") +arr.push("237722b95f886f10f99f31bbb587bed7d158b0cb461833d7a885ea001eead535") +arr.push("3baaaa84ede4623d5dff3fb51ba3632ca0d6569300c9bff8dffc8c74e935f5dd") +arr.push("11ca68ee8458f9e6a98c8231a7727990345341d00c98fb1e97382cdf2fca2c0b") +arr.push("ad5daa43d6d04531c197f78b320216abecf6cf6c1e3125d0d69d4fc9e74dab7a") +arr.push("74ab13d3627c215f166a92ac95dcd30eb32e72e1dd056c287faeb43fa535afb9") +arr.push("d6d5a431a78a6ae9e8c574db44d2f947d01dc6b4f258385bceb2861da34ced4a") +arr.push("f9b85b1828d90f01f6d963ce5b40edb0a59fa37019ecf7270437ec1b6b6fa397") +arr.push("fb1037e69305e993b5514f632464b104a3b97edd8c0afe551201d6e774b813ea") +arr.push("3d06824af27e46524fc54f88db5a8c369b8eba3710d73e169a27535888f442f4") +arr.push("bea5603faa3a0d7e531de2e33f63640979f4fe2a00c527a0163ca65d021c0390") +arr.push("b3dbb552a0009150698399d2c6537e01b5352810f28cc82ac152d76415b92c93") +arr.push("398d7333f4cb25630c1fa25f33d138e882d2c70a06cc69c9b9c93b4e633f71e1") +arr.push("49f6b9bdc7bbc8d4dc87c74520d25c1a902096fce52c6de931ceb62f2821bf2c") +arr.push("813ff14dfac0921877b8fe8822bdbb73059a384e292af7e06e366cd883cf5f4a") +arr.push("c4dc4ce783144b239409787ffe9b967918c554e97c412017f9b65e4a652c05d5") +arr.push("b290dad2fae16c711959ba807e8294cf3ba715ea4084d36cb9023b5e21d5892d") +arr.push("f7388a3d07a8514c1b550398f593d689fb3ad044c7009da59fb836dd7de16299") +arr.push("0269a96a401761fb535df084b253b7995368e402dc920db9b80586a43410ef80") +arr.push("6d81908430df04afcdb211536b9321148dff75a44153d7df67534583563be24d") +arr.push("601185524bb990d7e83a2cb64f0d52ca14990776e8d36c32e3ff43c97a731271") +arr.push("650eb9d92970e77ef1986554d3c4bd92dcaa7c88b9e2743a4dced0b823c4369f") +arr.push("961550579aca8cf43c3f966bf7e7aa670a75365701c1692c534e87b51569b5f1") +arr.push("91dcbf827e0243fbf59ccce0b18af1e54c7f7a9a52e0573199571f1ddb673970") +arr.push("5d0b91adeed2e8bbf2383f1a6bb59baae2349b80bc0037dcd68372770f0d56ab") +arr.push("a7d599923677f42eaff4e2a2689b5c99af00d5a8f8cca2f7ecf90bece1e3eeba") +arr.push("74bce076e27b1511204446443689ef292b407351fea4b72d9bfa4618e1e136a6") +arr.push("1fc7aa516a743e55c38a5fb6214d14d45cb8e143aefc573817480e66bf4659ce") +arr.push("bade54620265126f6a46820e2e71a4133b39cd5b4601ba9389de39f1f94ecd42") +arr.push("22a054c5c2eed1ab7bad63eced45997ab640ed7cc83fbb8b51b369dc390540aa") +arr.push("5a3a3478bc76a8034a4692ab92ee9217a906175392893a99f9746c7d9bdd7669") +arr.push("136ede6519da83fde2fe83b5095e56d7a0aa8a9afded2d99b06bca4966101e22") +arr.push("729c7f20427b32416dee45ec6a9f12dca5a2e341aa03099030d149ac0a7d2d5b") +arr.push("20dfbb244221b85c78815e906b4633c235a8fbd5f876fca6aabce7bb26683196") +arr.push("bbcb89b75bd3f6ce186bd43c3b860b925d851b5ee965a8776f2817f000dc2f98") +arr.push("dd48ef82968e81643c697e016ef1a0045a4b920a96b96fbe6516604db49dea02") +arr.push("d6268b142ec8166126efb2f4c1b861329e0f6321a6d2d71ee873de8d118d8426") +arr.push("6b4a2ebac38f8d3bdf1013d3569554273bc32b352745b28b7e86edfa5836ebcf") +arr.push("6d0ca85fa8b7f1a183cb9d1cfb7df336a36158abd2cd958f702d058394e31e97") +arr.push("2b7ffc3a2addc293e9e1eee9dc50862681d81e6a636531f0cace6b6a42ba62b8") +arr.push("4c048402440b5488577b63c95a639f00c0c70c47a2db7c8f13162de497cb7c10") +arr.push("f2e9db5368314bb3277e931acb08199213cd1b86013b53d104d80b65cd8f9867") +arr.push("0defa3b4edb67bd0b020de70cf12368721a7df46fcefd5b7efc775f93894a3f5") +arr.push("ff54c3f2222cadca16017420150cde5270d6c032bcc7c31a9ecc8a79168bd269") +arr.push("04bed29a23a329ee013d7bd910b5c622bdd9d5f7ab7193ccfbdeca0c4bcae03c") +arr.push("64bd26fd48edaacf770bf65677a003b0e3d78a45c82a8922b69a6419c561ea27") +arr.push("bd5134160a406840b3889829a81e48230e3e67d0eb9729d86d89d0969c652e29") +arr.push("9f77315a571fc0f018ea46e1e9c652285236b0e789bc60c6fb746aff83ec2d1c") +arr.push("5e4d750ec4b473f9f828e20f49d18948ea15053ff1d7d0df37e7fe7bde8da28c") +arr.push("6bee24209632ff8031edd6bcb63fea8159f29172fd04285cf7391c281fc09571") +arr.push("8cb671b4424bc8ffa152fef656816698e34761e4db0881466e76421a748af49d") +arr.push("f796d058520718b5f0dd5fa81d0c39a4a14f8a7c2305765fa8bfb218edf171a2") +arr.push("f556d5f21b6862555a566cbe3d15334c66309cc5134e0245f70802541a0afb60") +arr.push("fb065e7f52ec18f4a664b91abe600b96d52d190101cf469e71c3c326baf0ed0e") +arr.push("e9209993182ac3042b138636ac63797e47ccc6034a2ec42657cdffa9260d2b0e") +arr.push("1e94b0cd7010cbfa7257901c3a85eefcd714f0619171255f9724d918492447c3") +arr.push("bddc5204fc80a4d6cada4bcf7a95a3964daef56d0fbc8a8a25ca9c84073e83d6") +arr.push("bdca9a211dd3ad42556ed35ee99e3616fa7681c451c1cf6e580b77aeaa87ec3a") +arr.push("a4d4f7c5035c5149fa259c033e339e7b86f1baa52dbd0906b2a2de6f070cce33") +arr.push("3a318867dba6d44c7d89a6dbd82ab263e3965d753fecfad0cb71cda2f140423d") +arr.push("ab05ed848fdfd46d05614af027a7d1ad6c1ed09a41cdc270c2fefea409b6944c") +arr.push("8d1c8cdfb2173e9faecf16ee86a85f9b8a7de6565d1e88388c2af3381298f84f") +arr.push("3680bdb914e326ca595cf009b590b18519d07ec42ab0eb4b9f96790539c6bc64") +arr.push("9a7e089d9ee1433d959e6c4b1b702ed996e9f89254ff86ea16aa8a8ec8058b9d") +arr.push("6947e2a7a013a7ca1425d5251a783abf07124411eec05abaab62409d2dd8e665") +arr.push("0e3675ea3e773d4797f2defb71c22be3f0e645b592153e7a5cc82dd525445b42") +arr.push("4124427604a46b69cbe7a7b03d1d35744f2c6aa7989f0b2ad823dba15d0f74dc") +arr.push("4e504517ab04d32d95018921e77e7156f489706a60cc929f004af3fa196ddade") +arr.push("c292496bb5a461a566041e072af193b8936b7628f389e37f8746b4be57f877b0") +arr.push("dd05e2e49acfd82ece3122492785a77ce06870f7dee8395e39fbe909d369804a") +arr.push("20f122178900ef69be45072829b325f9131ef292d6f1c8a790854ff554a479a9") +arr.push("8d219f9b46108e74f4e52dcd15113100c112a03388726b27594274c3750e680c") +arr.push("d4eae7174b8c7d1f8de9b02d70d271eeb96fa55773f85a83e414ea1d13447748") +arr.push("27f10162e38f6960dabb26d3e2607ffb4ae4ec79bbc174aeaecd6d01e90a88f3") +arr.push("f7f3bf2a32dbf1fed3c3142c92f7d732beec3e5552b15f5c8095e07a45259290") +arr.push("29d760511a29c1415de36ced92d2b40333e1d9db144eda4099e646612218a995") +arr.push("6ae407c1ff466cc0e0ee3745372eb54f619bc50d80766b162161c09f22b3e02c") +arr.push("9f3928afa4c2297797c7cc80395aed6b32c84665594fd561357b100ef8cc28c3") +arr.push("ebfa0c991c0ba7dbe61ad41f9353d8b2f1d8b5d2089197e798fd5a74bc294ee4") +arr.push("018bb94f9289ae1612caca5ac2d0317469fce911ed10576f374268a2922af1d2") +arr.push("c715acff8b9e532c0bf0dd9b9a35f8b2b023294dc968d8cc49f69d2a21c4cc18") +arr.push("c5adb92de5f64166d9268e2db4e946b6811f6d091b3bba69c75d0eb76d00e96c") +arr.push("a4f9b52e07fc9633bca97b0efceee2bc5d091ff648896f35e138a675fcb1f742") +arr.push("f4b6aba9e42d395f7b135e5730debc1bf54904d4c2aeadc2b87bf65411382544") +arr.push("480538c3263817b9c932d549878fd1563d286d5a53e5a84fe6efdbdb0b10c076") +arr.push("588711d3cad098306d8c0ce769de37cbb84e4514fc89c8b98f3dd258aaa67544") +arr.push("e3e4b0709ada1f5322f4112996eb43ab1e92f96894a6f29002ad0e640557d8c5") +arr.push("fad8caa0564a6fa3844eccf89c6ad7cb4b8827b5d1db0ae60ac3dee8cd678972") +arr.push("629aff6deac90a9c85e57a0f5edd8f0662f26517be26ac674853a08c9c2a5884") +arr.push("2bc9a41164ba15fc8dbf07640a09230fcc81538223ec24cb542adab078f6ea60") +arr.push("d12c2672f5063b41129e9f4494079b36bca8f4cec654e37e398b37bf8151ac13") +arr.push("569ccc84538d30e1b207029e454a4961549dd96c009da6152bc426a5bb73e468") +arr.push("cf46a19a3490c2f085cda586d292e8fc3495214b6f53e18ffc4e850d05fca429") +arr.push("13c188817baf5a73d49473f767f379f55d7399ff751b90e7546ac8fbe99e1219") +arr.push("8c4cbcc29cb310e36703e91afa7a7415855ad3aac672e647fb673d5231d6917a") +arr.push("a2e6702636028e5030dd77b93d6aced686db3863e08bfd491ad1adab6724bff0") +arr.push("acd0a7f074ed6217667efa4ffff4b7ceb2a84bb20074c2013f96ba5577570e90") +arr.push("12b07fc9c47f01bed08c0046ea5cbcb6f86090678c284ad711f05716730adddf") +arr.push("0ea5d76f0e5edef37212b8dc5bcb00f56322e2a303e10b0941486ead2317cafa") +arr.push("0ba1c0617ff8ee1a3b9333842347fb420ba0b5d785b966d80b5d532724b2f454") +arr.push("8b64e9afe630797e2671f3e2e5fe8944c63415ed27c5c025a3649ea8c16f750b") +arr.push("b2e1cbd1de273cb6c17f244863769f5f6815979a632b48c398b1e7d46e3f31e2") +arr.push("483cc777c4ae98fee92ba1b7d4d4c2eb68c889ef0dcf515966abc4f707d99518") +arr.push("48969897a5955b7d02f5c2d094e0546477dc51746d26ca7ba175bc908452ac8d") +arr.push("83610082d579a540be8c503b381646cbd3b044aab415fe50d7e23a032396d77b") +arr.push("0d30fdeabf8b13c13bb759845801169bfaf6a4fd9e17fd079eebeaf0cf96a7b2") +arr.push("3d6bb102b0bcf12565f92731619e399b38b86d8393a1314d79f90bdc961a4d7e") +arr.push("3724d0528d161f37a2dc56aed90f82c4a4c9a4aa831fddf64e40ca71687647d3") +arr.push("1faf8eb83b859c3e6d53ec2a862ef049bf20a3102b917b50cf6ca13224511873") +arr.push("3bfe654a22198d670a58294766ff5f6723a10468ab801761e4f01d8a875298e6") +arr.push("b93c5a8d2a971de4933a581ce8ca2c21fece8d7a33baecedf94a1c9dd1c4a19c") +arr.push("bf09f6e30cb56962b6b113b26d8e88c3090c1be69eaf3ab238b3189ddedac738") +arr.push("77a00b73e9cae1878e51b960fd457766f82d866c00a4925ee3d237033abbd1aa") +arr.push("6aafce6d1a2b1ace6e56db4b4495758a70697198e6ec8e996aa3dc7646d66d1f") +arr.push("80759fd8455eb0f2c2c7339eb41eec6bbb37845770e34a1bb5feed767c277576") +arr.push("6a933b416b554e613d8c0f0946bc9d50aecb6c42f3e5258a29941bf98b23d7e0") +arr.push("7cbb0cbdcb37a37956b135350caca2f7c4c10774653e1bed8d7dae93a143b107") +arr.push("349f541ad07317582e0664e0efd52045a98cfdd6e130f13ce9c99d2e1e33c2c1") +arr.push("a7d76591ac60604a23c20381e1ba35fa6ad8dc18f09faa9469911a7105ae5f98") +arr.push("240d7e03cde9f10a5967ea8803dc6792e140c67318ddc06990ba404e4fb5454b") +arr.push("7c039890d57e250b69c575b7a01286041d3aa4bb8c60989f4554b5f803a7ead5") +arr.push("316ec8e3c2f5626ea87889e432b24c29d6fe67f40de27fece7eaab8a3051db45") +arr.push("5366f831d90a61e87dcfad997c8bec8b52ed7cf80ec4901ca9dde09ee5669b47") +arr.push("b2f758bcac4eee0905d284f4c5abd662a3b13d7542c367a8650c571fe213299f") +arr.push("3cc2a7b4aee73f4945187854f6ce06d54b4743a6e28e5ebb25dbbbc888fdbe9f") +arr.push("8fea4114d3beec1b18f5d27fca48fb52e9316aded9c0a9858354071fa34071c3") +arr.push("33c168aee913d6397514c113f002c51a6a13f6c6eb86a92e1120edabc82ece6f") +arr.push("21fbc6b170bf77f015af5d2fd274f318bc5098f1b5c701b26a91e496971c330f") +arr.push("3433d2d8a2b6c35bdfb21914b76b617d6b0c454c2a376ef60d669308d6164fab") +arr.push("f8fc2f0b3f714ca6b9dca88eedc975e1dc55776c6276a63624df5db7068b1a92") +arr.push("6d54b33c3e53adc444356a7e8dc89b7cc049a1cae74d276f77a073e668989ee2") +arr.push("ed01701b99547c7077842c746d6967adfac6ce86adde3db5c58d56d0bc439279") +arr.push("c81f39737977500d0f44f1a06dd68cdf88a1ffdc0b290ffafe0f86cbbaabb0fe") +arr.push("15b66284b63cafdb408da3a841315295ed104f988f2e56ced4a1e9ed676f42f0") +arr.push("4a7946fde0b31b5b99c7fc7791492c963804e01673743097fedad1e31c783ad4") +arr.push("b9525c78323564a8aa4eb84fd93a08426277f6e882cc849eb7facef523547b14") +arr.push("4298e64c5cd9268dc3169d599431082671802c1a488ac7cbba5b469090673f5f") +arr.push("ec196760a013eff9dc2173859a6de46b17ab0139b7627ea11ff99065d79bd4bb") +arr.push("fe586e804032fae12c5bfac3d447f64e005844687c9b191ef9fbe6893c4dfacf") +arr.push("04c712c43d8961eef7cb48288a83b47c9634ca4cdf74d18782159504610806e6") +arr.push("b37dc91aa739ea7267d2cdd3628df4a73583ae8442b5a82dd6aa53eeb188ad46") +arr.push("60c805e2631cf76fa7feb9d99bba0236ca63770308ac23332863a73abfd99c68") +arr.push("5e2d0ed63d248399670d3af6587ab5c18309ba44dfdf4e8b8be74381de667be4") +arr.push("41860a1758b097885c6d0c42fac39ad109ab18cae8c73ed32467e65866428cec") +arr.push("e3ee4cf72e6cb3d2a594bc9fbbf08ef9e47f38d3547f9b65606633e96ff1c3c2") +arr.push("fd3d820e830568e6b911b723d21ffbdccb19ded25227c0b28f4e793c0b434361") +arr.push("09e68ba7d8e7dade7ff2c11d8f0a196d31a08d4edf71b62c5a52112b2c19026b") +arr.push("212fc45f558603ca5e9ad38ae7d4e3151c81216cf15cbd29ece588b0ac163cd8") +arr.push("cc32069e12d7eb45877e4c7f5275a9df3e67009624dbc1b810d309c0cfa9b3ef") +arr.push("75a6138a66f57d5902828ced9c56622009619baa9492b19191c9040c36a2c3b2") +arr.push("5378c097f524ded98164ea2471f96592630fe3ec3683d2c931b720dc2bc508f7") +arr.push("08f1143f7860c22cac5e1ec933148093816bfc7746f67e7d78fca4dd0eb20521") +arr.push("423298f1aac58c9791c9a34b27bb37f82635ea4b684709843c1b01cc016c3f9e") +arr.push("2359a96403c3eaf34993a21299db01072f83fb050f4ed79b4d2f308efac645e8") +arr.push("2f75480e37d4dfbb0a750985a505d2e43fe58805dc4899e62ceda623edeae6da") +arr.push("9d765c54cc68e8d0f63b877c7e8c4ffd13f5e181629fee7277c1c5c842345a76") +arr.push("6b2e3fa1396ec0167a7468f8b692f3b5e81f7b601bcf53fd4f4a54849b28ace6") +arr.push("8b7d9e50a73b492e3f59555f2a45068c0eb6966b1438f374d3434628c9c5d2e1") +arr.push("89862a4525c18cda166f2eb8a5b56cc7dd467fc9541d498c1f09f6ef39d1cdea") +arr.push("652a5d97be6fc3900c4862c6a69c1f2e05fc6c24707df01754f2d29c4ef88454") +arr.push("56e838255b2bf6a3288812d92e922434b47d029d9df77003d0f1b358cef10156") +arr.push("a872de5e5965f663770fd3d74c0c30c1791a0e41c24a3e792543faf07a748359") +arr.push("75a47c22ed6b37dc3e74cb278acd3cdcda18f24438adf2b50afcf7156201a094") +arr.push("7c59240622a06609bfe5d7d6dad633e003d33914bf7d9556ee492bfe7b06495a") +arr.push("37d263d4974b76478a1c7dd9ca61a125cf1cb401b544dfec9d8656ca984c5cc0") +arr.push("7c940f575044deee1ca431b30a44f6f6792e155d1f1c7f155b3261cfbeff8722") +arr.push("4a633a3c4bf74300b1afe36706bee179910738c7462c50f77f457cc5c702f4db") +arr.push("264e2a84b093c8bcb90c8cca4e0df4add2287ece13079e991ff733fb73860700") +arr.push("d0afe2c4919b0d72db4551a5579c49e20aad0dac32ab9c22919c05f2d1c1e486") +arr.push("e9d01458f0f0e75d11c8f7124ea1d2bb2770bc6c863d7d6153c07f709ae9066b") +arr.push("4e5d0a511a0058f99076108fae0c96cd52a5366f26c9fcb184380d78c37e64ff") +arr.push("8193b36e8c846a05a3a4754037cb13ac3bdb7a6f2fc4683ebd68a64f98c95afc") +arr.push("5bf1103e2d0a5d4bacd7aa8e5066d6ee2c1fe30a7f5b24c42c0705a6e41cf9f4") +arr.push("7901710026b1aad2441f8d7772f5219e173376f921a59c0ba0cd229f94cda23e") +arr.push("6e936fe10afdce50d4cd73d442b0a830ae48b290d8506b80465370e5022fafef") +arr.push("6242d966bf72cee85ca2bd7b1d94bb02b37394ad2a7acc918d3d950764ef5d27") +arr.push("aad3fa72d5b0e5e3917b9e83541c55a3602d60e3693a55fbb6b061cbd947f571") +arr.push("ff2f47f33ac2a9120847f03aaf7339d476479c71eca1b9769aa2e3f0458d531c") +arr.push("fd369c7e140a6d3be5fa2ca8d5c7bddb3b50ce138b952eb716d481a51bfba2ef") +arr.push("353d570647dc333a598c29b1d5df60cd40ef30e57224db19ea5b680305f3aa42") +arr.push("d6c4d8b640779a3584867930b589acce9c7429265004a675629742cfbd59ffbc") +arr.push("98326836c02e2abb359d0f5b9dccde64f6afc684ee4a83fe85d4c87fdfc7072f") +arr.push("29a7a81b4d6dfea547f822365705e9b6146356381229a985862ad550f8f03af3") +arr.push("e4f5058bda16bec5bc4a56c77131e26a4472fddbb09fcc8a4355fb73e5203f75") +arr.push("7f70972b68a2d26bc220cf3c34353dd59904229ffe0569f7beb1374ed35f4ad9") +arr.push("17ae71cef122dd41dc9f4809a1a025f9196346f3ad70ae320b75abeff303ecf7") +arr.push("1c5b1b1419d759d4f5f1cc7de646073741fca20df82f3dadbe607df1269b7b0c") +arr.push("321387af8dc397da4181b1515bb0f90908284fdb87b1831eeb31d8dfb3c081b3") +arr.push("f8a80dec7909ed22d3beb6b2f206c6e70ef5a7788f519a031ebaf3e486c2889e") +arr.push("90fd744ec70a8ea512799df910ed044639ce2689490641018353dc3c5cbfc7e5") +arr.push("937f780b045ad017ec7762e663f151dd48ea36d9d5fffe34988631ff2ca4b139") +arr.push("48f8cc8327c039466c8df706cc82cbc75725df14e24d9519400db7d83f6b59f1") +arr.push("8d55d4e1720e6adc39d55e8c90e7f54435617172fa76527dcff0059328c5eeb3") +arr.push("a013ba57fd1b4ec129767deb723ac84cb9f7bf20ec6a9ab32331c750e9c10c00") +arr.push("3a092adb81a3cbb2690ebb011a0b0521510a4bcfcc67abfefa8b567c7c30d6e1") +arr.push("a4d80b05a7212b2c8bb4b29d48a5a3a0db81923dce4fcaae9d6c68d587b701bf") +arr.push("acb9abaf420154dd00694d99458d0c7b957966181e83f0bbebc7ccc37f26c2ea") +arr.push("4d03e204cc7da4408818224955195537c45380437e9df25408a60b38cec21c19") +arr.push("007087c056390ae3f08f3659848493e6d6c5d907ca003be71159529b70b5e6f1") +arr.push("f913f602179163f03359f0491b0e419f1dc3804ddcf840ce147577795fb98a6d") +arr.push("6d5894497ab47bb60df8e6436469bb714821d94e9f812ec933dee25e5a97954d") +arr.push("af92627d94d9f97f555570d1364b60f769d542e08667687670f37a42a3c8452b") +arr.push("739c721b0704c4a6e4283a261c4e5f130562222f7530f97989d6c3ee4d64ee38") +arr.push("7cca7a4c098b9f0f4a3bc0a9c234fde1e4dd06362b29a60383cb6fd84f7077a4") +arr.push("a22cf87cea0a97d8c8935183b203a35ea1c236ac49853e3ac459030099485525") +arr.push("1f470f6131998a6f23ea27731459f2c8029456d4489468e13695c7490e6981f1") +arr.push("6a7c8a7e90871492932a4006978d5c939f6874d966931771f0b13eec6d4ecb78") +arr.push("79c301ffd2f8654577ab9721b1d7e08649e2bfafac2859afd93792d4f548e5b2") +arr.push("b9f448a1b544d4a61b4bad431342d40e3d3fcfbb32c20cbf09b43b36a10a42c2") +arr.push("79f2ecc9a6e08bcbcafb862b136277f133e5e0b0e2afd28c47a81aee1a4e9c72") +arr.push("22e888b244bf35ba25448b4e06b821f3c44af90fac8b26b69bdaf94b8e4e8ab2") +arr.push("d53a944d5a22917815ec755f4efa338ddc59a3fab415fe878896a1f4f50a0f4f") +arr.push("8b30a33b0c03fe8c22e346c03ad126689fa16bd096ef49e2d629f932765d4008") +arr.push("e0e2755fcc1a0ca356072f83772123a64391633cf24eb33835b077711a94ec49") +arr.push("258c88aa944f24257e49e5eb481a03dab1412b3558cc9c677c461eea496a06a1") +arr.push("661499fe7b7ce743f92f1f55627e220452d13134c48d60f333dcbee7c7a52d54") +arr.push("42fd04b225fe0a95235e8e53d2879eb09bc155d319086a62cfa2aeef7b60570b") +arr.push("c20b87ba9b11a8aef89053487b002e0acb4346574729b77844f84bb6ce948ba8") +arr.push("6173593fe2a8fabb620df9f4f9fceedceb278ef1270169e5c9744af82e954c66") +arr.push("b713512831c841b781f1f2a66607ed8251fc5eb57918c3c5570b026525b8fa5b") +arr.push("bbdfd086a859db48dcc0a291700ca7f1eff19d3488b31d79a74bdbe9e2f589d0") +arr.push("4c62bf464f94e64b40d658adb2feb6729ee65a6ff591499f7311f27ab40b9066") +arr.push("6f44d7523a8d86b3cfb8ebea6a0fa023f27963f02aad0f1418c2d6b106db8aeb") +arr.push("3dbf045d8f8679776dcb46580f5457a4a120facbdc0bb1271a11e3f1b412a131") +arr.push("5eeb5db9215f1ad71ba73ff5c951d4f6f05685262574002e1209c3c804b94bdf") +arr.push("40fb2e493f66068643ee5fc4a0c636f62bf1a6e75a481efd8d571e5efa5adf80") +arr.push("6974ad9b7402d55469910b2d97c255bb39921fe9c12f6e49d7552efc44e29e5a") +arr.push("7b5378a27119bb0b08d1a30e052eaca20bf69bffca3c1c47b506f4a11952967d") +arr.push("7703dd4ebae87729f6b523e0c9c85706657efa4f139d13fb015127a4d5185c69") +arr.push("d419debd14fb4ae781184d9e1c5dbeb541f939e6422ecd36c333992b59223633") +arr.push("aa369be2af39767eff429f04147714956dfa093140adaa3c33a68d5a623d9a00") +arr.push("5662247a14f66235e49adbda890244611cf900b038526d6b7ec871f41bc0beea") +arr.push("900a2d60ee9ef46fce8b03f1f51ec01d0a17729188f7ea61a088f7b583f06e3e") +arr.push("713f2647e8491db9889ef4d46a2ef9fab65dfd8dc3cd657aa8a7a49ba3e85b72") +arr.push("26d28e0f1e4a4b49bd11245f0f5adbaffeeb30d3063bd13071a27ee32a9d4796") +arr.push("6dea7f344066051a52b4e18b7e19f9bcc01d97580093fe6763ab423e83d863bf") +arr.push("9520b7231e78ea0ac5d01853cfce0cba1f2cdc72dba162c26fb7f98dfaa55e46") +arr.push("ece7af531076a148d56fd866076bd28e88e22f5365efe9194ca85ff3f7a8fd5c") +arr.push("e3724202b2f69ccdbe165899e265435cd03f52d4bad128be5300913382c92727") +arr.push("a03e968f7a15552abcfc4e2e8eee99ae1332b30cdca8b017f54c95545b385b7e") +arr.push("6dc6c20dc260e3b0836320a3a9d8e82765b866827edec26d3aff0e350564f3b6") +arr.push("92276ab6f83f478aec708e26881b9c5a21148808283c01d917302a58704b62d9") +arr.push("700ef918cda12a1b004e9aa767098e8742cc5d85b4b73eee5aa6d10764be6e74") +arr.push("cfc12f2df39a92952ed95b9517c163efaf07248ecb9f8e1a0479d75bb05ec5c1") +arr.push("72607aeb77fc11f02689deb23e6838e1bf26465b079c9a55cc15d7aae767e4ec") +arr.push("a6c444ffa63c77aa8d65fc7adf6279c9ba8f86d3c24ab9c5c8b3d5cd35531fd0") +arr.push("f9c3827e2a7147b002a92ee28d3bfddb66e318765c4a801891c0d94dcf5af86d") +arr.push("db618e181974b9443a85de6eee0c4419e1f61886de3d8973d83f79f30381948b") +arr.push("cc57abc5a09fa802b7104a02e739b217d875efd1703cfb5d2542ff862266446b") +arr.push("ab0c3ed59442d3c80b8a4f291c07882813aa77c4e548f2cf498ede5924e6a603") +arr.push("55aa859d6bdacb961468d3218e674d9496919c51f0ffeecaebfda69295e079b4") +arr.push("34e68a1e0796c8fdba2c6153ee639253d07dec02a97953b1520cd67fe018f6db") +arr.push("d667a58211b2d2e549cf09014afaafbdfc3d15de8871bf7013895ebedb2d66a0") +arr.push("87dd6283bd65da2c33aef984ce10c3ab8ba390dbb0352da7b53c401f13e629f3") +arr.push("fb50480cf794d5970a1c5916063257fc10cd79f1f970f0f0ef2b3b016d054d87") +arr.push("f7812fd62adf3a10a4a7613f4075572df60449682064df1be201bda06d9d538f") +arr.push("2c14757d67a50321dc29c4b4fd52876f30f730ca5680cc7367b418dcf200acb2") +arr.push("055f7977eca64a80be15d6b3508441b7262651d8e8c3a5c3668df9ccbbf56352") +arr.push("f875439b7535ae575884223f99e56a7d40fd6be593615df57f6d31c9d48b5805") +arr.push("1b802cb0fa46b67049c011e43a46152b605be787d768baaef908733939198ce3") +arr.push("bc41e5106ecf357cfd635de6ac145e65f549057b7b83296b4c5fa208d5758c81") +arr.push("55c5e4bd4f3f59340f67b869003d177c5b3bcf07d92cff114ba686a39c9576fa") +arr.push("a251448c043f8914ce94b54e12a93de0e1204dbc36c9a27abab79740aae85b52") +arr.push("4be0a9f5727e28cd026a25cfd632beb9d6988b524bbf3f8872a6963b15f9e5ba") +arr.push("69891033b55ef1b4b7c14f2b55ce47f375d5e706cc3cd8a2cc021d3981c6f81a") +arr.push("6aae0791ecec7202ce12946037ed08080daf9c54e2c6d3651676803e60c59bd1") +arr.push("6bf500b4ce36c468a7b8c376ff3d6c805953a5d082c656811ca24b19b6064657") +arr.push("02607a5018a61ea166361e8c6e1fbd910a6e4c4e8ad73c7f1483e695672c7961") +arr.push("5c1bbbeaa212f0083928fccb868e540a25fc150d32fc47d5b6d1eb1669794820") +arr.push("614a0b4a62c13d561ecf3b64c20b160b3baa9c88fdc720b9c00ac15d594ad644") +arr.push("d90b20542d4d87d96a893c45087c9a74ae96a69f2a3a8414ece1232466e57d58") +let sum = 0 +for (i = 0; i < arr.length; i++) { + sum += arr[i].length +} +if (sum != 131072) { + throw 'Fail' +} +(() => { print(sum)})() diff --git a/tests/runtime/common/bitwiseop/CMakeLists.txt b/tests/runtime/common/bitwiseop/CMakeLists.txt new file mode 100644 index 000000000..1d7561757 --- /dev/null +++ b/tests/runtime/common/bitwiseop/CMakeLists.txt @@ -0,0 +1,23 @@ +# Huawei Technologies Co.,Ltd. + +set(BITWISEOP_OUTPUT ${CMAKE_CURRENT_BINARY_DIR}/bitwiseop.txt) +set(BITWISEOP_BIN ${CMAKE_CURRENT_BINARY_DIR}/bitwiseop.abc) +set(BITWISEOP_PA ${CMAKE_CURRENT_BINARY_DIR}/bitwiseop.pa) +set(BITWISEOP_JS ${CMAKE_CURRENT_SOURCE_DIR}/bitwiseop.js) +set(BITWISEOP_VERIFY ${CMAKE_CURRENT_SOURCE_DIR}/verify.sh) + +set(RUNTIME_ARGUMENTS --boot-panda-files=${PANDA_BINARY_ROOT}/pandastdlib/arkstdlib.abc --load-runtimes=\"ecmascript\" --compiler-enable-jit=false --gc-type=stw --run-gc-in-place ${BITWISEOP_BIN} _GLOBAL::func_main_0) + +add_custom_command( + OUTPUT ${BITWISEOP_OUTPUT} + COMMENT "running javascript bitwiseop testcase" + COMMAND ${PANDA_RUN_PREFIX} $ ${BITWISEOP_JS} --dump-assembly --output ${BITWISEOP_BIN} > ${BITWISEOP_PA} + COMMAND rm -f ${BITWISEOP_OUTPUT} + COMMAND ${PANDA_RUN_PREFIX} $ ${RUNTIME_ARGUMENTS} > ${BITWISEOP_OUTPUT} + COMMAND bash ${BITWISEOP_VERIFY} ${BITWISEOP_OUTPUT} +) +add_custom_target(bitwiseop + DEPENDS ${BITWISEOP_OUTPUT} ${BITWISEOP_VERIFY} +) +add_dependencies(bitwiseop es2panda ark) +add_dependencies(ecmascript_common_tests bitwiseop) diff --git a/tests/runtime/common/bitwiseop/bitwiseop.js b/tests/runtime/common/bitwiseop/bitwiseop.js new file mode 100644 index 000000000..e73431cbc --- /dev/null +++ b/tests/runtime/common/bitwiseop/bitwiseop.js @@ -0,0 +1,7 @@ +print(13 & 3) +print(13 | 3) +print(13 ^ 3) +print(13 << 3) +print(13 >> 3) +print(13 >>> 3) +print(~13) \ No newline at end of file diff --git a/tests/runtime/common/bitwiseop/verify.sh b/tests/runtime/common/bitwiseop/verify.sh new file mode 100755 index 000000000..b6016edc6 --- /dev/null +++ b/tests/runtime/common/bitwiseop/verify.sh @@ -0,0 +1,21 @@ +#!/bin/bash +# Copyright (c) Huawei Technologies Co., Ltd. 2020-2021. All rights reserved. + +set -eo pipefail + +expected="1 +15 +14 +104 +1 +1 +-14" +actual=$(cat "$1") +if [[ "$actual" == "$expected" ]];then + exit 0; +else + echo -e "expected:"$expected + echo -e "actual:"$actual + echo -e "\033[31mbitwiseop test failed\033[0m" + exit 1; +fi diff --git a/tests/runtime/common/builtins_test.cpp b/tests/runtime/common/builtins_test.cpp new file mode 100644 index 000000000..e4710324b --- /dev/null +++ b/tests/runtime/common/builtins_test.cpp @@ -0,0 +1,119 @@ +/* + * Copyright (c) 2021-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 "gtest/gtest.h" +#include "test_helper.h" + +#include "plugins/ecmascript/runtime/builtins.h" +#include "plugins/ecmascript/runtime/ecma_vm.h" +#include "plugins/ecmascript/runtime/global_env.h" +#include "plugins/ecmascript/runtime/js_thread.h" +#include "include/runtime.h" +#include "plugins/ecmascript/runtime/js_handle.h" +#include "plugins/ecmascript/runtime/js_object.h" +#include "plugins/ecmascript/runtime/base/builtins_base.h" + +using namespace panda::coretypes; +using namespace panda::ecmascript; +using namespace panda::ecmascript::base; + +namespace panda::test { +class BuiltinsTest : public testing::Test { +public: + void SetUp() override + { + TestHelper::CreateEcmaVMWithScope(instance, thread, scope); + } + + void TearDown() override + { + TestHelper::DestroyEcmaVMWithScope(instance, scope); + } + + PandaVM *instance {nullptr}; + EcmaHandleScope *scope {nullptr}; + JSThread *thread; +}; + +TEST_F(BuiltinsTest, ObjectInit) +{ + ASSERT_NE(thread, nullptr); + auto ecma_vm = thread->GetEcmaVM(); + JSHandle env = ecma_vm->GetGlobalEnv(); + + JSHandle object_function(env->GetObjectFunction()); + ASSERT_NE(*object_function, nullptr); +} + +TEST_F(BuiltinsTest, FunctionInit) +{ + ASSERT_NE(thread, nullptr); + auto ecma_vm = thread->GetEcmaVM(); + JSHandle env = ecma_vm->GetGlobalEnv(); + + JSHandle function_function(env->GetFunctionFunction()); + ASSERT_NE(*function_function, nullptr); +} + +TEST_F(BuiltinsTest, NumberInit) +{ + ASSERT_NE(thread, nullptr); + auto ecma_vm = thread->GetEcmaVM(); + JSHandle env = ecma_vm->GetGlobalEnv(); + + JSHandle number_function(env->GetNumberFunction()); + ASSERT_NE(*number_function, nullptr); +} + +TEST_F(BuiltinsTest, SetInit) +{ + ASSERT_NE(thread, nullptr); + auto ecma_vm = thread->GetEcmaVM(); + JSHandle env = ecma_vm->GetGlobalEnv(); + + JSHandle set_function(env->GetBuiltinsSetFunction()); + ASSERT_NE(*set_function, nullptr); +} + +TEST_F(BuiltinsTest, MapInit) +{ + ASSERT_NE(thread, nullptr); + auto ecma_vm = thread->GetEcmaVM(); + JSHandle env = ecma_vm->GetGlobalEnv(); + + JSHandle map_function(env->GetBuiltinsMapFunction()); + ASSERT_NE(*map_function, nullptr); +} + +TEST_F(BuiltinsTest, StrictModeForbiddenAccess) +{ + ASSERT_NE(thread, nullptr); + auto ecma_vm = thread->GetEcmaVM(); + JSHandle env = ecma_vm->GetGlobalEnv(); + ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); + + JSHandle function = factory->NewJSFunction(env, static_cast(nullptr)); + + JSHandle caller_key(factory->NewFromString("caller")); + JSHandle arguments_key(factory->NewFromString("arguments")); + + JSObject::GetProperty(thread, JSHandle(function), caller_key); + ASSERT_EQ(thread->HasPendingException(), true); + + JSObject::GetProperty(thread, JSHandle(function), arguments_key); + ASSERT_EQ(thread->HasPendingException(), true); +} + +} // namespace panda::test diff --git a/tests/runtime/common/class/CMakeLists.txt b/tests/runtime/common/class/CMakeLists.txt new file mode 100644 index 000000000..7a581f732 --- /dev/null +++ b/tests/runtime/common/class/CMakeLists.txt @@ -0,0 +1,23 @@ +# Huawei Technologies Co.,Ltd. +# Huawei Technologies Co.,Ltd. + +set(CLASS_OUTPUT ${CMAKE_CURRENT_BINARY_DIR}/class.txt) +set(CLASS_BIN ${CMAKE_CURRENT_BINARY_DIR}/class.abc) +set(CLASS_JS ${CMAKE_CURRENT_SOURCE_DIR}/class.js) +set(CLASS_VERIFY ${CMAKE_CURRENT_SOURCE_DIR}/verify.sh) + +set(RUNTIME_ARGUMENTS --boot-panda-files=${PANDA_BINARY_ROOT}/pandastdlib/arkstdlib.abc --load-runtimes=\"ecmascript\" --compiler-enable-jit=false --gc-type=stw --run-gc-in-place ${CLASS_BIN} _GLOBAL::func_main_0) + +add_custom_command( + OUTPUT ${CLASS_OUTPUT} + COMMENT "running javascript class testcase" + COMMAND ${PANDA_RUN_PREFIX} $ ${CLASS_JS} --output ${CLASS_BIN} + COMMAND rm -f ${CLASS_OUTPUT} + COMMAND ${PANDA_RUN_PREFIX} $ ${RUNTIME_ARGUMENTS} 2>&1 > ${CLASS_OUTPUT} + COMMAND bash ${CLASS_VERIFY} ${CLASS_OUTPUT} +) +add_custom_target(class + DEPENDS ${CLASS_OUTPUT} ${CLASS_VERIFY} +) +add_dependencies(class ark_asm ark) +add_dependencies(ecmascript_common_tests class) diff --git a/tests/runtime/common/class/class.js b/tests/runtime/common/class/class.js new file mode 100644 index 000000000..a3d774fb9 --- /dev/null +++ b/tests/runtime/common/class/class.js @@ -0,0 +1,28 @@ +class Parent { + constructor(x) { + this.x = x; + } + + static toString() { + return 'parent'; + } +} + +class Child extends Parent { + constructor(x, y) { + super(x); + this.y = y; + } + + value() { + return this.x * this.y; + } + + static toString() { + return super.toString() + ' child'; + } +} + +var c = new Child(2, 3); +print(c.value()); +print(Child.toString()); \ No newline at end of file diff --git a/tests/runtime/common/class/verify.sh b/tests/runtime/common/class/verify.sh new file mode 100755 index 000000000..deae53732 --- /dev/null +++ b/tests/runtime/common/class/verify.sh @@ -0,0 +1,16 @@ +#!/bin/bash +# Copyright (c) Huawei Technologies Co., Ltd. 2020-2021. All rights reserved. + +set -eo pipefail + +expected="6 +parent child" +actual=$(cat "$1") +if [[ "$actual" == "$expected" ]];then + exit 0; +else + echo -e "expected:"$expected + echo -e "actual:"$actual + echo -e "\033[31mhelloworld test failed\033[0m" + exit 1; +fi diff --git a/tests/runtime/common/concurrent_marking_test.cpp b/tests/runtime/common/concurrent_marking_test.cpp new file mode 100644 index 000000000..5baafc23b --- /dev/null +++ b/tests/runtime/common/concurrent_marking_test.cpp @@ -0,0 +1,112 @@ +/* + * Copyright (c) 2021-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 "plugins/ecmascript/tests/runtime/common/test_helper.h" + +#include "plugins/ecmascript/runtime/ecma_vm.h" +#include "plugins/ecmascript/runtime/global_env.h" +#include "plugins/ecmascript/runtime/js_handle.h" +#include "plugins/ecmascript/runtime/mem/clock_scope.h" +#include "plugins/ecmascript/runtime/mem/verification.h" + +using namespace panda::ecmascript; + +namespace panda::test { +class ConcurrentMarkingTest : public testing::Test { +public: + static void SetUpTestCase() + { + GTEST_LOG_(INFO) << "SetUpTestCase"; + } + + static void TearDownTestCase() + { + GTEST_LOG_(INFO) << "TearDownCase"; + } + + void SetUp() override + { + RuntimeOptions options; + options.SetShouldLoadBootPandaFiles(false); + options.SetShouldInitializeIntrinsics(false); + options.SetBootClassSpaces({"ecmascript"}); + options.SetRuntimeType("ecmascript"); + static EcmaLanguageContext lcEcma; + [[maybe_unused]] bool success = Runtime::Create(options, {&lcEcma}); + ASSERT_TRUE(success) << "Cannot create Runtime"; + instance = Runtime::GetCurrent()->GetPandaVM(); + ASSERT_TRUE(instance != nullptr) << "Cannot create EcmaVM"; + thread = EcmaVM::Cast(instance)->GetJSThread(); + scope = new EcmaHandleScope(thread); + EcmaVM::Cast(instance)->SetEnableForceGC(false); + auto heap = const_cast(thread->GetEcmaVM()->GetHeap()); + heap->SetConcurrentMarkingEnable(true); + } + + void TearDown() override + { + TestHelper::DestroyEcmaVMWithScope(instance, scope); + } + + JSHandle CreateTaggedArray(uint32_t length, JSTaggedValue initVal, MemSpaceType spaceType) + { + ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); + return factory->NewTaggedArray(length, initVal, spaceType); + } + + PandaVM *instance {nullptr}; + ecmascript::EcmaHandleScope *scope {nullptr}; + JSThread *thread {nullptr}; +}; + +TEST_F(ConcurrentMarkingTest, PerformanceWithConcurrentMarking) +{ + uint32_t rootLength = 1024; + JSHandle rootArray = + CreateTaggedArray(rootLength, JSTaggedValue::Undefined(), MemSpaceType::OLD_SPACE); + for (uint32_t i = 0; i < rootLength; i++) { + uint32_t subArrayLength = 1024; + auto array = CreateTaggedArray(subArrayLength, JSTaggedValue::Undefined(), MemSpaceType::OLD_SPACE); + rootArray->Set(thread, i, array); + } + auto heap = const_cast(thread->GetEcmaVM()->GetHeap()); + heap->TriggerConcurrentMarking(); // concurrent mark + for (uint32_t i = 0; i < rootLength; i++) { + uint32_t subArrayLength = 1024; + auto array = CreateTaggedArray(subArrayLength, JSTaggedValue::Undefined(), MemSpaceType::OLD_SPACE); + rootArray->Set(thread, i, array); + } + heap->CollectGarbage(TriggerGCType::OLD_GC); +} + +TEST_F(ConcurrentMarkingTest, PerformanceWithoutConcurrentMarking) +{ + uint32_t rootLength = 1024; + JSHandle rootArray = + CreateTaggedArray(rootLength, JSTaggedValue::Undefined(), MemSpaceType::OLD_SPACE); + for (uint32_t i = 0; i < rootLength; i++) { + uint32_t subArrayLength = 1024; + auto array = CreateTaggedArray(subArrayLength, JSTaggedValue::Undefined(), MemSpaceType::OLD_SPACE); + rootArray->Set(thread, i, array); + } + auto heap = const_cast(thread->GetEcmaVM()->GetHeap()); + for (uint32_t i = 0; i < rootLength; i++) { + uint32_t subArrayLength = 1024; + auto array = CreateTaggedArray(subArrayLength, JSTaggedValue::Undefined(), MemSpaceType::OLD_SPACE); + rootArray->Set(thread, i, array); + } + heap->CollectGarbage(TriggerGCType::OLD_GC); +} +} // namespace panda::test diff --git a/tests/runtime/common/concurrent_sweep_test.cpp b/tests/runtime/common/concurrent_sweep_test.cpp new file mode 100644 index 000000000..6bb531d80 --- /dev/null +++ b/tests/runtime/common/concurrent_sweep_test.cpp @@ -0,0 +1,64 @@ +/* + * Copyright (c) 2021-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 "plugins/ecmascript/tests/runtime/common/test_helper.h" + +#include "plugins/ecmascript/runtime/ecma_vm.h" +#include "plugins/ecmascript/runtime/global_env.h" +#include "plugins/ecmascript/runtime/js_handle.h" + +using namespace panda::ecmascript; + +namespace panda::test { +class ConcurrentSweepTest : public testing::Test { +public: + static void SetUpTestCase() + { + GTEST_LOG_(INFO) << "SetUpTestCase"; + } + + static void TearDownTestCase() + { + GTEST_LOG_(INFO) << "TearDownCase"; + } + + void SetUp() override + { + TestHelper::CreateEcmaVMWithScope(instance, thread, scope); + } + + void TearDown() override + { + TestHelper::DestroyEcmaVMWithScope(instance, scope); + } + + PandaVM *instance {nullptr}; + EcmaHandleScope *scope {nullptr}; + JSThread *thread; +}; + +TEST_F(ConcurrentSweepTest, ConcurrentSweep) +{ + auto vm = EcmaVM::Cast(instance); + const uint8_t *utf8 = reinterpret_cast("test"); + JSHandle test1(thread, EcmaString::CreateFromUtf8(utf8, 4, vm, false)); + if (vm->IsInitialized()) { + vm->CollectGarbage(ecmascript::TriggerGCType::OLD_GC); + } + JSHandle test2(thread, EcmaString::CreateFromUtf8(utf8, 4, vm, false)); + ASSERT_EQ(test1->GetLength(), 4); + ASSERT_NE(test1.GetTaggedValue().GetHeapObject(), test2.GetTaggedValue().GetHeapObject()); +} +} // namespace panda::test diff --git a/tests/runtime/common/dump_test.cpp b/tests/runtime/common/dump_test.cpp new file mode 100644 index 000000000..84f905e7a --- /dev/null +++ b/tests/runtime/common/dump_test.cpp @@ -0,0 +1,672 @@ +/* + * Copyright (c) 2021-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 "gtest/gtest.h" +#include "test_helper.h" + +#include "plugins/ecmascript/runtime/accessor_data.h" +#include "plugins/ecmascript/runtime/class_info_extractor.h" +#include "plugins/ecmascript/runtime/class_linker/program_object-inl.h" +#include "plugins/ecmascript/runtime/ecma_module.h" +#include "plugins/ecmascript/runtime/ecma_vm.h" +#include "plugins/ecmascript/runtime/global_dictionary-inl.h" +#include "plugins/ecmascript/runtime/global_env.h" +#include "plugins/ecmascript/runtime/hprof/heap_profiler.h" +#include "plugins/ecmascript/runtime/hprof/heap_profiler_interface.h" +#include "plugins/ecmascript/runtime/hprof/heap_snapshot.h" +#include "plugins/ecmascript/runtime/hprof/heap_snapshot_json_serializer.h" +#include "plugins/ecmascript/runtime/hprof/string_hashmap.h" +#include "plugins/ecmascript/runtime/ic/ic_handler.h" +#include "plugins/ecmascript/runtime/ic/property_box.h" +#include "plugins/ecmascript/runtime/ic/proto_change_details.h" +#include "plugins/ecmascript/runtime/jobs/micro_job_queue.h" +#include "plugins/ecmascript/runtime/jobs/pending_job.h" +#include "plugins/ecmascript/runtime/js_arguments.h" +#include "plugins/ecmascript/runtime/js_array.h" +#include "plugins/ecmascript/runtime/js_arraylist.h" +#include "plugins/ecmascript/runtime/js_array_iterator.h" +#include "plugins/ecmascript/runtime/js_arraybuffer.h" +#include "plugins/ecmascript/runtime/js_async_from_sync_iterator_object.h" +#include "plugins/ecmascript/runtime/js_async_function.h" +#include "plugins/ecmascript/runtime/js_async_generator_object.h" +#include "plugins/ecmascript/runtime/js_collator.h" +#include "plugins/ecmascript/runtime/js_dataview.h" +#include "plugins/ecmascript/runtime/js_date.h" +#include "plugins/ecmascript/runtime/js_date_time_format.h" +#include "plugins/ecmascript/runtime/js_for_in_iterator.h" +#include "plugins/ecmascript/runtime/js_function.h" +#include "plugins/ecmascript/runtime/js_function_extra_info.h" +#include "plugins/ecmascript/runtime/js_generator_object.h" +#include "plugins/ecmascript/runtime/js_global_object.h" +#include "plugins/ecmascript/runtime/js_handle.h" +#include "plugins/ecmascript/runtime/js_intl.h" +#include "plugins/ecmascript/runtime/js_locale.h" +#include "plugins/ecmascript/runtime/js_map.h" +#include "plugins/ecmascript/runtime/js_map_iterator.h" +#include "plugins/ecmascript/runtime/js_number_format.h" +#include "plugins/ecmascript/runtime/js_object-inl.h" +#include "plugins/ecmascript/runtime/js_plural_rules.h" +#include "plugins/ecmascript/runtime/js_primitive_ref.h" +#include "plugins/ecmascript/runtime/js_promise.h" +#include "plugins/ecmascript/runtime/js_realm.h" +#include "plugins/ecmascript/runtime/js_regexp.h" +#include "plugins/ecmascript/runtime/js_relative_time_format.h" +#include "plugins/ecmascript/runtime/js_set.h" +#include "plugins/ecmascript/runtime/js_set_iterator.h" +#include "plugins/ecmascript/runtime/js_string_iterator.h" +#include "plugins/ecmascript/runtime/js_tagged_number.h" +#include "plugins/ecmascript/runtime/js_tagged_value-inl.h" +#include "plugins/ecmascript/runtime/js_thread.h" +#include "plugins/ecmascript/runtime/js_typed_array.h" +#include "plugins/ecmascript/runtime/js_weak_container.h" +#include "plugins/ecmascript/runtime/layout_info-inl.h" +#include "plugins/ecmascript/runtime/lexical_env.h" +#include "plugins/ecmascript/runtime/linked_hash_table-inl.h" +#include "plugins/ecmascript/runtime/mem/assert_scope-inl.h" +#include "plugins/ecmascript/runtime/mem/c_containers.h" +#include "plugins/ecmascript/runtime/mem/machine_code.h" +#include "plugins/ecmascript/runtime/object_factory.h" +#include "plugins/ecmascript/runtime/tagged_array.h" +#include "plugins/ecmascript/runtime/tagged_dictionary.h" +#include "plugins/ecmascript/runtime/template_map.h" +#include "plugins/ecmascript/runtime/transitions_dictionary.h" + +using namespace panda::coretypes; +using namespace panda::ecmascript; + +namespace panda::test { +class EcmaDumpTest : public testing::Test { +public: + static void SetUpTestCase() + { + GTEST_LOG_(INFO) << "SetUpTestCase"; + } + + static void TearDownTestCase() + { + GTEST_LOG_(INFO) << "TearDownCase"; + } + + void SetUp() override + { + TestHelper::CreateEcmaVMWithScope(instance, thread, scope); + } + + void TearDown() override + { + TestHelper::DestroyEcmaVMWithScope(instance, scope); + } + + PandaVM *instance {nullptr}; + ecmascript::EcmaHandleScope *scope {nullptr}; + JSThread *thread {nullptr}; +}; + +#ifndef NDEBUG +TEST_F(EcmaDumpTest, Dump) +{ + JSTaggedValue value1(100); + value1.D(); + + JSTaggedValue value2(100.0); + JSTaggedValue::DV(value2.GetRawData()); + + JSTaggedValue::Undefined().D(); + JSHandle env = thread->GetEcmaVM()->GetGlobalEnv(); + env.Dump(); + + JSHandle objFunc(env->GetObjectFunction()); + objFunc.Dump(); +} +#endif // #ifndef NDEBUG + +static JSHandle NewJSMap(JSThread *thread, ObjectFactory *factory, JSHandle proto) +{ + JSHandle mapClass = factory->NewEcmaDynClass(JSMap::SIZE, JSType::JS_MAP, proto); + JSHandle jsMap = JSHandle::Cast(factory->NewJSObject(mapClass)); + JSHandle linkedMap(LinkedHashMap::Create(thread)); + jsMap->SetLinkedMap(thread, linkedMap); + return jsMap; +} + +static JSHandle NewJSSet(JSThread *thread, ObjectFactory *factory, JSHandle proto) +{ + JSHandle setClass = factory->NewEcmaDynClass(JSSet::SIZE, JSType::JS_SET, proto); + JSHandle jsSet = JSHandle::Cast(factory->NewJSObject(setClass)); + JSHandle linkedSet(LinkedHashSet::Create(thread)); + jsSet->SetLinkedSet(thread, linkedSet); + return jsSet; +} + +static JSHandle NewJSObject(JSThread *thread, ObjectFactory *factory, JSHandle globalEnv) +{ + JSFunction *jsFunc = globalEnv->GetObjectFunction().GetObject(); + JSHandle jsFunc1(thread, jsFunc); + JSHandle jsObj = factory->NewJSObjectByConstructor(JSHandle(jsFunc1), jsFunc1); + return jsObj; +} + +TEST_F(EcmaDumpTest, HeapProfileDump) +{ + [[maybe_unused]] ecmascript::EcmaHandleScope scope_nested(thread); + auto factory = thread->GetEcmaVM()->GetFactory(); + auto globalEnv = thread->GetEcmaVM()->GetGlobalEnv(); + auto globalConst = const_cast(thread->GlobalConstants()); + JSHandle proto = globalEnv->GetFunctionPrototype(); + std::vector> snapshotVector; + +#define DUMP_FOR_HANDLE(dumpHandle) \ + dumpHandle.GetTaggedValue().D(); \ + dumpHandle.GetTaggedValue().DumpForSnapshot(thread, snapshotVector); + +#define NEW_OBJECT_AND_DUMP(ClassName, TypeName) \ + JSHandle class##ClassName = factory->NewEcmaDynClass(ClassName::SIZE, JSType::TypeName, proto); \ + JSHandle object##ClassName = factory->NewJSObject(class##ClassName); \ + object##ClassName.GetTaggedValue().D(); \ + object##ClassName.GetTaggedValue().DumpForSnapshot(thread, snapshotVector); + + for (JSType type = JSType::JS_OBJECT; type <= JSType::JS_TYPE_LAST; type = JSType(static_cast(type) + 1)) { + switch (type) { + case JSType::JS_ERROR: + case JSType::JS_EVAL_ERROR: + case JSType::JS_RANGE_ERROR: + case JSType::JS_TYPE_ERROR: + case JSType::JS_REFERENCE_ERROR: + case JSType::JS_URI_ERROR: + case JSType::JS_SYNTAX_ERROR: + case JSType::JS_OBJECT: { + CHECK_DUMP_FILEDS(ECMAObject::SIZE, JSObject::SIZE, 2) + JSHandle jsObj = NewJSObject(thread, factory, globalEnv); + DUMP_FOR_HANDLE(jsObj) + break; + } + case JSType::JS_REALM: { + CHECK_DUMP_FILEDS(JSObject::SIZE, JSRealm::SIZE, 2) + JSHandle jsRealm = factory->NewJSRealm(); + DUMP_FOR_HANDLE(jsRealm) + break; + } + case JSType::JS_FUNCTION_BASE: { + CHECK_DUMP_FILEDS(JSObject::SIZE, JSFunctionBase::SIZE, 1) + break; + } + case JSType::JS_FUNCTION: { + CHECK_DUMP_FILEDS(JSFunctionBase::SIZE, JSFunction::SIZE, 7) + JSHandle jsFunc = globalEnv->GetFunctionFunction(); + DUMP_FOR_HANDLE(jsFunc) + break; + } + case JSType::JS_PROXY_REVOC_FUNCTION: { + CHECK_DUMP_FILEDS(JSFunction::SIZE, JSProxyRevocFunction::SIZE, 1) + JSHandle proxyRevocClass = JSHandle::Cast(globalEnv->GetProxyRevocFunctionClass()); + JSHandle proxyRevocFunc = factory->NewJSObject(proxyRevocClass); + DUMP_FOR_HANDLE(proxyRevocFunc) + break; + } + case JSType::JS_PROMISE_REACTIONS_FUNCTION: { + CHECK_DUMP_FILEDS(JSFunction::SIZE, JSPromiseReactionsFunction::SIZE, 2) + JSHandle promiseReactClass = + JSHandle::Cast(globalEnv->GetPromiseReactionFunctionClass()); + JSHandle promiseReactFunc = factory->NewJSObject(promiseReactClass); + DUMP_FOR_HANDLE(promiseReactFunc) + break; + } + case JSType::JS_PROMISE_EXECUTOR_FUNCTION: { + CHECK_DUMP_FILEDS(JSFunction::SIZE, JSPromiseExecutorFunction::SIZE, 1) + JSHandle promiseExeClass = + JSHandle::Cast(globalEnv->GetPromiseExecutorFunctionClass()); + JSHandle promiseExeFunc = factory->NewJSObject(promiseExeClass); + DUMP_FOR_HANDLE(promiseExeFunc) + break; + } + case JSType::JS_PROMISE_ALL_RESOLVE_ELEMENT_FUNCTION: { + CHECK_DUMP_FILEDS(JSFunction::SIZE, JSPromiseAllResolveElementFunction::SIZE, 5) + JSHandle promiseAllClass = + JSHandle::Cast(globalEnv->GetPromiseAllResolveElementFunctionClass()); + JSHandle promiseAllFunc = factory->NewJSObject(promiseAllClass); + DUMP_FOR_HANDLE(promiseAllFunc) + break; + } + case JSType::JS_GENERATOR_FUNCTION: { + CHECK_DUMP_FILEDS(JSFunction::SIZE, JSGeneratorFunction::SIZE, 0) + break; + } + case JSType::JS_ASYNC_FUNCTION: { + CHECK_DUMP_FILEDS(JSFunction::SIZE, JSAsyncFunction::SIZE, 0) + break; + } + case JSType::JS_ASYNC_GENERATOR_FUNCTION: { + CHECK_DUMP_FILEDS(JSFunction::SIZE, JSAsyncGeneratorFunction::SIZE, 0) + break; + } + case JSType::JS_INTL_BOUND_FUNCTION: { + CHECK_DUMP_FILEDS(JSFunction::SIZE, JSIntlBoundFunction::SIZE, 3) + JSHandle intlBoundFunc = factory->NewJSIntlBoundFunction(); + DUMP_FOR_HANDLE(intlBoundFunc) + break; + } + case JSType::JS_ASYNC_AWAIT_STATUS_FUNCTION: { + CHECK_DUMP_FILEDS(JSFunction::SIZE, JSAsyncAwaitStatusFunction::SIZE, 1) + JSHandle asyncAwaitFunc = factory->NewJSAsyncAwaitStatusFunction(); + DUMP_FOR_HANDLE(asyncAwaitFunc) + break; + } + case JSType::JS_ASYNC_GENERATOR_RESOLVE_NEXT_FUNCTION: { + CHECK_DUMP_FILEDS(JSFunction::SIZE, JSAsyncGeneratorResolveNextFunction::SIZE, 1) + JSHandle asyncGenResolveNextFunc = + factory->NewJSAsyncGeneratorResolveNextFunction(); + DUMP_FOR_HANDLE(asyncGenResolveNextFunc) + break; + } + case JSType::JS_ASYNC_FROM_SYNC_ITERATOR_VALUE_UNWRAP_FUNCTION: { + CHECK_DUMP_FILEDS(JSFunction::SIZE, JSAsyncFromSyncIteratorValueUnwrapFunction::SIZE, 2) + JSHandle asyncFromSyncIteratorValueUnwrapFunction = + factory->NewJSAsyncFromSyncIteratorValueUnwrapFunction(); + DUMP_FOR_HANDLE(asyncFromSyncIteratorValueUnwrapFunction) + break; + } + case JSType::JS_BOUND_FUNCTION: { + CHECK_DUMP_FILEDS(JSFunctionBase::SIZE, JSBoundFunction::SIZE, 3) + NEW_OBJECT_AND_DUMP(JSBoundFunction, JS_BOUND_FUNCTION) + break; + } + case JSType::JS_REG_EXP: { + CHECK_DUMP_FILEDS(JSObject::SIZE, JSRegExp::SIZE, 5) + NEW_OBJECT_AND_DUMP(JSRegExp, JS_REG_EXP) + break; + } + case JSType::JS_SET: { + CHECK_DUMP_FILEDS(JSObject::SIZE, JSSet::SIZE, 1) + JSHandle jsSet = NewJSSet(thread, factory, proto); + DUMP_FOR_HANDLE(jsSet) + break; + } + case JSType::JS_MAP: { + CHECK_DUMP_FILEDS(JSObject::SIZE, JSMap::SIZE, 1) + JSHandle jsMap = NewJSMap(thread, factory, proto); + DUMP_FOR_HANDLE(jsMap) + break; + } + case JSType::JS_WEAK_MAP: { + CHECK_DUMP_FILEDS(JSObject::SIZE, JSWeakMap::SIZE, 1) + JSHandle weakMapClass = factory->NewEcmaDynClass(JSWeakMap::SIZE, JSType::JS_WEAK_MAP, proto); + JSHandle jsWeakMap = JSHandle::Cast(factory->NewJSObject(weakMapClass)); + JSHandle weakLinkedMap(LinkedHashMap::Create(thread)); + jsWeakMap->SetLinkedMap(thread, weakLinkedMap); + DUMP_FOR_HANDLE(jsWeakMap) + break; + } + case JSType::JS_WEAK_SET: { + CHECK_DUMP_FILEDS(JSObject::SIZE, JSWeakSet::SIZE, 1) + JSHandle weakSetClass = factory->NewEcmaDynClass(JSWeakSet::SIZE, JSType::JS_WEAK_SET, proto); + JSHandle jsWeakSet = JSHandle::Cast(factory->NewJSObject(weakSetClass)); + JSHandle weakLinkedSet(LinkedHashSet::Create(thread)); + jsWeakSet->SetLinkedSet(thread, weakLinkedSet); + DUMP_FOR_HANDLE(jsWeakSet) + break; + } + case JSType::JS_DATE: { + CHECK_DUMP_FILEDS(JSObject::SIZE, JSDate::SIZE, 2) + JSHandle dateClass = factory->NewEcmaDynClass(JSDate::SIZE, JSType::JS_DATE, proto); + JSHandle date = JSHandle::Cast(factory->NewJSObject(dateClass)); + date->SetTimeValue(thread, JSTaggedValue(0.0)); + date->SetLocalOffset(thread, JSTaggedValue(0.0)); + DUMP_FOR_HANDLE(date) + break; + } + case JSType::JS_ITERATOR: + // JS Iterate is a tool class, so we don't need to check it. + break; + case JSType::JS_FORIN_ITERATOR: { + CHECK_DUMP_FILEDS(JSObject::SIZE, JSForInIterator::SIZE, 4) + JSHandle array(thread, factory->NewJSArray().GetTaggedValue()); + JSHandle forInIter = factory->NewJSForinIterator(array); + DUMP_FOR_HANDLE(forInIter) + break; + } + case JSType::JS_MAP_ITERATOR: { + CHECK_DUMP_FILEDS(JSObject::SIZE, JSMapIterator::SIZE, 3) + JSHandle jsMapIter = + factory->NewJSMapIterator(NewJSMap(thread, factory, proto), IterationKind::KEY); + DUMP_FOR_HANDLE(jsMapIter) + break; + } + case JSType::JS_SET_ITERATOR: { + CHECK_DUMP_FILEDS(JSObject::SIZE, JSSetIterator::SIZE, 3) + JSHandle jsSetIter = + factory->NewJSSetIterator(NewJSSet(thread, factory, proto), IterationKind::KEY); + DUMP_FOR_HANDLE(jsSetIter) + break; + } + case JSType::JS_ARRAY_ITERATOR: { + CHECK_DUMP_FILEDS(JSObject::SIZE, JSArrayIterator::SIZE, 3) + JSHandle arrayIter = + factory->NewJSArrayIterator(JSHandle::Cast(factory->NewJSArray()), IterationKind::KEY); + DUMP_FOR_HANDLE(arrayIter) + break; + } + case JSType::JS_STRING_ITERATOR: { + CHECK_DUMP_FILEDS(JSObject::SIZE, JSStringIterator::SIZE, 2) + JSHandle stringIter = globalEnv->GetStringIterator(); + DUMP_FOR_HANDLE(stringIter) + break; + } + case JSType::JS_INTL: { + CHECK_DUMP_FILEDS(JSObject::SIZE, JSIntl::SIZE, 1) + NEW_OBJECT_AND_DUMP(JSIntl, JS_INTL) + break; + } + case JSType::JS_LOCALE: { + CHECK_DUMP_FILEDS(JSObject::SIZE, JSLocale::SIZE, 1) + NEW_OBJECT_AND_DUMP(JSLocale, JS_LOCALE) + break; + } + case JSType::JS_DATE_TIME_FORMAT: { + CHECK_DUMP_FILEDS(JSObject::SIZE, JSDateTimeFormat::SIZE, 11) + NEW_OBJECT_AND_DUMP(JSDateTimeFormat, JS_DATE_TIME_FORMAT) + break; + } + case JSType::JS_RELATIVE_TIME_FORMAT: { + CHECK_DUMP_FILEDS(JSObject::SIZE, JSRelativeTimeFormat::SIZE, 7) + NEW_OBJECT_AND_DUMP(JSRelativeTimeFormat, JS_RELATIVE_TIME_FORMAT) + break; + } + case JSType::JS_NUMBER_FORMAT: { + CHECK_DUMP_FILEDS(JSObject::SIZE, JSNumberFormat::SIZE, 20) + NEW_OBJECT_AND_DUMP(JSNumberFormat, JS_NUMBER_FORMAT) + break; + } + case JSType::JS_COLLATOR: { + CHECK_DUMP_FILEDS(JSObject::SIZE, JSCollator::SIZE, 9) + NEW_OBJECT_AND_DUMP(JSCollator, JS_COLLATOR) + break; + } + case JSType::JS_PLURAL_RULES: { + CHECK_DUMP_FILEDS(JSObject::SIZE, JSPluralRules::SIZE, 11) + NEW_OBJECT_AND_DUMP(JSPluralRules, JS_PLURAL_RULES) + break; + } + case JSType::JS_ARRAY_BUFFER: { + CHECK_DUMP_FILEDS(JSObject::SIZE, JSArrayBuffer::SIZE, 3) + NEW_OBJECT_AND_DUMP(JSArrayBuffer, JS_ARRAY_BUFFER) + break; + } + case JSType::JS_PROMISE: { + CHECK_DUMP_FILEDS(JSObject::SIZE, JSPromise::SIZE, 5) + NEW_OBJECT_AND_DUMP(JSPromise, JS_PROMISE) + break; + } + case JSType::JS_DATA_VIEW: { + CHECK_DUMP_FILEDS(JSObject::SIZE, JSDataView::SIZE, 4) + NEW_OBJECT_AND_DUMP(JSDataView, JS_DATA_VIEW) + break; + } + case JSType::JS_ARGUMENTS: { + CHECK_DUMP_FILEDS(JSObject::SIZE, JSArguments::SIZE, 1) + NEW_OBJECT_AND_DUMP(JSArguments, JS_ARGUMENTS) + break; + } + case JSType::JS_GENERATOR_OBJECT: { + CHECK_DUMP_FILEDS(JSObject::SIZE, JSGeneratorObject::SIZE, 4) + NEW_OBJECT_AND_DUMP(JSGeneratorObject, JS_GENERATOR_OBJECT) + break; + } + case JSType::JS_ASYNC_FROM_SYNC_ITERATOR_OBJECT: { + CHECK_DUMP_FILEDS(JSObject::SIZE, ecmascript::JSAsyncFromSyncIteratorObject::SIZE, 2) + JSHandle asyncFromSyncIteratorObject = + factory->NewJSAsyncFromSyncIteratorObject(); + DUMP_FOR_HANDLE(asyncFromSyncIteratorObject) + break; + } + case JSType::JS_ASYNC_FUNC_OBJECT: { + CHECK_DUMP_FILEDS(JSGeneratorObject::SIZE, JSAsyncFuncObject::SIZE, 1) + JSHandle asyncFuncObject = factory->NewJSAsyncFuncObject(); + DUMP_FOR_HANDLE(asyncFuncObject) + break; + } + case JSType::JS_ASYNC_GENERATOR_OBJECT: { + CHECK_DUMP_FILEDS(ecmascript::JSAsyncFuncObject::SIZE, ecmascript::JSAsyncGeneratorObject::SIZE, 1) + JSHandle asyncGenObject = factory->NewJSAsyncGeneratorObject(); + DUMP_FOR_HANDLE(asyncGenObject) + break; + } + case JSType::JS_ARRAY: { + CHECK_DUMP_FILEDS(JSObject::SIZE, JSArray::SIZE, 1) + JSHandle jsArray = factory->NewJSArray(); + DUMP_FOR_HANDLE(jsArray) + break; + } + case JSType::JS_TYPED_ARRAY: + case JSType::JS_INT8_ARRAY: + case JSType::JS_UINT8_ARRAY: + case JSType::JS_UINT8_CLAMPED_ARRAY: + case JSType::JS_INT16_ARRAY: + case JSType::JS_UINT16_ARRAY: + case JSType::JS_INT32_ARRAY: + case JSType::JS_UINT32_ARRAY: + case JSType::JS_FLOAT32_ARRAY: + case JSType::JS_FLOAT64_ARRAY: { + CHECK_DUMP_FILEDS(JSObject::SIZE, JSTypedArray::SIZE, 5) + NEW_OBJECT_AND_DUMP(JSTypedArray, JS_TYPED_ARRAY) + break; + } + case JSType::JS_PRIMITIVE_REF: { + CHECK_DUMP_FILEDS(JSObject::SIZE, JSPrimitiveRef::SIZE, 1) + NEW_OBJECT_AND_DUMP(JSPrimitiveRef, JS_PRIMITIVE_REF) + break; + } + case JSType::JS_GLOBAL_OBJECT: { + CHECK_DUMP_FILEDS(JSObject::SIZE, JSGlobalObject::SIZE, 0) + JSHandle globalObject = globalEnv->GetJSGlobalObject(); + DUMP_FOR_HANDLE(globalObject) + break; + } + case JSType::JS_PROXY: { + CHECK_DUMP_FILEDS(ECMAObject::SIZE, JSProxy::SIZE, 3) + JSHandle emptyObj(thread, NewJSObject(thread, factory, globalEnv).GetTaggedValue()); + JSHandle proxy = factory->NewJSProxy(emptyObj, emptyObj); + DUMP_FOR_HANDLE(proxy) + break; + } + case JSType::HCLASS: { + CHECK_DUMP_FILEDS(TaggedObject::TaggedObjectSize(), JSHClass::SIZE, 9) + JSHandle hclass = factory->NewEcmaDynClass(JSHClass::SIZE, JSType::HCLASS, proto); + DUMP_FOR_HANDLE(hclass) + break; + } + case JSType::STRING: { + DUMP_FOR_HANDLE(globalEnv->GetObjectFunction()) + break; + } + case JSType::TAGGED_ARRAY: { + JSHandle taggedArray = factory->NewTaggedArray(4); + DUMP_FOR_HANDLE(taggedArray) + break; + } + case JSType::TAGGED_DICTIONARY: { + JSHandle dict = factory->NewDictionaryArray(4); + DUMP_FOR_HANDLE(dict) + break; + } + case JSType::FREE_OBJECT_WITH_ONE_FIELD: + case JSType::FREE_OBJECT_WITH_NONE_FIELD: + case JSType::FREE_OBJECT_WITH_TWO_FIELD: { + break; + } + case JSType::JS_NATIVE_POINTER: { + break; + } + case JSType::GLOBAL_ENV: { + DUMP_FOR_HANDLE(globalEnv) + break; + } + case JSType::ACCESSOR_DATA: + case JSType::INTERNAL_ACCESSOR: { + CHECK_DUMP_FILEDS(Record::SIZE, AccessorData::SIZE, 2) + JSHandle accessor = factory->NewAccessorData(); + DUMP_FOR_HANDLE(accessor) + break; + } + case JSType::SYMBOL: { + CHECK_DUMP_FILEDS(TaggedObject::TaggedObjectSize(), JSSymbol::SIZE, 3) + JSHandle symbol = factory->NewJSSymbol(); + DUMP_FOR_HANDLE(symbol) + break; + } + case JSType::JS_GENERATOR_CONTEXT: { + CHECK_DUMP_FILEDS(TaggedObject::TaggedObjectSize(), GeneratorContext::SIZE, 7) + JSHandle genContext = factory->NewGeneratorContext(); + DUMP_FOR_HANDLE(genContext) + break; + } + case JSType::PROTOTYPE_HANDLER: { + CHECK_DUMP_FILEDS(TaggedObject::TaggedObjectSize(), PrototypeHandler::SIZE, 3) + JSHandle protoHandler = factory->NewPrototypeHandler(); + DUMP_FOR_HANDLE(protoHandler) + break; + } + case JSType::TRANSITION_HANDLER: { + CHECK_DUMP_FILEDS(TaggedObject::TaggedObjectSize(), TransitionHandler::SIZE, 2) + JSHandle transitionHandler = factory->NewTransitionHandler(); + DUMP_FOR_HANDLE(transitionHandler) + break; + } + case JSType::PROPERTY_BOX: { + CHECK_DUMP_FILEDS(TaggedObject::TaggedObjectSize(), PropertyBox::SIZE, 1) + JSHandle PropertyBox = factory->NewPropertyBox(globalEnv->GetEmptyArray()); + DUMP_FOR_HANDLE(PropertyBox) + break; + } + case JSType::PROTO_CHANGE_MARKER: { + CHECK_DUMP_FILEDS(TaggedObject::TaggedObjectSize(), ProtoChangeMarker::SIZE, 1) + JSHandle protoMaker = factory->NewProtoChangeMarker(); + DUMP_FOR_HANDLE(protoMaker) + break; + } + case JSType::PROTOTYPE_INFO: { + CHECK_DUMP_FILEDS(TaggedObject::TaggedObjectSize(), ProtoChangeDetails::SIZE, 2) + JSHandle protoDetails = factory->NewProtoChangeDetails(); + DUMP_FOR_HANDLE(protoDetails) + break; + } + case JSType::TEMPLATE_MAP: { + JSHandle templateMap = globalEnv->GetTemplateMap(); + DUMP_FOR_HANDLE(templateMap) + break; + } + case JSType::PROGRAM: { + CHECK_DUMP_FILEDS(ECMAObject::SIZE, Program::SIZE, 5) + JSHandle program = factory->NewProgram(); + DUMP_FOR_HANDLE(program) + break; + } + case JSType::LEXICAL_FUNCTION: { + CHECK_DUMP_FILEDS(ECMAObject::SIZE, LexicalFunction::SIZE, 5) + // unused + break; + } + case JSType::PROMISE_CAPABILITY: { + CHECK_DUMP_FILEDS(Record::SIZE, PromiseCapability::SIZE, 3) + JSHandle promiseCapa = factory->NewPromiseCapability(); + DUMP_FOR_HANDLE(promiseCapa) + break; + } + case JSType::PROMISE_RECORD: { + CHECK_DUMP_FILEDS(Record::SIZE, PromiseRecord::SIZE, 1) + JSHandle promiseRecord = factory->NewPromiseRecord(); + DUMP_FOR_HANDLE(promiseRecord) + break; + } + case JSType::RESOLVING_FUNCTIONS_RECORD: { + CHECK_DUMP_FILEDS(Record::SIZE, ResolvingFunctionsRecord::SIZE, 2) + JSHandle ResolvingFunc = factory->NewResolvingFunctionsRecord(); + DUMP_FOR_HANDLE(ResolvingFunc) + break; + } + case JSType::PROMISE_REACTIONS: { + CHECK_DUMP_FILEDS(Record::SIZE, PromiseReaction::SIZE, 3) + JSHandle promiseReact = factory->NewPromiseReaction(); + DUMP_FOR_HANDLE(promiseReact) + break; + } + case JSType::PROMISE_ITERATOR_RECORD: { + CHECK_DUMP_FILEDS(Record::SIZE, PromiseIteratorRecord::SIZE, 2) + JSHandle emptyObj(thread, NewJSObject(thread, factory, globalEnv).GetTaggedValue()); + JSHandle promiseIter = factory->NewPromiseIteratorRecord(emptyObj, emptyObj); + DUMP_FOR_HANDLE(promiseIter) + break; + } + case JSType::MICRO_JOB_QUEUE: { + CHECK_DUMP_FILEDS(Record::SIZE, ecmascript::job::MicroJobQueue::SIZE, 2) + JSHandle microJob = factory->NewMicroJobQueue(); + DUMP_FOR_HANDLE(microJob) + break; + } + case JSType::PENDING_JOB: { + CHECK_DUMP_FILEDS(Record::SIZE, ecmascript::job::PendingJob::SIZE, 2) + JSHandle pendingClass(thread, + JSHClass::Cast(globalConst->GetPendingJobClass().GetTaggedObject())); + JSHandle pendingJob(thread, factory->NewDynObject(pendingClass)); + DUMP_FOR_HANDLE(pendingJob) + break; + } + case JSType::FUNCTION_EXTRA_INFO: { + CHECK_DUMP_FILEDS(TaggedObject::TaggedObjectSize(), JSFunctionExtraInfo::SIZE, 2) + JSHandle funcExtraClass( + thread, JSHClass::Cast(globalConst->GetFunctionExtraInfoClass().GetTaggedObject())); + JSHandle funcInfo(thread, factory->NewDynObject(funcExtraClass)); + DUMP_FOR_HANDLE(funcInfo) + break; + } + case JSType::COMPLETION_RECORD: { + CHECK_DUMP_FILEDS(Record::SIZE, CompletionRecord::SIZE, 2) + JSHandle comRecord = factory->NewCompletionRecord(0, globalEnv->GetEmptyArray()); + DUMP_FOR_HANDLE(comRecord) + break; + } + case JSType::MACHINE_CODE_OBJECT: { + CHECK_DUMP_FILEDS(TaggedObject::TaggedObjectSize(), MachineCode::DATA_OFFSET, 1) + JSHandle machineCode = factory->NewMachineCodeObject(16, nullptr); + DUMP_FOR_HANDLE(machineCode) + break; + } + case JSType::ECMA_MODULE: { + CHECK_DUMP_FILEDS(ECMAObject::SIZE, EcmaModule::SIZE, 1) + JSHandle ecmaModule = factory->NewEmptyEcmaModule(); + DUMP_FOR_HANDLE(ecmaModule) + break; + } + case JSType::CLASS_INFO_EXTRACTOR: { + CHECK_DUMP_FILEDS(TaggedObject::TaggedObjectSize(), ClassInfoExtractor::SIZE, 10) + JSHandle classInfoExtractor = factory->NewClassInfoExtractor(nullptr); + DUMP_FOR_HANDLE(classInfoExtractor) + break; + } + case JSType::JS_QUEUE: + case JSType::JS_ARRAY_LIST: { + CHECK_DUMP_FILEDS(JSObject::SIZE, JSArrayList::SIZE, 1) + // unused + break; + } + default: + LOG_ECMA_MEM(ERROR) << "JSType " << static_cast(type) << " cannot be dumped."; + UNREACHABLE(); + break; + } + } +#undef NEW_OBJECT_AND_DUMP +#undef DUMP_FOR_HANDLE +} +} // namespace panda::test diff --git a/tests/runtime/common/dyninstruction/CMakeLists.txt b/tests/runtime/common/dyninstruction/CMakeLists.txt new file mode 100644 index 000000000..7d0af50a1 --- /dev/null +++ b/tests/runtime/common/dyninstruction/CMakeLists.txt @@ -0,0 +1,22 @@ +# Huawei Technologies Co.,Ltd. + +set(DYNINSTRUCTION_OUTPUT ${CMAKE_CURRENT_BINARY_DIR}/dyninstruction.txt) +set(DYNINSTRUCTION_BIN ${CMAKE_CURRENT_BINARY_DIR}/dyninstruction.abc) +set(DYNINSTRUCTION_JS ${CMAKE_CURRENT_SOURCE_DIR}/dyninstruction.js) +set(DYNINSTRUCTION_VERIFY ${CMAKE_CURRENT_SOURCE_DIR}/verify.sh) + +set(RUNTIME_ARGUMENTS --boot-panda-files=${PANDA_BINARY_ROOT}/pandastdlib/arkstdlib.abc --load-runtimes=\"ecmascript\" --compiler-enable-jit=false --gc-type=stw --run-gc-in-place ${DYNINSTRUCTION_BIN} _GLOBAL::func_main_0) + +add_custom_command( + OUTPUT ${DYNINSTRUCTION_OUTPUT} + COMMENT "running javascript dyninstruction testcase" + COMMAND ${PANDA_RUN_PREFIX} $ ${DYNINSTRUCTION_JS} --output ${DYNINSTRUCTION_BIN} + COMMAND rm -f ${DYNINSTRUCTION_OUTPUT} + COMMAND ${PANDA_RUN_PREFIX} $ ${RUNTIME_ARGUMENTS} > ${DYNINSTRUCTION_OUTPUT} + COMMAND bash ${DYNINSTRUCTION_VERIFY} ${DYNINSTRUCTION_OUTPUT} +) +add_custom_target(dyninstruction + DEPENDS ${DYNINSTRUCTION_OUTPUT} ${DYNINSTRUCTION_VERIFY} +) +add_dependencies(dyninstruction es2panda ark) +add_dependencies(ecmascript_common_tests dyninstruction) diff --git a/tests/runtime/common/dyninstruction/dyninstruction.js b/tests/runtime/common/dyninstruction/dyninstruction.js new file mode 100644 index 000000000..45ccb08e2 --- /dev/null +++ b/tests/runtime/common/dyninstruction/dyninstruction.js @@ -0,0 +1,7 @@ +function Foo() { + print(3) + print(1.1) + return 1.1 +} +print(Foo()) +print(9) \ No newline at end of file diff --git a/tests/runtime/common/dyninstruction/verify.sh b/tests/runtime/common/dyninstruction/verify.sh new file mode 100755 index 000000000..6dc07e1d0 --- /dev/null +++ b/tests/runtime/common/dyninstruction/verify.sh @@ -0,0 +1,18 @@ +#!/bin/bash +# Copyright (c) Huawei Technologies Co., Ltd. 2020-2021. All rights reserved. + +set -eo pipefail + +expected="3 +1.1 +1.1 +9" +actual=$(cat "$1") +if [[ "$actual" == "$expected" ]];then + exit 0; +else + echo -e "expected:""$expected" + echo -e "actual:""$actual" + echo -e "\033[31mdyninstruction test failed\033[0m" + exit 1; +fi diff --git a/tests/runtime/common/ecma_empty_class_check/CMakeLists.txt b/tests/runtime/common/ecma_empty_class_check/CMakeLists.txt new file mode 100644 index 000000000..a6e4f7218 --- /dev/null +++ b/tests/runtime/common/ecma_empty_class_check/CMakeLists.txt @@ -0,0 +1,50 @@ +# Huawei Technologies Co.,Ltd. + +set(ECMA_EMPTY_CLASS_CHECK_OUTPUT ${CMAKE_CURRENT_BINARY_DIR}/ecma_empty_class_check_out.txt) +set(ECMA_EMPTY_CLASS_CHECK_BIN ${CMAKE_CURRENT_BINARY_DIR}/ecma_empty_class_check.abc) +set(ECMA_EMPTY_CLASS_CHECK_JS ${CMAKE_CURRENT_SOURCE_DIR}/ecma_empty_class_check.js) +set(ECMA_EMPTY_CLASS_CHECK_SAMPLE ${CMAKE_CURRENT_SOURCE_DIR}/ecma_empty_class_check_sample.txt) + +set(RUNTIME_ARGUMENTS + --load-runtimes=ecmascript + --gc-type=stw + --run-gc-in-place + ${ECMA_EMPTY_CLASS_CHECK_BIN} + _GLOBAL::func_main_0 +) + +add_custom_target(ecma_empty_class_check_es2panda + COMMENT "running es2panda javascript ecma_empty_class_check testcase" + COMMAND rm -f ${ECMA_EMPTY_CLASS_CHECK_BIN} + COMMAND ${es2panda_bin} ${ECMA_EMPTY_CLASS_CHECK_JS} --output ${ECMA_EMPTY_CLASS_CHECK_BIN} + DEPENDS ${es2panda_target} +) + +add_custom_target(ecma_empty_class_check_ark + COMMENT "running ark javascript ecma_empty_class_check testcase" + COMMAND rm -f ${ECMA_EMPTY_CLASS_CHECK_OUTPUT} + COMMAND ${PANDA_RUN_PREFIX} $ ${RUNTIME_ARGUMENTS} > ${ECMA_EMPTY_CLASS_CHECK_OUTPUT} + DEPENDS ecma_empty_class_check_es2panda ark +) + +add_custom_target(ecma_empty_class_check_check_out + COMMENT "Check output javascript ecma_empty_class_check testcase" + COMMAND ${CMAKE_COMMAND} -E compare_files ${ECMA_EMPTY_CLASS_CHECK_OUTPUT} ${ECMA_EMPTY_CLASS_CHECK_SAMPLE} + DEPENDS ecma_empty_class_check_ark +) + +add_dependencies(ecmascript_common_tests ecma_empty_class_check_check_out) + +panda_add_gtest( + NAME ecma_empty_class_check + SOURCES + ecma_empty_class_check.cpp + LIBRARIES + arkruntime + DEPS_TARGETS + ecma_empty_class_check_es2panda + SANITIZERS + ${PANDA_SANITIZERS_LIST} +) + +add_dependencies(ecmascript_common_tests ecma_empty_class_check_check_out) diff --git a/tests/runtime/common/ecma_empty_class_check/ecma_empty_class_check.cpp b/tests/runtime/common/ecma_empty_class_check/ecma_empty_class_check.cpp new file mode 100644 index 000000000..6e3fbe01c --- /dev/null +++ b/tests/runtime/common/ecma_empty_class_check/ecma_empty_class_check.cpp @@ -0,0 +1,79 @@ +/* + * Copyright (c) 2021-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 + +#include "runtime/include/runtime.h" +#include "plugins/ecmascript/runtime/ecma_vm.h" + +namespace panda::test { +class EcmaEmptyClassCheck : public testing::Test { +public: + std::string GetJSAbcFile() + { + auto exec_path = panda::os::file::File::GetExecutablePath(); + return exec_path.Value() + + "/../plugins/ecmascript/tests/runtime/common/ecma_empty_class_check/ecma_empty_class_check.abc"; + } +}; + +static void CheckNames(const panda_file::File &pf) +{ + Span classIndexes = pf.GetClasses(); + + for (const uint32_t index : classIndexes) { + panda_file::File::EntityId classId(index); + if (pf.IsExternal(classId)) { + continue; + } + panda_file::ClassDataAccessor cda(pf, classId); + cda.EnumerateMethods([&pf](panda_file::MethodDataAccessor &mda) { + auto sd_mname = mda.GetName(); + std::string mname(reinterpret_cast(sd_mname.data), sd_mname.utf16_length); + ASSERT(!mname.empty()); + + auto sd_cname = pf.GetStringData(mda.GetClassId()); + std::string cname(reinterpret_cast(sd_cname.data), sd_cname.utf16_length); + ASSERT(!cname.empty()); + }); + } +} + +TEST_F(EcmaEmptyClassCheck, TestJSABC) +{ + RuntimeOptions options; + options.SetLoadRuntimes({"ecmascript"}); + options.SetBootPandaFiles({}); + options.SetHeapSizeLimit(50_MB); + options.SetGcType("epsilon"); + + ASSERT_TRUE(Runtime::Create(options)); + + panda::ecmascript::EcmaVM *ecma_vm = + reinterpret_cast(Runtime::GetCurrent()->GetPandaVM()); + + ASSERT_TRUE(ecma_vm); + + std::unique_ptr pf = + panda_file::OpenPandaFile(GetJSAbcFile(), "", panda_file::File::OpenMode::READ_WRITE); + + ASSERT_TRUE(pf); + CheckNames(*pf.get()); + + ASSERT_TRUE(ecma_vm->ExecuteFromBuffer(pf->GetBase(), pf->GetPtr().GetSize(), "_GLOBAL::func_main_0", {})); + + Runtime::Destroy(); +} +} // namespace panda::test diff --git a/tests/runtime/common/ecma_empty_class_check/ecma_empty_class_check.js b/tests/runtime/common/ecma_empty_class_check/ecma_empty_class_check.js new file mode 100644 index 000000000..95c2edc87 --- /dev/null +++ b/tests/runtime/common/ecma_empty_class_check/ecma_empty_class_check.js @@ -0,0 +1,20 @@ +let foo_no_class = function(n) { + return "foo_no_class_1"; +} + +print(foo_no_class()); + +class FooClass { + foo_class(){ + return "foo_class"; + } +} + +let obj_foo_class = new FooClass(); +print(obj_foo_class.foo_class()); + +foo_no_class = function(n) { + return "foo_no_class_2"; +} + +print(foo_no_class()); diff --git a/tests/runtime/common/ecma_empty_class_check/ecma_empty_class_check_sample.txt b/tests/runtime/common/ecma_empty_class_check/ecma_empty_class_check_sample.txt new file mode 100644 index 000000000..2e3de9287 --- /dev/null +++ b/tests/runtime/common/ecma_empty_class_check/ecma_empty_class_check_sample.txt @@ -0,0 +1,3 @@ +foo_no_class_1 +foo_class +foo_no_class_2 diff --git a/tests/runtime/common/ecma_module_test.cpp b/tests/runtime/common/ecma_module_test.cpp new file mode 100644 index 000000000..2cdd0d725 --- /dev/null +++ b/tests/runtime/common/ecma_module_test.cpp @@ -0,0 +1,467 @@ +/* + * Copyright (c) 2021-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 "plugins/ecmascript/runtime/ecma_module.h" +#include "plugins/ecmascript/runtime/global_env.h" +#include "plugins/ecmascript/runtime/js_locale.h" +#include "plugins/ecmascript/runtime/tagged_dictionary.h" +#include "plugins/ecmascript/tests/runtime/common/test_helper.h" + +using namespace panda::ecmascript; + +namespace panda::test { +class EcmaModuleTest : public testing::Test { +public: + static void SetUpTestCase() + { + GTEST_LOG_(INFO) << "SetUpTestCase"; + } + + static void TearDownTestCase() + { + GTEST_LOG_(INFO) << "TearDownCase"; + } + + void SetUp() override + { + TestHelper::CreateEcmaVMWithScope(instance, thread, scope); + } + + void TearDown() override + { + TestHelper::DestroyEcmaVMWithScope(instance, scope); + } + PandaVM *instance {nullptr}; + ecmascript::EcmaHandleScope *scope {nullptr}; + JSThread *thread {nullptr}; +}; + +EcmaModule *EcmaModuleCreate(JSThread *thread) +{ + ObjectFactory *objectFactory = thread->GetEcmaVM()->GetFactory(); + JSHandle handleEcmaModule = objectFactory->NewEmptyEcmaModule(); + return *handleEcmaModule; +} + +/* + * Feature: EcmaModule + * Function: AddItem + * SubFunction: GetItem + * FunctionPoints: Add Item To EcmaModule + * CaseDescription: Add an item for a EcmaModule that has called "SetNameDictionary" function in the TEST_F, and + * check its value through 'GetItem' function. + */ +TEST_F(EcmaModuleTest, AddItem_001) +{ + int numOfElementsDict = 4; + CString cStrItemName = "key1"; + int intItemValue = 1; + JSHandle handleEcmaModule(thread, EcmaModuleCreate(thread)); + JSHandle handleNameDict(NameDictionary::Create(thread, numOfElementsDict)); + JSHandle handleTagValItemName( + thread->GetEcmaVM()->GetFactory()->NewFromCanBeCompressString(cStrItemName)); + JSHandle handleTagValItemValue(thread, JSTaggedValue(intItemValue)); + + handleEcmaModule->SetNameDictionary(thread, handleNameDict); // Call SetNameDictionary in TEST_F + EcmaModule::AddItem(thread, handleEcmaModule, handleTagValItemName, handleTagValItemValue); + EXPECT_EQ(handleEcmaModule->GetItem(thread, handleTagValItemName)->GetNumber(), intItemValue); +} + +/* + * Feature: EcmaModule + * Function: AddItem + * SubFunction: GetItem + * FunctionPoints: Add Item To EcmaModule + * CaseDescription: Add an item for a EcmaModule that has not called "SetNameDictionary" function in the TEST_F, + * and check its value through 'GetItem' function. + */ +TEST_F(EcmaModuleTest, AddItem_002) +{ + CString cStrItemName = "cStrItemName"; + int intItemValue = 1; + JSHandle handleEcmaModule(thread, EcmaModuleCreate(thread)); + JSHandle handleTagValItemName( + thread->GetEcmaVM()->GetFactory()->NewFromCanBeCompressString(cStrItemName)); + JSHandle handleTagValItemValue(thread, JSTaggedValue(intItemValue)); + + // This EcmaModule calls 'SetNameDictionary' function through 'AddItem' function of "ecma_module.cpp". + EcmaModule::AddItem(thread, handleEcmaModule, handleTagValItemName, handleTagValItemValue); + EXPECT_EQ(handleEcmaModule->GetItem(thread, handleTagValItemName)->GetNumber(), intItemValue); +} + +/* + * Feature: EcmaModule + * Function: RemoveItem + * SubFunction: AddItem/GetItem + * FunctionPoints: Remove Item From EcmaModule + * CaseDescription: Add an item for an empty EcmaModule through 'AddItem' function, then remove the item from the + * EcmaModule through 'RemoveItem' function, finally check whether the item obtained from the + * EcmaModule through 'GetItem' function is undefined. + */ +TEST_F(EcmaModuleTest, RemoveItem) +{ + ObjectFactory* objFactory = thread->GetEcmaVM()->GetFactory(); + + CString cStrItemName = "cStrItemName"; + int intItemValue = 1; + JSHandle handleEcmaModule(thread, EcmaModuleCreate(thread)); + JSHandle handleTagValItemName(objFactory->NewFromCanBeCompressString(cStrItemName)); + JSHandle handleTagValItemValue(thread, JSTaggedValue(intItemValue)); + + EcmaModule::AddItem(thread, handleEcmaModule, handleTagValItemName, handleTagValItemValue); + EcmaModule::RemoveItem(thread, handleEcmaModule, handleTagValItemName); + EXPECT_TRUE(handleEcmaModule->GetItem(thread, handleTagValItemName)->IsUndefined()); +} + +/* + * Feature: EcmaModule + * Function: SetNameDictionary + * SubFunction: NameDictionary::Put/GetNameDictionary + * FunctionPoints: Set NameDictionary For EcmaModule + * CaseDescription: Create a source key, a source value, a source NameDictionary and a target EcmaModule, change the + * NameDictionary through 'NameDictionary::Put' function, set the changed source NameDictionary as + * this target EcmaModule's NameDictionary through 'SetNameDictionary' function, check whether the + * result returned through 'GetNameDictionary' function from the target EcmaModule are within + * expectations. + */ +TEST_F(EcmaModuleTest, SetNameDictionary) +{ + ObjectFactory* objFactory = thread->GetEcmaVM()->GetFactory(); + + int numOfElementsDict = 4; + JSHandle handleNameDict(NameDictionary::Create(thread, numOfElementsDict)); + JSHandle handleObjFunc = thread->GetEcmaVM()->GetGlobalEnv()->GetObjectFunction(); + CString keyArray1 = "hello1"; + JSHandle stringKey1 = objFactory->NewFromCanBeCompressString(keyArray1); + JSHandle key1(stringKey1); + JSHandle value1(objFactory + ->NewJSObjectByConstructor(JSHandle(handleObjFunc), handleObjFunc)); + JSHandle handleNameDictionaryFrom( + NameDictionary::Put(thread, handleNameDict, key1, value1, PropertyAttributes::Default())); + JSHandle handleEcmaModule(thread, EcmaModuleCreate(thread)); + + handleEcmaModule->SetNameDictionary(thread, handleNameDictionaryFrom); + JSHandle handleNameDictionaryTo(thread, + NameDictionary::Cast(handleEcmaModule->GetNameDictionary().GetTaggedObject())); + EXPECT_EQ(handleNameDictionaryTo->EntriesCount(), 1); + int entry1 = handleNameDictionaryTo->FindEntry(key1.GetTaggedValue()); + EXPECT_TRUE(key1.GetTaggedValue() == handleNameDictionaryTo->GetKey(entry1)); + EXPECT_TRUE(value1.GetTaggedValue() == handleNameDictionaryTo->GetValue(entry1)); +} + +/* + * Feature: ModuleManager + * Function: AddModule + * SubFunction: AddItem/GetModule/GetItem + * FunctionPoints: Add EcmaModule To ModuleManager + * CaseDescription: Create 2 source EcmaModules that both add an item, create a ModuleManager that add the 2 source + * EcmaModule, check whether the items of EcmaModules obtained from the ModuleManager through + * 'GetModule' function and 'GetItem' function are within expectations. + */ +TEST_F(EcmaModuleTest, ModuleManager_AddModule) +{ + ObjectFactory* objFactory = thread->GetEcmaVM()->GetFactory(); + ModuleManager *moduleManager = thread->GetEcmaVM()->GetModuleManager(); + + int numOfElementsDict1 = 4; + int numOfElementsDict2 = 4; + CString cStrItemName1 = "cStrItemName1"; + CString cStrItemName2 = "cStrItemName2"; + int intItemValue1 = 1; + int intItemValue2 = 2; + JSHandle handleNameDict1(NameDictionary::Create(thread, numOfElementsDict1)); + JSHandle handleNameDict2(NameDictionary::Create(thread, numOfElementsDict2)); + JSHandle handleItemName1( + thread->GetEcmaVM()->GetFactory()->NewFromCanBeCompressString(cStrItemName1)); + JSHandle handleItemName2( + thread->GetEcmaVM()->GetFactory()->NewFromCanBeCompressString(cStrItemName2)); + JSHandle handleItemValue1(thread, JSTaggedValue(intItemValue1)); + JSHandle handleItemValue2(thread, JSTaggedValue(intItemValue2)); + JSHandle handleEcmaModuleAddFrom1(thread, EcmaModuleCreate(thread)); + JSHandle handleEcmaModuleAddFrom2(thread, EcmaModuleCreate(thread)); + handleEcmaModuleAddFrom1->SetNameDictionary(thread, handleNameDict1); + handleEcmaModuleAddFrom2->SetNameDictionary(thread, handleNameDict2); + + EcmaModule::AddItem(thread, handleEcmaModuleAddFrom1, handleItemName1, handleItemValue1); + JSHandle handleTagValEcmaModuleAddFrom1(thread, handleEcmaModuleAddFrom1.GetTaggedValue()); + std::string stdStrNameEcmaModuleAdd1 = "NameEcmaModule1"; + JSHandle handleTagValNameEcmaModuleAdd1(objFactory->NewFromStdString(stdStrNameEcmaModuleAdd1)); + EcmaModule::AddItem(thread, handleEcmaModuleAddFrom2, handleItemName2, handleItemValue2); + JSHandle handleTagValEcmaModuleAddFrom2(thread, handleEcmaModuleAddFrom2.GetTaggedValue()); + std::string stdStrNameEcmaModuleAdd2 = "NameEcmaModule2"; + JSHandle handleTagValNameEcmaModuleAdd2(objFactory->NewFromStdString(stdStrNameEcmaModuleAdd2)); + + moduleManager->AddModule(handleTagValNameEcmaModuleAdd1, handleTagValEcmaModuleAddFrom1); + moduleManager->AddModule(handleTagValNameEcmaModuleAdd2, handleTagValEcmaModuleAddFrom2); + JSHandle handleTagValEcmaModuleGet1 = moduleManager->GetModule(thread, + handleTagValNameEcmaModuleAdd1); + JSHandle handleTagValEcmaModuleGet2 = moduleManager->GetModule(thread, + handleTagValNameEcmaModuleAdd2); + EXPECT_EQ(JSHandle::Cast(handleTagValEcmaModuleGet1)->GetItem(thread, handleItemName1)->GetNumber(), + intItemValue1); + EXPECT_EQ(JSHandle::Cast(handleTagValEcmaModuleGet2)->GetItem(thread, handleItemName2)->GetNumber(), + intItemValue2); +} + +/* + * Feature: ModuleManager + * Function: RemoveModule + * SubFunction: AddItem/GetModule/AddModule/GetItem + * FunctionPoints: Remove EcmaModule From ModuleManager + * CaseDescription: Create two source EcmaModules that add different items, create a ModuleManager that add the two + * source EcmaModules, check whether the properties of the EcmaModules obtained from the ModuleManager + * through 'GetModule' function are within expectations while removing EcmaModules from the + * ModuleManager through 'RemoveModule' function one by one. + */ +TEST_F(EcmaModuleTest, ModuleManager_RemoveModule) +{ + ObjectFactory* objFactory = thread->GetEcmaVM()->GetFactory(); + ModuleManager *moduleManager = thread->GetEcmaVM()->GetModuleManager(); + + std::string stdStrNameEcmaModuleAdd1 = "NameEcmaModule1"; + std::string stdStrNameEcmaModuleAdd2 = "NameEcmaModule2"; + int intItemValue1 = 1; + int intItemValue2 = 2; + int numOfElementsDict1 = 4; + int numOfElementsDict2 = 4; + JSHandle handleTagValNameEcmaModuleAdd1(objFactory->NewFromStdString(stdStrNameEcmaModuleAdd1)); + JSHandle handleTagValNameEcmaModuleAdd2(objFactory->NewFromStdString(stdStrNameEcmaModuleAdd2)); + JSHandle handleTagValItemName1(objFactory->NewFromCanBeCompressString("name1")); + JSHandle handleTagValItemName2(objFactory->NewFromCanBeCompressString("name2")); + JSHandle handleTagValItemValue1(thread, JSTaggedValue(intItemValue1)); + JSHandle handleTagValItemValue2(thread, JSTaggedValue(intItemValue2)); + JSHandle handleEcmaModuleAddFrom1(thread, EcmaModuleCreate(thread)); + JSHandle handleEcmaModuleAddFrom2(thread, EcmaModuleCreate(thread)); + JSHandle handleNameDict1(NameDictionary::Create(thread, numOfElementsDict1)); + JSHandle handleNameDict2(NameDictionary::Create(thread, numOfElementsDict2)); + handleEcmaModuleAddFrom1->SetNameDictionary(thread, handleNameDict1); + handleEcmaModuleAddFrom2->SetNameDictionary(thread, handleNameDict2); + EcmaModule::AddItem(thread, handleEcmaModuleAddFrom1, handleTagValItemName1, handleTagValItemValue1); + EcmaModule::AddItem(thread, handleEcmaModuleAddFrom2, handleTagValItemName2, handleTagValItemValue2); + JSHandle handleTaggedValueEcmaModuleAddFrom1(thread, handleEcmaModuleAddFrom1.GetTaggedValue()); + JSHandle handleTaggedValueEcmaModuleAddFrom2(thread, handleEcmaModuleAddFrom2.GetTaggedValue()); + + moduleManager->AddModule(handleTagValNameEcmaModuleAdd1, handleTaggedValueEcmaModuleAddFrom1); + moduleManager->AddModule(handleTagValNameEcmaModuleAdd2, handleTaggedValueEcmaModuleAddFrom2); + EXPECT_EQ(JSHandle::Cast(moduleManager->GetModule(thread, handleTagValNameEcmaModuleAdd1)) + ->GetItem(thread, handleTagValItemName1)->GetNumber(), intItemValue1); + EXPECT_EQ(JSHandle::Cast(moduleManager->GetModule(thread, handleTagValNameEcmaModuleAdd2)) + ->GetItem(thread, handleTagValItemName2)->GetNumber(), intItemValue2); + + moduleManager->RemoveModule(handleTagValNameEcmaModuleAdd1); + EXPECT_TRUE(moduleManager->GetModule(thread, handleTagValNameEcmaModuleAdd1)->IsUndefined()); + EXPECT_EQ(JSHandle::Cast(moduleManager->GetModule(thread, handleTagValNameEcmaModuleAdd2)) + ->GetItem(thread, handleTagValItemName2)->GetNumber(), intItemValue2); + + moduleManager->RemoveModule(handleTagValNameEcmaModuleAdd2); + EXPECT_TRUE(moduleManager->GetModule(thread, handleTagValNameEcmaModuleAdd1)->IsUndefined()); + EXPECT_TRUE(moduleManager->GetModule(thread, handleTagValNameEcmaModuleAdd2)->IsUndefined()); +} + +/* + * Feature: ModuleManager + * Function: SetCurrentExportModuleName + * SubFunction: GetCurrentExportModuleName + * FunctionPoints: Get Current ExportModuleName Of ModuleManager + * CaseDescription: Create a ModuleManager, check whether the ExportModuleName obtained from the ModuleManager through + * 'GetCurrentExportModuleName' function is within expectations while changing the Current + * ExportModuleName of the ModuleManager through 'SetCurrentExportModuleName' function. + */ +TEST_F(EcmaModuleTest, ModuleManager_SetCurrentExportModuleName) +{ + ModuleManager *moduleManager = thread->GetEcmaVM()->GetModuleManager(); + + std::string_view strViewNameEcmaModule1 = "NameEcmaModule1"; + std::string_view strViewNameEcmaModule2 = "NameEcmaModule2"; + moduleManager->SetCurrentExportModuleName(strViewNameEcmaModule1); + EXPECT_STREQ(moduleManager->GetCurrentExportModuleName().c_str(), CString(strViewNameEcmaModule1).c_str()); + moduleManager->SetCurrentExportModuleName(strViewNameEcmaModule2); + EXPECT_STREQ(moduleManager->GetCurrentExportModuleName().c_str(), CString(strViewNameEcmaModule2).c_str()); +} + +/* + * Feature: ModuleManager + * Function: GetPrevExportModuleName + * SubFunction: SetCurrentExportModuleName + * FunctionPoints: Get Previous ExportModuleName Of ModuleManager + * CaseDescription: Create a ModuleManager, check whether the previous ExportModuleName obtained from the ModuleManager + * through 'GetPrevExportModuleName' function is within expectations while changing the Current + * ExportModuleName of the ModuleManager through 'SetCurrentExportModuleName' function. + */ +TEST_F(EcmaModuleTest, ModuleManager_GetPrevExportModuleName) +{ + ModuleManager *moduleManager = thread->GetEcmaVM()->GetModuleManager(); + + std::string_view strViewNameEcmaModule1 = "NameEcmaModule1"; + std::string_view strViewNameEcmaModule2 = "NameEcmaModule2"; + std::string_view strViewNameEcmaModule3 = "NameEcmaModule3"; + moduleManager->SetCurrentExportModuleName(strViewNameEcmaModule1); + moduleManager->SetCurrentExportModuleName(strViewNameEcmaModule2); + EXPECT_STREQ(moduleManager->GetPrevExportModuleName().c_str(), CString(strViewNameEcmaModule1).c_str()); + moduleManager->SetCurrentExportModuleName(strViewNameEcmaModule3); + EXPECT_STREQ(moduleManager->GetPrevExportModuleName().c_str(), CString(strViewNameEcmaModule2).c_str()); +} + +/* + * Feature: ModuleManager + * Function: RestoreCurrentExportModuleName + * SubFunction: SetCurrentExportModuleName/GetCurrentExportModuleName + * FunctionPoints: Restore Current ExportModuleName Of ModuleManager + * CaseDescription: Create a ModuleManager, check whether the current ExportModuleName obtained from the ModuleManager + * through 'GetCurrentExportModuleName' function is within expectations while changing the Current + * ExportModuleName of the ModuleManager through 'SetCurrentExportModuleName' function and + * 'RestoreCurrentExportModuleName' function. + */ +TEST_F(EcmaModuleTest, ModuleManager_RestoreCurrentExportModuleName) +{ + ModuleManager *moduleManager = thread->GetEcmaVM()->GetModuleManager(); + + std::string_view strViewNameEcmaModule1 = "NameEcmaModule1"; + std::string_view strViewNameEcmaModule2 = "NameEcmaModule2"; + std::string_view strViewNameEcmaModule3 = "NameEcmaModule3"; + moduleManager->SetCurrentExportModuleName(strViewNameEcmaModule1); + moduleManager->SetCurrentExportModuleName(strViewNameEcmaModule2); + moduleManager->SetCurrentExportModuleName(strViewNameEcmaModule3); + EXPECT_STREQ(moduleManager->GetCurrentExportModuleName().c_str(), CString(strViewNameEcmaModule3).c_str()); + moduleManager->RestoreCurrentExportModuleName(); + EXPECT_STREQ(moduleManager->GetCurrentExportModuleName().c_str(), CString(strViewNameEcmaModule2).c_str()); + moduleManager->RestoreCurrentExportModuleName(); + EXPECT_STREQ(moduleManager->GetCurrentExportModuleName().c_str(), CString(strViewNameEcmaModule1).c_str()); +} + +/* + * Feature: ModuleManager + * Function: AddModuleItem + * SubFunction: SetCurrentExportModuleName/GetModule/GetModuleItem + * FunctionPoints: Add ModuleItem For Current EcmaModule Of ModuleManager + * CaseDescription: Create a ModuleManager, set the current EcmaModule for the ModuleManager through + * 'SetCurrentExportModuleName' function, add source ModuleItems for the current EcmaModule Of the + * ModuleManager, check whether the ModuleItems obtained through 'GetModuleItem' function from the + * ModuleManager are within expectations. + */ +TEST_F(EcmaModuleTest, ModuleManager_AddModuleItem) +{ + ObjectFactory *objFactory = thread->GetEcmaVM()->GetFactory(); + ModuleManager *moduleManager = thread->GetEcmaVM()->GetModuleManager(); + + int intItemValue11 = 11; + int intItemValue12 = 12; + int intItemValue21 = 21; + int intItemValue22 = 22; + JSHandle handleTagValItemName11(objFactory->NewFromCanBeCompressString("cStrItemName11")); + JSHandle handleTagValItemName12(objFactory->NewFromCanBeCompressString("cStrItemName12")); + JSHandle handleTagValItemName21(objFactory->NewFromCanBeCompressString("cStrItemName21")); + JSHandle handleTagValItemName22(objFactory->NewFromCanBeCompressString("cStrItemName22")); + JSHandle handleTagValItemValue11(thread, JSTaggedValue(intItemValue11)); + JSHandle handleTagValItemValue12(thread, JSTaggedValue(intItemValue12)); + JSHandle handleTagValItemValue21(thread, JSTaggedValue(intItemValue21)); + JSHandle handleTagValItemValue22(thread, JSTaggedValue(intItemValue22)); + JSHandle handleEcmaStrNameEcmaModule1 = objFactory->NewFromString("cStrNameEcmaModule1"); + JSHandle handleEcmaStrNameEcmaModule2 = objFactory->NewFromString("cStrNameEcmaModule2"); + std::string stdStrModuleFileName1 = JSLocale::ConvertToStdString(handleEcmaStrNameEcmaModule1); + std::string stdStrModuleFileName2 = JSLocale::ConvertToStdString(handleEcmaStrNameEcmaModule2); + JSHandle handleTagValEcmaModuleName1(handleEcmaStrNameEcmaModule1); + JSHandle handleTagValEcmaModuleName2(handleEcmaStrNameEcmaModule2); + + // Test when the module is created through 'NewEmptyEcmaModule' function called at TEST_F. + JSHandle handleEcmaModule1(thread, EcmaModuleCreate(thread)); + JSHandle handleTagValEcmaModule1(thread, handleEcmaModule1.GetTaggedValue()); + moduleManager->AddModule(handleTagValEcmaModuleName1, handleTagValEcmaModule1); + moduleManager->SetCurrentExportModuleName(stdStrModuleFileName1); + moduleManager->AddModuleItem(thread, handleTagValItemName11, handleTagValItemValue11); + moduleManager->AddModuleItem(thread, handleTagValItemName12, handleTagValItemValue12); + + EXPECT_EQ(moduleManager->GetModuleItem(thread, handleTagValEcmaModule1, handleTagValItemName11)->GetNumber(), + intItemValue11); + EXPECT_EQ(moduleManager->GetModuleItem(thread, handleTagValEcmaModule1, handleTagValItemName12)->GetNumber(), + intItemValue12); + + // Test when the module is created through 'NewEmptyEcmaModule' function called at "ecma_module.cpp". + moduleManager->SetCurrentExportModuleName(stdStrModuleFileName2); + moduleManager->AddModuleItem(thread, handleTagValItemName21, handleTagValItemValue21); + moduleManager->AddModuleItem(thread, handleTagValItemName22, handleTagValItemValue22); + + JSHandle handleTagValEcmaModule2 = moduleManager->GetModule(thread, handleTagValEcmaModuleName2); + EXPECT_EQ(moduleManager->GetModuleItem(thread, handleTagValEcmaModule1, handleTagValItemName11)->GetNumber(), + intItemValue11); + EXPECT_EQ(moduleManager->GetModuleItem(thread, handleTagValEcmaModule1, handleTagValItemName12)->GetNumber(), + intItemValue12); + EXPECT_EQ(moduleManager->GetModuleItem(thread, handleTagValEcmaModule2, handleTagValItemName21)->GetNumber(), + intItemValue21); + EXPECT_EQ(moduleManager->GetModuleItem(thread, handleTagValEcmaModule2, handleTagValItemName22)->GetNumber(), + intItemValue22); +} + +/* + * Feature: ModuleManager + * Function: CopyModule + * SubFunction: AddItem/SetCurrentExportModuleName/GetModule/GetModuleItem + * FunctionPoints: Copy EcmaModule To ModuleManager + * CaseDescription: Create two source EcmaModules and one target ModuleManager, prepare the two source EcmaModules + * through 'AddItem' function, check whether the the ModuleItems obtained through 'GetModuleItem' from + * the target ModuleManager are within expectations while changing the target ModuleManager through + * 'SetCurrentExportModuleName' function and 'CopyModule' function. + */ +TEST_F(EcmaModuleTest, ModuleManager_CopyModule) +{ + ObjectFactory *objFactory = thread->GetEcmaVM()->GetFactory(); + ModuleManager *moduleManager = thread->GetEcmaVM()->GetModuleManager(); + + int intItemValue11 = 11; + int intItemValue12 = 12; + int intItemValue21 = 21; + int intItemValue22 = 22; + std::string_view fileNameEcmaModuleCopyTo1 = "fileNameEcmaModuleCopyTo1"; + std::string_view fileNameEcmaModuleCopyTo2 = "fileNameEcmaModuleCopyTo2"; + JSHandle handleTagValItemName11(objFactory->NewFromCanBeCompressString("ItemName11")); + JSHandle handleTagValItemName12(objFactory->NewFromCanBeCompressString("ItemName12")); + JSHandle handleTagValItemName21(objFactory->NewFromCanBeCompressString("ItemName21")); + JSHandle handleTagValItemName22(objFactory->NewFromCanBeCompressString("ItemName22")); + JSHandle handleTagValItemValue11(thread, JSTaggedValue(intItemValue11)); + JSHandle handleTagValItemValue12(thread, JSTaggedValue(intItemValue12)); + JSHandle handleTagValItemValue21(thread, JSTaggedValue(intItemValue21)); + JSHandle handleTagValItemValue22(thread, JSTaggedValue(intItemValue22)); + JSHandle handleEcmaModuleCopyFrom1(thread, EcmaModuleCreate(thread)); + JSHandle handleEcmaModuleCopyFrom2(thread, EcmaModuleCreate(thread)); + JSHandle handleTagValEcmaModuleCopyFrom1(thread, handleEcmaModuleCopyFrom1.GetTaggedValue()); + JSHandle handleTagValEcmaModuleCopyFrom2(thread, handleEcmaModuleCopyFrom2.GetTaggedValue()); + EcmaModule::AddItem(thread, handleEcmaModuleCopyFrom1, handleTagValItemName11, handleTagValItemValue11); + EcmaModule::AddItem(thread, handleEcmaModuleCopyFrom1, handleTagValItemName12, handleTagValItemValue12); + EcmaModule::AddItem(thread, handleEcmaModuleCopyFrom2, handleTagValItemName21, handleTagValItemValue21); + EcmaModule::AddItem(thread, handleEcmaModuleCopyFrom2, handleTagValItemName22, handleTagValItemValue22); + + moduleManager->SetCurrentExportModuleName(fileNameEcmaModuleCopyTo1); + moduleManager->CopyModule(thread, handleTagValEcmaModuleCopyFrom1); + JSHandle handleTagValEcmaModuleCopyTo1 = moduleManager->GetModule(thread, + JSHandle::Cast(objFactory->NewFromString(CString(fileNameEcmaModuleCopyTo1)))); + EXPECT_EQ(intItemValue11, + moduleManager->GetModuleItem(thread, handleTagValEcmaModuleCopyTo1, handleTagValItemName11)->GetNumber()); + EXPECT_EQ(intItemValue12, + moduleManager->GetModuleItem(thread, handleTagValEcmaModuleCopyTo1, handleTagValItemName12)->GetNumber()); + + moduleManager->SetCurrentExportModuleName(fileNameEcmaModuleCopyTo2); + moduleManager->CopyModule(thread, handleTagValEcmaModuleCopyFrom2); + JSHandle handleTagValEcmaModuleCopyTo2 = moduleManager->GetModule(thread, + JSHandle::Cast(objFactory->NewFromString(CString(fileNameEcmaModuleCopyTo2)))); + EXPECT_EQ(intItemValue11, + moduleManager->GetModuleItem(thread, handleTagValEcmaModuleCopyTo1, handleTagValItemName11)->GetNumber()); + EXPECT_EQ(intItemValue12, + moduleManager->GetModuleItem(thread, handleTagValEcmaModuleCopyTo1, handleTagValItemName12)->GetNumber()); + EXPECT_EQ(intItemValue21, + moduleManager->GetModuleItem(thread, handleTagValEcmaModuleCopyTo2, handleTagValItemName21)->GetNumber()); + EXPECT_EQ(intItemValue22, + moduleManager->GetModuleItem(thread, handleTagValEcmaModuleCopyTo2, handleTagValItemName22)->GetNumber()); +} +} // namespace panda::ecmascript diff --git a/tests/runtime/common/ecma_string_test.cpp b/tests/runtime/common/ecma_string_test.cpp new file mode 100644 index 000000000..09ce4358b --- /dev/null +++ b/tests/runtime/common/ecma_string_test.cpp @@ -0,0 +1,1859 @@ +/* + * Copyright (c) 2021-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 "plugins/ecmascript/runtime/ecma_string-inl.h" +#include "plugins/ecmascript/tests/runtime/common/test_helper.h" + +using namespace panda::ecmascript; + +namespace panda::test { +class EcmaStringTest : public testing::Test { +public: + static void SetUpTestCase() + { + GTEST_LOG_(INFO) << "SetUpTestCase"; + } + + static void TearDownTestCase() + { + GTEST_LOG_(INFO) << "TearDownCase"; + } + + void SetUp() override + { + TestHelper::CreateEcmaVMWithScope(instance, thread, scope); + } + + void TearDown() override + { + TestHelper::DestroyEcmaVMWithScope(instance, scope); + } + PandaVM *instance {nullptr}; + ecmascript::EcmaHandleScope *scope {nullptr}; + JSThread *thread {nullptr}; +}; + +/* + * @tc.name: SetCompressedStringsEnabled + * @tc.desc: Check whether the bool returned through calling GetCompressedStringsEnabled function is within + * expectations after calling SetCompressedStringsEnabled function. + * @tc.type: FUNC + * @tc.require: + */ +TEST_F(EcmaStringTest, SetCompressedStringsEnabled) +{ + EXPECT_TRUE(EcmaString::GetCompressedStringsEnabled()); + EcmaString::SetCompressedStringsEnabled(false); + EXPECT_FALSE(EcmaString::GetCompressedStringsEnabled()); + EcmaString::SetCompressedStringsEnabled(true); + EXPECT_TRUE(EcmaString::GetCompressedStringsEnabled()); +} + +/* + * @tc.name: CanBeCompressed + * @tc.desc: Check whether the bool returned through calling CanBeCompressed function is within expectations before and + * after calling SetCompressedStringsEnabled function with false. + * @tc.type: FUNC + * @tc.require: + */ +TEST_F(EcmaStringTest, CanBeCompressed) +{ + uint8_t arrayU8[] = {12, 34, 77, 127, 99, 1}; + uint16_t arrayU16Comp[] = {1, 4, 37, 91, 127, 1}; + uint16_t arrayU16NotComp[] = {72, 43, 337, 961, 1317, 65535}; + EXPECT_TRUE(EcmaString::CanBeCompressed(arrayU8, sizeof(arrayU8) / sizeof(arrayU8[0]))); + EXPECT_TRUE(EcmaString::CanBeCompressed(arrayU16Comp, sizeof(arrayU16Comp) / sizeof(arrayU16Comp[0]))); + EXPECT_FALSE(EcmaString::CanBeCompressed(arrayU16NotComp, sizeof(arrayU16Comp) / sizeof(arrayU16Comp[0]))); + + EcmaString::SetCompressedStringsEnabled(false); // Set compressedStringsEnabled false. + EXPECT_FALSE(EcmaString::CanBeCompressed(arrayU16NotComp, sizeof(arrayU16Comp) / sizeof(arrayU16Comp[0]))); + /* Set compressedStringsEnabled default, because it is a static boolean that some other functions rely on.The foll- + * owing TEST_F will come to an unexpected result if we do not set it default in the end of this TEST_F. + */ + EcmaString::SetCompressedStringsEnabled(true); // Set compressedStringsEnabled true(default). +} + +/* + * @tc.name: CreateEmptyString + * @tc.desc: Check whether the EcmaString created through calling CreateEmptyString function is within expectations + * before and after calling SetCompressedStringsEnabled function with false. + * @tc.type: FUNC + * @tc.require: + */ +TEST_F(EcmaStringTest, CreateEmptyString) +{ + EcmaVM *ecmaVMPtr = EcmaVM::Cast(instance); + + JSHandle handleEcmaStrEmpty(thread, EcmaString::CreateEmptyString(ecmaVMPtr)); + EXPECT_EQ(handleEcmaStrEmpty->GetLength(), 0); + EXPECT_TRUE(handleEcmaStrEmpty->IsUtf8()); + EXPECT_FALSE(handleEcmaStrEmpty->IsUtf16()); + + EcmaString::SetCompressedStringsEnabled(false); // Set compressedStringsEnabled false. + JSHandle handleEcmaStrEmptyDisableComp(thread, EcmaString::CreateEmptyString(ecmaVMPtr)); + EXPECT_EQ(handleEcmaStrEmptyDisableComp->GetLength(), 0); + EXPECT_TRUE(handleEcmaStrEmptyDisableComp->IsUtf16()); + EcmaString::SetCompressedStringsEnabled(true); // Set compressedStringsEnabled true(default). +} + +/* + * @tc.name: AllocStringObject + * @tc.desc: Check whether the EcmaString created through calling AllocStringObject function is within expectations + * before and after calling SetCompressedStringsEnabled function with false. + * @tc.type: FUNC + * @tc.require: + */ +TEST_F(EcmaStringTest, AllocStringObject) +{ + EcmaVM* ecmaVMPtr = EcmaVM::Cast(instance); + + // AllocStringObject( , true, ). + size_t sizeAllocComp = 5; + JSHandle handleEcmaStrAllocComp(thread, EcmaString::AllocStringObject(sizeAllocComp, true, ecmaVMPtr)); + for (size_t i = 0; i < sizeAllocComp; i++) { + EXPECT_EQ(handleEcmaStrAllocComp->At(i), 0); + } + EXPECT_EQ(handleEcmaStrAllocComp->GetLength(), sizeAllocComp); + EXPECT_TRUE(handleEcmaStrAllocComp->IsUtf8()); + EXPECT_FALSE(handleEcmaStrAllocComp->IsUtf16()); + + // AllocStringObject( , false, ). + size_t sizeAllocNotComp = 5; + JSHandle handleEcmaStrAllocNotComp(thread, + EcmaString::AllocStringObject(sizeAllocNotComp, false, ecmaVMPtr)); + for (size_t i = 0; i < sizeAllocNotComp; i++) { + EXPECT_EQ(handleEcmaStrAllocNotComp->At(i), 0); + } + EXPECT_EQ(handleEcmaStrAllocNotComp->GetLength(), sizeAllocNotComp); + EXPECT_FALSE(handleEcmaStrAllocNotComp->IsUtf8()); + EXPECT_TRUE(handleEcmaStrAllocNotComp->IsUtf16()); + EcmaString::SetCompressedStringsEnabled(false); // Set compressedStringsEnabled false. + JSHandle handleEcmaStrAllocNotCompDisableComp(thread, + EcmaString::AllocStringObject(sizeAllocNotComp, false, ecmaVMPtr)); + EXPECT_TRUE(handleEcmaStrAllocNotCompDisableComp->IsUtf16()); + EcmaString::SetCompressedStringsEnabled(true); // Set compressedStringsEnabled true(default). +} + +/* + * @tc.name: CreateFromUtf8 + * @tc.desc: Check whether the EcmaString created through calling CreateFromUtf8 function is within expectations. + * @tc.type: FUNC + * @tc.require: + */ +TEST_F(EcmaStringTest, CreateFromUtf8) +{ + EcmaVM* ecmaVMPtr = EcmaVM::Cast(instance); + uint8_t arrayU8[] = {"xyz123!@#"}; + size_t lengthEcmaStrU8 = sizeof(arrayU8) - 1; + JSHandle handleEcmaStrU8(thread, + EcmaString::CreateFromUtf8(&arrayU8[0], lengthEcmaStrU8, ecmaVMPtr, true)); + for (size_t i = 0; i < lengthEcmaStrU8; i++) { + EXPECT_EQ(arrayU8[i], handleEcmaStrU8->At(i)); + } + EXPECT_EQ(handleEcmaStrU8->GetLength(), lengthEcmaStrU8); + EXPECT_TRUE(handleEcmaStrU8->IsUtf8()); + EXPECT_FALSE(handleEcmaStrU8->IsUtf16()); +} + +/* + * @tc.name: CreateFromUtf16 + * @tc.desc: Check whether the EcmaString created through calling CreateFromUtf16 function is within expectations + * before and after calling SetCompressedStringsEnabled function with false. + * @tc.type: FUNC + * @tc.require: + */ +TEST_F(EcmaStringTest, CreateFromUtf16) +{ + EcmaVM* ecmaVMPtr = EcmaVM::Cast(instance); + + // CreateFromUtf16( , , , true). + uint16_t arrayU16Comp[] = {1, 23, 45, 67, 127}; + size_t lengthEcmaStrU16Comp = sizeof(arrayU16Comp) / sizeof(arrayU16Comp[0]); + JSHandle handleEcmaStrU16Comp(thread, + EcmaString::CreateFromUtf16(&arrayU16Comp[0], lengthEcmaStrU16Comp, ecmaVMPtr, true)); + EXPECT_EQ(handleEcmaStrU16Comp->GetLength(), lengthEcmaStrU16Comp); + EXPECT_TRUE(handleEcmaStrU16Comp->IsUtf8()); + EXPECT_FALSE(handleEcmaStrU16Comp->IsUtf16()); + + // CreateFromUtf16( , , , false). + uint16_t arrayU16NotComp[] = {127, 33, 128, 12, 256, 11100, 65535}; + size_t lengthEcmaStrU16NotComp = sizeof(arrayU16NotComp) / sizeof(arrayU16NotComp[0]); + JSHandle handleEcmaStrU16NotComp(thread, + EcmaString::CreateFromUtf16(&arrayU16NotComp[0], lengthEcmaStrU16NotComp, ecmaVMPtr, false)); + EXPECT_EQ(handleEcmaStrU16NotComp->GetLength(), lengthEcmaStrU16NotComp); + EXPECT_FALSE(handleEcmaStrU16NotComp->IsUtf8()); + EXPECT_TRUE(handleEcmaStrU16NotComp->IsUtf16()); + EcmaString::SetCompressedStringsEnabled(false); // Set compressedStringsEnabled false. + JSHandle handleEcmaStrU16NotCompDisableComp(thread, + EcmaString::CreateFromUtf16(&arrayU16NotComp[0], lengthEcmaStrU16NotComp, ecmaVMPtr, false)); + EXPECT_TRUE(handleEcmaStrU16NotCompDisableComp->IsUtf16()); + EcmaString::SetCompressedStringsEnabled(true); // Set compressedStringsEnabled true(default). +} + +/* + * @tc.name: ComputeSizeUtf8 + * @tc.desc: Check whether the value returned through calling ComputeSizeUtf8 function is within expectations. + * @tc.type: FUNC + * @tc.require: + */ +TEST_F(EcmaStringTest, ComputeSizeUtf8) +{ + uint32_t scale = 3333; + for (uint32_t i = 0x40000000U - 1; i > scale; i = i - scale) { + uint32_t length = i; + EXPECT_EQ(EcmaString::ComputeSizeUtf8(length), length + EcmaString::SIZE); + } +} + +/* + * @tc.name: ComputeDataSizeUtf16 + * @tc.desc: Check whether the value returned through calling ComputeDataSizeUtf16 function is within expectations. + * @tc.type: FUNC + * @tc.require: + */ +TEST_F(EcmaStringTest, ComputeDataSizeUtf16) +{ + uint32_t scale = 3333; + for (uint32_t i = 0x40000000U - 1; i > scale; i = i - scale) { + uint32_t length = i; + EXPECT_EQ(EcmaString::ComputeDataSizeUtf16(length), 2 * length); + } +} + +/* + * @tc.name: ComputeSizeUtf16 + * @tc.desc: Check whether the value returned through calling ComputeSizeUtf16 function is within expectations. + * @tc.type: FUNC + * @tc.require: + */ +TEST_F(EcmaStringTest, ComputeSizeUtf16) +{ + uint32_t scale = 3333; + for (uint32_t i = 0x40000000U - 1; i > scale; i = i - scale) { + uint32_t length = i; + EXPECT_EQ(EcmaString::ComputeSizeUtf16(length), 2 * length + EcmaString::SIZE); + } +} + +/* + * @tc.name: ObjectSize + * @tc.desc: Check whether the value returned through calling ObjectSize function is within expectations. + * @tc.type: FUNC + * @tc.require: + */ +TEST_F(EcmaStringTest, ObjectSize) +{ + EcmaVM* ecmaVMPtr = EcmaVM::Cast(instance); + + JSHandle handleEcmaStrEmpty(thread, EcmaString::CreateEmptyString(ecmaVMPtr)); + EXPECT_EQ(handleEcmaStrEmpty->ObjectSize(), EcmaString::SIZE + 0); + + size_t lengthEcmaStrAllocComp = 5; + JSHandle handleEcmaStrAllocComp(thread, + EcmaString::AllocStringObject(lengthEcmaStrAllocComp, true, ecmaVMPtr)); + EXPECT_EQ(handleEcmaStrAllocComp->ObjectSize(), EcmaString::SIZE + sizeof(uint8_t) * lengthEcmaStrAllocComp); + + size_t lengthEcmaStrAllocNotComp = 5; + JSHandle handleEcmaStrAllocNotComp(thread, + EcmaString::AllocStringObject(lengthEcmaStrAllocNotComp, false, ecmaVMPtr)); + EXPECT_EQ(handleEcmaStrAllocNotComp->ObjectSize(), + EcmaString::SIZE + sizeof(uint16_t) * lengthEcmaStrAllocNotComp); + + uint8_t arrayU8[] = {"abcde"}; + size_t lengthEcmaStrU8 = sizeof(arrayU8) - 1; + JSHandle handleEcmaStrU8(thread, + EcmaString::CreateFromUtf8(&arrayU8[0], lengthEcmaStrU8, ecmaVMPtr, true)); + EXPECT_EQ(handleEcmaStrU8->ObjectSize(), EcmaString::SIZE + sizeof(uint8_t) * lengthEcmaStrU8); + + // ObjectSize(). EcmaString made by CreateFromUtf16( , , , true). + uint16_t arrayU16Comp[] = {1, 23, 45, 67, 127}; + size_t lengthEcmaStrU16Comp = sizeof(arrayU16Comp) / sizeof(arrayU16Comp[0]); + JSHandle handleEcmaStrU16Comp(thread, + EcmaString::CreateFromUtf16(&arrayU16Comp[0], lengthEcmaStrU16Comp, ecmaVMPtr, true)); + EXPECT_EQ(handleEcmaStrU16Comp->ObjectSize(), EcmaString::SIZE + sizeof(uint8_t) * lengthEcmaStrU16Comp); + + // ObjectSize(). EcmaString made by CreateFromUtf16( , , , false). + uint16_t arrayU16NotComp[] = {127, 128, 256, 11100, 65535}; + size_t lengthEcmaStrU16NotComp = sizeof(arrayU16NotComp) / sizeof(arrayU16NotComp[0]); + JSHandle handleEcmaStrU16NotComp(thread, + EcmaString::CreateFromUtf16(&arrayU16NotComp[0], lengthEcmaStrU16NotComp, ecmaVMPtr, false)); + EXPECT_EQ(handleEcmaStrU16NotComp->ObjectSize(), EcmaString::SIZE + sizeof(uint16_t) * lengthEcmaStrU16NotComp); +} + +/* + * @tc.name: Compare_001 + * @tc.desc: Check whether the value returned through calling Compare function between EcmaStrings made by + * CreateFromUtf8() is within expectations. + * @tc.type: FUNC + * @tc.require: + */ +TEST_F(EcmaStringTest, Compare_001) +{ + EcmaVM* ecmaVMPtr = EcmaVM::Cast(instance); + + // Compare(). Between EcmaStrings made by CreateFromUtf8(). + uint8_t arrayU8No1[3] = {1, 23}; + uint8_t arrayU8No2[4] = {1, 23, 49}; + uint8_t arrayU8No3[6] = {1, 23, 45, 97, 127}; + uint32_t lengthEcmaStrU8No1 = sizeof(arrayU8No1) - 1; + uint32_t lengthEcmaStrU8No2 = sizeof(arrayU8No2) - 1; + uint32_t lengthEcmaStrU8No3 = sizeof(arrayU8No3) - 1; + JSHandle handleEcmaStrU8No1(thread, + EcmaString::CreateFromUtf8(&arrayU8No1[0], lengthEcmaStrU8No1, ecmaVMPtr, true)); + JSHandle handleEcmaStrU8No2(thread, + EcmaString::CreateFromUtf8(&arrayU8No2[0], lengthEcmaStrU8No2, ecmaVMPtr, true)); + JSHandle handleEcmaStrU8No3(thread, + EcmaString::CreateFromUtf8(&arrayU8No3[0], lengthEcmaStrU8No3, ecmaVMPtr, true)); + EXPECT_EQ(handleEcmaStrU8No1->Compare(*handleEcmaStrU8No2), -1); + EXPECT_EQ(handleEcmaStrU8No2->Compare(*handleEcmaStrU8No1), 1); + EXPECT_EQ(handleEcmaStrU8No2->Compare(*handleEcmaStrU8No3), 49 - 45); + EXPECT_EQ(handleEcmaStrU8No3->Compare(*handleEcmaStrU8No2), 45 - 49); +} + +/* + * @tc.name: Compare_002 + * @tc.desc: Check whether the value returned through calling Compare function between EcmaStrings made by + * CreateFromUtf16( , , , true) is within expectations. + * @tc.type: FUNC + * @tc.require: + */ +TEST_F(EcmaStringTest, Compare_002) +{ + EcmaVM* ecmaVMPtr = EcmaVM::Cast(instance); + + // Compare(). Between EcmaStrings made by CreateFromUtf16( , , , true). + uint16_t arrayU16CompNo1[] = {1, 23}; + uint16_t arrayU16CompNo2[] = {1, 23, 49}; + uint16_t arrayU16CompNo3[] = {1, 23, 45, 97, 127}; + uint32_t lengthEcmaStrU16CompNo1 = sizeof(arrayU16CompNo1) / sizeof(arrayU16CompNo1[0]); + uint32_t lengthEcmaStrU16CompNo2 = sizeof(arrayU16CompNo2) / sizeof(arrayU16CompNo2[0]); + uint32_t lengthEcmaStrU16CompNo3 = sizeof(arrayU16CompNo3) / sizeof(arrayU16CompNo3[0]); + JSHandle handleEcmaStrU16CompNo1(thread, + EcmaString::CreateFromUtf16(&arrayU16CompNo1[0], lengthEcmaStrU16CompNo1, ecmaVMPtr, true)); + JSHandle handleEcmaStrU16CompNo2(thread, + EcmaString::CreateFromUtf16(&arrayU16CompNo2[0], lengthEcmaStrU16CompNo2, ecmaVMPtr, true)); + JSHandle handleEcmaStrU16CompNo3(thread, + EcmaString::CreateFromUtf16(&arrayU16CompNo3[0], lengthEcmaStrU16CompNo3, ecmaVMPtr, true)); + EXPECT_EQ(handleEcmaStrU16CompNo1->Compare(*handleEcmaStrU16CompNo2), -1); + EXPECT_EQ(handleEcmaStrU16CompNo2->Compare(*handleEcmaStrU16CompNo1), 1); + EXPECT_EQ(handleEcmaStrU16CompNo2->Compare(*handleEcmaStrU16CompNo3), 49 - 45); + EXPECT_EQ(handleEcmaStrU16CompNo3->Compare(*handleEcmaStrU16CompNo2), 45 - 49); +} + +/* + * @tc.name: Compare_003 + * @tc.desc: Check whether the value returned through calling Compare function between EcmaString made by + * CreateFromUtf8() and EcmaString made by CreateFromUtf16( , , , true) made by CreateFromUtf16( , , , true) is within + * expectations. + * @tc.type: FUNC + * @tc.require: + */ +TEST_F(EcmaStringTest, Compare_003) +{ + EcmaVM* ecmaVMPtr = EcmaVM::Cast(instance); + + // Compare(). EcmaString made by CreateFromUtf8() and EcmaString made by CreateFromUtf16( , , , true). + uint8_t arrayU8No1[3] = {1, 23}; + uint8_t arrayU8No2[4] = {1, 23, 49}; + uint16_t arrayU16CompNo1[] = {1, 23}; + uint16_t arrayU16CompNo2[] = {1, 23, 49}; + uint16_t arrayU16CompNo3[] = {1, 23, 45, 97, 127}; + uint32_t lengthEcmaStrU8No1 = sizeof(arrayU8No1) - 1; + uint32_t lengthEcmaStrU8No2 = sizeof(arrayU8No2) - 1; + uint32_t lengthEcmaStrU16CompNo1 = sizeof(arrayU16CompNo1) / sizeof(arrayU16CompNo1[0]); + uint32_t lengthEcmaStrU16CompNo2 = sizeof(arrayU16CompNo2) / sizeof(arrayU16CompNo2[0]); + uint32_t lengthEcmaStrU16CompNo3 = sizeof(arrayU16CompNo3) / sizeof(arrayU16CompNo3[0]); + JSHandle handleEcmaStrU8No1(thread, + EcmaString::CreateFromUtf8(&arrayU8No1[0], lengthEcmaStrU8No1, ecmaVMPtr, true)); + JSHandle handleEcmaStrU8No2(thread, + EcmaString::CreateFromUtf8(&arrayU8No2[0], lengthEcmaStrU8No2, ecmaVMPtr, true)); + JSHandle handleEcmaStrU16CompNo1(thread, + EcmaString::CreateFromUtf16(&arrayU16CompNo1[0], lengthEcmaStrU16CompNo1, ecmaVMPtr, true)); + JSHandle handleEcmaStrU16CompNo2(thread, + EcmaString::CreateFromUtf16(&arrayU16CompNo2[0], lengthEcmaStrU16CompNo2, ecmaVMPtr, true)); + JSHandle handleEcmaStrU16CompNo3(thread, + EcmaString::CreateFromUtf16(&arrayU16CompNo3[0], lengthEcmaStrU16CompNo3, ecmaVMPtr, true)); + EXPECT_EQ(handleEcmaStrU8No1->Compare(*handleEcmaStrU16CompNo1), 0); + EXPECT_EQ(handleEcmaStrU16CompNo1->Compare(*handleEcmaStrU8No1), 0); + EXPECT_EQ(handleEcmaStrU8No1->Compare(*handleEcmaStrU16CompNo2), -1); + EXPECT_EQ(handleEcmaStrU16CompNo2->Compare(*handleEcmaStrU8No1), 1); + EXPECT_EQ(handleEcmaStrU8No2->Compare(*handleEcmaStrU16CompNo3), 49 - 45); + EXPECT_EQ(handleEcmaStrU16CompNo3->Compare(*handleEcmaStrU8No2), 45 - 49); +} + +/* + * @tc.name: Compare_004 + * @tc.desc: Check whether the value returned through calling Compare function between EcmaStrings made by + * CreateFromUtf16( , , , false) is within expectations. + * @tc.type: FUNC + * @tc.require: + */ +TEST_F(EcmaStringTest, Compare_004) +{ + EcmaVM* ecmaVMPtr = EcmaVM::Cast(instance); + + // Compare(). Between EcmaStrings made by CreateFromUtf16( , , , false). + uint16_t arrayU16NotCompNo1[] = {1, 23}; + uint16_t arrayU16NotCompNo2[] = {1, 23, 49}; + uint16_t arrayU16NotCompNo3[] = {1, 23, 456, 6789, 65535, 127}; + uint32_t lengthEcmaStrU16NotCompNo1 = sizeof(arrayU16NotCompNo1) / sizeof(arrayU16NotCompNo1[0]); + uint32_t lengthEcmaStrU16NotCompNo2 = sizeof(arrayU16NotCompNo2) / sizeof(arrayU16NotCompNo2[0]); + uint32_t lengthEcmaStrU16NotCompNo3 = sizeof(arrayU16NotCompNo3) / sizeof(arrayU16NotCompNo3[0]); + JSHandle handleEcmaStrU16NotCompNo1(thread, + EcmaString::CreateFromUtf16(&arrayU16NotCompNo1[0], lengthEcmaStrU16NotCompNo1, ecmaVMPtr, false)); + JSHandle handleEcmaStrU16NotCompNo2(thread, + EcmaString::CreateFromUtf16(&arrayU16NotCompNo2[0], lengthEcmaStrU16NotCompNo2, ecmaVMPtr, false)); + JSHandle handleEcmaStrU16NotCompNo3(thread, + EcmaString::CreateFromUtf16(&arrayU16NotCompNo3[0], lengthEcmaStrU16NotCompNo3, ecmaVMPtr, false)); + EXPECT_EQ(handleEcmaStrU16NotCompNo1->Compare(*handleEcmaStrU16NotCompNo2), -1); + EXPECT_EQ(handleEcmaStrU16NotCompNo2->Compare(*handleEcmaStrU16NotCompNo1), 1); + EXPECT_EQ(handleEcmaStrU16NotCompNo2->Compare(*handleEcmaStrU16NotCompNo3), 49 - 456); + EXPECT_EQ(handleEcmaStrU16NotCompNo3->Compare(*handleEcmaStrU16NotCompNo2), 456 - 49); +} + +/* + * @tc.name: Compare_005 + * @tc.desc: Check whether the value returned through calling Compare function between EcmaString made by + * CreateFromUtf8() and EcmaString made by CreateFromUtf16( , , , false) is within expectations. + * @tc.type: FUNC + * @tc.require: + */ +TEST_F(EcmaStringTest, Compare_005) +{ + EcmaVM* ecmaVMPtr = EcmaVM::Cast(instance); + + // Compare(). EcmaString made by CreateFromUtf8() and EcmaString made by CreateFromUtf16( , , , false). + uint8_t arrayU8No1[3] = {1, 23}; + uint8_t arrayU8No2[4] = {1, 23, 49}; + uint16_t arrayU16NotCompNo1[] = {1, 23}; + uint16_t arrayU16NotCompNo2[] = {1, 23, 49}; + uint16_t arrayU16NotCompNo3[] = {1, 23, 456, 6789, 65535, 127}; + uint32_t lengthEcmaStrU8No1 = sizeof(arrayU8No1) - 1; + uint32_t lengthEcmaStrU8No2 = sizeof(arrayU8No2) - 1; + uint32_t lengthEcmaStrU16NotCompNo1 = sizeof(arrayU16NotCompNo1) / sizeof(arrayU16NotCompNo1[0]); + uint32_t lengthEcmaStrU16NotCompNo2 = sizeof(arrayU16NotCompNo2) / sizeof(arrayU16NotCompNo2[0]); + uint32_t lengthEcmaStrU16NotCompNo3 = sizeof(arrayU16NotCompNo3) / sizeof(arrayU16NotCompNo3[0]); + JSHandle handleEcmaStrU8No1(thread, + EcmaString::CreateFromUtf8(&arrayU8No1[0], lengthEcmaStrU8No1, ecmaVMPtr, true)); + JSHandle handleEcmaStrU8No2(thread, + EcmaString::CreateFromUtf8(&arrayU8No2[0], lengthEcmaStrU8No2, ecmaVMPtr, true)); + JSHandle handleEcmaStrU16NotCompNo1(thread, + EcmaString::CreateFromUtf16(&arrayU16NotCompNo1[0], lengthEcmaStrU16NotCompNo1, ecmaVMPtr, false)); + JSHandle handleEcmaStrU16NotCompNo2(thread, + EcmaString::CreateFromUtf16(&arrayU16NotCompNo2[0], lengthEcmaStrU16NotCompNo2, ecmaVMPtr, false)); + JSHandle handleEcmaStrU16NotCompNo3(thread, + EcmaString::CreateFromUtf16(&arrayU16NotCompNo3[0], lengthEcmaStrU16NotCompNo3, ecmaVMPtr, false)); + EXPECT_EQ(handleEcmaStrU8No1->Compare(*handleEcmaStrU16NotCompNo1), 0); + EXPECT_EQ(handleEcmaStrU16NotCompNo1->Compare(*handleEcmaStrU8No1), 0); + EXPECT_EQ(handleEcmaStrU8No1->Compare(*handleEcmaStrU16NotCompNo2), -1); + EXPECT_EQ(handleEcmaStrU16NotCompNo2->Compare(*handleEcmaStrU8No1), 1); + EXPECT_EQ(handleEcmaStrU8No2->Compare(*handleEcmaStrU16NotCompNo3), 49 - 456); + EXPECT_EQ(handleEcmaStrU16NotCompNo3->Compare(*handleEcmaStrU8No2), 456 - 49); +} + +/* + * @tc.name: Compare_006 + * @tc.desc: Check whether the value returned through calling Compare function between EcmaString made by + * CreateFromUtf16( , , , true) and EcmaString made by CreateFromUtf16( , , , false) is within expectations. + * @tc.type: FUNC + * @tc.require: + */ +TEST_F(EcmaStringTest, Compare_006) +{ + EcmaVM* ecmaVMPtr = EcmaVM::Cast(instance); + + // Compare(). EcmaString made by CreateFromUtf16( , , , true) and EcmaString made by CreateFromUtf16( , , , false). + uint16_t arrayU16CompNo1[] = {1, 23}; + uint16_t arrayU16CompNo2[] = {1, 23, 49}; + uint16_t arrayU16NotCompNo1[] = {1, 23}; + uint16_t arrayU16NotCompNo2[] = {1, 23, 49}; + uint16_t arrayU16NotCompNo3[] = {1, 23, 456, 6789, 65535, 127}; + uint32_t lengthEcmaStrU16CompNo1 = sizeof(arrayU16CompNo1) / sizeof(arrayU16CompNo1[0]); + uint32_t lengthEcmaStrU16CompNo2 = sizeof(arrayU16CompNo2) / sizeof(arrayU16CompNo2[0]); + uint32_t lengthEcmaStrU16NotCompNo1 = sizeof(arrayU16NotCompNo1) / sizeof(arrayU16NotCompNo1[0]); + uint32_t lengthEcmaStrU16NotCompNo2 = sizeof(arrayU16NotCompNo2) / sizeof(arrayU16NotCompNo2[0]); + uint32_t lengthEcmaStrU16NotCompNo3 = sizeof(arrayU16NotCompNo3) / sizeof(arrayU16NotCompNo3[0]); + JSHandle handleEcmaStrU16CompNo1(thread, + EcmaString::CreateFromUtf16(&arrayU16CompNo1[0], lengthEcmaStrU16CompNo1, ecmaVMPtr, true)); + JSHandle handleEcmaStrU16CompNo2(thread, + EcmaString::CreateFromUtf16(&arrayU16CompNo2[0], lengthEcmaStrU16CompNo2, ecmaVMPtr, true)); + JSHandle handleEcmaStrU16NotCompNo1(thread, + EcmaString::CreateFromUtf16(&arrayU16NotCompNo1[0], lengthEcmaStrU16NotCompNo1, ecmaVMPtr, false)); + JSHandle handleEcmaStrU16NotCompNo2(thread, + EcmaString::CreateFromUtf16(&arrayU16NotCompNo2[0], lengthEcmaStrU16NotCompNo2, ecmaVMPtr, false)); + JSHandle handleEcmaStrU16NotCompNo3(thread, + EcmaString::CreateFromUtf16(&arrayU16NotCompNo3[0], lengthEcmaStrU16NotCompNo3, ecmaVMPtr, false)); + EXPECT_EQ(handleEcmaStrU16CompNo1->Compare(*handleEcmaStrU16NotCompNo1), 0); + EXPECT_EQ(handleEcmaStrU16NotCompNo1->Compare(*handleEcmaStrU16CompNo1), 0); + EXPECT_EQ(handleEcmaStrU16CompNo1->Compare(*handleEcmaStrU16NotCompNo2), -1); + EXPECT_EQ(handleEcmaStrU16NotCompNo2->Compare(*handleEcmaStrU16CompNo1), 1); + EXPECT_EQ(handleEcmaStrU16CompNo2->Compare(*handleEcmaStrU16NotCompNo3), 49 - 456); + EXPECT_EQ(handleEcmaStrU16NotCompNo3->Compare(*handleEcmaStrU16CompNo2), 456 - 49); +} + +/* + * @tc.name: Concat_001 + * @tc.desc: Check whether the EcmaString returned through calling Concat function between EcmaString made by + * CreateFromUtf8() and EcmaString made by CreateFromUtf8() is within expectations. + * @tc.type: FUNC + * @tc.require: + */ +TEST_F(EcmaStringTest, Concat_001) +{ + EcmaVM* ecmaVMPtr = EcmaVM::Cast(instance); + + // Concat(). EcmaString made by CreateFromUtf8() and EcmaString made by CreateFromUtf8(). + uint8_t arrayFrontU8[] = {"abcdef"}; + uint8_t arrayBackU8[] = {"ABCDEF"}; + uint32_t lengthEcmaStrFrontU8 = sizeof(arrayFrontU8) - 1; + uint32_t lengthEcmaStrBackU8 = sizeof(arrayBackU8) - 1; + JSHandle handleEcmaStrFrontU8(thread, + EcmaString::CreateFromUtf8(&arrayFrontU8[0], lengthEcmaStrFrontU8, ecmaVMPtr, true)); + JSHandle handleEcmaStrBackU8(thread, + EcmaString::CreateFromUtf8(&arrayBackU8[0], lengthEcmaStrBackU8, ecmaVMPtr, true)); + JSHandle handleEcmaStrConcatU8(thread, + EcmaString::Concat(handleEcmaStrFrontU8, handleEcmaStrBackU8, ecmaVMPtr)); + EXPECT_TRUE(handleEcmaStrConcatU8->IsUtf8()); + for (size_t i = 0; i < lengthEcmaStrFrontU8; i++) { + EXPECT_EQ(handleEcmaStrConcatU8->At(i), arrayFrontU8[i]); + } + for (size_t i = 0; i < lengthEcmaStrBackU8; i++) { + EXPECT_EQ(handleEcmaStrConcatU8->At(i + lengthEcmaStrFrontU8), arrayBackU8[i]); + } + EXPECT_EQ(handleEcmaStrConcatU8->GetLength(), lengthEcmaStrFrontU8 + lengthEcmaStrBackU8); +} + +/* + * @tc.name: Concat_002 + * @tc.desc: Check whether the EcmaString returned through calling Concat function between EcmaString made by + * CreateFromUtf16( , , , false) and EcmaString made by CreateFromUtf16( , , , false) is within expectations. + * @tc.type: FUNC + * @tc.require: + */ +TEST_F(EcmaStringTest, Concat_002) +{ + EcmaVM* ecmaVMPtr = EcmaVM::Cast(instance); + + // Concat(). EcmaString made by CreateFromUtf16( , , , false) and EcmaString made by CreateFromUtf16( , , , false). + uint16_t arrayFrontU16NotComp[] = {128, 129, 256, 11100, 65535, 100}; + uint16_t arrayBackU16NotComp[] = {88, 768, 1, 270, 345, 333}; + uint32_t lengthEcmaStrFrontU16NotComp = sizeof(arrayFrontU16NotComp) / sizeof(arrayFrontU16NotComp[0]); + uint32_t lengthEcmaStrBackU16NotComp = sizeof(arrayBackU16NotComp) / sizeof(arrayBackU16NotComp[0]); + JSHandle handleEcmaStrFrontU16NotComp(thread, + EcmaString::CreateFromUtf16(&arrayFrontU16NotComp[0], lengthEcmaStrFrontU16NotComp, ecmaVMPtr, false)); + JSHandle handleEcmaStrBackU16NotComp(thread, + EcmaString::CreateFromUtf16(&arrayBackU16NotComp[0], lengthEcmaStrBackU16NotComp, ecmaVMPtr, false)); + JSHandle handleEcmaStrConcatU16NotComp(thread, + EcmaString::Concat(handleEcmaStrFrontU16NotComp, handleEcmaStrBackU16NotComp, ecmaVMPtr)); + EXPECT_TRUE(handleEcmaStrConcatU16NotComp->IsUtf16()); + for (size_t i = 0; i < lengthEcmaStrFrontU16NotComp; i++) { + EXPECT_EQ(handleEcmaStrConcatU16NotComp->At(i), arrayFrontU16NotComp[i]); + } + for (size_t i = 0; i < lengthEcmaStrBackU16NotComp; i++) { + EXPECT_EQ(handleEcmaStrConcatU16NotComp->At(i + lengthEcmaStrFrontU16NotComp), arrayBackU16NotComp[i]); + } + EXPECT_EQ(handleEcmaStrConcatU16NotComp->GetLength(), lengthEcmaStrFrontU16NotComp + lengthEcmaStrBackU16NotComp); +} + +/* + * @tc.name: Concat_003 + * @tc.desc: Check whether the EcmaString returned through calling Concat function between EcmaString made by + * CreateFromUtf8() and EcmaString made by CreateFromUtf16( , , , false) is within expectations. + * @tc.type: FUNC + * @tc.require: + */ +TEST_F(EcmaStringTest, Concat_003) +{ + EcmaVM* ecmaVMPtr = EcmaVM::Cast(instance); + + // Concat(). EcmaString made by CreateFromUtf8() and EcmaString made by CreateFromUtf16( , , , false). + uint8_t arrayFrontU8[] = {"abcdef"}; + uint16_t arrayBackU16NotComp[] = {88, 768, 1, 270, 345, 333}; + uint32_t lengthEcmaStrFrontU8 = sizeof(arrayFrontU8) - 1; + uint32_t lengthEcmaStrBackU16NotComp = sizeof(arrayBackU16NotComp) / sizeof(arrayBackU16NotComp[0]); + JSHandle handleEcmaStrFrontU8(thread, + EcmaString::CreateFromUtf8(&arrayFrontU8[0], lengthEcmaStrFrontU8, ecmaVMPtr, true)); + JSHandle handleEcmaStrBackU16NotComp(thread, + EcmaString::CreateFromUtf16(&arrayBackU16NotComp[0], lengthEcmaStrBackU16NotComp, ecmaVMPtr, false)); + JSHandle handleEcmaStrConcatU8U16NotComp(thread, + EcmaString::Concat(handleEcmaStrFrontU8, handleEcmaStrBackU16NotComp, ecmaVMPtr)); + EXPECT_TRUE(handleEcmaStrConcatU8U16NotComp->IsUtf16()); + for (size_t i = 0; i < lengthEcmaStrFrontU8; i++) { + EXPECT_EQ(handleEcmaStrConcatU8U16NotComp->At(i), arrayFrontU8[i]); + } + for (size_t i = 0; i < lengthEcmaStrBackU16NotComp; i++) { + EXPECT_EQ(handleEcmaStrConcatU8U16NotComp->At(i + lengthEcmaStrFrontU8), arrayBackU16NotComp[i]); + } + EXPECT_EQ(handleEcmaStrConcatU8U16NotComp->GetLength(), lengthEcmaStrFrontU8 + lengthEcmaStrBackU16NotComp); +} + +/* + * @tc.name: Concat_004 + * @tc.desc: Call SetCompressedStringsEnabled() function with false, check whether the EcmaString returned through + * calling Concat function between EcmaString made by CreateFromUtf8() and EcmaString made by + * CreateFromUtf16( , , , false) is within expectations. + * @tc.type: FUNC + * @tc.require: + */ +TEST_F(EcmaStringTest, Concat_004) +{ + EcmaVM* ecmaVMPtr = EcmaVM::Cast(instance); + + /* Concat() after SetCompressedStringsEnabled(false). EcmaString made by CreateFromUtf16( , , , false) and + * EcmaString made by CreateFromUtf16( , , , false). + */ + EcmaString::SetCompressedStringsEnabled(false); // Set compressedStringsEnabled false. + uint16_t arrayFrontU16NotComp[] = {128, 129, 256, 11100, 65535, 100}; + uint16_t arrayBackU16NotComp[] = {88, 768, 1, 270, 345, 333}; + uint32_t lengthEcmaStrFrontU16NotComp = sizeof(arrayFrontU16NotComp) / sizeof(arrayFrontU16NotComp[0]); + uint32_t lengthEcmaStrBackU16NotComp = sizeof(arrayBackU16NotComp) / sizeof(arrayBackU16NotComp[0]); + JSHandle handleEcmaStrFrontU16NotComp(thread, + EcmaString::CreateFromUtf16(&arrayFrontU16NotComp[0], lengthEcmaStrFrontU16NotComp, ecmaVMPtr, false)); + JSHandle handleEcmaStrBackU16NotComp(thread, + EcmaString::CreateFromUtf16(&arrayBackU16NotComp[0], lengthEcmaStrBackU16NotComp, ecmaVMPtr, false)); + JSHandle handleEcmaStrConcatU16NotCompAfterSetFalse(thread, + EcmaString::Concat(handleEcmaStrFrontU16NotComp, handleEcmaStrBackU16NotComp, ecmaVMPtr)); + EXPECT_TRUE(handleEcmaStrConcatU16NotCompAfterSetFalse->IsUtf16()); + for (size_t i = 0; i < lengthEcmaStrFrontU16NotComp; i++) { + EXPECT_EQ(handleEcmaStrConcatU16NotCompAfterSetFalse->At(i), arrayFrontU16NotComp[i]); + } + for (size_t i = 0; i < lengthEcmaStrBackU16NotComp; i++) { + EXPECT_EQ(handleEcmaStrConcatU16NotCompAfterSetFalse->At(i + lengthEcmaStrFrontU16NotComp), + arrayBackU16NotComp[i]); + } + EXPECT_EQ(handleEcmaStrConcatU16NotCompAfterSetFalse->GetLength(), + lengthEcmaStrFrontU16NotComp + lengthEcmaStrBackU16NotComp); + EcmaString::SetCompressedStringsEnabled(true); // Set compressedStringsEnabled true(default). +} + +/* + * @tc.name: FastSubString_001 + * @tc.desc: Check whether the EcmaString returned through calling FastSubString function from EcmaString made by + * CreateFromUtf8() is within expectations. + * @tc.type: FUNC + * @tc.require: + */ +TEST_F(EcmaStringTest, FastSubString_001) +{ + EcmaVM* ecmaVMPtr = EcmaVM::Cast(instance); + + // FastSubString(). From EcmaString made by CreateFromUtf8(). + uint8_t arrayU8[6] = {3, 7, 19, 54, 99}; + uint32_t lengthEcmaStrU8 = sizeof(arrayU8) - 1; + JSHandle handleEcmaStrU8(thread, + EcmaString::CreateFromUtf8(&arrayU8[0], lengthEcmaStrU8, ecmaVMPtr, true)); + uint32_t indexStartSubU8 = 2; + uint32_t lengthSubU8 = 2; + JSHandle handleEcmaStrSubU8(thread, + EcmaString::FastSubString(handleEcmaStrU8, indexStartSubU8, lengthSubU8, ecmaVMPtr)); + for (size_t i = 0; i < lengthSubU8; i++) { + EXPECT_EQ(handleEcmaStrSubU8->At(i), handleEcmaStrU8->At(i + indexStartSubU8)); + } + EXPECT_EQ(handleEcmaStrSubU8->GetLength(), lengthSubU8); +} + +/* + * @tc.name: FastSubString_002 + * @tc.desc: Check whether the EcmaString returned through calling FastSubString function from EcmaString made by + * CreateFromUtf16( , , , true) is within expectations. + * @tc.type: FUNC + * @tc.require: + */ +TEST_F(EcmaStringTest, FastSubString_002) +{ + EcmaVM* ecmaVMPtr = EcmaVM::Cast(instance); + + // FastSubString(). From EcmaString made by CreateFromUtf16( , , , true). + uint16_t arrayU16Comp[] = {1, 12, 34, 56, 127}; + uint32_t lengthEcmaStrU16Comp = sizeof(arrayU16Comp) / sizeof(arrayU16Comp[0]); + JSHandle handleEcmaStrU16Comp(thread, + EcmaString::CreateFromUtf16(&arrayU16Comp[0], lengthEcmaStrU16Comp, ecmaVMPtr, true)); + uint32_t indexStartSubU16Comp = 0; + uint32_t lengthSubU16Comp = 2; + JSHandle handleEcmaStrSubU16Comp(thread, + EcmaString::FastSubString(handleEcmaStrU16Comp, indexStartSubU16Comp, lengthSubU16Comp, ecmaVMPtr)); + for (size_t i = 0; i < lengthSubU16Comp; i++) { + EXPECT_EQ(handleEcmaStrSubU16Comp->At(i), handleEcmaStrU16Comp->At(i + indexStartSubU16Comp)); + } + EXPECT_EQ(handleEcmaStrSubU16Comp->GetLength(), lengthSubU16Comp); +} + +/* + * @tc.name: FastSubString_003 + * @tc.desc: Check whether the EcmaString returned through calling FastSubString function from EcmaString made by + * CreateFromUtf16( , , , false) is within expectations. + * @tc.type: FUNC + * @tc.require: + */ +TEST_F(EcmaStringTest, FastSubString_003) +{ + EcmaVM* ecmaVMPtr = EcmaVM::Cast(instance); + + // FastSubString(). From EcmaString made by CreateFromUtf16( , , , false). + uint16_t arrayU16NotComp[] = {19, 54, 256, 11100, 65535}; + uint32_t lengthEcmaStrU16NotComp = sizeof(arrayU16NotComp) / sizeof(arrayU16NotComp[0]); + JSHandle handleEcmaStrU16NotComp(thread, + EcmaString::CreateFromUtf16(&arrayU16NotComp[0], lengthEcmaStrU16NotComp, ecmaVMPtr, false)); + uint32_t indexStartSubU16NotComp = 0; + uint32_t lengthSubU16NotComp = 2; + JSHandle handleEcmaStrSubU16NotComp(thread, + EcmaString::FastSubString(handleEcmaStrU16NotComp, indexStartSubU16NotComp, lengthSubU16NotComp, ecmaVMPtr)); + for (size_t i = 0; i < lengthSubU16NotComp; i++) { + EXPECT_EQ(handleEcmaStrSubU16NotComp->At(i), handleEcmaStrU16NotComp->At(i + indexStartSubU16NotComp)); + } + EXPECT_EQ(handleEcmaStrSubU16NotComp->GetLength(), lengthSubU16NotComp); +} + +/* + * @tc.name: WriteData_001 + * @tc.desc: Check whether the target EcmaString made by AllocStringObject( , true, ) changed through calling WriteData + * function with a source EcmaString made by CreateFromUtf8() is within expectations. + * @tc.type: FUNC + * @tc.require: + */ +TEST_F(EcmaStringTest, WriteData_001) +{ + EcmaVM* ecmaVMPtr = EcmaVM::Cast(instance); + + // WriteData(). From EcmaString made by CreateFromUtf8() to EcmaString made by AllocStringObject( , true, ). + uint8_t arrayU8WriteFrom[6] = {1, 12, 34, 56, 127}; + uint32_t lengthEcmaStrU8WriteFrom = sizeof(arrayU8WriteFrom) - 1; + JSHandle handleEcmaStrU8WriteFrom(thread, + EcmaString::CreateFromUtf8(&arrayU8WriteFrom[0], lengthEcmaStrU8WriteFrom, ecmaVMPtr, true)); + size_t sizeEcmaStrU8WriteTo = 5; + JSHandle handleEcmaStrAllocTrueWriteTo(thread, + EcmaString::AllocStringObject(sizeEcmaStrU8WriteTo, true, ecmaVMPtr)); + uint32_t indexStartWriteFromArrayU8 = 2; + uint32_t lengthWriteFromArrayU8 = 2; + handleEcmaStrAllocTrueWriteTo->WriteData(*handleEcmaStrU8WriteFrom, indexStartWriteFromArrayU8, + sizeEcmaStrU8WriteTo, lengthWriteFromArrayU8); + for (size_t i = 0; i < lengthWriteFromArrayU8; i++) { + EXPECT_EQ(handleEcmaStrAllocTrueWriteTo->At(i + indexStartWriteFromArrayU8), handleEcmaStrU8WriteFrom->At(i)); + } +} + +/* + * @tc.name: WriteData_002 + * @tc.desc: Check whether the target EcmaString made by AllocStringObject( , true, ) changed through calling WriteData + * function from a source char is within expectations. + * @tc.type: FUNC + * @tc.require: + */ +TEST_F(EcmaStringTest, WriteData_002) +{ + EcmaVM* ecmaVMPtr = EcmaVM::Cast(instance); + + // WriteData(). From char to EcmaString made by AllocStringObject( , true, ). + char u8Write = 'a'; + size_t sizeEcmaStrU8WriteTo = 5; + JSHandle handleEcmaStrAllocTrueWriteTo(thread, + EcmaString::AllocStringObject(sizeEcmaStrU8WriteTo, true, ecmaVMPtr)); + uint32_t indexAtWriteFromU8 = 4; + handleEcmaStrAllocTrueWriteTo->WriteData(u8Write, indexAtWriteFromU8); + EXPECT_EQ(handleEcmaStrAllocTrueWriteTo->At(indexAtWriteFromU8), u8Write); +} + +/* + * @tc.name: WriteData_003 + * @tc.desc: Check whether the target EcmaString made by AllocStringObject( , false, ) changed through calling + * WriteData function with a source EcmaString made by CreateFromUtf16( , , , false) is within expectations. + * @tc.type: FUNC + * @tc.require: + */ +TEST_F(EcmaStringTest, WriteData_003) +{ + EcmaVM* ecmaVMPtr = EcmaVM::Cast(instance); + + /* WriteData(). From EcmaString made by CreateFromUtf16( , , , false) to EcmaStringU16 made by + * AllocStringObject( , false, ). + */ + uint16_t arrayU16WriteFrom[10] = {67, 777, 1999, 1, 45, 66, 23456, 65535, 127, 333}; + uint32_t lengthEcmaStrU16WriteFrom = sizeof(arrayU16WriteFrom) / sizeof(arrayU16WriteFrom[0]); + JSHandle handleEcmaStrU16WriteFrom(thread, + EcmaString::CreateFromUtf16(&arrayU16WriteFrom[0], lengthEcmaStrU16WriteFrom, ecmaVMPtr, false)); + size_t sizeEcmaStrU16WriteTo = 10; + JSHandle handleEcmaStrU16WriteTo(thread, + EcmaString::AllocStringObject(sizeEcmaStrU16WriteTo, false, ecmaVMPtr)); + uint32_t indexStartWriteFromArrayU16 = 3; + uint32_t numBytesWriteFromArrayU16 = 2 * 3; + handleEcmaStrU16WriteTo->WriteData(*handleEcmaStrU16WriteFrom, indexStartWriteFromArrayU16, sizeEcmaStrU16WriteTo, + numBytesWriteFromArrayU16); + for (size_t i = 0; i < (numBytesWriteFromArrayU16 / 2); i++) { + EXPECT_EQ(handleEcmaStrU16WriteTo->At(i + indexStartWriteFromArrayU16), handleEcmaStrU16WriteFrom->At(i)); + } +} + +/* + * @tc.name: WriteData_004 + * @tc.desc: Check whether the target EcmaString made by AllocStringObject( , false, ) changed through calling + * WriteData function with a source EcmaString made by CreateFromUtf8() is within expectations. + * @tc.type: FUNC + * @tc.require: + */ +TEST_F(EcmaStringTest, WriteData_004) +{ + EcmaVM* ecmaVMPtr = EcmaVM::Cast(instance); + + // WriteData(). From EcmaString made by CreateFromUtf8() to EcmaString made by AllocStringObject( , false, ). + uint8_t arrayU8WriteFrom[6] = {1, 12, 34, 56, 127}; + uint32_t lengthEcmaStrU8WriteFrom = sizeof(arrayU8WriteFrom) - 1; + JSHandle handleEcmaStrU8WriteFrom(thread, + EcmaString::CreateFromUtf8(&arrayU8WriteFrom[0], lengthEcmaStrU8WriteFrom, ecmaVMPtr, true)); + size_t sizeEcmaStrU16WriteTo = 10; + JSHandle handleEcmaStrU16WriteTo(thread, + EcmaString::AllocStringObject(sizeEcmaStrU16WriteTo, false, ecmaVMPtr)); + uint32_t indexStartWriteFromU8ToU16 = 1; + uint32_t numBytesWriteFromU8ToU16 = 4; + handleEcmaStrU16WriteTo->WriteData(*handleEcmaStrU8WriteFrom, indexStartWriteFromU8ToU16, sizeEcmaStrU16WriteTo, + numBytesWriteFromU8ToU16); + for (size_t i = 0; i < numBytesWriteFromU8ToU16; i++) { + EXPECT_EQ(handleEcmaStrU16WriteTo->At(i + indexStartWriteFromU8ToU16), handleEcmaStrU8WriteFrom->At(i)); + } +} + +/* + * @tc.name: WriteData_005 + * @tc.desc: Check whether the target EcmaString made by AllocStringObject( , false, ) changed through calling + * WriteData function with a source char is within expectations. + * @tc.type: FUNC + * @tc.require: + */ +TEST_F(EcmaStringTest, WriteData_005) +{ + EcmaVM* ecmaVMPtr = EcmaVM::Cast(instance); + + // WriteData(). From char to EcmaString made by AllocStringObject( , false, ). + size_t sizeEcmaStrU16WriteTo = 10; + JSHandle handleEcmaStrU16WriteTo(thread, + EcmaString::AllocStringObject(sizeEcmaStrU16WriteTo, false, ecmaVMPtr)); + char u8Write = 'a'; + uint32_t indexAt = 4; + handleEcmaStrU16WriteTo->WriteData(u8Write, indexAt); + EXPECT_EQ(handleEcmaStrU16WriteTo->At(indexAt), u8Write); +} + +/* + * @tc.name: GetUtf8Length + * @tc.desc: Check whether the value returned through calling GetUtf8Length function is within expectations. + * @tc.type: FUNC + * @tc.require: + */ +TEST_F(EcmaStringTest, GetUtf8Length) +{ + EcmaVM* ecmaVMPtr = EcmaVM::Cast(instance); + uint8_t arrayU8[6] = {3, 7, 19, 54, 99}; + uint16_t arrayU16Comp[] = {1, 12, 34, 56, 127}; + uint16_t arrayU16NotComp[] = {19, 54, 256, 11100, 65535}; + uint32_t lengthEcmaStrU8 = sizeof(arrayU8) - 1; + uint32_t lengthEcmaStrU16Comp = sizeof(arrayU16Comp) / sizeof(arrayU16Comp[0]); + uint32_t lengthEcmaStrU16NotComp = sizeof(arrayU16NotComp) / sizeof(arrayU16NotComp[0]); + JSHandle handleEcmaStrU8(thread, + EcmaString::CreateFromUtf8(&arrayU8[0], lengthEcmaStrU8, ecmaVMPtr, true)); + JSHandle handleEcmaStrU16Comp(thread, + EcmaString::CreateFromUtf16(&arrayU16Comp[0], lengthEcmaStrU16Comp, ecmaVMPtr, true)); + JSHandle handleEcmaStrU16NotComp(thread, + EcmaString::CreateFromUtf16(&arrayU16NotComp[0], lengthEcmaStrU16NotComp, ecmaVMPtr, false)); + EXPECT_EQ(handleEcmaStrU8->GetUtf8Length(), lengthEcmaStrU8 + 1); + EXPECT_EQ(handleEcmaStrU16Comp->GetUtf8Length(), lengthEcmaStrU16Comp + 1); + EXPECT_EQ(handleEcmaStrU16NotComp->GetUtf8Length(), 2 * lengthEcmaStrU16NotComp + 1); +} + +/* + * @tc.name: GetUtf16Length + * @tc.desc: Check whether the value returned through calling GetUtf16Length function is within expectations. + * @tc.type: FUNC + * @tc.require: + */ +TEST_F(EcmaStringTest, GetUtf16Length) +{ + EcmaVM* ecmaVMPtr = EcmaVM::Cast(instance); + + uint8_t arrayU8[6] = {3, 7, 19, 54, 99}; + uint16_t arrayU16Comp[] = {1, 12, 34, 56, 127}; + uint16_t arrayU16NotComp[] = {19, 54, 256, 11100, 65535}; + uint32_t lengthEcmaStrU8 = sizeof(arrayU8) - 1; + uint32_t lengthEcmaStrU16Comp = sizeof(arrayU16Comp) / sizeof(arrayU16Comp[0]); + uint32_t lengthEcmaStrU16NotComp = sizeof(arrayU16NotComp) / sizeof(arrayU16NotComp[0]); + JSHandle handleEcmaStrU8(thread, + EcmaString::CreateFromUtf8(&arrayU8[0], lengthEcmaStrU8, ecmaVMPtr, true)); + JSHandle handleEcmaStrU16Comp(thread, + EcmaString::CreateFromUtf16(&arrayU16Comp[0], lengthEcmaStrU16Comp, ecmaVMPtr, true)); + JSHandle handleEcmaStrU16NotComp(thread, + EcmaString::CreateFromUtf16(&arrayU16NotComp[0], lengthEcmaStrU16NotComp, ecmaVMPtr, false)); + EXPECT_EQ(handleEcmaStrU8->GetUtf16Length(), lengthEcmaStrU8); + EXPECT_EQ(handleEcmaStrU16Comp->GetUtf16Length(), lengthEcmaStrU16Comp); + EXPECT_EQ(handleEcmaStrU16NotComp->GetUtf16Length(), lengthEcmaStrU16NotComp); +} + +/* + * @tc.name: GetDataUtf8 + * @tc.desc: Check whether the pointer returned through calling GetDataUtf8 function is within expectations. + * @tc.type: FUNC + * @tc.require: + */ +TEST_F(EcmaStringTest, GetDataUtf8) +{ + EcmaVM* ecmaVMPtr = EcmaVM::Cast(instance); + + // From EcmaString made by CreateFromUtf8(). + uint8_t arrayU8[] = {"abcde"}; + uint32_t lengthEcmaStrU8 = sizeof(arrayU8) - 1; + JSHandle handleEcmaStrU8(thread, + EcmaString::CreateFromUtf8(&arrayU8[0], lengthEcmaStrU8, ecmaVMPtr, true)); + for (size_t i =0; i < lengthEcmaStrU8; i++) { + EXPECT_EQ(*(handleEcmaStrU8->GetDataUtf8() + i), arrayU8[i]); + } + + // From EcmaString made by CreateFromUtf16( , , , true). + uint16_t arrayU16Comp[] = {3, 1, 34, 123, 127, 111, 42, 3, 20, 10}; + uint32_t lengthEcmaStrU16Comp = sizeof(arrayU16Comp) / sizeof(arrayU16Comp[0]); + JSHandle handleEcmaStrU16Comp(thread, + EcmaString::CreateFromUtf16(&arrayU16Comp[0], lengthEcmaStrU16Comp, ecmaVMPtr, true)); + for (size_t i = 0; i < sizeof(arrayU16Comp) / arrayU16Comp[0]; i++) { + EXPECT_EQ(*(handleEcmaStrU16Comp->GetDataUtf8() + i), arrayU16Comp[i]); + } +} + +/* + * @tc.name: GetDataUtf16 + * @tc.desc: Check whether the pointer returned through calling GetDataUtf16 function is within expectations. + * @tc.type: FUNC + * @tc.require: + */ +TEST_F(EcmaStringTest, GetDataUtf16) +{ + EcmaVM* ecmaVMPtr = EcmaVM::Cast(instance); + + // From EcmaString made by CreateFromUtf16( , , , false). + uint16_t arrayU16NotComp[] = {67, 777, 1999, 1, 45, 66, 23456, 65535, 127, 333}; + uint32_t lengthEcmaStrU16NotComp = sizeof(arrayU16NotComp) / sizeof(arrayU16NotComp[0]); + JSHandle handleEcmaStrU16NotComp(thread, + EcmaString::CreateFromUtf16(&arrayU16NotComp[0], lengthEcmaStrU16NotComp, ecmaVMPtr, false)); + for (size_t i = 0; i < lengthEcmaStrU16NotComp; i++) { + EXPECT_EQ(*(handleEcmaStrU16NotComp->GetDataUtf16() + i), arrayU16NotComp[i]); + } +} + +/* + * @tc.name: CopyDataRegionUtf8 + * @tc.desc: Check whether the returned value and the changed array through a source EcmaString's calling + * CopyDataRegionUtf8 function are within expectations. + * @tc.type: FUNC + * @tc.require: + */ +TEST_F(EcmaStringTest, CopyDataRegionUtf8) +{ + EcmaVM* ecmaVMPtr = EcmaVM::Cast(instance); + + // CopyDataRegionUtf8(). From EcmaString made by CreateFromUtf8(). + uint8_t arrayU8CopyFrom[6] = {1, 12, 34, 56, 127}; + uint32_t lengthEcmaStrU8CopyFrom = sizeof(arrayU8CopyFrom) - 1; + JSHandle handleEcmaStrU8CopyFrom(thread, + EcmaString::CreateFromUtf8(&arrayU8CopyFrom[0], lengthEcmaStrU8CopyFrom, ecmaVMPtr, true)); + const size_t lengthArrayU8Target = 7; + uint8_t defaultByteForU8CopyTo = 1; + uint8_t arrayU8CopyTo[lengthArrayU8Target]; + memset_s(&arrayU8CopyTo[0], lengthArrayU8Target, defaultByteForU8CopyTo, lengthArrayU8Target); + + size_t indexStartFromArrayU8 = 2; + size_t lengthCopyToEcmaStrU8 = 3; + size_t lengthReturnU8 = handleEcmaStrU8CopyFrom->CopyDataRegionUtf8(arrayU8CopyTo, indexStartFromArrayU8, + lengthCopyToEcmaStrU8, lengthArrayU8Target); + + EXPECT_EQ(lengthReturnU8, lengthCopyToEcmaStrU8); + for (size_t i = 0; i < lengthCopyToEcmaStrU8; i++) { + EXPECT_EQ(arrayU8CopyTo[i], handleEcmaStrU8CopyFrom->At(i + indexStartFromArrayU8)); + } + for (size_t i = lengthCopyToEcmaStrU8; i < lengthArrayU8Target; i++) { + EXPECT_EQ(arrayU8CopyTo[i], defaultByteForU8CopyTo); + } + + // CopyDataRegionUtf8(). From EcmaString made by CreateFromUtf16( , , , true). + uint16_t arrayU16CompCopyFrom[] = {1, 12, 34, 56, 127}; + uint32_t lengthEcmaStrU16CompCopyFrom = sizeof(arrayU16CompCopyFrom) / sizeof(arrayU16CompCopyFrom[0]); + JSHandle handleEcmaStrU16CompCopyFrom(thread, + EcmaString::CreateFromUtf16(&arrayU16CompCopyFrom[0], lengthEcmaStrU16CompCopyFrom, ecmaVMPtr, true)); + const size_t lengthArrayU16Target = 8; + uint8_t defaultByteForU16CompCopyTo = 1; + uint8_t arrayU16CompCopyTo[lengthArrayU16Target]; + memset_s(&arrayU16CompCopyTo[0], lengthArrayU16Target, defaultByteForU16CompCopyTo, lengthArrayU16Target); + + size_t indexStartFromArrayU16Comp = 2; + size_t lengthCopyToEcmaStrU16Comp = 3; + size_t lengthReturnU16Comp = handleEcmaStrU16CompCopyFrom->CopyDataRegionUtf8(&arrayU16CompCopyTo[0], + indexStartFromArrayU16Comp, lengthCopyToEcmaStrU16Comp, lengthArrayU16Target); + + EXPECT_EQ(lengthReturnU16Comp, lengthCopyToEcmaStrU16Comp); + for (size_t i = 0; i < lengthReturnU16Comp; i++) { + EXPECT_EQ(arrayU16CompCopyTo[i], handleEcmaStrU16CompCopyFrom->At(i + indexStartFromArrayU16Comp)); + } + for (size_t i = lengthReturnU16Comp; i < lengthArrayU16Target; i++) { + EXPECT_EQ(arrayU16CompCopyTo[i], defaultByteForU16CompCopyTo); + } +} + +/* + * @tc.name: CopyDataUtf8 + * @tc.desc: Check whether the returned value and the changed array through a source EcmaString's calling + * CopyDataUtf8 function are within expectations. + * @tc.type: FUNC + * @tc.require: + */ +TEST_F(EcmaStringTest, CopyDataUtf8) +{ + EcmaVM* ecmaVMPtr = EcmaVM::Cast(instance); + + // CopyDataUtf8(). From EcmaString made by CreateFromUtf8(). + uint8_t arrayU8CopyFrom[6] = {1, 12, 34, 56, 127}; + uint32_t lengthEcmaStrU8CopyFrom = sizeof(arrayU8CopyFrom) - 1; + JSHandle handleEcmaStrU8CopyFrom(thread, + EcmaString::CreateFromUtf8(&arrayU8CopyFrom[0], lengthEcmaStrU8CopyFrom, ecmaVMPtr, true)); + const size_t lengthArrayU8Target = 6; + uint8_t arrayU8CopyTo[lengthArrayU8Target]; + + size_t lengthReturnU8 = handleEcmaStrU8CopyFrom->CopyDataUtf8(&arrayU8CopyTo[0], lengthArrayU8Target); + + EXPECT_EQ(lengthReturnU8, lengthArrayU8Target); + for (size_t i = 0; i < lengthReturnU8 - 1; i++) { + EXPECT_EQ(arrayU8CopyTo[i], arrayU8CopyFrom[i]); + } + EXPECT_EQ(arrayU8CopyTo[lengthReturnU8 - 1], 0); + + // CopyDataUtf8(). From EcmaString made by CreateFromUtf16( , , , true). + uint16_t arrayU16CompCopyFrom[] = {1, 12, 34, 56, 127}; + uint32_t lengthEcmaStrU16CompCopyFrom = sizeof(arrayU16CompCopyFrom) / sizeof(arrayU16CompCopyFrom[0]); + JSHandle handleEcmaStrU16CompCopyFrom(thread, + EcmaString::CreateFromUtf16(&arrayU16CompCopyFrom[0], lengthEcmaStrU16CompCopyFrom, ecmaVMPtr, true)); + const size_t lengthArrayU16Target = 6; + uint8_t arrayU8CompCopyTo[lengthArrayU16Target]; + + size_t lengthReturnU16Comp = handleEcmaStrU16CompCopyFrom->CopyDataUtf8(&arrayU8CompCopyTo[0], + lengthArrayU16Target); + + EXPECT_EQ(lengthReturnU16Comp, lengthArrayU16Target); + for (size_t i = 0; i < lengthReturnU16Comp - 1; i++) { + EXPECT_EQ(arrayU8CompCopyTo[i], arrayU16CompCopyFrom[i]); + } + EXPECT_EQ(arrayU8CompCopyTo[lengthReturnU16Comp - 1], 0); +} + +/* + * @tc.name: CopyDataRegionUtf16 + * @tc.desc: Check whether the returned value and the changed array through a source EcmaString's calling + * CopyDataRegionUtf16 function are within expectations. + * @tc.type: FUNC + * @tc.require: + */ +TEST_F(EcmaStringTest, CopyDataRegionUtf16) +{ + EcmaVM* ecmaVMPtr = EcmaVM::Cast(instance); + + // CopyDataRegionUtf16(). From EcmaString made by CreateFromUtf16( , , , false). + uint16_t arrayU16NotCompCopyFrom[10] = {67, 777, 1999, 1, 45, 66, 23456, 65535, 127, 333}; + uint32_t lengthEcmaStrU16NotCompCopyFrom = sizeof(arrayU16NotCompCopyFrom) / sizeof(arrayU16NotCompCopyFrom[0]); + JSHandle handleEcmaStrU16NotCompCopyFrom(thread, + EcmaString::CreateFromUtf16(&arrayU16NotCompCopyFrom[0], lengthEcmaStrU16NotCompCopyFrom, ecmaVMPtr, false)); + const size_t lengthArrayU16Target = 13; + uint16_t arrayU16NotCompCopyTo[lengthArrayU16Target]; + uint8_t defaultOneByteValueOfArrayU16NotCompCopyTo = 244; + memset_s(&arrayU16NotCompCopyTo[0], sizeof(uint16_t) * lengthArrayU16Target, + defaultOneByteValueOfArrayU16NotCompCopyTo, sizeof(uint16_t) * lengthArrayU16Target); + + size_t startIndexFromArrayU16NotComp = 2; + size_t lengthCopyFromArrayU16NotComp = 3; + size_t lengthReturnU16NotComp = handleEcmaStrU16NotCompCopyFrom->CopyDataRegionUtf16(&arrayU16NotCompCopyTo[0], + startIndexFromArrayU16NotComp, lengthCopyFromArrayU16NotComp, lengthArrayU16Target); + + EXPECT_EQ(lengthReturnU16NotComp, lengthCopyFromArrayU16NotComp); + for (size_t i = 0; i < lengthReturnU16NotComp; i++) { + EXPECT_EQ(arrayU16NotCompCopyTo[i], handleEcmaStrU16NotCompCopyFrom->At(i + startIndexFromArrayU16NotComp)); + } + for (size_t i = lengthReturnU16NotComp; i < lengthArrayU16Target; i++) { + EXPECT_EQ(arrayU16NotCompCopyTo[i], ((uint16_t)defaultOneByteValueOfArrayU16NotCompCopyTo) * (1 + (1 << 8))); + } +} + +/* + * @tc.name: CopyDataUtf16 + * @tc.desc: Check whether the returned value and the changed array through a source EcmaString's calling + * CopyDataUtf16 function are within expectations. + * @tc.type: FUNC + * @tc.require: + */ +TEST_F(EcmaStringTest, CopyDataUtf16) +{ + EcmaVM* ecmaVMPtr = EcmaVM::Cast(instance); + + // CopyDataUtf16(). From EcmaString made by CreateFromUtf16( , , , false). + uint16_t arrayU16NotCompCopyFrom[10] = {67, 777, 1999, 1, 45, 66, 23456, 65535, 127, 333}; + uint32_t lengthEcmaStrU16NotCompCopyFrom = sizeof(arrayU16NotCompCopyFrom) / sizeof(arrayU16NotCompCopyFrom[0]); + JSHandle handleEcmaStrU16NotCompCopyFrom(thread, + EcmaString::CreateFromUtf16(&arrayU16NotCompCopyFrom[0], lengthEcmaStrU16NotCompCopyFrom, ecmaVMPtr, false)); + const size_t lengthArrayU16Target = 13; + uint16_t arrayU16NotCompCopyTo[lengthArrayU16Target]; + uint8_t defaultOneByteValueOfArrayU16NotCompCopyTo = 244; + memset_s(&arrayU16NotCompCopyTo[0], sizeof(uint16_t) * lengthArrayU16Target, + defaultOneByteValueOfArrayU16NotCompCopyTo, sizeof(uint16_t) * lengthArrayU16Target); + + size_t lengthReturnU16NotComp = handleEcmaStrU16NotCompCopyFrom->CopyDataUtf16(&arrayU16NotCompCopyTo[0], + lengthArrayU16Target); + + EXPECT_EQ(lengthReturnU16NotComp, lengthEcmaStrU16NotCompCopyFrom); + for (size_t i = 0; i < lengthReturnU16NotComp; i++) { + EXPECT_EQ(arrayU16NotCompCopyTo[i], handleEcmaStrU16NotCompCopyFrom->At(i)); + } + for (size_t i = lengthReturnU16NotComp; i < lengthArrayU16Target; i++) { + EXPECT_EQ(arrayU16NotCompCopyTo[i], ((uint16_t)defaultOneByteValueOfArrayU16NotCompCopyTo) * (1 + (1 << 8))); + } +} + +/* + * @tc.name: IndexOf_001 + * @tc.desc: Check whether the value returned through a source EcmaString made by CreateFromUtf8() calling IndexOf + * function with a target EcmaString made by CreateFromUtf8() is within expectations. + * @tc.type: FUNC + * @tc.require: + */ +TEST_F(EcmaStringTest, IndexOf_001) +{ + EcmaVM* ecmaVMPtr = EcmaVM::Cast(instance); + + // IndexOf(). Find EcmaString made by CreateFromUtf8() From EcmaString made by CreateFromUtf8(). + uint8_t arrayU8From[7] = {23, 25, 1, 3, 39, 80}; + uint8_t arrayU8Target[4] = {1, 3, 39}; + uint32_t lengthEcmaStrU8From = sizeof(arrayU8From) - 1; + uint32_t lengthEcmaStrU8Target = sizeof(arrayU8Target) - 1; + JSHandle handleEcmaStr(thread, EcmaString::CreateFromUtf8(&arrayU8From[0], lengthEcmaStrU8From, + ecmaVMPtr, true)); + JSHandle handleEcmaStr1(thread, EcmaString::CreateFromUtf8(&arrayU8Target[0], lengthEcmaStrU8Target, + ecmaVMPtr, true)); + int32_t posStart = 0; + EXPECT_EQ(handleEcmaStr->IndexOf(*handleEcmaStr1, posStart), 2); + EXPECT_EQ(handleEcmaStr1->IndexOf(*handleEcmaStr, posStart), -1); + posStart = -1; + EXPECT_EQ(handleEcmaStr->IndexOf(*handleEcmaStr1, posStart), 2); + posStart = 1; + EXPECT_EQ(handleEcmaStr->IndexOf(*handleEcmaStr1, posStart), 2); + posStart = 2; + EXPECT_EQ(handleEcmaStr->IndexOf(*handleEcmaStr1, posStart), 2); + posStart = 3; + EXPECT_EQ(handleEcmaStr->IndexOf(*handleEcmaStr1, posStart), -1); +} + +/* + * @tc.name: IndexOf_002 + * @tc.desc: Check whether the value returned through a source EcmaString made by CreateFromUtf16( , , , false) calling + * IndexOf function with a target EcmaString made by CreateFromUtf8() is within expectations. + * @tc.type: FUNC + * @tc.require: + */ +TEST_F(EcmaStringTest, IndexOf_002) +{ + EcmaVM* ecmaVMPtr = EcmaVM::Cast(instance); + + // IndexOf(). Find EcmaString made by CreateFromUtf8() From EcmaString made by CreateFromUtf16( , , , false). + uint8_t arrayU8Target[4] = {1, 3, 39}; + uint16_t arrayU16NotCompFromNo1[] = {67, 65535, 127, 777, 1453, 44, 1, 3, 39, 80, 333}; + uint32_t lengthEcmaStrU8Target = sizeof(arrayU8Target) - 1; + uint32_t lengthEcmaStrU16NotCompFromNo1 = sizeof(arrayU16NotCompFromNo1) / sizeof(arrayU16NotCompFromNo1[0]); + JSHandle handleEcmaStr(thread, + EcmaString::CreateFromUtf8(&arrayU8Target[0], lengthEcmaStrU8Target, ecmaVMPtr, true)); + JSHandle handleEcmaStr1(thread, + EcmaString::CreateFromUtf16(&arrayU16NotCompFromNo1[0], lengthEcmaStrU16NotCompFromNo1, ecmaVMPtr, false)); + int32_t posStart = 0; + EXPECT_EQ(handleEcmaStr1->IndexOf(*handleEcmaStr, posStart), 6); + EXPECT_EQ(handleEcmaStr->IndexOf(*handleEcmaStr1, posStart), -1); + posStart = -1; + EXPECT_EQ(handleEcmaStr1->IndexOf(*handleEcmaStr, posStart), 6); + posStart = 1; + EXPECT_EQ(handleEcmaStr1->IndexOf(*handleEcmaStr, posStart), 6); + posStart = 6; + EXPECT_EQ(handleEcmaStr1->IndexOf(*handleEcmaStr, posStart), 6); + posStart = 7; + EXPECT_EQ(handleEcmaStr1->IndexOf(*handleEcmaStr, posStart), -1); +} + +/* + * @tc.name: IndexOf_003 + * @tc.desc: Check whether the value returned through a source EcmaString made by CreateFromUtf16( , , , false) calling + * IndexOf function with a target EcmaString made by CreateFromUtf16( , , , false) is within expectations. + * @tc.type: FUNC + * @tc.require: + */ +TEST_F(EcmaStringTest, IndexOf_003) +{ + EcmaVM* ecmaVMPtr = EcmaVM::Cast(instance); + + /* IndexOf(). Find EcmaString made by CreateFromUtf16( , , , false) From EcmaString made by + * CreateFromUtf16( , , , false). + */ + uint16_t arrayU16NotCompTarget[] = {1453, 44}; + uint16_t arrayU16NotCompFrom[] = {67, 65535, 127, 777, 1453, 44, 1, 3, 39, 80, 333}; + uint32_t lengthEcmaStrU16NotCompTarget = sizeof(arrayU16NotCompTarget) / sizeof(arrayU16NotCompTarget[0]); + uint32_t lengthEcmaStrU16NotCompFrom = sizeof(arrayU16NotCompFrom) / sizeof(arrayU16NotCompFrom[0]); + JSHandle handleEcmaStrU16NotCompTarget(thread, + EcmaString::CreateFromUtf16(&arrayU16NotCompTarget[0], lengthEcmaStrU16NotCompTarget, ecmaVMPtr, false)); + JSHandle handleEcmaStrU16NotCompFrom(thread, + EcmaString::CreateFromUtf16(&arrayU16NotCompFrom[0], lengthEcmaStrU16NotCompFrom, ecmaVMPtr, false)); + int32_t posStart = 0; + EXPECT_EQ(handleEcmaStrU16NotCompFrom->IndexOf(*handleEcmaStrU16NotCompTarget, posStart), 4); + EXPECT_EQ(handleEcmaStrU16NotCompTarget->IndexOf(*handleEcmaStrU16NotCompFrom, posStart), -1); + posStart = -1; + EXPECT_EQ(handleEcmaStrU16NotCompFrom->IndexOf(*handleEcmaStrU16NotCompTarget, posStart), 4); + posStart = 1; + EXPECT_EQ(handleEcmaStrU16NotCompFrom->IndexOf(*handleEcmaStrU16NotCompTarget, posStart), 4); + posStart = 4; + EXPECT_EQ(handleEcmaStrU16NotCompFrom->IndexOf(*handleEcmaStrU16NotCompTarget, posStart), 4); + posStart = 5; + EXPECT_EQ(handleEcmaStrU16NotCompFrom->IndexOf(*handleEcmaStrU16NotCompTarget, posStart), -1); +} + +/* + * @tc.name: IndexOf_004 + * @tc.desc: Check whether the value returned through a source EcmaString made by CreateFromUtf8() calling IndexOf + * function with a target EcmaString made by CreateFromUtf16( , , , false) is within expectations. + * @tc.type: FUNC + * @tc.require: + */ +TEST_F(EcmaStringTest, IndexOf_004) +{ + EcmaVM* ecmaVMPtr = EcmaVM::Cast(instance); + + // IndexOf(). Find EcmaString made by CreateFromUtf16( , , , false) From EcmaString made by CreateFromUtf8(). + uint16_t ecmaStrU16NotCompTarget[] = {3, 39, 80}; + uint8_t arrayU8From[7] = {23, 25, 1, 3, 39, 80}; + uint32_t lengthEcmaStrU16NotCompTarget = sizeof(ecmaStrU16NotCompTarget) / sizeof(ecmaStrU16NotCompTarget[0]); + uint32_t lengthEcmaStrU8From = sizeof(arrayU8From) - 1; + JSHandle handleEcmaStrU16NotCompTarget(thread, + EcmaString::CreateFromUtf16(&ecmaStrU16NotCompTarget[0], lengthEcmaStrU16NotCompTarget, ecmaVMPtr, false)); + JSHandle handleEcmaStrU8From(thread, + EcmaString::CreateFromUtf8(&arrayU8From[0], lengthEcmaStrU8From, ecmaVMPtr, true)); + int32_t posStart = 0; + EXPECT_EQ(handleEcmaStrU8From->IndexOf(*handleEcmaStrU16NotCompTarget, posStart), 3); + EXPECT_EQ(handleEcmaStrU16NotCompTarget->IndexOf(*handleEcmaStrU8From, posStart), -1); + posStart = -1; + EXPECT_EQ(handleEcmaStrU8From->IndexOf(*handleEcmaStrU16NotCompTarget, posStart), 3); + posStart = 1; + EXPECT_EQ(handleEcmaStrU8From->IndexOf(*handleEcmaStrU16NotCompTarget, posStart), 3); + posStart = 3; + EXPECT_EQ(handleEcmaStrU8From->IndexOf(*handleEcmaStrU16NotCompTarget, posStart), 3); + posStart = 4; + EXPECT_EQ(handleEcmaStrU8From->IndexOf(*handleEcmaStrU16NotCompTarget, posStart), -1); +} + +/* + * @tc.name: StringsAreEqual_001 + * @tc.desc: Check whether the bool returned through calling StringsAreEqual function with two EcmaStrings made by + * CreateFromUtf8() is within expectations. + * @tc.type: FUNC + * @tc.require: + */ +TEST_F(EcmaStringTest, StringsAreEqual_001) +{ + EcmaVM* ecmaVMPtr = EcmaVM::Cast(instance); + + // StringsAreEqual(). + uint8_t arrayU8No1[4] = {45, 92, 78}; + uint8_t arrayU8No2[4] = {45, 92, 78}; + uint8_t arrayU8No3[5] = {45, 92, 78, 1}; + uint32_t lengthEcmaStrU8No1 = sizeof(arrayU8No1) - 1; + uint32_t lengthEcmaStrU8No2 = sizeof(arrayU8No2) - 1; + uint32_t lengthEcmaStrU8No3 = sizeof(arrayU8No3) - 1; + JSHandle handleEcmaStrU8No1(thread, + EcmaString::CreateFromUtf8(&arrayU8No1[0], lengthEcmaStrU8No1, ecmaVMPtr, true)); + JSHandle handleEcmaStrU8No2(thread, + EcmaString::CreateFromUtf8(&arrayU8No2[0], lengthEcmaStrU8No2, ecmaVMPtr, true)); + JSHandle handleEcmaStrU8No3(thread, + EcmaString::CreateFromUtf8(&arrayU8No3[0], lengthEcmaStrU8No3, ecmaVMPtr, true)); + EXPECT_TRUE(EcmaString::StringsAreEqual(*handleEcmaStrU8No1, *handleEcmaStrU8No2)); + EXPECT_FALSE(EcmaString::StringsAreEqual(*handleEcmaStrU8No1, *handleEcmaStrU8No3)); + EXPECT_FALSE(EcmaString::StringsAreEqual(*handleEcmaStrU8No3, *handleEcmaStrU8No1)); +} + +/* + * @tc.name: StringsAreEqual_002 + * @tc.desc: Check whether the bool returned through calling StringsAreEqual function with a EcmaString made by + * CreateFromUtf8() and a EcmaString made by CreateFromUtf16(, , , true) is within expectations. + * @tc.type: FUNC + * @tc.require: + */ +TEST_F(EcmaStringTest, StringsAreEqual_002) +{ + EcmaVM* ecmaVMPtr = EcmaVM::Cast(instance); + + // StringsAreEqual(). + uint8_t arrayU8No1[4] = {45, 92, 78}; + uint16_t arrayU16CompNo2[] = {45, 92, 78}; + uint16_t arrayU16CompNo3[] = {45, 92, 78, 1}; + uint32_t lengthEcmaStrU8No1 = sizeof(arrayU8No1) - 1; + uint32_t lengthEcmaStrU16CompNo2 = sizeof(arrayU16CompNo2) / sizeof(arrayU16CompNo2[0]); + uint32_t lengthEcmaStrU16CompNo3 = sizeof(arrayU16CompNo3) / sizeof(arrayU16CompNo3[0]); + JSHandle handleEcmaStrU8No1(thread, + EcmaString::CreateFromUtf8(&arrayU8No1[0], lengthEcmaStrU8No1, ecmaVMPtr, true)); + JSHandle handleEcmaStrU16CompNo2(thread, + EcmaString::CreateFromUtf16(&arrayU16CompNo2[0], lengthEcmaStrU16CompNo2, ecmaVMPtr, true)); + JSHandle handleEcmaStrU16CompNo3(thread, + EcmaString::CreateFromUtf16(&arrayU16CompNo3[0], lengthEcmaStrU16CompNo3, ecmaVMPtr, true)); + EXPECT_TRUE(EcmaString::StringsAreEqual(*handleEcmaStrU8No1, *handleEcmaStrU16CompNo2)); + EXPECT_FALSE(EcmaString::StringsAreEqual(*handleEcmaStrU8No1, *handleEcmaStrU16CompNo3)); +} + +/* + * @tc.name: StringsAreEqual_003 + * @tc.desc: Check whether the bool returned through calling StringsAreEqual function with two EcmaStrings made by + * CreateFromUtf16(, , , true) is within expectations. + * @tc.type: FUNC + * @tc.require: + */ +TEST_F(EcmaStringTest, StringsAreEqual_003) +{ + EcmaVM* ecmaVMPtr = EcmaVM::Cast(instance); + + // StringsAreEqual(). + uint16_t arrayU16CompNo1[] = {45, 92, 78}; + uint16_t arrayU16CompNo2[] = {45, 92, 78}; + uint16_t arrayU16CompNo3[] = {45, 92, 78, 1}; + uint32_t lengthEcmaStrU16CompNo1 = sizeof(arrayU16CompNo1) / sizeof(arrayU16CompNo1[0]); + uint32_t lengthEcmaStrU16CompNo2 = sizeof(arrayU16CompNo2) / sizeof(arrayU16CompNo2[0]); + uint32_t lengthEcmaStrU16CompNo3 = sizeof(arrayU16CompNo3) / sizeof(arrayU16CompNo3[0]); + JSHandle handleEcmaStrU16CompNo1(thread, + EcmaString::CreateFromUtf16(&arrayU16CompNo1[0], lengthEcmaStrU16CompNo1, ecmaVMPtr, true)); + JSHandle handleEcmaStrU16CompNo2(thread, + EcmaString::CreateFromUtf16(&arrayU16CompNo2[0], lengthEcmaStrU16CompNo2, ecmaVMPtr, true)); + JSHandle handleEcmaStrU16CompNo3(thread, + EcmaString::CreateFromUtf16(&arrayU16CompNo3[0], lengthEcmaStrU16CompNo3, ecmaVMPtr, true)); + EXPECT_TRUE(EcmaString::StringsAreEqual(*handleEcmaStrU16CompNo1, *handleEcmaStrU16CompNo2)); + EXPECT_FALSE(EcmaString::StringsAreEqual(*handleEcmaStrU16CompNo1, *handleEcmaStrU16CompNo3)); + EXPECT_FALSE(EcmaString::StringsAreEqual(*handleEcmaStrU16CompNo3, *handleEcmaStrU16CompNo1)); +} + +/* + * @tc.name: StringsAreEqual_004 + * @tc.desc: Check whether the bool returned through calling StringsAreEqual function with a EcmaString made by + * CreateFromUtf8() and a EcmaString made by CreateFromUtf16(, , , false) is within expectations. + * @tc.type: FUNC + * @tc.require: + */ +TEST_F(EcmaStringTest, StringsAreEqual_004) +{ + EcmaVM* ecmaVMPtr = EcmaVM::Cast(instance); + + // StringsAreEqual(). + uint8_t arrayU8No1[4] = {45, 92, 78}; + uint16_t arrayU16NotCompNo1[] = {45, 92, 78}; + uint32_t lengthEcmaStrU8No1 = sizeof(arrayU8No1) - 1; + uint32_t lengthEcmaStrU16NotCompNo1 = sizeof(arrayU16NotCompNo1) / sizeof(arrayU16NotCompNo1[0]); + JSHandle handleEcmaStrU8No1(thread, + EcmaString::CreateFromUtf8(&arrayU8No1[0], lengthEcmaStrU8No1, ecmaVMPtr, true)); + JSHandle handleEcmaStrU16NotCompNo1(thread, + EcmaString::CreateFromUtf16(&arrayU16NotCompNo1[0], lengthEcmaStrU16NotCompNo1, ecmaVMPtr, false)); + EXPECT_FALSE(EcmaString::StringsAreEqual(*handleEcmaStrU8No1, *handleEcmaStrU16NotCompNo1)); + EXPECT_FALSE(EcmaString::StringsAreEqual(*handleEcmaStrU16NotCompNo1, *handleEcmaStrU8No1)); +} + +/* + * @tc.name: StringsAreEqual_005 + * @tc.desc: Check whether the bool returned through calling StringsAreEqual function with a EcmaString made by + * CreateFromUtf16(, , , true) and a EcmaString made by CreateFromUtf16(, , , false) is within expectations. + * @tc.type: FUNC + * @tc.require: + */ +TEST_F(EcmaStringTest, StringsAreEqual_005) +{ + EcmaVM* ecmaVMPtr = EcmaVM::Cast(instance); + + // StringsAreEqual(). + uint16_t arrayU16CompNo1[] = {45, 92, 78}; + uint16_t arrayU16NotCompNo1[] = {45, 92, 78}; + uint32_t lengthEcmaStrU16CompNo1 = sizeof(arrayU16CompNo1) / sizeof(arrayU16CompNo1[0]); + uint32_t lengthEcmaStrU16NotCompNo1 = sizeof(arrayU16NotCompNo1) / sizeof(arrayU16NotCompNo1[0]); + JSHandle handleEcmaStrU16CompNo1(thread, + EcmaString::CreateFromUtf16(&arrayU16CompNo1[0], lengthEcmaStrU16CompNo1, ecmaVMPtr, true)); + JSHandle handleEcmaStrU16NotCompNo1(thread, + EcmaString::CreateFromUtf16(&arrayU16NotCompNo1[0], lengthEcmaStrU16NotCompNo1, ecmaVMPtr, false)); + EXPECT_FALSE(EcmaString::StringsAreEqual(*handleEcmaStrU16CompNo1, *handleEcmaStrU16NotCompNo1)); + EXPECT_FALSE(EcmaString::StringsAreEqual(*handleEcmaStrU16NotCompNo1, *handleEcmaStrU16CompNo1)); +} + +/* + * @tc.name: StringsAreEqual_006 + * @tc.desc: Check whether the bool returned through calling StringsAreEqual function with two EcmaStrings made by + * CreateFromUtf16(, , , false) is within expectations. + * @tc.type: FUNC + * @tc.require: + */ +TEST_F(EcmaStringTest, StringsAreEqual_006) +{ + EcmaVM* ecmaVMPtr = EcmaVM::Cast(instance); + + // StringsAreEqual(). + uint16_t arrayU16NotCompNo1[] = {234, 345, 127, 2345, 65535, 5}; + uint16_t arrayU16NotCompNo2[] = {234, 345, 127, 2345, 65535, 5}; + uint16_t arrayU16NotCompNo3[] = {1, 234, 345, 127, 2345, 65535, 5}; + uint32_t lengthEcmaStrU16NotCompNo1 = sizeof(arrayU16NotCompNo1) / sizeof(arrayU16NotCompNo1[0]); + uint32_t lengthEcmaStrU16NotCompNo2 = sizeof(arrayU16NotCompNo2) / sizeof(arrayU16NotCompNo2[0]); + uint32_t lengthEcmaStrU16NotCompNo3 = sizeof(arrayU16NotCompNo3) / sizeof(arrayU16NotCompNo3[0]); + JSHandle handleEcmaStrU16NotCompNo1(thread, + EcmaString::CreateFromUtf16(&arrayU16NotCompNo1[0], lengthEcmaStrU16NotCompNo1, ecmaVMPtr, false)); + JSHandle handleEcmaStrU16NotCompNo2(thread, + EcmaString::CreateFromUtf16(&arrayU16NotCompNo2[0], lengthEcmaStrU16NotCompNo2, ecmaVMPtr, false)); + JSHandle handleEcmaStrU16NotCompNo3(thread, + EcmaString::CreateFromUtf16(&arrayU16NotCompNo3[0], lengthEcmaStrU16NotCompNo3, ecmaVMPtr, false)); + EXPECT_TRUE(EcmaString::StringsAreEqual(*handleEcmaStrU16NotCompNo1, *handleEcmaStrU16NotCompNo2)); + EXPECT_FALSE(EcmaString::StringsAreEqual(*handleEcmaStrU16NotCompNo1, *handleEcmaStrU16NotCompNo3)); + EXPECT_FALSE(EcmaString::StringsAreEqual(*handleEcmaStrU16NotCompNo3, *handleEcmaStrU16NotCompNo1)); +} + +/* + * @tc.name: StringsAreEqualUtf8_001 + * @tc.desc: Check whether the bool returned through calling StringsAreEqualUtf8 function with an EcmaString made by + * CreateFromUtf8() and an Array(uint8_t) is within expectations. + * @tc.type: FUNC + * @tc.require: + */ +TEST_F(EcmaStringTest, StringsAreEqualUtf8_001) +{ + EcmaVM* ecmaVMPtr = EcmaVM::Cast(instance); + + // StringsAreEqualUtf8(). 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}; + uint32_t lengthEcmaStrU8No1 = sizeof(arrayU8No1) - 1; + uint32_t lengthEcmaStrU8No2 = sizeof(arrayU8No2) - 1; + uint32_t lengthEcmaStrU8No3 = sizeof(arrayU8No3) - 1; + JSHandle handleEcmaStrU8No1(thread, + EcmaString::CreateFromUtf8(&arrayU8No1[0], lengthEcmaStrU8No1, ecmaVMPtr, true)); + JSHandle handleEcmaStrU8No2(thread, + EcmaString::CreateFromUtf8(&arrayU8No2[0], lengthEcmaStrU8No2, ecmaVMPtr, true)); + JSHandle handleEcmaStrU8No3(thread, + EcmaString::CreateFromUtf8(&arrayU8No3[0], lengthEcmaStrU8No3, ecmaVMPtr, 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)); +} + +/* + * @tc.name: StringsAreEqualUtf8_002 + * @tc.desc: Check whether the bool returned through calling StringsAreEqualUtf8 function with an EcmaString made by + * CreateFromUtf16( , , , true) and an Array(uint8_t) is within expectations. + * @tc.type: FUNC + * @tc.require: + */ +TEST_F(EcmaStringTest, StringsAreEqualUtf8_002) +{ + EcmaVM* ecmaVMPtr = EcmaVM::Cast(instance); + + // StringsAreEqualUtf8(). 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}; + uint16_t arrayU16CompNo3[] = {45, 92}; + uint32_t lengthEcmaStrU8No1 = sizeof(arrayU8No1) - 1; + uint32_t lengthEcmaStrU16CompNo1 = sizeof(arrayU16CompNo1) / sizeof(arrayU16CompNo1[0]); + uint32_t lengthEcmaStrU16CompNo2 = sizeof(arrayU16CompNo2) / sizeof(arrayU16CompNo2[0]); + uint32_t lengthEcmaStrU16CompNo3 = sizeof(arrayU16CompNo3) / sizeof(arrayU16CompNo3[0]); + JSHandle handleEcmaStrU16CompNo1(thread, + EcmaString::CreateFromUtf16(&arrayU16CompNo1[0], lengthEcmaStrU16CompNo1, ecmaVMPtr, true)); + JSHandle handleEcmaStrU16CompNo2(thread, + EcmaString::CreateFromUtf16(&arrayU16CompNo2[0], lengthEcmaStrU16CompNo2, ecmaVMPtr, true)); + JSHandle handleEcmaStrU16CompNo3(thread, + EcmaString::CreateFromUtf16(&arrayU16CompNo3[0], lengthEcmaStrU16CompNo3, ecmaVMPtr, 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)); +} + +/* + * @tc.name: StringsAreEqualUtf8_003 + * @tc.desc: Check whether the bool returned through calling StringsAreEqualUtf8 function with an EcmaString made by + * CreateFromUtf16( , , , false) and an Array(uint8_t) is within expectations. + * @tc.type: FUNC + * @tc.require: + */ +TEST_F(EcmaStringTest, StringsAreEqualUtf8_003) +{ + EcmaVM* ecmaVMPtr = EcmaVM::Cast(instance); + + // StringsAreEqualUtf8(). 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}; + uint16_t arrayU16NotCompNo3[] = {45, 92}; + uint16_t arrayU16NotCompNo4[] = {25645, 25692, 25678}; + uint32_t lengthEcmaStrU8No1 = sizeof(arrayU8No1) - 1; + uint32_t lengthEcmaStrU16NotCompNo1 = sizeof(arrayU16NotCompNo1) / sizeof(arrayU16NotCompNo1[0]); + uint32_t lengthEcmaStrU16NotCompNo2 = sizeof(arrayU16NotCompNo2) / sizeof(arrayU16NotCompNo2[0]); + uint32_t lengthEcmaStrU16NotCompNo3 = sizeof(arrayU16NotCompNo3) / sizeof(arrayU16NotCompNo3[0]); + uint32_t lengthEcmaStrU16NotCompNo4 = sizeof(arrayU16NotCompNo4) / sizeof(arrayU16NotCompNo4[0]); + JSHandle handleEcmaStrU16NotCompNo1(thread, + EcmaString::CreateFromUtf16(&arrayU16NotCompNo1[0], lengthEcmaStrU16NotCompNo1, ecmaVMPtr, false)); + JSHandle handleEcmaStrU16NotCompNo2(thread, + EcmaString::CreateFromUtf16(&arrayU16NotCompNo2[0], lengthEcmaStrU16NotCompNo2, ecmaVMPtr, false)); + JSHandle handleEcmaStrU16NotCompNo3(thread, + EcmaString::CreateFromUtf16(&arrayU16NotCompNo3[0], lengthEcmaStrU16NotCompNo3, ecmaVMPtr, false)); + JSHandle handleEcmaStrU16NotCompNo4(thread, + EcmaString::CreateFromUtf16(&arrayU16NotCompNo4[0], lengthEcmaStrU16NotCompNo4, ecmaVMPtr, false)); + EXPECT_TRUE( + EcmaString::StringsAreEqualUtf8(*handleEcmaStrU16NotCompNo1, &arrayU8No1[0], lengthEcmaStrU8No1, false)); + EXPECT_FALSE( + EcmaString::StringsAreEqualUtf8(*handleEcmaStrU16NotCompNo1, &arrayU8No1[0], lengthEcmaStrU8No1, true)); + EXPECT_FALSE( + EcmaString::StringsAreEqualUtf8(*handleEcmaStrU16NotCompNo2, &arrayU8No1[0], lengthEcmaStrU8No1, false)); + EXPECT_FALSE( + EcmaString::StringsAreEqualUtf8(*handleEcmaStrU16NotCompNo3, &arrayU8No1[0], lengthEcmaStrU8No1, false)); + EXPECT_FALSE( + EcmaString::StringsAreEqualUtf8(*handleEcmaStrU16NotCompNo4, &arrayU8No1[0], lengthEcmaStrU8No1, false)); +} + +/* + * @tc.name: StringsAreEqualUtf16_001 + * @tc.desc: Check whether the bool returned through calling StringsAreEqualUtf16 function with an EcmaString made by + * CreateFromUtf8() and an Array(uint16_t) is within expectations. + * @tc.type: FUNC + * @tc.require: + */ +TEST_F(EcmaStringTest, StringsAreEqualUtf16_001) +{ + EcmaVM* ecmaVMPtr = EcmaVM::Cast(instance); + + // StringsAreEqualUtf16(). EcmaString made by CreateFromUtf8, Array:U16(1-127). + uint8_t arrayU8No1[4] = {45, 92, 78}; + uint8_t arrayU8No2[5] = {45, 92, 78, 24}; + uint8_t arrayU8No3[3] = {45, 92}; + uint16_t arrayU16NotCompNo1[] = {45, 92, 78}; + uint32_t lengthEcmaStrU8No1 = sizeof(arrayU8No1) - 1; + uint32_t lengthEcmaStrU8No2 = sizeof(arrayU8No2) - 1; + uint32_t lengthEcmaStrU8No3 = sizeof(arrayU8No3) - 1; + uint32_t lengthEcmaStrU16NotCompNo1 = sizeof(arrayU16NotCompNo1) / sizeof(arrayU16NotCompNo1[0]); + JSHandle handleEcmaStrU8No1(thread, + EcmaString::CreateFromUtf8(&arrayU8No1[0], lengthEcmaStrU8No1, ecmaVMPtr, true)); + JSHandle handleEcmaStrU8No2(thread, + EcmaString::CreateFromUtf8(&arrayU8No2[0], lengthEcmaStrU8No2, ecmaVMPtr, true)); + JSHandle handleEcmaStrU8No3(thread, + EcmaString::CreateFromUtf8(&arrayU8No3[0], lengthEcmaStrU8No3, ecmaVMPtr, true)); + EXPECT_TRUE( + EcmaString::StringsAreEqualUtf16(*handleEcmaStrU8No1, &arrayU16NotCompNo1[0], lengthEcmaStrU16NotCompNo1)); + EXPECT_FALSE( + EcmaString::StringsAreEqualUtf16(*handleEcmaStrU8No2, &arrayU16NotCompNo1[0], lengthEcmaStrU16NotCompNo1)); + EXPECT_FALSE( + EcmaString::StringsAreEqualUtf16(*handleEcmaStrU8No3, &arrayU16NotCompNo1[0], lengthEcmaStrU16NotCompNo1)); +} + +/* + * @tc.name: StringsAreEqualUtf16_002 + * @tc.desc: Check whether the bool returned through calling StringsAreEqualUtf16 function with an EcmaString made by + * CreateFromUtf16( , , , true) and an Array(uint16_t) is within expectations. + * @tc.type: FUNC + * @tc.require: + */ +TEST_F(EcmaStringTest, StringsAreEqualUtf16_002) +{ + EcmaVM* ecmaVMPtr = EcmaVM::Cast(instance); + + // StringsAreEqualUtf16(). EcmaString made by CreateFromUtf16( , , , true), Array:U16(1-127). + uint16_t arrayU16CompNo1[] = {45, 92, 78}; + uint16_t arrayU16CompNo2[] = {45, 92, 78, 24}; + uint16_t arrayU16CompNo3[] = {45, 92}; + uint16_t arrayU16CompNo4[] = {25645, 25692, 25678}; // 25645 % 256 == 45... + uint32_t lengthEcmaStrU16CompNo1 = sizeof(arrayU16CompNo1) / sizeof(arrayU16CompNo1[0]); + uint32_t lengthEcmaStrU16CompNo2 = sizeof(arrayU16CompNo2) / sizeof(arrayU16CompNo2[0]); + uint32_t lengthEcmaStrU16CompNo3 = sizeof(arrayU16CompNo3) / sizeof(arrayU16CompNo3[0]); + uint32_t lengthEcmaStrU16CompNo4 = sizeof(arrayU16CompNo4) / sizeof(arrayU16CompNo4[0]); + JSHandle handleEcmaStrU16CompNo1(thread, + EcmaString::CreateFromUtf16(&arrayU16CompNo1[0], lengthEcmaStrU16CompNo1, ecmaVMPtr, true)); + JSHandle handleEcmaStrU16CompNo2(thread, + EcmaString::CreateFromUtf16(&arrayU16CompNo2[0], lengthEcmaStrU16CompNo2, ecmaVMPtr, true)); + JSHandle handleEcmaStrU16CompNo3(thread, + EcmaString::CreateFromUtf16(&arrayU16CompNo3[0], lengthEcmaStrU16CompNo3, ecmaVMPtr, true)); + JSHandle handleEcmaStrU16CompNo4(thread, + EcmaString::CreateFromUtf16(&arrayU16CompNo4[0], lengthEcmaStrU16CompNo4, ecmaVMPtr, true)); + EXPECT_TRUE( + EcmaString::StringsAreEqualUtf16(*handleEcmaStrU16CompNo1, &arrayU16CompNo1[0], lengthEcmaStrU16CompNo1)); + EXPECT_FALSE( + EcmaString::StringsAreEqualUtf16(*handleEcmaStrU16CompNo2, &arrayU16CompNo1[0], lengthEcmaStrU16CompNo1)); + EXPECT_FALSE( + EcmaString::StringsAreEqualUtf16(*handleEcmaStrU16CompNo3, &arrayU16CompNo1[0], lengthEcmaStrU16CompNo1)); + EXPECT_TRUE( + EcmaString::StringsAreEqualUtf16(*handleEcmaStrU16CompNo4, &arrayU16CompNo1[0], lengthEcmaStrU16CompNo1)); +} + +/* + * @tc.name: StringsAreEqualUtf16_003 + * @tc.desc: Check whether the bool returned through calling StringsAreEqualUtf16 function with an EcmaString made by + * CreateFromUtf16( , , , false) and an Array(uint16_t) is within expectations. + * @tc.type: FUNC + * @tc.require: + */ +TEST_F(EcmaStringTest, StringsAreEqualUtf16_003) +{ + EcmaVM* ecmaVMPtr = EcmaVM::Cast(instance); + + // StringsAreEqualUtf16(). EcmaString made by CreateFromUtf16( , , , false), Array:U16(0-65535). + uint16_t arrayU16NotCompNo1[] = {25645, 25692, 25678}; + uint16_t arrayU16NotCompNo2[] = {25645, 25692, 78}; // 25645 % 256 == 45... + uint16_t arrayU16NotCompNo3[] = {25645, 25692, 25678, 65535}; + uint16_t arrayU16NotCompNo4[] = {25645, 25692}; + uint32_t lengthEcmaStrU16NotCompNo1 = sizeof(arrayU16NotCompNo1) / sizeof(arrayU16NotCompNo1[0]); + uint32_t lengthEcmaStrU16NotCompNo2 = sizeof(arrayU16NotCompNo2) / sizeof(arrayU16NotCompNo2[0]); + uint32_t lengthEcmaStrU16NotCompNo3 = sizeof(arrayU16NotCompNo3) / sizeof(arrayU16NotCompNo3[0]); + uint32_t lengthEcmaStrU16NotCompNo4 = sizeof(arrayU16NotCompNo4) / sizeof(arrayU16NotCompNo4[0]); + JSHandle handleEcmaStrU16NotCompNo1(thread, + EcmaString::CreateFromUtf16(&arrayU16NotCompNo1[0], lengthEcmaStrU16NotCompNo1, ecmaVMPtr, false)); + JSHandle handleEcmaStrU16NotCompNo2(thread, + EcmaString::CreateFromUtf16(&arrayU16NotCompNo2[0], lengthEcmaStrU16NotCompNo2, ecmaVMPtr, false)); + JSHandle handleEcmaStrU16NotCompNo3(thread, + EcmaString::CreateFromUtf16(&arrayU16NotCompNo3[0], lengthEcmaStrU16NotCompNo3, ecmaVMPtr, false)); + JSHandle handleEcmaStrU16NotCompNo4(thread, + EcmaString::CreateFromUtf16(&arrayU16NotCompNo4[0], lengthEcmaStrU16NotCompNo4, ecmaVMPtr, false)); + EXPECT_TRUE(EcmaString::StringsAreEqualUtf16(*handleEcmaStrU16NotCompNo1, &arrayU16NotCompNo1[0], + lengthEcmaStrU16NotCompNo1)); + EXPECT_FALSE(EcmaString::StringsAreEqualUtf16(*handleEcmaStrU16NotCompNo1, &arrayU16NotCompNo2[0], + lengthEcmaStrU16NotCompNo2)); + EXPECT_FALSE(EcmaString::StringsAreEqualUtf16(*handleEcmaStrU16NotCompNo2, &arrayU16NotCompNo1[0], + lengthEcmaStrU16NotCompNo1)); + EXPECT_FALSE(EcmaString::StringsAreEqualUtf16(*handleEcmaStrU16NotCompNo3, &arrayU16NotCompNo1[0], + lengthEcmaStrU16NotCompNo1)); + EXPECT_FALSE(EcmaString::StringsAreEqualUtf16(*handleEcmaStrU16NotCompNo4, &arrayU16NotCompNo1[0], + lengthEcmaStrU16NotCompNo1)); +} + +/* + * @tc.name: ComputeHashcodeUtf8 + * @tc.desc: Check whether the value returned through calling ComputeHashcodeUtf8 function with an Array(uint8_t) is + * within expectations. + * @tc.type: FUNC + * @tc.require: + */ +TEST_F(EcmaStringTest, ComputeHashcodeUtf8) +{ + uint8_t arrayU8[] = {"abc"}; + uint32_t lengthEcmaStrU8 = sizeof(arrayU8) - 1; + uint32_t hashExpect = 0; + for (uint32_t i = 0; i < lengthEcmaStrU8; i++) { + hashExpect = hashExpect * 31 + arrayU8[i]; + } + EXPECT_EQ(EcmaString::ComputeHashcodeUtf8(&arrayU8[0], lengthEcmaStrU8, true), static_cast(hashExpect)); +} + +/* + * @tc.name: ComputeHashcodeUtf16 + * @tc.desc: Check whether the value returned through calling ComputeHashcodeUtf16 function with an Array(uint16_t) is + * within expectations. + * @tc.type: FUNC + * @tc.require: + */ +TEST_F(EcmaStringTest, ComputeHashcodeUtf16) +{ + uint16_t arrayU16[] = {199, 1, 256, 65535, 777}; + uint32_t lengthEcmaStrU16 = sizeof(arrayU16) / sizeof(arrayU16[0]); + uint32_t hashExpect = 0; + for (uint32_t i = 0; i < lengthEcmaStrU16; i++) { + hashExpect = hashExpect * 31 + arrayU16[i]; + } + EXPECT_EQ(EcmaString::ComputeHashcodeUtf16(&arrayU16[0], lengthEcmaStrU16), static_cast(hashExpect)); +} + +/* + * @tc.name: GetHashcode_001 + * @tc.desc: Check whether the value returned through an EcmaString made by CreateFromUtf8() calling GetHashcode + * function is within expectations. + * @tc.type: FUNC + * @tc.require: + */ +TEST_F(EcmaStringTest, GetHashcode_001) +{ + EcmaVM* ecmaVMPtr = EcmaVM::Cast(instance); + + // GetHashcode(). EcmaString made by CreateFromUtf8(). + uint8_t arrayU8[] = {"abc"}; + uint32_t lengthEcmaStrU8 = sizeof(arrayU8) - 1; + JSHandle handleEcmaStrU8(thread, + EcmaString::CreateFromUtf8(&arrayU8[0], lengthEcmaStrU8, ecmaVMPtr, true)); + uint32_t hashExpect = 0; + for (uint32_t i = 0; i < lengthEcmaStrU8; i++) { + hashExpect = hashExpect * 31 + arrayU8[i]; + } + EXPECT_EQ(handleEcmaStrU8->GetHashcode(), static_cast(hashExpect)); +} + +/* + * @tc.name: GetHashcode_002 + * @tc.desc: Check whether the value returned through an EcmaString made by CreateFromUtf16( , , , true) calling + * GetHashcode function is within expectations. + * @tc.type: FUNC + * @tc.require: + */ +TEST_F(EcmaStringTest, GetHashcode_002) +{ + EcmaVM* ecmaVMPtr = EcmaVM::Cast(instance); + + // GetHashcode(). EcmaString made by CreateFromUtf16( , , , true). + uint16_t arrayU16Comp[] = {45, 92, 78, 24}; + uint32_t lengthEcmaStrU16Comp = sizeof(arrayU16Comp) / sizeof(arrayU16Comp[0]); + JSHandle handleEcmaStrU16Comp(thread, + EcmaString::CreateFromUtf16(&arrayU16Comp[0], lengthEcmaStrU16Comp, ecmaVMPtr, true)); + uint32_t hashExpect = 0; + for (uint32_t i = 0; i < lengthEcmaStrU16Comp; i++) { + hashExpect = hashExpect * 31 + arrayU16Comp[i]; + } + EXPECT_EQ(handleEcmaStrU16Comp->GetHashcode(), static_cast(hashExpect)); +} + +/* + * @tc.name: GetHashcode_003 + * @tc.desc: Check whether the value returned through an EcmaString made by CreateFromUtf16( , , , false) calling + * GetHashcode function is within expectations before and after calling SetCompressedStringsEnabled function with + * false. + * @tc.type: FUNC + * @tc.require: + */ +TEST_F(EcmaStringTest, GetHashcode_003) +{ + EcmaVM* ecmaVMPtr = EcmaVM::Cast(instance); + + // GetHashcode(). EcmaString made by CreateFromUtf16( , , , false). + uint16_t arrayU16NotComp[] = {199, 1, 256, 65535, 777}; + uint32_t lengthEcmaStrU16NotComp = sizeof(arrayU16NotComp) / sizeof(arrayU16NotComp[0]); + JSHandle handleEcmaStrU16NotComp(thread, + EcmaString::CreateFromUtf16(&arrayU16NotComp[0], lengthEcmaStrU16NotComp, ecmaVMPtr, false)); + uint32_t hashExpect = 0; + for (uint32_t i = 0; i < lengthEcmaStrU16NotComp; i++) { + hashExpect = hashExpect * 31 + arrayU16NotComp[i]; + } + EXPECT_EQ(handleEcmaStrU16NotComp->GetHashcode(), static_cast(hashExpect)); + + EcmaString::SetCompressedStringsEnabled(false); // Set compressedStringsEnabled false. + JSHandle handleEcmaStrU16NotCompDisableComp(thread, + EcmaString::CreateFromUtf16(&arrayU16NotComp[0], lengthEcmaStrU16NotComp, ecmaVMPtr, false)); + EXPECT_EQ(handleEcmaStrU16NotCompDisableComp->GetHashcode(), static_cast(hashExpect)); + EcmaString::SetCompressedStringsEnabled(true); // Set compressedStringsEnabled true(default). +} + +/* + * @tc.name: GetHashcode_004 + * @tc.desc: Check whether the value returned through an EcmaString made by CreateEmptyString() calling GetHashcode + * function is within expectations before and after calling SetCompressedStringsEnabled function with false. + * @tc.type: FUNC + * @tc.require: + */ +TEST_F(EcmaStringTest, GetHashcode_004) +{ + EcmaVM* ecmaVMPtr = EcmaVM::Cast(instance); + + // GetHashcode(). EcmaString made by CreateEmptyString(). + JSHandle handleEcmaStrEmpty(thread, EcmaString::CreateEmptyString(ecmaVMPtr)); + EXPECT_EQ(handleEcmaStrEmpty->GetHashcode(), 0); + + EcmaString::SetCompressedStringsEnabled(false); // Set compressedStringsEnabled false. + JSHandle handleEcmaStrEmptyDisableComp(thread, EcmaString::CreateEmptyString(ecmaVMPtr)); + EXPECT_EQ(handleEcmaStrEmptyDisableComp->GetHashcode(), 0); + EcmaString::SetCompressedStringsEnabled(true); // Set compressedStringsEnabled true(default). +} + +/* + * @tc.name: GetHashcode_005 + * @tc.desc: Check whether the value returned through an EcmaString made by AllocStringObject(, true/false, ) calling + * GetHashcode function is within expectations before and after calling SetCompressedStringsEnabled function with + * false. + * @tc.type: FUNC + * @tc.require: + */ +TEST_F(EcmaStringTest, GetHashcode_005) +{ + EcmaVM* ecmaVMPtr = EcmaVM::Cast(instance); + + // GetHashcode(). EcmaString made by AllocStringObject(). + size_t sizeAlloc = 5; + JSHandle handleEcmaStrAllocComp(thread, EcmaString::AllocStringObject(sizeAlloc, true, ecmaVMPtr)); + JSHandle handleEcmaStrAllocNotComp(thread, EcmaString::AllocStringObject(sizeAlloc, false, ecmaVMPtr)); + EXPECT_EQ(handleEcmaStrAllocComp->GetHashcode(), 0); + EXPECT_EQ(handleEcmaStrAllocNotComp->GetHashcode(), 0); + + EcmaString::SetCompressedStringsEnabled(false); // Set compressedStringsEnabled false. + JSHandle handleEcmaStrAllocNotCompDisableComp(thread, + EcmaString::AllocStringObject(sizeAlloc, false, ecmaVMPtr)); + EXPECT_EQ(handleEcmaStrAllocNotCompDisableComp->GetHashcode(), 0); + EcmaString::SetCompressedStringsEnabled(true); // Set compressedStringsEnabled true(default). +} + +/* + * @tc.name: GetCString + * @tc.desc: Check whether the std::unique_ptr returned through calling GetCString function is within + * expectations. + * @tc.type: FUNC + * @tc.require: + */ +TEST_F(EcmaStringTest, GetCString) +{ + EcmaVM* ecmaVMPtr = EcmaVM::Cast(instance); + + uint8_t arrayU8[] = {"abc"}; + uint32_t lengthEcmaStrU8 = sizeof(arrayU8) - 1; + JSHandle handleEcmaStrU8(thread, + EcmaString::CreateFromUtf8(&arrayU8[0], lengthEcmaStrU8, ecmaVMPtr, true)); + EXPECT_STREQ(CString(handleEcmaStrU8->GetCString().get()).c_str(), "abc"); + + uint16_t arrayU16Comp[] = {97, 98, 99}; + uint32_t lengthEcmaStrU16Comp = sizeof(arrayU16Comp) / sizeof(arrayU16Comp[0]); + JSHandle handleEcmaStrU16Comp(thread, + EcmaString::CreateFromUtf16(&arrayU16Comp[0], lengthEcmaStrU16Comp, ecmaVMPtr, true)); + EXPECT_STREQ(CString(handleEcmaStrU16Comp->GetCString().get()).c_str(), "abc"); + + uint16_t arrayU16NotComp[] = {97, 98, 99}; + uint32_t lengthEcmaStrU16NotComp = sizeof(arrayU16NotComp) / sizeof(arrayU16NotComp[0]); + JSHandle handleEcmaStrU16NotComp(thread, + EcmaString::CreateFromUtf16(&arrayU16NotComp[0], lengthEcmaStrU16NotComp, ecmaVMPtr, false)); + EXPECT_STREQ(CString(handleEcmaStrU16NotComp->GetCString().get()).c_str(), "abc"); +} + +/* + * @tc.name: SetIsInternString + * @tc.desc: Call SetIsInternString function, check whether the bool returned through calling IsInternString function + * is within expectations. + * @tc.type: FUNC + * @tc.require: + */ +TEST_F(EcmaStringTest, SetIsInternString) +{ + EcmaVM* ecmaVMPtr = EcmaVM::Cast(instance); + + uint8_t arrayU8[] = {"abc"}; + uint32_t lengthEcmaStrU8 = sizeof(arrayU8) - 1; + JSHandle handleEcmaStrU8(thread, + EcmaString::CreateFromUtf8(&arrayU8[0], lengthEcmaStrU8, ecmaVMPtr, true)); + EXPECT_FALSE(handleEcmaStrU8->IsInternString()); + handleEcmaStrU8->SetIsInternString(); + EXPECT_TRUE(handleEcmaStrU8->IsInternString()); + + uint16_t arrayU16Comp[] = {97, 98, 99}; + uint32_t lengthEcmaStrU16Comp = sizeof(arrayU16Comp) / sizeof(arrayU16Comp[0]); + JSHandle handleEcmaStrU16Comp(thread, + EcmaString::CreateFromUtf16(&arrayU16Comp[0], lengthEcmaStrU16Comp, ecmaVMPtr, true)); + EXPECT_FALSE(handleEcmaStrU16Comp->IsInternString()); + handleEcmaStrU16Comp->SetIsInternString(); + EXPECT_TRUE(handleEcmaStrU16Comp->IsInternString()); + + uint16_t arrayU16NotComp[] = {97, 98, 99}; + uint32_t lengthEcmaStrU16NotComp = sizeof(arrayU16NotComp) / sizeof(arrayU16NotComp[0]); + JSHandle handleEcmaStrU16NotComp(thread, + EcmaString::CreateFromUtf16(&arrayU16NotComp[0], lengthEcmaStrU16NotComp, ecmaVMPtr, false)); + EXPECT_FALSE(handleEcmaStrU16NotComp->IsInternString()); + handleEcmaStrU16NotComp->SetIsInternString(); + EXPECT_TRUE(handleEcmaStrU16NotComp->IsInternString()); +} +} // namespace panda::ecmascript diff --git a/tests/runtime/common/fortest/CMakeLists.txt b/tests/runtime/common/fortest/CMakeLists.txt new file mode 100644 index 000000000..bc6898382 --- /dev/null +++ b/tests/runtime/common/fortest/CMakeLists.txt @@ -0,0 +1,23 @@ +# Huawei Technologies Co.,Ltd. + +set(FORTEST_OUTPUT ${CMAKE_CURRENT_BINARY_DIR}/fortest.txt) +set(FORTEST_JS ${CMAKE_CURRENT_SOURCE_DIR}/fortest.js) +set(FORTEST_BIN ${CMAKE_CURRENT_BINARY_DIR}/fortest.abc) +set(FORTEST_PA ${CMAKE_CURRENT_BINARY_DIR}/fortest.pa) +set(FORTEST_VERIFY ${CMAKE_CURRENT_SOURCE_DIR}/verify.sh) + +set(RUNTIME_ARGUMENTS --boot-panda-files=${PANDA_BINARY_ROOT}/pandastdlib/arkstdlib.abc --load-runtimes=\"ecmascript\" --compiler-enable-jit=false --gc-type=stw --run-gc-in-place ${FORTEST_BIN} _GLOBAL::func_main_0) + +add_custom_command( + OUTPUT ${FORTEST_OUTPUT} + COMMENT "running javascript fortest testcase" + COMMAND ${PANDA_RUN_PREFIX} $ ${FORTEST_JS} --dump-assembly --output ${FORTEST_BIN} > ${FORTEST_PA} + COMMAND rm -f ${FORTEST_OUTPUT} + COMMAND ${PANDA_RUN_PREFIX} $ ${RUNTIME_ARGUMENTS} > ${FORTEST_OUTPUT} + COMMAND bash ${FORTEST_VERIFY} ${FORTEST_OUTPUT} +) +add_custom_target(fortest + DEPENDS ${FORTEST_OUTPUT} ${FORTEST_VERIFY} +) +add_dependencies(fortest es2panda ark) +add_dependencies(ecmascript_common_tests fortest) diff --git a/tests/runtime/common/fortest/fortest.js b/tests/runtime/common/fortest/fortest.js new file mode 100644 index 000000000..c82166386 --- /dev/null +++ b/tests/runtime/common/fortest/fortest.js @@ -0,0 +1,18 @@ +let a = '*' +let s = '' + +for (let i = 0; i < 10; i++) +{ + s += a + print(s) +} + +let i = 10 + +while (i > 0) { + print(i); + i--; +} + + + diff --git a/tests/runtime/common/fortest/verify.sh b/tests/runtime/common/fortest/verify.sh new file mode 100755 index 000000000..de8385ae6 --- /dev/null +++ b/tests/runtime/common/fortest/verify.sh @@ -0,0 +1,34 @@ +#!/bin/bash +# Copyright (c) Huawei Technologies Co., Ltd. 2020-2021. All rights reserved. + +set -eo pipefail + +expected="* +** +*** +**** +***** +****** +******* +******** +********* +********** +10 +9 +8 +7 +6 +5 +4 +3 +2 +1" +actual=$(cat "$1") +if [[ "$actual" == "$expected" ]];then + exit 0; +else + echo -e "expected:"$expected + echo -e "actual:"$actual + echo -e "\033[31mfortest test failed\033[0m" + exit 1; +fi \ No newline at end of file diff --git a/tests/runtime/common/gc_test.cpp b/tests/runtime/common/gc_test.cpp new file mode 100644 index 000000000..8de8aa474 --- /dev/null +++ b/tests/runtime/common/gc_test.cpp @@ -0,0 +1,86 @@ +/* + * Copyright (c) 2021-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 "plugins/ecmascript/runtime/ecma_vm.h" +#include "plugins/ecmascript/runtime/mem/compress_collector.h" +#include "plugins/ecmascript/runtime/mem/semi_space_collector.h" +#include "plugins/ecmascript/runtime/object_factory.h" +#include "plugins/ecmascript/tests/runtime/common/test_helper.h" + +using namespace panda; + +using namespace panda::ecmascript; + +namespace panda::test { +class GCTest : public testing::Test { +public: + static void SetUpTestCase() + { + GTEST_LOG_(INFO) << "SetUpTestCase"; + } + + static void TearDownTestCase() + { + GTEST_LOG_(INFO) << "TearDownCase"; + } + + void SetUp() override + { + JSRuntimeOptions options; + options.SetShouldLoadBootPandaFiles(false); + options.SetShouldInitializeIntrinsics(false); + options.SetBootClassSpaces({"ecmascript"}); + options.SetRuntimeType("ecmascript"); + options.SetEnableParalledYoungGc(false); + static EcmaLanguageContext lcEcma; + [[maybe_unused]] bool success = Runtime::Create(options, {&lcEcma}); + ASSERT_TRUE(success) << "Cannot create Runtime"; + instance = Runtime::GetCurrent()->GetPandaVM(); + ASSERT_TRUE(instance != nullptr) << "Cannot create EcmaVM"; + thread = EcmaVM::Cast(instance)->GetJSThread(); + scope = new EcmaHandleScope(thread); + } + + void TearDown() override + { + TestHelper::DestroyEcmaVMWithScope(instance, scope); + } + + PandaVM *instance {nullptr}; + ecmascript::EcmaHandleScope *scope {nullptr}; + JSThread *thread {nullptr}; +}; + +TEST_F(GCTest, CompressGCOne) +{ + ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); + auto heap = thread->GetEcmaVM()->GetHeap(); + auto compressGc = heap->GetCompressCollector(); + compressGc->RunPhases(); + auto oldSizebase = heap->GetOldSpace()->GetHeapObjectSize(); + size_t oldSizeBefore = 0; + { + [[maybe_unused]] ecmascript::EcmaHandleScope baseScope(thread); + for (int i = 0; i < 1024; i++) { + factory->NewTaggedArray(512, JSTaggedValue::Undefined(), MemSpaceType::OLD_SPACE); + } + oldSizeBefore = heap->GetOldSpace()->GetHeapObjectSize(); + EXPECT_TRUE(oldSizeBefore > oldSizebase); + } + compressGc->RunPhases(); + auto oldSizeAfter = heap->GetOldSpace()->GetHeapObjectSize(); + EXPECT_TRUE(oldSizeBefore > oldSizeAfter); +} +} // namespace panda::test diff --git a/tests/runtime/common/generator/CMakeLists.txt b/tests/runtime/common/generator/CMakeLists.txt new file mode 100644 index 000000000..3b354176c --- /dev/null +++ b/tests/runtime/common/generator/CMakeLists.txt @@ -0,0 +1,22 @@ +# Huawei Technologies Co.,Ltd. + +set(GENERATOR_OUTPUT ${CMAKE_CURRENT_BINARY_DIR}/generator.txt) +set(GENERATOR_BIN ${CMAKE_CURRENT_BINARY_DIR}/generator.abc) +set(GENERATOR_JS ${CMAKE_CURRENT_SOURCE_DIR}/generator.js) +set(GENERATOR_VERIFY ${CMAKE_CURRENT_SOURCE_DIR}/verify.sh) + +set(RUNTIME_ARGUMENTS --boot-panda-files=${PANDA_BINARY_ROOT}/pandastdlib/arkstdlib.abc --load-runtimes=\"ecmascript\" --compiler-enable-jit=false --gc-type=stw --run-gc-in-place ${GENERATOR_BIN} _GLOBAL::func_main_0) + +add_custom_command( + OUTPUT ${GENERATOR_OUTPUT} + COMMENT "running javascript generator testcase" + COMMAND ${PANDA_RUN_PREFIX} $ ${GENERATOR_JS} --output ${GENERATOR_BIN} + COMMAND rm -f ${GENERATOR_OUTPUT} + COMMAND ${PANDA_RUN_PREFIX} $ ${RUNTIME_ARGUMENTS} 2>&1 > ${GENERATOR_OUTPUT} + COMMAND bash ${GENERATOR_VERIFY} ${GENERATOR_OUTPUT} +) +add_custom_target(generator + DEPENDS ${GENERATOR_OUTPUT} ${GENERATOR_VERIFY} + ) +add_dependencies(generator ark_asm ark) +add_dependencies(ecmascript_common_tests generator) diff --git a/tests/runtime/common/generator/generator.js b/tests/runtime/common/generator/generator.js new file mode 100644 index 000000000..210aebdcf --- /dev/null +++ b/tests/runtime/common/generator/generator.js @@ -0,0 +1,14 @@ +function *foo() { + yield 1 + yield 2 +} + +var p = foo() +var a = p.next() +print(a.value, a.done) +var b = p.next() +print(b.value, b.done) +var c = p.next() +print(c.value, c.done) +var d = p.next() +print(d.value, d.done) \ No newline at end of file diff --git a/tests/runtime/common/generator/verify.sh b/tests/runtime/common/generator/verify.sh new file mode 100755 index 000000000..80c7c03bf --- /dev/null +++ b/tests/runtime/common/generator/verify.sh @@ -0,0 +1,18 @@ +#!/bin/bash +# Copyright (c) Huawei Technologies Co., Ltd. 2020-2021. All rights reserved. + +set -eo pipefail + +expected="1 false +2 false +undefined true +undefined true" +actual=$(cat "$1") +if [[ "$actual" == "$expected" ]];then + exit 0; +else + echo -e "expected:"$expected + echo -e "actual:"$actual + echo -e "\033[31mgenerator test failed\033[0m" + exit 1; +fi diff --git a/tests/runtime/common/getunmappedargs/CMakeLists.txt b/tests/runtime/common/getunmappedargs/CMakeLists.txt new file mode 100644 index 000000000..94d3cfcd6 --- /dev/null +++ b/tests/runtime/common/getunmappedargs/CMakeLists.txt @@ -0,0 +1,23 @@ +# Huawei Technologies Co.,Ltd. + +set(GETUNMAPPEDARGS_OUTPUT ${CMAKE_CURRENT_BINARY_DIR}/getunmappedargs.txt) +set(GETUNMAPPEDARGS_BIN ${CMAKE_CURRENT_BINARY_DIR}/getunmappedargs.abc) +set(GETUNMAPPEDARGS_PA ${CMAKE_CURRENT_BINARY_DIR}/getunmappedargs.pa) +set(GETUNMAPPEDARGS_JS ${CMAKE_CURRENT_SOURCE_DIR}/getunmappedargs.js) +set(GETUNMAPPEDARGS_VERIFY ${CMAKE_CURRENT_SOURCE_DIR}/verify.sh) + +set(RUNTIME_ARGUMENTS --boot-panda-files=${PANDA_BINARY_ROOT}/pandastdlib/arkstdlib.abc --load-runtimes=\"ecmascript\" --compiler-enable-jit=false --gc-type=stw --run-gc-in-place ${GETUNMAPPEDARGS_BIN} _GLOBAL::func_main_0) + +add_custom_command( + OUTPUT ${GETUNMAPPEDARGS_OUTPUT} + COMMENT "running javascript getunmappedargs testcase" + COMMAND ${PANDA_RUN_PREFIX} $ ${GETUNMAPPEDARGS_JS} --dump-assembly --output ${GETUNMAPPEDARGS_BIN} > ${GETUNMAPPEDARGS_PA} + COMMAND rm -f ${GETUNMAPPEDARGS_OUTPUT} + COMMAND ${PANDA_RUN_PREFIX} $ ${RUNTIME_ARGUMENTS} > ${GETUNMAPPEDARGS_OUTPUT} + COMMAND bash ${GETUNMAPPEDARGS_VERIFY} ${GETUNMAPPEDARGS_OUTPUT} +) +add_custom_target(getunmappedargs + DEPENDS ${GETUNMAPPEDARGS_OUTPUT} ${GETUNMAPPEDARGS_VERIFY} +) +add_dependencies(getunmappedargs es2panda ark) +add_dependencies(ecmascript_common_tests getunmappedargs) diff --git a/tests/runtime/common/getunmappedargs/getunmappedargs.js b/tests/runtime/common/getunmappedargs/getunmappedargs.js new file mode 100644 index 000000000..2ba6dabf9 --- /dev/null +++ b/tests/runtime/common/getunmappedargs/getunmappedargs.js @@ -0,0 +1,7 @@ +function Foo() { + print(arguments[0]) + print(arguments[1]) + print(arguments[2]) +} + +Foo(1) \ No newline at end of file diff --git a/tests/runtime/common/getunmappedargs/verify.sh b/tests/runtime/common/getunmappedargs/verify.sh new file mode 100755 index 000000000..f332ff921 --- /dev/null +++ b/tests/runtime/common/getunmappedargs/verify.sh @@ -0,0 +1,17 @@ +#!/bin/bash +# Copyright (c) Huawei Technologies Co., Ltd. 2020-2021. All rights reserved. + +set -eo pipefail + +expected="1 +undefined +undefined" +actual=$(cat "$1") +if [[ "$actual" == "$expected" ]];then + exit 0; +else + echo -e "expected:"$expected + echo -e "actual:"$actual + echo -e "\033[31mgetunmappedargs test failed\033[0m" + exit 1; +fi diff --git a/tests/runtime/common/glue_regs_test.cpp b/tests/runtime/common/glue_regs_test.cpp new file mode 100644 index 000000000..364737fe5 --- /dev/null +++ b/tests/runtime/common/glue_regs_test.cpp @@ -0,0 +1,100 @@ +/* + * Copyright (c) 2021-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 "gtest/gtest.h" +#include "test_helper.h" + +#include "plugins/ecmascript/runtime/builtins.h" +#include "plugins/ecmascript/runtime/ecma_vm.h" +#include "plugins/ecmascript/runtime/global_env.h" +#include "plugins/ecmascript/runtime/js_thread.h" +#include "plugins/ecmascript/runtime/js_handle.h" +#include "plugins/ecmascript/runtime/base/builtins_base.h" +#include "plugins/ecmascript/runtime/global_env_constants-inl.h" + +using namespace panda::ecmascript; +using namespace panda::ecmascript::base; + +namespace panda::test { +class GlueRegsTest : public testing::Test { +public: + void SetUp() override + { + TestHelper::CreateEcmaVMWithScope(instance, thread, scope); + } + + void TearDown() override + { + TestHelper::DestroyEcmaVMWithScope(instance, scope); + } + + PandaVM *instance {nullptr}; + ecmascript::EcmaHandleScope *scope {nullptr}; + JSThread *thread; +}; + +TEST_F(GlueRegsTest, ConstantClassTest) +{ + const GlobalEnvConstants *globalConst = thread->GlobalConstants(); + ASSERT_NE(globalConst, nullptr); + + const JSTaggedValue *address = globalConst->BeginSlot(); + while (address < globalConst->EndSlot()) { + EXPECT_TRUE(!(*address).IsNull()); // Visit barely + address += sizeof(JSTaggedValue); + } +} + +TEST_F(GlueRegsTest, ConstantSpecialTest) +{ + auto globalConst = const_cast(thread->GlobalConstants()); + ASSERT_NE(globalConst, nullptr); + + EXPECT_TRUE(globalConst->GetUndefined().IsUndefined()); + EXPECT_TRUE(globalConst->GetHandledUndefined()->IsUndefined()); + EXPECT_TRUE(globalConst->GetNull().IsNull()); + EXPECT_TRUE(globalConst->GetHandledNull()->IsNull()); + EXPECT_TRUE(globalConst->GetEmptyString().IsString()); + EXPECT_TRUE(globalConst->GetHandledEmptyString()->IsString()); +} + +TEST_F(GlueRegsTest, ConstantStringTest) +{ + auto globalConst = const_cast(thread->GlobalConstants()); + ASSERT_NE(globalConst, nullptr); + +#define CONSTANT_STRING_ITERATOR(Type, Name, Index, Desc) \ + Type Name##value = globalConst->Get##Name(); \ + ASSERT_TRUE(!Name##value.IsNull()); \ + JSHandle Name##handledValue = globalConst->GetHandled##Name(); \ + ASSERT_TRUE(!Name##handledValue->IsNull()); \ + GLOBAL_ENV_CONSTANT_CONSTANT(CONSTANT_STRING_ITERATOR) +#undef CONSTANT_STRING_ITERATOR +} + +TEST_F(GlueRegsTest, ConstantAccessorTest) +{ + auto globalConst = const_cast(thread->GlobalConstants()); + ASSERT_NE(globalConst, nullptr); + +#define CONSTANT_ACCESSOR_ITERATOR(Type, Name, Index, Desc) \ + Type Name##value = globalConst->Get##Name(); \ + ASSERT_TRUE(!Name##value.IsNull()); \ + JSHandle Name##handledValue = globalConst->GetHandled##Name(); \ + ASSERT_TRUE(!Name##handledValue->IsNull()); \ + GLOBAL_ENV_CONSTANT_ACCESSOR(CONSTANT_ACCESSOR_ITERATOR) +#undef CONSTANT_ACCESSOR_ITERATOR +} +} // namespace panda::test \ No newline at end of file diff --git a/tests/runtime/common/helloworld/CMakeLists.txt b/tests/runtime/common/helloworld/CMakeLists.txt new file mode 100644 index 000000000..6374700aa --- /dev/null +++ b/tests/runtime/common/helloworld/CMakeLists.txt @@ -0,0 +1,23 @@ +# Huawei Technologies Co.,Ltd. + +set(HELLOWOLRD_OUTPUT ${CMAKE_CURRENT_BINARY_DIR}/helloworld.txt) +set(HELLOWOLRD_BIN ${CMAKE_CURRENT_BINARY_DIR}/helloworld.abc) +set(HELLOWOLRD_PA ${CMAKE_CURRENT_BINARY_DIR}/helloworld.pa) +set(HELLOWOLRD_JS ${CMAKE_CURRENT_SOURCE_DIR}/helloworld.js) +set(HELLOWORLD_VERIFY ${CMAKE_CURRENT_SOURCE_DIR}/verify.sh) + +set(RUNTIME_ARGUMENTS --boot-panda-files=${PANDA_BINARY_ROOT}/pandastdlib/arkstdlib.abc --load-runtimes=\"ecmascript\" --compiler-enable-jit=false --gc-type=stw --run-gc-in-place ${HELLOWOLRD_BIN} _GLOBAL::func_main_0) + +add_custom_command( + OUTPUT ${HELLOWOLRD_OUTPUT} + COMMENT "running javascript newobjdynrange testcase" + COMMAND ${PANDA_RUN_PREFIX} $ ${HELLOWOLRD_JS} --dump-assembly --output ${HELLOWOLRD_BIN} > ${HELLOWOLRD_PA} + COMMAND rm -f ${HELLOWOLRD_OUTPUT} + COMMAND ${PANDA_RUN_PREFIX} $ ${RUNTIME_ARGUMENTS} > ${HELLOWOLRD_OUTPUT} + COMMAND bash ${HELLOWORLD_VERIFY} ${HELLOWOLRD_OUTPUT} +) +add_custom_target(helloworld + DEPENDS ${HELLOWOLRD_OUTPUT} ${HELLOWORLD_VERIFY} +) +add_dependencies(helloworld es2panda ark) +add_dependencies(ecmascript_common_tests helloworld) diff --git a/tests/runtime/common/helloworld/helloworld.js b/tests/runtime/common/helloworld/helloworld.js new file mode 100644 index 000000000..e6cc52c4c --- /dev/null +++ b/tests/runtime/common/helloworld/helloworld.js @@ -0,0 +1 @@ +print("hello world !!") \ No newline at end of file diff --git a/tests/runtime/common/helloworld/verify.sh b/tests/runtime/common/helloworld/verify.sh new file mode 100755 index 000000000..6a9add2a4 --- /dev/null +++ b/tests/runtime/common/helloworld/verify.sh @@ -0,0 +1,15 @@ +#!/bin/bash +# Copyright (c) Huawei Technologies Co., Ltd. 2020-2021. All rights reserved. + +set -eo pipefail + +expected="hello world !!" +actual=$(cat "$1") +if [[ "$actual" == "$expected" ]];then + exit 0; +else + echo -e "expected:"$expected + echo -e "actual:"$actual + echo -e "\033[31mhelloworld test failed\033[0m" + exit 1; +fi \ No newline at end of file diff --git a/tests/runtime/common/huge_object_test.cpp b/tests/runtime/common/huge_object_test.cpp new file mode 100644 index 000000000..95c12adf4 --- /dev/null +++ b/tests/runtime/common/huge_object_test.cpp @@ -0,0 +1,128 @@ +/* + * Copyright (c) 2021-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 "plugins/ecmascript/runtime/ecma_vm.h" +#include "plugins/ecmascript/runtime/global_env.h" +#include "plugins/ecmascript/runtime/js_handle.h" +#include "plugins/ecmascript/runtime/mem/space.h" +#include "plugins/ecmascript/runtime/mem/verification.h" +#include "plugins/ecmascript/runtime/object_factory.h" +#include "plugins/ecmascript/runtime/tagged_array-inl.h" +#include "plugins/ecmascript/tests/runtime/common/test_helper.h" + +using namespace panda::ecmascript; + +namespace panda::test { +class HugeObjectTest : public testing::Test { +public: + static void SetUpTestCase() + { + GTEST_LOG_(INFO) << "SetUpTestCase"; + } + + static void TearDownTestCase() + { + GTEST_LOG_(INFO) << "TearDownCase"; + } + + void SetUp() override + { + TestHelper::CreateEcmaVMWithScope(instance, thread, scope); + thread->GetEcmaVM()->SetEnableForceGC(false); + const_cast(thread->GetEcmaVM()->GetHeap())->SetOnlyMarkSemi(false); + } + + void TearDown() override + { + TestHelper::DestroyEcmaVMWithScope(instance, scope); + } + + JSThread *thread {nullptr}; + PandaVM *instance {nullptr}; + ecmascript::EcmaHandleScope *scope {nullptr}; +}; + +#if !defined(NDEBUG) +static JSObject *JSObjectTestCreate(JSThread *thread) +{ + [[maybe_unused]] ecmascript::EcmaHandleScope scope(thread); + EcmaVM *ecmaVM = thread->GetEcmaVM(); + auto globalEnv = ecmaVM->GetGlobalEnv(); + JSHandle jsFunc = globalEnv->GetObjectFunction(); + JSHandle newObj = + ecmaVM->GetFactory()->NewJSObjectByConstructor(JSHandle(jsFunc), jsFunc); + return *newObj; +} +#endif + +#if !defined(NDEBUG) +static TaggedArray *LargeArrayTestCreate(JSThread *thread) +{ + [[maybe_unused]] ecmascript::EcmaHandleScope scope(thread); + static constexpr size_t SIZE = 1024 * 1024; + JSHandle array = thread->GetEcmaVM()->GetFactory()->NewTaggedArray(SIZE); + return *array; +} +#endif + +TEST_F(HugeObjectTest, LargeArrayKeep) +{ +#if !defined(NDEBUG) + TaggedArray *array = LargeArrayTestCreate(thread); + EXPECT_TRUE(array != nullptr); + JSHandle arrayHandle(thread, array); + JSHandle newObj(thread, JSObjectTestCreate(thread)); + arrayHandle->Set(thread, 0, newObj.GetTaggedValue()); + auto ecmaVm = thread->GetEcmaVM(); + EXPECT_EQ(*arrayHandle, reinterpret_cast(array)); + ecmaVm->CollectGarbage(TriggerGCType::SEMI_GC); // Trigger GC. + ecmaVm->CollectGarbage(TriggerGCType::HUGE_GC); // Trigger GC. + EXPECT_EQ(*newObj, array->Get(0).GetTaggedObject()); + EXPECT_EQ(*arrayHandle, reinterpret_cast(array)); +#endif +} + +TEST_F(HugeObjectTest, DISABLED_MultipleArrays) // TODO(vpukhov) +{ +#if !defined(NDEBUG) + auto ecmaVm = thread->GetEcmaVM(); + auto heap = ecmaVm->GetHeap(); + Region *firstPage = nullptr; + Region *secondPage = nullptr; + Region *thirdPage = nullptr; + JSHandle array1(thread, LargeArrayTestCreate(thread)); + firstPage = Region::ObjectAddressToRange(*array1); + { + DISALLOW_GARBAGE_COLLECTION; + [[maybe_unused]] TaggedArray *array2 = LargeArrayTestCreate(thread); + secondPage = Region::ObjectAddressToRange(array2); + } + JSHandle array3(thread, LargeArrayTestCreate(thread)); + thirdPage = Region::ObjectAddressToRange(*array3); + + EXPECT_EQ(firstPage->GetNext(), secondPage); + EXPECT_EQ(secondPage->GetNext(), thirdPage); + + ecmaVm->CollectGarbage(TriggerGCType::HUGE_GC); // Trigger GC. + + EXPECT_EQ(firstPage->GetNext(), thirdPage); + + size_t failCount = 0; + VerifyObjectVisitor objVerifier(heap, &failCount); + heap->GetHugeObjectSpace()->IterateOverObjects(objVerifier); // newspace reference the old space + EXPECT_TRUE(failCount == 0); +#endif +} +} // namespace panda::test diff --git a/tests/runtime/common/js_arguments_test.cpp b/tests/runtime/common/js_arguments_test.cpp new file mode 100644 index 000000000..c0ed517f7 --- /dev/null +++ b/tests/runtime/common/js_arguments_test.cpp @@ -0,0 +1,165 @@ +/* + * Copyright (c) 2021-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 "plugins/ecmascript/runtime/js_arguments.h" +#include "plugins/ecmascript/runtime/ecma_vm.h" +#include "plugins/ecmascript/runtime/global_env.h" +#include "plugins/ecmascript/runtime/js_handle.h" +#include "plugins/ecmascript/runtime/object_factory.h" +#include "plugins/ecmascript/tests/runtime/common/test_helper.h" + +using namespace panda::ecmascript; + +namespace panda::test { +class JsArgumentsTest : public testing::Test { +public: + static void SetUpTestCase() + { + GTEST_LOG_(INFO) << "SetUpTestCase"; + } + + static void TearDownTestCase() + { + GTEST_LOG_(INFO) << "TearDownCase"; + } + + void SetUp() override + { + TestHelper::CreateEcmaVMWithScope(instance, thread, scope); + } + + void TearDown() override + { + TestHelper::DestroyEcmaVMWithScope(instance, scope); + } + ecmascript::EcmaHandleScope *scope {nullptr}; + PandaVM *instance {nullptr}; + JSThread *thread {nullptr}; +}; + +static JSFunction *JSObjectTestCreate(JSThread *thread) +{ + EcmaVM *ecmaVM = thread->GetEcmaVM(); + JSHandle globalEnv = ecmaVM->GetGlobalEnv(); + return globalEnv->GetObjectFunction().GetObject(); +} + +TEST_F(JsArgumentsTest, SetProperty) +{ + JSHandle argFunc(thread, JSObjectTestCreate(thread)); + JSHandle jsarg = + thread->GetEcmaVM()->GetFactory()->NewJSObjectByConstructor(JSHandle(argFunc), argFunc); + JSHandle arg = thread->GetEcmaVM()->GetFactory()->NewJSArguments(); + + char array[] = "x"; + JSHandle key(thread->GetEcmaVM()->GetFactory()->NewFromCanBeCompressString(array)); + JSHandle value(thread, JSTaggedValue(1)); + + // receive must be jsarg's conversion + JSHandle receiver = JSHandle::Cast(jsarg); + EXPECT_TRUE(JSArguments::SetProperty(thread, arg, key, value, receiver)); + EXPECT_EQ(JSObject::GetProperty(thread, JSHandle(jsarg), key).GetValue()->GetInt(), 1); + EXPECT_EQ(JSArguments::GetProperty(thread, jsarg, key).GetValue()->GetInt(), 1); + + JSHandle value2(thread, JSTaggedValue(2)); + EXPECT_TRUE(JSArguments::SetProperty(thread, arg, key, value2, receiver)); + EXPECT_EQ(JSObject::GetProperty(thread, JSHandle(jsarg), key).GetValue()->GetInt(), 2); + EXPECT_EQ(JSArguments::GetProperty(thread, jsarg, key).GetValue()->GetInt(), 2); +} + +TEST_F(JsArgumentsTest, GetProperty) +{ + JSHandle argFunc(thread, JSObjectTestCreate(thread)); + JSHandle jsarg = + thread->GetEcmaVM()->GetFactory()->NewJSObjectByConstructor(JSHandle(argFunc), argFunc); + JSHandle arg = thread->GetEcmaVM()->GetFactory()->NewJSArguments(); + + char array[] = "x"; + JSHandle key(thread->GetEcmaVM()->GetFactory()->NewFromCanBeCompressString(array)); + JSHandle value(thread, JSTaggedValue(1)); + + JSHandle receiver = JSHandle::Cast(jsarg); + JSArguments::SetProperty(thread, arg, key, value, receiver); + EXPECT_EQ(JSObject::GetProperty(thread, JSHandle(jsarg), key).GetValue()->GetInt(), 1); + EXPECT_EQ(JSArguments::GetProperty(thread, JSHandle(jsarg), key, receiver).GetValue()->GetInt(), 1); + + JSHandle value2(thread, JSTaggedValue(2)); + JSArguments::SetProperty(thread, arg, key, value2, receiver); + EXPECT_EQ(JSArguments::GetProperty(thread, jsarg, key).GetValue()->GetInt(), 2); + EXPECT_EQ(JSObject::GetProperty(thread, JSHandle(jsarg), key).GetValue()->GetInt(), 2); +} + +TEST_F(JsArgumentsTest, DeleteProperty) +{ + JSHandle argFunc(thread, JSObjectTestCreate(thread)); + JSHandle jsarg = + thread->GetEcmaVM()->GetFactory()->NewJSObjectByConstructor(JSHandle(argFunc), argFunc); + JSHandle arg = thread->GetEcmaVM()->GetFactory()->NewJSArguments(); + + char array[] = "delete"; + JSHandle key(thread->GetEcmaVM()->GetFactory()->NewFromCanBeCompressString(array)); + JSHandle value(thread, JSTaggedValue(1)); + JSHandle receiver = JSHandle::Cast(jsarg); + JSArguments::SetProperty(thread, arg, key, value, receiver); + EXPECT_EQ(JSArguments::GetProperty(thread, jsarg, key).GetValue()->GetInt(), 1); + + // test delete + bool result = JSArguments::DeleteProperty(thread, JSHandle(jsarg), key); + EXPECT_TRUE(result); + EXPECT_TRUE(JSObject::GetProperty(thread, JSHandle(jsarg), key).GetValue()->IsUndefined()); +} + +TEST_F(JsArgumentsTest, DefineOwnProperty) +{ + JSHandle argFunc(thread, JSObjectTestCreate(thread)); + JSHandle jsarg = + thread->GetEcmaVM()->GetFactory()->NewJSObjectByConstructor(JSHandle(argFunc), argFunc); + JSHandle arg = thread->GetEcmaVM()->GetFactory()->NewJSArguments(); + + JSHandle key(thread->GetEcmaVM()->GetFactory()->NewFromCanBeCompressString("x")); + JSHandle value1(thread, JSTaggedValue(1)); + JSHandle value2(thread, JSTaggedValue(2)); + JSHandle receiver = JSHandle::Cast(jsarg); + JSArguments::SetProperty(thread, arg, key, value2, receiver); + EXPECT_EQ(JSObject::GetProperty(thread, JSHandle(jsarg), key).GetValue()->GetInt(), 2); + + PropertyDescriptor Desc(thread); + // set value1 + Desc.SetValue(value1); + Desc.SetWritable(false); + EXPECT_TRUE(JSArguments::DefineOwnProperty(thread, JSHandle(jsarg), key, Desc)); + EXPECT_EQ(JSObject::GetProperty(thread, JSHandle(jsarg), key).GetValue()->GetInt(), 1); +} + +TEST_F(JsArgumentsTest, GetOwnProperty) +{ + JSHandle argFunc(thread, JSObjectTestCreate(thread)); + JSHandle jsarg = + thread->GetEcmaVM()->GetFactory()->NewJSObjectByConstructor(JSHandle(argFunc), argFunc); + JSHandle arg = thread->GetEcmaVM()->GetFactory()->NewJSArguments(); + + JSHandle key(thread->GetEcmaVM()->GetFactory()->NewFromCanBeCompressString("x")); + JSHandle value(thread, JSTaggedValue(1)); + JSHandle receiver = JSHandle::Cast(jsarg); + JSArguments::SetProperty(thread, arg, key, value, receiver); + + PropertyDescriptor Desc(thread); + JSHandle caller = thread->GetEcmaVM()->GetFactory()->NewFromCanBeCompressString("caller"); + // key is not caller + EXPECT_FALSE(JSTaggedValue::SameValue(key.GetTaggedValue(), caller.GetTaggedValue())); + EXPECT_TRUE(JSArguments::GetOwnProperty(thread, JSHandle(jsarg), key, Desc)); + EXPECT_EQ(Desc.GetValue()->GetInt(), 1); +} +} // namespace panda::test diff --git a/tests/runtime/common/js_array_iterator_test.cpp b/tests/runtime/common/js_array_iterator_test.cpp new file mode 100644 index 000000000..0b50dbcd1 --- /dev/null +++ b/tests/runtime/common/js_array_iterator_test.cpp @@ -0,0 +1,178 @@ +/* + * Copyright (c) 2021-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 "plugins/ecmascript/runtime/global_env.h" +#include "plugins/ecmascript/runtime/js_array.h" +#include "plugins/ecmascript/runtime/js_array_iterator.h" +#include "plugins/ecmascript/runtime/object_factory.h" +#include "plugins/ecmascript/tests/runtime/common/test_helper.h" + +using namespace panda::ecmascript; + +namespace panda::test { +class JSArrayIteratorTest : public testing::Test { +public: + static void SetUpTestCase() + { + GTEST_LOG_(INFO) << "SetUpTestCase"; + } + + static void TearDownTestCase() + { + GTEST_LOG_(INFO) << "TearDownCase"; + } + + void SetUp() override + { + TestHelper::CreateEcmaVMWithScope(instance, thread, scope); + } + + void TearDown() override + { + TestHelper::DestroyEcmaVMWithScope(instance, scope); + } + PandaVM *instance {nullptr}; + ecmascript::EcmaHandleScope *scope {nullptr}; + JSThread *thread {nullptr}; +}; + +/* + * Feature: JSArrayIterator + * Function: SetIteratedArray + * SubFunction: GetIteratedArray + * FunctionPoints: Set Iterated Array + * CaseDescription: Call the "SetIteratedArray" function, check whether the result returned through "GetIteratedArray" + * function from the JSArrayIterator is within expectations. + */ +TEST_F(JSArrayIteratorTest, SetIteratedArray) +{ + EcmaVM *ecmaVMPtr = thread->GetEcmaVM(); + ObjectFactory *factory = ecmaVMPtr->GetFactory(); + + uint32_t arrayFrom1[10] = {0, 6, 8, 99, 200, 1, static_cast(-1), static_cast(-199), 33, 100}; + uint32_t arrayFrom2[10] = {1111, 3211, 737, 0, 1267, 174, 2763, 832, 11, 93}; + int numArrayFrom1 = sizeof(arrayFrom1)/sizeof(arrayFrom1[0]); + int numArrayFrom2 = sizeof(arrayFrom2)/sizeof(arrayFrom2[0]); + JSHandle handleTaggedArrayFrom1(factory->NewTaggedArray(numArrayFrom1)); + JSHandle handleTaggedArrayFrom2(factory->NewTaggedArray(numArrayFrom2)); + for (int i = 0; i < numArrayFrom1; i++) { + handleTaggedArrayFrom1->Set(thread, i, JSTaggedValue(arrayFrom1[i])); + } + for (int i = 0; i < numArrayFrom2; i++) { + handleTaggedArrayFrom2->Set(thread, i, JSTaggedValue(arrayFrom2[i])); + } + JSHandle handleJSObjectTaggedArrayFrom1(JSArray::CreateArrayFromList(thread, handleTaggedArrayFrom1)); + JSHandle handleJSObjectTaggedArrayFrom2(JSArray::CreateArrayFromList(thread, handleTaggedArrayFrom2)); + + // Call "SetIteratedArray" function through "NewJSArrayIterator" function of "object_factory.cpp". + JSHandle handleJSArrayIter = factory->NewJSArrayIterator(handleJSObjectTaggedArrayFrom1, + IterationKind::KEY); + + JSHandle handleJSArrayTo1(thread, JSArray::Cast(handleJSArrayIter->GetIteratedArray().GetTaggedObject())); + EXPECT_EQ(handleJSArrayTo1->GetArrayLength(), numArrayFrom1); + for (int i = 0; i < numArrayFrom1; i++) { + EXPECT_EQ(JSArray::FastGetPropertyByValue(thread, JSHandle(handleJSArrayTo1), i)->GetNumber(), + arrayFrom1[i]); + } + + // Call "SetIteratedArray" function in this TEST_F. + handleJSArrayIter->SetIteratedArray(thread, handleJSObjectTaggedArrayFrom2); + + JSHandle handleJSArrayTo2(thread, JSArray::Cast(handleJSArrayIter->GetIteratedArray().GetTaggedObject())); + EXPECT_EQ(handleJSArrayTo2->GetArrayLength(), numArrayFrom2); + for (int i = 0; i < numArrayFrom2; i++) { + EXPECT_EQ(JSArray::FastGetPropertyByValue(thread, JSHandle(handleJSArrayTo2), i)->GetNumber(), + arrayFrom2[i]); + } +} + +/* + * Feature: JSArrayIterator + * Function: SetNextIndex + * SubFunction: GetNextIndex + * FunctionPoints: Set Next Index + * CaseDescription: Call the "SetNextIndex" function, check whether the result returned through "GetNextIndex" function + * from the JSArrayIterator is within expectations. + */ +TEST_F(JSArrayIteratorTest, SetNextIndex) +{ + EcmaVM *ecmaVMPtr = thread->GetEcmaVM(); + ObjectFactory *factory = ecmaVMPtr->GetFactory(); + + uint32_t array[10] = {0, 6, 8, 99, 200, 1, static_cast(-1), static_cast(-199), 33, 100}; + int numArray = sizeof(array)/sizeof(array[0]); + JSHandle handleTaggedArray(factory->NewTaggedArray(numArray)); + for (int i = 0; i < numArray; i++) { + handleTaggedArray->Set(thread, i, JSTaggedValue(array[i])); + } + JSHandle handleJSObjectTaggedArray(JSArray::CreateArrayFromList(thread, handleTaggedArray)); + + // Call "SetNextIndex" function through "NewJSArrayIterator" function of "object_factory.cpp". + JSHandle handleJSArrayIter = factory->NewJSArrayIterator(handleJSObjectTaggedArray, + IterationKind::KEY); + EXPECT_EQ(handleJSArrayIter->GetNextIndex().GetNumber(), 0); + + int testQuantity = 100; + for (int i = 1; i <= testQuantity; i++) { + JSHandle handleTagValNextIndex(thread, JSTaggedValue(i)); + + // Call "SetNextIndex" function in this TEST_F. + handleJSArrayIter->SetNextIndex(thread, handleTagValNextIndex); + EXPECT_EQ(handleJSArrayIter->GetNextIndex().GetNumber(), i); + } +} + +/* + * Feature: JSArrayIterator + * Function: SetIterationKind + * SubFunction: GetIterationKind + * FunctionPoints: Set Iteration Kind + * CaseDescription: Call the "SetIterationKind" function, check whether the result returned through "GetIterationKind" + * function from the JSArrayIterator is within expectations. + */ +TEST_F(JSArrayIteratorTest, SetIterationKind) +{ + EcmaVM *ecmaVMPtr = thread->GetEcmaVM(); + ObjectFactory *factory = ecmaVMPtr->GetFactory(); + + uint32_t array[10] = {0, 6, 8, 99, 200, 1, static_cast(-1), static_cast(-199), 33, 100}; + int numArray = sizeof(array)/sizeof(array[0]); + JSHandle handleTaggedArray(factory->NewTaggedArray(numArray)); + for (int i = 0; i < numArray; i++) { + handleTaggedArray->Set(thread, i, JSTaggedValue(array[i])); + } + JSHandle handleTagVal0(thread, JSTaggedValue(0)); + JSHandle handleTagVal1(thread, JSTaggedValue(1)); + JSHandle handleTagVal2(thread, JSTaggedValue(2)); + JSHandle handleJSObjectTaggedArray(JSArray::CreateArrayFromList(thread, handleTaggedArray)); + + // Call "SetIterationKind" function through "NewJSArrayIterator" function of "object_factory.cpp". + JSHandle handleJSArrayIter = factory->NewJSArrayIterator(handleJSObjectTaggedArray, + IterationKind::KEY); + EXPECT_EQ(handleJSArrayIter->GetIterationKind().GetNumber(), 0); + handleJSArrayIter = factory->NewJSArrayIterator(handleJSObjectTaggedArray, IterationKind::VALUE); + EXPECT_EQ(handleJSArrayIter->GetIterationKind().GetNumber(), 1); + handleJSArrayIter = factory->NewJSArrayIterator(handleJSObjectTaggedArray, IterationKind::KEY_AND_VALUE); + EXPECT_EQ(handleJSArrayIter->GetIterationKind().GetNumber(), 2); + + // Call "SetIterationKind" function in this TEST_F. + handleJSArrayIter->SetIterationKind(thread, handleTagVal0); + EXPECT_EQ(handleJSArrayIter->GetIterationKind().GetNumber(), 0); + handleJSArrayIter->SetIterationKind(thread, handleTagVal1); + EXPECT_EQ(handleJSArrayIter->GetIterationKind().GetNumber(), 1); + handleJSArrayIter->SetIterationKind(thread, handleTagVal2); + EXPECT_EQ(handleJSArrayIter->GetIterationKind().GetNumber(), 2); +} +} // namespace panda::ecmascript diff --git a/tests/runtime/common/js_array_test.cpp b/tests/runtime/common/js_array_test.cpp new file mode 100644 index 000000000..5625398ee --- /dev/null +++ b/tests/runtime/common/js_array_test.cpp @@ -0,0 +1,189 @@ +/* + * Copyright (c) 2021-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 "gtest/gtest.h" +#include "test_helper.h" +#include "plugins/ecmascript/runtime/js_array.h" + +#include "plugins/ecmascript/runtime/ecma_string.h" +#include "include/coretypes/array.h" +#include "plugins/ecmascript/runtime/js_object.h" +#include "plugins/ecmascript/runtime/js_hclass.h" +#include "plugins/ecmascript/runtime/js_tagged_value-inl.h" +#include "plugins/ecmascript/runtime/js_handle.h" +#include "plugins/ecmascript/runtime/object_operator.h" +#include "include/runtime.h" +#include "plugins/ecmascript/runtime/object_factory.h" +#include "plugins/ecmascript/runtime/ecma_vm.h" +#include "plugins/ecmascript/runtime/global_env.h" +#include "plugins/ecmascript/runtime/js_array_iterator.h" +#include "plugins/ecmascript/runtime/js_iterator.h" +#include "plugins/ecmascript/runtime/tagged_array-inl.h" + +using namespace panda::ecmascript; +using namespace panda::coretypes; + +namespace panda::test { +class JSArrayTest : public testing::Test { +public: + void SetUp() override + { + TestHelper::CreateEcmaVMWithScope(instance, thread, scope); + } + + void TearDown() override + { + TestHelper::DestroyEcmaVMWithScope(instance, scope); + } + PandaVM *instance {nullptr}; + EcmaHandleScope *scope {nullptr}; + JSThread *thread {nullptr}; +}; + +TEST_F(JSArrayTest, ArrayCreate) +{ + JSHandle length_key_handle(thread->GlobalConstants()->GetHandledLengthString()); + JSArray *arr = JSArray::ArrayCreate(thread, JSTaggedNumber(0)).GetObject(); + EXPECT_TRUE(arr != nullptr); + JSHandle obj(thread, arr); + EXPECT_EQ(JSArray::GetProperty(thread, obj, length_key_handle).GetValue()->GetInt(), 0); + + JSArray *arr2 = JSArray::ArrayCreate(thread, JSTaggedNumber(10)).GetObject(); + EXPECT_TRUE(arr2 != nullptr); + JSHandle obj2(thread, arr2); + EXPECT_EQ(JSArray::GetProperty(thread, obj2, length_key_handle).GetValue()->GetInt(), 10); +} + +TEST_F(JSArrayTest, ArraySpeciesCreate) +{ + JSHandle length_key_handle(thread->GlobalConstants()->GetHandledLengthString()); + JSArray *arr = JSArray::ArrayCreate(thread, JSTaggedNumber(0)).GetObject(); + EXPECT_TRUE(arr != nullptr); + JSHandle obj(thread, arr); + EXPECT_EQ(JSArray::GetProperty(thread, JSHandle(obj), length_key_handle).GetValue()->GetInt(), 0); + + JSArray *arr2 = JSArray::Cast(JSArray::ArraySpeciesCreate(thread, obj, JSTaggedNumber(10)).GetHeapObject()); + EXPECT_TRUE(arr2 != nullptr); + JSHandle obj2(thread, arr2); + EXPECT_EQ(JSArray::GetProperty(thread, obj2, length_key_handle).GetValue()->GetInt(), 10); +} + +TEST_F(JSArrayTest, DefineOwnProperty) +{ + auto ecma_vm = thread->GetEcmaVM(); + auto factory = ecma_vm->GetFactory(); + JSHandle length_key_handle(thread->GlobalConstants()->GetHandledLengthString()); + JSArray *arr = JSArray::ArrayCreate(thread, JSTaggedNumber(0)).GetObject(); + EXPECT_TRUE(arr != nullptr); + JSHandle obj(thread, arr); + EXPECT_EQ(JSArray::GetProperty(thread, obj, length_key_handle).GetValue()->GetInt(), 0); + + PropertyDescriptor desc(thread, JSHandle(thread, JSTaggedValue(100)), true, true, true); + + EcmaLanguageContext ecmaLanguageContext; + LanguageContext ctx(&ecmaLanguageContext); + EcmaString *string1 = *factory->NewFromString("1"); + JSHandle key1(thread, static_cast(string1)); + JSHandle index1(thread, JSTaggedValue(1)); + EXPECT_TRUE(JSArray::DefineOwnProperty(thread, JSHandle(obj), key1, desc)); + EXPECT_EQ(JSArray::GetProperty(thread, obj, length_key_handle).GetValue()->GetInt(), 2); + TaggedValue v = JSArray::GetProperty(thread, obj, key1).GetValue().GetTaggedValue(); + EXPECT_EQ(v.GetInt(), 100); + v = JSArray::GetProperty(thread, obj, index1).GetValue().GetTaggedValue(); + EXPECT_EQ(v.GetInt(), 100); + EXPECT_EQ(JSArray::GetProperty(thread, obj, length_key_handle).GetValue()->GetInt(), 2); + + EcmaString *string100 = *factory->NewFromString("100"); + JSHandle key100(thread, static_cast(string100)); + JSHandle index100(thread, JSTaggedValue(100)); + + EXPECT_TRUE(JSArray::DefineOwnProperty(thread, JSHandle(obj), key100, desc)); + EXPECT_EQ(JSArray::GetProperty(thread, obj, key100).GetValue()->GetInt(), 100); + EXPECT_EQ(JSArray::GetProperty(thread, obj, index100).GetValue()->GetInt(), 100); + EXPECT_EQ(JSArray::GetProperty(thread, obj, length_key_handle).GetValue()->GetInt(), 101); + + EcmaString *stringx = *factory->NewFromString("2147483646"); + JSHandle keyx(thread, static_cast(stringx)); + JSHandle indexx(thread, JSTaggedValue(2147483646U)); // 2147483646U + + EXPECT_TRUE(JSArray::DefineOwnProperty(thread, JSHandle(obj), keyx, desc)); + EXPECT_EQ(JSArray::GetProperty(thread, obj, keyx).GetValue()->GetInt(), 100); + EXPECT_EQ(JSArray::GetProperty(thread, obj, indexx).GetValue()->GetInt(), 100); + EXPECT_EQ(JSArray::GetProperty(thread, obj, length_key_handle).GetValue()->GetInt(), 2147483647); + + EXPECT_TRUE(JSArray::DeleteProperty(thread, JSHandle(obj), indexx)); + EXPECT_TRUE(JSArray::GetProperty(thread, obj, keyx).GetValue()->IsUndefined()); + EXPECT_TRUE(JSArray::GetProperty(thread, obj, indexx).GetValue()->IsUndefined()); +} + +TEST_F(JSArrayTest, Next) +{ + ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); + JSHandle values(factory->NewTaggedArray(5)); + for (int i = 0; i < 5; i++) { + values->Set(thread, i, JSTaggedValue(i)); + } + JSHandle array(JSArray::CreateArrayFromList(thread, values)); + JSHandle iter(factory->NewJSArrayIterator(array, IterationKind::KEY)); + auto ecmaRuntimeCallInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread, JSTaggedValue::Undefined(), 4); + ecmaRuntimeCallInfo->SetFunction(JSTaggedValue::Undefined()); + [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread, ecmaRuntimeCallInfo.get()); + for (int i = 0; i < 5; i++) { + ecmaRuntimeCallInfo->SetThis(iter.GetTaggedValue()); + JSTaggedValue ret = JSArrayIterator::Next(ecmaRuntimeCallInfo.get()); + JSHandle result(thread, ret); + EXPECT_EQ(JSIterator::IteratorValue(thread, result)->GetInt(), i); + } + TestHelper::TearDownFrame(thread, prev); +} + +TEST_F(JSArrayTest, Iterator) +{ + ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); + JSHandle values(factory->NewTaggedArray(5)); + for (int i = 0; i < 5; i++) { + values->Set(thread, i, JSTaggedValue(i)); + } + JSHandle array(JSArray::CreateArrayFromList(thread, values)); + JSHandle key_iter(factory->NewJSArrayIterator(array, IterationKind::KEY)); + JSHandle value_iter(factory->NewJSArrayIterator(array, IterationKind::VALUE)); + JSHandle iter(factory->NewJSArrayIterator(array, IterationKind::KEY_AND_VALUE)); + + for (int i = 0; i < 5; i++) { + if (i == 2) { + JSHandle key(thread, JSTaggedValue(i)); + JSObject::DeleteProperty(thread, JSHandle(array), key); + } + JSHandle key_result(JSIterator::IteratorStep(thread, key_iter)); + JSHandle value_result(JSIterator::IteratorStep(thread, value_iter)); + JSHandle iter_result(JSIterator::IteratorStep(thread, iter)); + JSHandle iter_value(JSIterator::IteratorValue(thread, iter_result)); + JSHandle index_key(thread, JSTaggedValue(0)); + JSHandle element_key(thread, JSTaggedValue(1)); + if (i == 2) { + EXPECT_EQ(i, JSIterator::IteratorValue(thread, key_result)->GetInt()); + EXPECT_EQ(JSTaggedValue::Undefined(), JSIterator::IteratorValue(thread, value_result).GetTaggedValue()); + EXPECT_EQ(i, JSObject::GetProperty(thread, iter_value, index_key).GetValue()->GetInt()); + EXPECT_EQ(JSTaggedValue::Undefined(), + JSObject::GetProperty(thread, iter_value, element_key).GetValue().GetTaggedValue()); + continue; + } + EXPECT_EQ(i, JSIterator::IteratorValue(thread, key_result)->GetInt()); + EXPECT_EQ(i, JSIterator::IteratorValue(thread, value_result)->GetInt()); + EXPECT_EQ(i, JSObject::GetProperty(thread, iter_value, index_key).GetValue()->GetInt()); + EXPECT_EQ(i, JSObject::GetProperty(thread, iter_value, element_key).GetValue()->GetInt()); + } +} +} // namespace panda::test diff --git a/tests/runtime/common/js_dataview_test.cpp b/tests/runtime/common/js_dataview_test.cpp new file mode 100644 index 000000000..fee8a84b3 --- /dev/null +++ b/tests/runtime/common/js_dataview_test.cpp @@ -0,0 +1,214 @@ +/* + * Copyright (c) 2021-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 "plugins/ecmascript/runtime/global_env.h" +#include "plugins/ecmascript/runtime/js_arraybuffer.h" +#include "plugins/ecmascript/runtime/js_dataview.h" +#include "plugins/ecmascript/runtime/object_factory.h" +#include "plugins/ecmascript/tests/runtime/common/test_helper.h" + +using namespace panda::ecmascript; + +namespace panda::test { +class JSDataViewTest : public testing::Test { +public: + static void SetUpTestCase() + { + GTEST_LOG_(INFO) << "SetUpTestCase"; + } + + static void TearDownTestCase() + { + GTEST_LOG_(INFO) << "TearDownCase"; + } + + void SetUp() override + { + TestHelper::CreateEcmaVMWithScope(instance, thread, scope); + } + + void TearDown() override + { + TestHelper::DestroyEcmaVMWithScope(instance, scope); + } + PandaVM *instance {nullptr}; + ecmascript::EcmaHandleScope *scope {nullptr}; + JSThread *thread {nullptr}; +}; + +/* + * Feature: JSDataView + * Function: GetElementSize + * SubFunction: N/A + * FunctionPoints: Get ElementSize + * CaseDescription: Check whether the returned value through "GetElementSize" function is within expectations. + */ +TEST_F(JSDataViewTest, GetElementSize) +{ + EXPECT_EQ(JSDataView::GetElementSize(DataViewType::INT8), 1); + EXPECT_EQ(JSDataView::GetElementSize(DataViewType::UINT8), 1); + EXPECT_EQ(JSDataView::GetElementSize(DataViewType::UINT8_CLAMPED), 1); + EXPECT_EQ(JSDataView::GetElementSize(DataViewType::INT16), 2); + EXPECT_EQ(JSDataView::GetElementSize(DataViewType::UINT16), 2); + EXPECT_EQ(JSDataView::GetElementSize(DataViewType::INT32), 4); + EXPECT_EQ(JSDataView::GetElementSize(DataViewType::UINT32), 4); + EXPECT_EQ(JSDataView::GetElementSize(DataViewType::FLOAT32), 4); + EXPECT_EQ(JSDataView::GetElementSize(DataViewType::FLOAT64), 8); +} + +/* + * Feature: JSDataView + * Function: SetDataView + * SubFunction: GetDataView + * FunctionPoints: Set DataView + * CaseDescription: Check whether the returned value through "GetDataView" function is within expectations after + * calling "SetDataView" function. + */ +TEST_F(JSDataViewTest, SetDataView) +{ + EcmaVM *ecmaVMPtr = thread->GetEcmaVM(); + ObjectFactory *factory = ecmaVMPtr->GetFactory(); + JSHandle handleGlobalEnv = ecmaVMPtr->GetGlobalEnv(); + + int32_t lengthDataArrayBuf = 8; + int32_t offsetDataView = 4; + int32_t lengthDataView = 4; + JSHandle handleFuncArrayBuf(handleGlobalEnv->GetArrayBufferFunction()); + JSHandle handleTagValFuncArrayBuf(handleFuncArrayBuf); + JSHandle handleArrayBuf( + factory->NewJSObjectByConstructor(handleFuncArrayBuf, handleTagValFuncArrayBuf)); + handleArrayBuf->SetArrayBufferByteLength(thread, JSTaggedValue(lengthDataArrayBuf)); + + // Call "SetDataView" function through "NewJSDataView" function of "object_factory.cpp" + JSHandle handleDataView = factory->NewJSDataView(handleArrayBuf, offsetDataView, + lengthDataView); + EXPECT_TRUE(handleDataView->GetDataView().IsTrue()); + + // Call "SetDataView" function in this TEST_F. + handleDataView->SetDataView(thread, JSTaggedValue::False()); + EXPECT_TRUE(handleDataView->GetDataView().IsFalse()); + handleDataView->SetDataView(thread, JSTaggedValue::True()); + EXPECT_TRUE(handleDataView->GetDataView().IsTrue()); +} + +/* + * Feature: JSDataView + * Function: SetViewedArrayBuffer + * SubFunction: GetViewedArrayBuffer + * FunctionPoints: Set ViewedArrayBuffer + * CaseDescription: Check whether the returned value through "GetViewedArrayBuffer" function is within expectations + * after calling "SetViewedArrayBuffer" function. + */ +TEST_F(JSDataViewTest, SetViewedArrayBuffer) +{ + EcmaVM *ecmaVMPtr = thread->GetEcmaVM(); + ObjectFactory *factory = ecmaVMPtr->GetFactory(); + JSHandle handleFuncArrayBuf(ecmaVMPtr->GetGlobalEnv()->GetArrayBufferFunction()); + JSHandle handleTagValFuncArrayBuf(handleFuncArrayBuf); + + int32_t lengthDataArrayBuf1 = 8; + int32_t lengthDataArrayBuf2 = 16; + int32_t offsetDataView = 4; + int32_t lengthDataView = 4; + JSHandle handleArrayBuf1( + factory->NewJSObjectByConstructor(handleFuncArrayBuf, handleTagValFuncArrayBuf)); + JSHandle handleArrayBuf2( + factory->NewJSObjectByConstructor(handleFuncArrayBuf, handleTagValFuncArrayBuf)); + handleArrayBuf1->SetArrayBufferByteLength(thread, JSTaggedValue(lengthDataArrayBuf1)); + handleArrayBuf2->SetArrayBufferByteLength(thread, JSTaggedValue(lengthDataArrayBuf2)); + + // Call "SetViewedArrayBuffer" function through "NewJSDataView" function of "object_factory.cpp" + JSHandle handleDataView = factory->NewJSDataView(handleArrayBuf1, offsetDataView, + lengthDataView); + JSHandle handleTagValDataViewFrom1(thread, handleArrayBuf1.GetTaggedValue()); + JSHandle handleTagValDataViewTo1(thread, handleDataView->GetViewedArrayBuffer()); + EXPECT_TRUE(JSTaggedValue::Equal(thread, handleTagValDataViewFrom1, handleTagValDataViewTo1)); + + // Call "SetViewedArrayBuffer" function in this TEST_F. + handleDataView->SetViewedArrayBuffer(thread, handleArrayBuf2.GetTaggedValue()); + JSHandle handleTagValDataViewFrom2(thread, handleArrayBuf2.GetTaggedValue()); + JSHandle handleTagValDataViewTo2(thread, handleDataView->GetViewedArrayBuffer()); + EXPECT_TRUE(JSTaggedValue::Equal(thread, handleTagValDataViewFrom2, handleTagValDataViewTo2)); + EXPECT_FALSE(JSTaggedValue::Equal(thread, handleTagValDataViewFrom1, handleTagValDataViewFrom2)); +} + +/* + * Feature: JSDataView + * Function: SetByteLength + * SubFunction: GetByteLength + * FunctionPoints: Set ByteLength + * CaseDescription: Check whether the returned value through "GetByteLength" function is within expectations after + * calling "SetByteLength" function. + */ +TEST_F(JSDataViewTest, SetByteLength) +{ + EcmaVM *ecmaVMPtr = thread->GetEcmaVM(); + ObjectFactory *factory = ecmaVMPtr->GetFactory(); + JSHandle handleFuncArrayBuf(ecmaVMPtr->GetGlobalEnv()->GetArrayBufferFunction()); + JSHandle handleTagValFuncArrayBuf(handleFuncArrayBuf); + + int32_t lengthDataArrayBuf = 8; + int32_t offsetDataView = 4; + int32_t lengthDataView1 = 4; + int32_t lengthDataView2 = 2; + JSHandle handleTagValLengthDataView2(thread, JSTaggedValue(lengthDataView2)); + JSHandle handleArrayBuf( + factory->NewJSObjectByConstructor(handleFuncArrayBuf, handleTagValFuncArrayBuf)); + handleArrayBuf->SetArrayBufferByteLength(thread, JSTaggedValue(lengthDataArrayBuf)); + + // Call "SetByteLength" function through "NewJSDataView" function of "object_factory.cpp" + JSHandle handleDataView = factory->NewJSDataView(handleArrayBuf, offsetDataView, + lengthDataView1); + EXPECT_EQ(handleDataView->GetByteLength().GetNumber(), lengthDataView1); + + // Call "SetByteLength" function in this TEST_F. + handleDataView->SetByteLength(thread, handleTagValLengthDataView2); + EXPECT_EQ(handleDataView->GetByteLength().GetNumber(), lengthDataView2); +} + +/* + * Feature: JSDataView + * Function: SetByteOffset + * SubFunction: GetByteOffset + * FunctionPoints: Set ByteOffset + * CaseDescription: Check whether the returned value through "GetByteOffset" function is within expectations after + * calling "SetByteOffset" function. + */ +TEST_F(JSDataViewTest, SetByteOffset) +{ + EcmaVM *ecmaVMPtr = thread->GetEcmaVM(); + ObjectFactory *factory = ecmaVMPtr->GetFactory(); + JSHandle handleFuncArrayBuf1(ecmaVMPtr->GetGlobalEnv()->GetArrayBufferFunction()); + JSHandle handleTagValFuncArrayBuf1(handleFuncArrayBuf1); + + int32_t lengthDataArrayBuf = 8; + int32_t offsetDataView1 = 4; + int32_t offsetDataView2 = 6; + int32_t lengthDataView = 2; + JSHandle handleTagValOffsetDataView2(thread, JSTaggedValue(offsetDataView2)); + JSHandle handleArrayBuf( + factory->NewJSObjectByConstructor(handleFuncArrayBuf1, handleTagValFuncArrayBuf1)); + handleArrayBuf->SetArrayBufferByteLength(thread, JSTaggedValue(lengthDataArrayBuf)); + + // Call "SetByteOffset" function through "NewJSDataView" function of "object_factory.cpp" + JSHandle handleDataView = factory->NewJSDataView(handleArrayBuf, offsetDataView1, + lengthDataView); + EXPECT_EQ(handleDataView->GetByteOffset().GetNumber(), offsetDataView1); + + // Call "SetByteOffset" function in this TEST_F. + handleDataView->SetByteOffset(thread, handleTagValOffsetDataView2); + EXPECT_EQ(handleDataView->GetByteOffset().GetNumber(), offsetDataView2); +} +} // namespace panda::ecmascript diff --git a/tests/runtime/common/js_date_test.cpp b/tests/runtime/common/js_date_test.cpp new file mode 100644 index 000000000..d2fd80aca --- /dev/null +++ b/tests/runtime/common/js_date_test.cpp @@ -0,0 +1,164 @@ +/* + * Copyright (c) 2021-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 "gtest/gtest.h" +#include "test_helper.h" +#include "include/runtime.h" +#include "plugins/ecmascript/runtime/js_date.h" +#include "plugins/ecmascript/runtime/ecma_vm.h" +#include "plugins/ecmascript/runtime/js_handle.h" +#include "plugins/ecmascript/runtime/js_function.h" +#include "plugins/ecmascript/runtime/global_env.h" + +using namespace panda::ecmascript; +using namespace panda::coretypes; + +namespace panda::test { +class JSDateTest : public testing::Test { +public: + void SetUp() override + { + TestHelper::CreateEcmaVMWithScope(instance, thread, scope); + } + + void TearDown() override + { + TestHelper::DestroyEcmaVMWithScope(instance, scope); + } + + PandaVM *instance {nullptr}; + ecmascript::EcmaHandleScope *scope {nullptr}; + JSThread *thread; +}; + +JSDate *JSDateCreate(JSThread *thread) +{ + ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); + EcmaVM *ecma_vm = thread->GetEcmaVM(); + JSHandle global_env = ecma_vm->GetGlobalEnv(); + JSHandle date_function = global_env->GetDateFunction(); + JSHandle date_object = + JSHandle::Cast(factory->NewJSObjectByConstructor(JSHandle(date_function), date_function)); + return *date_object; +} + +TEST_F(JSDateTest, Create) +{ + double tm = 0.0; + JSHandle js_date(thread, JSDateCreate(thread)); + EXPECT_EQ(js_date->GetTimeValue(), JSTaggedValue(tm)); + EXPECT_EQ(js_date->GetLocalOffset(), JSTaggedValue(JSDate::MAX_DOUBLE)); + tm = 28 * 60 * 60 * 1000; + js_date->SetTimeValue(thread, JSTaggedValue(tm)); + + [[maybe_unused]] double temp = js_date->GetTimeValue().GetDouble(); + EXPECT_EQ(js_date->GetTimeValue(), JSTaggedValue(tm)); +} + +TEST_F(JSDateTest, MakeTime) +{ + double const day1 = ecmascript::JSDate::MakeDay(0, 11, 31); + double const time1 = ecmascript::JSDate::MakeTime(0, 0, 0, 0); + double ms1 = ecmascript::JSDate::TimeClip(ecmascript::JSDate::MakeDate(day1, time1)); + EXPECT_EQ(ms1, -62135683200000.0); + + double const day = ecmascript::JSDate::MakeDay(-1, 11, 31); + double const time = ecmascript::JSDate::MakeTime(0, 0, 0, 0); + double ms = ecmascript::JSDate::TimeClip(ecmascript::JSDate::MakeDate(day, time)); + EXPECT_EQ(ms, -62167305600000.0); +} + +TEST_F(JSDateTest, IsoParseStringToMs) +{ + CString str = "2020-11-19T12:18:18.132Z"; + JSTaggedValue ms = ecmascript::JSDate::IsoParseStringToMs(str); + EXPECT_EQ(ms.GetDouble(), 1605788298132.0); + + str = "2020-11-19Z"; + ms = ecmascript::JSDate::IsoParseStringToMs(str); + EXPECT_EQ(ms.GetDouble(), 1605744000000.0); + + str = "2020-11"; + ms = ecmascript::JSDate::IsoParseStringToMs(str); + EXPECT_EQ(ms.GetDouble(), 1604188800000.0); + + str = "+275760-09-13T00:00:00.000Z"; + ms = ecmascript::JSDate::IsoParseStringToMs(str); + EXPECT_EQ(ms.GetDouble(), 8640000000000000.0); + + str = "-271821-04-20T00:00:00.000Z"; + ms = ecmascript::JSDate::IsoParseStringToMs(str); + EXPECT_EQ(ms.GetDouble(), -8640000000000000.0); + + str = "2020T12:18Z"; + ms = ecmascript::JSDate::IsoParseStringToMs(str); + EXPECT_EQ(ms.GetDouble(), 1577881080000.0); + + str = "2020T12:18:17.231Z"; + ms = ecmascript::JSDate::IsoParseStringToMs(str); + EXPECT_EQ(ms.GetDouble(), 1577881097231.0); + + str = "2020-11T12:18:17.231Z"; + ms = ecmascript::JSDate::IsoParseStringToMs(str); + EXPECT_EQ(ms.GetDouble(), 1604233097231.0); + + str = "1645-11T12:18:17.231+08:00"; + ms = ecmascript::JSDate::IsoParseStringToMs(str); + EXPECT_EQ(ms.GetDouble(), -10229658102769.0); + + str = "2020-11-19T12:18-08:12"; + ms = ecmascript::JSDate::IsoParseStringToMs(str); + EXPECT_EQ(ms.GetDouble(), 1605817800000.0); +} + +TEST_F(JSDateTest, LocalParseStringToMs) +{ + CString str = "Thu Nov 19 2020 20:18:18 GMT+0800"; + JSTaggedValue ms = ecmascript::JSDate::LocalParseStringToMs(str); + EXPECT_EQ(ms.GetDouble(), 1605788298000.0); + + str = "Thu Nov 19 2020 20:18 GMT-0800"; + ms = ecmascript::JSDate::LocalParseStringToMs(str); + EXPECT_EQ(ms.GetDouble(), 1605845880000.0); + + str = "Thu Nov 03 2093 04:18 GMT"; + ms = ecmascript::JSDate::LocalParseStringToMs(str); + EXPECT_EQ(ms.GetDouble(), 3908060280000.0); + + str = "Thu Nov 19 1820 GMT+1232"; + ms = ecmascript::JSDate::LocalParseStringToMs(str); + EXPECT_EQ(ms.GetDouble(), -4705734720000.0); +} + +TEST_F(JSDateTest, UtcParseStringToMs) +{ + CString str = "Thu, 19 Nov 2020 20:18:18 GMT+0800"; + JSTaggedValue ms = ecmascript::JSDate::UtcParseStringToMs(str); + EXPECT_EQ(ms.GetDouble(), 1605788298000.0); + + str = "Thu, 19 Nov 2020 20:18 GMT-0800"; + ms = ecmascript::JSDate::UtcParseStringToMs(str); + EXPECT_EQ(ms.GetDouble(), 1605845880000.0); + + str = "Thu 03 Jun 2093 04:18 GMT"; + ms = ecmascript::JSDate::UtcParseStringToMs(str); + EXPECT_EQ(ms.GetDouble(), 3894841080000.0); + + str = "Thu 19 Nov 1820 GMT+1232"; + ms = ecmascript::JSDate::UtcParseStringToMs(str); + EXPECT_EQ(ms.GetDouble(), -4705734720000.0); +} + +} // namespace panda::test diff --git a/tests/runtime/common/js_forin_iterator_test.cpp b/tests/runtime/common/js_forin_iterator_test.cpp new file mode 100644 index 000000000..244493aa4 --- /dev/null +++ b/tests/runtime/common/js_forin_iterator_test.cpp @@ -0,0 +1,86 @@ +/* + * Copyright (c) 2021-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 "gtest/gtest.h" +#include "test_helper.h" + +#include "plugins/ecmascript/runtime/js_for_in_iterator.h" +#include "include/runtime.h" +#include "plugins/ecmascript/runtime/ecma_vm.h" +#include "plugins/ecmascript/runtime/js_handle.h" + +using namespace panda::ecmascript; +using namespace panda::coretypes; + +namespace panda::test { +class JSForinIteratorTest : public testing::Test { +public: + void SetUp() override + { + TestHelper::CreateEcmaVMWithScope(instance, thread, scope); + } + + void TearDown() override + { + TestHelper::DestroyEcmaVMWithScope(instance, scope); + } + + PandaVM *instance {nullptr}; + ecmascript::EcmaHandleScope *scope {nullptr}; + JSThread *thread; +}; + +TEST_F(JSForinIteratorTest, Create) +{ + JSHandle null_handle(thread, JSTaggedValue::Null()); + JSHandle grandfather = JSObject::ObjectCreate(thread, null_handle); + EXPECT_TRUE(grandfather->GetPrototype(thread).IsNull()); + + JSHandle father = JSObject::ObjectCreate(thread, grandfather); + + JSHandle son = JSObject::ObjectCreate(thread, father); + + JSHandle key1(thread->GetEcmaVM()->GetFactory()->NewFromString("key1")); + JSHandle key2(thread->GetEcmaVM()->GetFactory()->NewFromString("key2")); + JSHandle key3(thread->GetEcmaVM()->GetFactory()->NewFromString("key3")); + JSHandle key1_value(thread, JSTaggedValue(1)); + JSHandle key2_value(thread, JSTaggedValue(2)); + JSHandle key3_value(thread, JSTaggedValue(3)); + + JSObject::SetProperty(thread, JSHandle(grandfather), key3, key3_value); + JSObject::SetProperty(thread, JSHandle(father), key2, key2_value); + + JSObject::SetProperty(thread, JSHandle(son), key1, key1_value); + JSObject::SetProperty(thread, JSHandle(son), key2, key1_value); + JSObject::SetProperty(thread, JSHandle(son), key3, key1_value); + + JSHandle it = thread->GetEcmaVM()->GetFactory()->NewJSForinIterator(JSHandle(son)); + std::pair n1 = JSForInIterator::NextInternal(thread, it); + EXPECT_EQ(n1.first, key1.GetTaggedValue()); + EXPECT_FALSE(n1.second); + + std::pair n2 = JSForInIterator::NextInternal(thread, it); + EXPECT_EQ(n2.first, key2.GetTaggedValue()); + EXPECT_FALSE(n2.second); + + std::pair n3 = JSForInIterator::NextInternal(thread, it); + EXPECT_EQ(n3.first, key3.GetTaggedValue()); + EXPECT_FALSE(n3.second); + + std::pair n4 = JSForInIterator::NextInternal(thread, it); + EXPECT_EQ(n4.first, JSTaggedValue::Undefined()); + EXPECT_TRUE(n4.second); +} +} // namespace panda::test diff --git a/tests/runtime/common/js_function_test.cpp b/tests/runtime/common/js_function_test.cpp new file mode 100644 index 000000000..1e76da4d5 --- /dev/null +++ b/tests/runtime/common/js_function_test.cpp @@ -0,0 +1,164 @@ +/* + * Copyright (c) 2021-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 "gtest/gtest.h" +#include "test_helper.h" +#include "plugins/ecmascript/runtime/ecma_string.h" +#include "plugins/ecmascript/runtime/ecma_vm.h" +#include "plugins/ecmascript/runtime/js_object.h" +#include "plugins/ecmascript/runtime/js_hclass.h" +#include "plugins/ecmascript/runtime/js_function.h" +#include "plugins/ecmascript/runtime/js_function_extra_info.h" +#include "plugins/ecmascript/runtime/tagged_array-inl.h" +#include "plugins/ecmascript/runtime/js_handle.h" +#include "plugins/ecmascript/runtime/ecma_vm.h" +#include "include/runtime.h" +#include "plugins/ecmascript/runtime/object_factory.h" +#include "plugins/ecmascript/runtime/global_env.h" +#include "plugins/ecmascript/runtime/base/builtins_base.h" +#include "plugins/ecmascript/runtime/internal_call_params.h" + +using namespace panda::ecmascript; +using namespace panda::coretypes; +using namespace panda::ecmascript::base; + +namespace panda::test { +class JSFunctionTest : public testing::Test { +public: + void SetUp() override + { + TestHelper::CreateEcmaVMWithScope(instance, thread, scope); + } + + void TearDown() override + { + TestHelper::DestroyEcmaVMWithScope(instance, scope); + } + + PandaVM *instance {nullptr}; + ecmascript::EcmaHandleScope *scope {nullptr}; + JSThread *thread; +}; + +JSFunction *JSObjectCreate(JSThread *thread) +{ + EcmaVM *ecma_vm = thread->GetEcmaVM(); + JSHandle global_env = ecma_vm->GetGlobalEnv(); + return global_env->GetObjectFunction().GetObject(); +} + +TEST_F(JSFunctionTest, Create) +{ + EcmaVM *ecma_vm = thread->GetEcmaVM(); + JSHandle env = ecma_vm->GetGlobalEnv(); + JSHandle fun_handle = thread->GetEcmaVM()->GetFactory()->NewJSFunction(env); + EXPECT_TRUE(*fun_handle != nullptr); + EXPECT_EQ(fun_handle->GetProtoOrDynClass(), JSTaggedValue::Hole()); + + JSHandle lexicalEnv = thread->GetEcmaVM()->GetFactory()->NewLexicalEnv(0); + fun_handle->SetLexicalEnv(thread, lexicalEnv.GetTaggedValue()); + EXPECT_EQ(fun_handle->GetLexicalEnv(), lexicalEnv.GetTaggedValue()); + EXPECT_TRUE(*lexicalEnv != nullptr); +} +TEST_F(JSFunctionTest, MakeConstructor) +{ + EcmaVM *ecma_vm = thread->GetEcmaVM(); + JSHandle env = ecma_vm->GetGlobalEnv(); + JSHandle func = thread->GetEcmaVM()->GetFactory()->NewJSFunction(env, static_cast(nullptr), + FunctionKind::BASE_CONSTRUCTOR); + EXPECT_TRUE(*func != nullptr); + JSHandle func_handle(func); + func->GetJSHClass()->SetExtensible(true); + + JSHandle null_handle(thread, JSTaggedValue::Null()); + JSHandle obj = JSObject::ObjectCreate(thread, null_handle); + JSHandle obj_value(obj); + + JSFunction::MakeConstructor(thread, func, obj_value); + + JSHandle constructor_key(thread->GetEcmaVM()->GetFactory()->NewFromString("constructor")); + + JSHandle proto_key(thread->GetEcmaVM()->GetFactory()->NewFromString("prototype")); + JSTaggedValue proto = JSObject::GetProperty(thread, func_handle, proto_key).GetValue().GetTaggedValue(); + JSTaggedValue constructor = + JSObject::GetProperty(thread, JSHandle(obj), constructor_key).GetValue().GetTaggedValue(); + EXPECT_EQ(constructor, func_handle.GetTaggedValue()); + EXPECT_EQ(proto, obj.GetTaggedValue()); + EXPECT_EQ(func->GetFunctionKind(), FunctionKind::BASE_CONSTRUCTOR); +} + +TEST_F(JSFunctionTest, OrdinaryHasInstance) +{ + JSHandle obj_fun(thread, JSObjectCreate(thread)); + + JSHandle jsobject = + thread->GetEcmaVM()->GetFactory()->NewJSObjectByConstructor(JSHandle(obj_fun), obj_fun); + JSHandle obj(thread, jsobject.GetTaggedValue()); + EXPECT_TRUE(*jsobject != nullptr); + + EcmaVM *ecma_vm = thread->GetEcmaVM(); + JSHandle global_env = ecma_vm->GetGlobalEnv(); + JSHandle constructor = global_env->GetObjectFunction(); + EXPECT_TRUE(ecmascript::JSFunction::OrdinaryHasInstance(thread, constructor, obj)); +} + +JSTaggedValue TestInvokeInternal(EcmaRuntimeCallInfo *argv) +{ + if (argv->GetArgsNumber() == 1 && argv->GetCallArg(0).GetTaggedValue() == JSTaggedValue(1)) { + return BuiltinsBase::GetTaggedBoolean(true); + } + return BuiltinsBase::GetTaggedBoolean(false); +} + +TEST_F(JSFunctionTest, Invoke) +{ + EcmaVM *ecmaVM = thread->GetEcmaVM(); + JSHandle env = ecmaVM->GetGlobalEnv(); + JSHandle dynclass(thread, JSObjectCreate(thread)); + JSHandle callee( + thread->GetEcmaVM()->GetFactory()->NewJSObjectByConstructor(JSHandle::Cast(dynclass), dynclass)); + EXPECT_TRUE(*callee != nullptr); + + char keyArray[] = "invoked"; + JSHandle calleeKey(thread->GetEcmaVM()->GetFactory()->NewFromCanBeCompressString(&keyArray[0])); + JSHandle calleeFunc = + thread->GetEcmaVM()->GetFactory()->NewJSFunction(env, reinterpret_cast(TestInvokeInternal)); + JSHandle calleeValue(calleeFunc); + JSObject::SetProperty(thread, JSHandle(callee), calleeKey, calleeValue); + + InternalCallParams *arguments = thread->GetInternalCallParams(); + arguments->MakeArgv(JSTaggedValue(1)); + JSTaggedValue res = JSFunction::Invoke(thread, callee, calleeKey, 1, arguments->GetArgv()); + + JSTaggedValue ruler = BuiltinsBase::GetTaggedBoolean(true); + EXPECT_EQ(res.GetRawData(), ruler.GetRawData()); +} + +TEST_F(JSFunctionTest, SetSymbolFunctionName) +{ + ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); + JSHandle env = thread->GetEcmaVM()->GetGlobalEnv(); + JSHandle js_function = factory->NewJSFunction(env); + JSHandle symbol = factory->NewPublicSymbolWithChar("name"); + JSHandle name = factory->NewFromString("[name]"); + JSHandle prefix(thread, JSTaggedValue::Undefined()); + JSFunction::SetFunctionName(thread, JSHandle(js_function), JSHandle(symbol), prefix); + JSHandle function_name = + JSFunctionBase::GetFunctionName(thread, JSHandle(js_function)); + EXPECT_TRUE(function_name->IsString()); + EXPECT_TRUE(EcmaString::StringsAreEqual(*(JSHandle(function_name)), *name)); +} + +} // namespace panda::test diff --git a/tests/runtime/common/js_handle_test.cpp b/tests/runtime/common/js_handle_test.cpp new file mode 100644 index 000000000..32134866e --- /dev/null +++ b/tests/runtime/common/js_handle_test.cpp @@ -0,0 +1,218 @@ +/* + * Copyright (c) 2021-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 "plugins/ecmascript/runtime/ecma_string-inl.h" +#include "plugins/ecmascript/runtime/ecma_global_storage-inl.h" +#include "plugins/ecmascript/runtime/ecma_vm.h" +#include "plugins/ecmascript/runtime/js_handle.h" +#include "plugins/ecmascript/runtime/object_factory.h" +#include "plugins/ecmascript/tests/runtime/common/test_helper.h" + +using namespace panda::ecmascript; + +namespace panda::test { +class JSHandleTest : public testing::Test { +public: + static void SetUpTestCase() + { + GTEST_LOG_(INFO) << "SetUpTestCase"; + } + + static void TearDownTestCase() + { + GTEST_LOG_(INFO) << "TearDownCase"; + } + + void SetUp() override + { + TestHelper::CreateEcmaVMWithScope(instance, thread, scope); + } + + void TearDown() override + { + TestHelper::DestroyEcmaVMWithScope(instance, scope); + } + + PandaVM *instance {nullptr}; + ecmascript::EcmaHandleScope *scope {nullptr}; + JSThread *thread {nullptr}; +}; + +TEST_F(JSHandleTest, NewGlobalHandle) +{ + ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); + auto global = thread->GetEcmaGlobalStorage(); + + uintptr_t globalString = 0; + { + [[maybe_unused]] EcmaHandleScope scope_nested(thread); + auto string1 = factory->NewFromString("test1"); + globalString = global->NewGlobalHandle(string1.GetTaggedType()); + } + // trigger GC + thread->GetEcmaVM()->CollectGarbage(TriggerGCType::COMPRESS_FULL_GC); + + // check result + EXPECT_TRUE(factory->NewFromString("test1")->Compare(*reinterpret_cast(globalString)) == 0); +} + +static void FillGlobalString(uintptr_t globalString[], int length, + EcmaGlobalStorage *global, ObjectFactory *factory, JSThread *thread) +{ + { + [[maybe_unused]] EcmaHandleScope scope_nested(thread); + for (int i = 0; i < length; i++) { + std::string test = "test" + std::to_string(i); + auto string1 = factory->NewFromString(test.c_str()); + globalString[i] = global->NewGlobalHandle(string1.GetTaggedType()); + } + } +} + +TEST_F(JSHandleTest, NewGlobalHandle1) +{ + ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); + auto global = thread->GetEcmaGlobalStorage(); + + uintptr_t globalString[600] = {0}; + FillGlobalString(globalString, 600, global, factory, thread); + // trigger GC + thread->GetEcmaVM()->CollectGarbage(TriggerGCType::COMPRESS_FULL_GC); + for (int i = 300; i > 200; i--) { + global->DisposeGlobalHandle(globalString[i]); + } + // check result + for (int i = 0; i <= 200; i++) { + std::string test = "test" + std::to_string(i); + EXPECT_TRUE( + factory->NewFromString(test.c_str())->Compare(*reinterpret_cast(globalString[i])) == 0); + } + // trigger GC + thread->GetEcmaVM()->CollectGarbage(TriggerGCType::COMPRESS_FULL_GC); + for (int i = 301; i < 600; i++) { + std::string test = "test" + std::to_string(i); + EXPECT_TRUE( + factory->NewFromString(test.c_str())->Compare(*reinterpret_cast(globalString[i])) == 0); + } +} + +TEST_F(JSHandleTest, DisposeGlobalHandle) +{ + ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); + auto global = thread->GetEcmaGlobalStorage(); + + uintptr_t globalString[600] = {0}; + FillGlobalString(globalString, 600, global, factory, thread); + for (int i = 512; i > 200; i--) { + global->DisposeGlobalHandle(globalString[i]); + } + int count = 0; + global->IterateUsageGlobal([&count] (EcmaGlobalStorage::Node *node) { + JSTaggedValue value(node->GetObject()); + EXPECT_TRUE(value.IsString()); + count++; + }); + EXPECT_TRUE(count == 288); +} + +TEST_F(JSHandleTest, DisposeAndNewGlobalHandle) +{ + ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); + auto global = thread->GetEcmaGlobalStorage(); + + uintptr_t globalString[768] = {0}; + FillGlobalString(globalString, 768, global, factory, thread); + for (int i = 767; i > 200; i--) { + global->DisposeGlobalHandle(globalString[i]); + } + // trigger GC + thread->GetEcmaVM()->CollectGarbage(TriggerGCType::COMPRESS_FULL_GC); + { + [[maybe_unused]] EcmaHandleScope scope_nested(thread); + for (int i = 200; i < 400; i++) { + std::string test = "test" + std::to_string(i); + auto string1 = factory->NewFromString(test.c_str()); + globalString[i] = global->NewGlobalHandle(string1.GetTaggedType()); + } + } + // check result + for (int i = 0; i <= 300; i++) { + std::string test = "test" + std::to_string(i); + EXPECT_TRUE( + factory->NewFromString(test.c_str())->Compare(*reinterpret_cast(globalString[i])) == 0); + } +} + +TEST_F(JSHandleTest, DISABLED_NewWeakGlobalHandle) +{ + ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); + auto global = thread->GetEcmaGlobalStorage(); + + uintptr_t globalString = 0; + { + [[maybe_unused]] EcmaHandleScope scope_nested(thread); + auto string1 = factory->NewFromString("test1"); + globalString = global->NewGlobalHandle(string1.GetTaggedType()); + globalString = global->SetWeak(globalString); + + // trigger GC + thread->GetEcmaVM()->CollectGarbage(TriggerGCType::COMPRESS_FULL_GC); + + // check result + EXPECT_TRUE(factory->NewFromString("test1")->Compare(*reinterpret_cast(globalString)) == 0); + EXPECT_TRUE(global->IsWeak(globalString)); + } + // trigger GC + thread->GetEcmaVM()->CollectGarbage(TriggerGCType::COMPRESS_FULL_GC); + + // check weak reference + JSTaggedType result = *reinterpret_cast(globalString); + EXPECT_TRUE(result == JSTaggedValue::Undefined().GetRawData()); +} + +TEST_F(JSHandleTest, DISABLED_NewWeakGlobalHandle1) +{ + ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); + auto global = thread->GetEcmaGlobalStorage(); + + uintptr_t globalString[800] = {0}; + { + [[maybe_unused]] EcmaHandleScope scope_nested(thread); + for (int i = 0; i < 800; i++) { + std::string test = "test" + std::to_string(i); + auto string1 = factory->NewFromString(test.c_str()); + globalString[i] = global->NewGlobalHandle(string1.GetTaggedType()); + globalString[i] = global->SetWeak(globalString[i]); + EXPECT_TRUE(global->IsWeak(globalString[i])); + } + for (int i = 600; i > 200; i--) { + global->DisposeGlobalHandle(globalString[i]); + } + // trigger GC + thread->GetEcmaVM()->CollectGarbage(TriggerGCType::COMPRESS_FULL_GC); + // check result + for (int i = 0; i <= 200; i++) { + std::string test = "test" + std::to_string(i); + EXPECT_TRUE( + factory->NewFromString(test.c_str())->Compare(*reinterpret_cast(globalString[i])) == 0); + } + } + // trigger GC + thread->GetEcmaVM()->CollectGarbage(TriggerGCType::COMPRESS_FULL_GC); + for (int i = 601; i < 800; i++) { + EXPECT_TRUE(*reinterpret_cast(globalString[i]) == JSTaggedValue::Undefined().GetRawData()); + } +} +} // namespace panda::test diff --git a/tests/runtime/common/js_iterator_test.cpp b/tests/runtime/common/js_iterator_test.cpp new file mode 100644 index 000000000..62a98e21a --- /dev/null +++ b/tests/runtime/common/js_iterator_test.cpp @@ -0,0 +1,130 @@ +/* + * Copyright (c) 2021-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 "gtest/gtest.h" +#include "test_helper.h" +#include "include/coretypes/dyn_objects.h" +#include "plugins/ecmascript/runtime/ecma_string.h" +#include "include/coretypes/tagged_value.h" +#include "include/runtime.h" +#include "include/runtime_options.h" +#include "plugins/ecmascript/runtime/js_object.h" +#include "plugins/ecmascript/runtime/js_handle.h" +#include "plugins/ecmascript/runtime/object_factory.h" +#include "plugins/ecmascript/runtime/global_env.h" +#include "plugins/ecmascript/runtime/ecma_vm.h" +#include "plugins/ecmascript/runtime/js_iterator.h" +#include "plugins/ecmascript/runtime/js_array_iterator.h" +#include "plugins/ecmascript/runtime/js_array.h" +#include "plugins/ecmascript/runtime/tagged_array-inl.h" + +using namespace panda; + +using namespace panda::ecmascript; +using namespace panda::coretypes; + +namespace panda::test { +class JSIteratorTest : public testing::Test { +public: + void SetUp() override + { + TestHelper::CreateEcmaVMWithScope(instance, thread, scope); + } + + void TearDown() override + { + TestHelper::DestroyEcmaVMWithScope(instance, scope); + } + + PandaVM *instance {nullptr}; + ecmascript::EcmaHandleScope *scope {nullptr}; + JSThread *thread; +}; + +TEST_F(JSIteratorTest, GetIterator) +{ + ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); + JSHandle data(factory->NewTaggedArray(2)); + data->Set(thread, 0, JSTaggedValue(1)); + data->Set(thread, 1, JSTaggedValue(1)); + JSHandle array(JSArray::CreateArrayFromList(thread, data)); + EXPECT_TRUE(array->IsArray(thread)); + JSHandle iter(JSIterator::GetIterator(thread, array)); + EXPECT_TRUE(iter->IsJSArrayIterator()); + EXPECT_TRUE(iter->GetIteratedArray().IsArray(thread)); +} + +TEST_F(JSIteratorTest, IteratorNext) +{ + ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); + JSHandle value_str = thread->GlobalConstants()->GetHandledValueString(); + + JSHandle data(factory->NewTaggedArray(1)); + data->Set(thread, 0, JSTaggedValue(1)); + JSHandle array(JSArray::CreateArrayFromList(thread, data)); + JSHandle iter(JSIterator::GetIterator(thread, array)); + JSHandle result(JSIterator::IteratorNextOld(thread, iter)); + JSHandle result_value(JSObject::GetProperty(thread, result, value_str).GetValue()); + EXPECT_EQ(result_value->GetInt(), 1); +} + +TEST_F(JSIteratorTest, IteratorComplete) +{ + ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); + JSHandle data(factory->NewTaggedArray(2)); + data->Set(thread, 0, JSTaggedValue(1)); + data->Set(thread, 1, JSTaggedValue(1)); + JSHandle array(JSArray::CreateArrayFromList(thread, data)); + JSHandle iter(JSIterator::GetIterator(thread, array)); + JSHandle result1(JSIterator::IteratorNextOld(thread, iter)); + EXPECT_EQ(false, JSIterator::IteratorComplete(thread, result1)); + JSHandle result2(JSIterator::IteratorNextOld(thread, iter)); + EXPECT_EQ(false, JSIterator::IteratorComplete(thread, result2)); + JSHandle result3(JSIterator::IteratorNextOld(thread, iter)); + EXPECT_EQ(true, JSIterator::IteratorComplete(thread, result3)); +} + +TEST_F(JSIteratorTest, IteratorValue) +{ + ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); + + JSHandle data(factory->NewTaggedArray(3)); + data->Set(thread, 0, JSTaggedValue(1)); + data->Set(thread, 1, JSTaggedValue(1)); + data->Set(thread, 2, JSTaggedValue(1)); + JSHandle array(JSArray::CreateArrayFromList(thread, data)); + JSHandle iter(JSIterator::GetIterator(thread, array)); + JSHandle result(JSIterator::IteratorNextOld(thread, iter)); + JSHandle result_value(JSIterator::IteratorValue(thread, result)); + EXPECT_EQ(result_value->GetInt(), 1); +} + +TEST_F(JSIteratorTest, IteratorStep) +{ + ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); + JSHandle data(factory->NewTaggedArray(2)); + data->Set(thread, 0, JSTaggedValue(1)); + data->Set(thread, 1, JSTaggedValue(2)); + JSHandle array(JSArray::CreateArrayFromList(thread, data)); + JSHandle iter(JSIterator::GetIterator(thread, array)); + JSHandle result1(JSIterator::IteratorStep(thread, iter)); + EXPECT_EQ(JSIterator::IteratorValue(thread, result1)->GetInt(), 1); + JSHandle result2(JSIterator::IteratorStep(thread, iter)); + EXPECT_EQ(JSIterator::IteratorValue(thread, result2)->GetInt(), 2); + JSHandle result3(JSIterator::IteratorStep(thread, iter)); + EXPECT_EQ(result3.GetTaggedValue(), JSTaggedValue::False()); +} + +} // namespace panda::test diff --git a/tests/runtime/common/js_map_test.cpp b/tests/runtime/common/js_map_test.cpp new file mode 100644 index 000000000..4e24b6964 --- /dev/null +++ b/tests/runtime/common/js_map_test.cpp @@ -0,0 +1,161 @@ +/* + * Copyright (c) 2021-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 "gtest/gtest.h" +#include "test_helper.h" +#include "include/coretypes/dyn_objects.h" +#include "plugins/ecmascript/runtime/linked_hash_table-inl.h" +#include "plugins/ecmascript/runtime/ecma_string.h" +#include "include/coretypes/tagged_value.h" +#include "include/runtime.h" +#include "include/runtime_options.h" +#include "plugins/ecmascript/runtime/tagged_hash_table-inl.h" +#include "plugins/ecmascript/runtime/js_object.h" +#include "plugins/ecmascript/runtime/js_handle.h" +#include "plugins/ecmascript/runtime/object_factory.h" +#include "plugins/ecmascript/runtime/ecma_vm.h" +#include "plugins/ecmascript/runtime/js_map.h" +#include "plugins/ecmascript/runtime/js_function.h" +#include "plugins/ecmascript/runtime/global_env.h" +#include "plugins/ecmascript/runtime/js_iterator.h" +#include "plugins/ecmascript/runtime/js_map_iterator.h" + +using namespace panda; + +using namespace panda::ecmascript; +using namespace panda::coretypes; + +namespace panda::test { +class JSMapTest : public testing::Test { +public: + void SetUp() override + { + TestHelper::CreateEcmaVMWithScope(instance, thread, scope); + } + + void TearDown() override + { + TestHelper::DestroyEcmaVMWithScope(instance, scope); + } + + PandaVM *instance {nullptr}; + ecmascript::EcmaHandleScope *scope {nullptr}; + JSThread *thread; + +protected: + JSMap *CreateMap() + { + ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); + JSHandle env = thread->GetEcmaVM()->GetGlobalEnv(); + JSHandle constructor = env->GetBuiltinsMapFunction(); + JSHandle map = + JSHandle::Cast(factory->NewJSObjectByConstructor(JSHandle(constructor), constructor)); + JSHandle hashMap = LinkedHashMap::Create(thread); + map->SetLinkedMap(thread, hashMap); + return *map; + } +}; + +TEST_F(JSMapTest, MapCreate) +{ + JSMap *map = CreateMap(); + EXPECT_TRUE(map != nullptr); +} + +TEST_F(JSMapTest, AddAndHas) +{ + ObjectFactory *factory_ = thread->GetEcmaVM()->GetFactory(); + // create js_map + JSHandle map(thread, CreateMap()); + + JSHandle key(factory_->NewFromString("key")); + JSHandle value(thread, JSTaggedValue(1)); + JSMap::Set(thread, map, key, value); + EXPECT_TRUE(map->Has(key.GetTaggedValue())); +} + +TEST_F(JSMapTest, DeleteAndGet) +{ + ObjectFactory *factory_ = thread->GetEcmaVM()->GetFactory(); + // create js_map + JSHandle map(thread, CreateMap()); + + // add 40 keys + char key_array[] = "key0"; + for (int i = 0; i < 40; i++) { + key_array[3] = '1' + i; + JSHandle key(factory_->NewFromString(key_array)); + JSHandle value(thread, JSTaggedValue(i)); + JSMap::Set(thread, map, key, value); + EXPECT_TRUE(map->Has(key.GetTaggedValue())); + } + EXPECT_EQ(map->GetSize(), 40); + // whether js_map has delete key + key_array[3] = '1' + 8; + JSHandle delete_key(factory_->NewFromString(key_array)); + EXPECT_EQ(map->GetValue(8), JSTaggedValue(8)); + JSMap::Delete(thread, map, delete_key); + EXPECT_FALSE(map->Has(delete_key.GetTaggedValue())); + EXPECT_EQ(map->GetSize(), 39); +} + +TEST_F(JSMapTest, Iterator) +{ + ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); + + JSHandle map(thread, CreateMap()); + for (int i = 0; i < 5; i++) { + JSHandle key(thread, JSTaggedValue(i)); + JSHandle value(thread, JSTaggedValue(i + 10)); + JSMap::Set(thread, map, key, value); + } + + JSHandle key_iter(factory->NewJSMapIterator(map, IterationKind::KEY)); + JSHandle value_iter(factory->NewJSMapIterator(map, IterationKind::VALUE)); + JSHandle iter(factory->NewJSMapIterator(map, IterationKind::KEY_AND_VALUE)); + + JSHandle index_key(thread, JSTaggedValue(0)); + JSHandle element_key(thread, JSTaggedValue(1)); + + JSHandle key_result0 = JSIterator::IteratorStep(thread, key_iter); + JSHandle value_result0 = JSIterator::IteratorStep(thread, value_iter); + JSHandle result0 = JSIterator::IteratorStep(thread, iter); + + EXPECT_EQ(0, JSIterator::IteratorValue(thread, key_result0)->GetInt()); + EXPECT_EQ(10, JSIterator::IteratorValue(thread, value_result0)->GetInt()); + JSHandle result0_handle = JSIterator::IteratorValue(thread, result0); + EXPECT_EQ(0, JSObject::GetProperty(thread, result0_handle, index_key).GetValue()->GetInt()); + EXPECT_EQ(10, JSObject::GetProperty(thread, result0_handle, element_key).GetValue()->GetInt()); + + JSHandle key_result1 = JSIterator::IteratorStep(thread, key_iter); + EXPECT_EQ(1, JSIterator::IteratorValue(thread, key_result1)->GetInt()); + for (int i = 0; i < 3; i++) { + JSHandle key(thread, JSTaggedValue(i)); + JSMap::Delete(thread, map, key); + } + JSHandle key_result2 = JSIterator::IteratorStep(thread, key_iter); + EXPECT_EQ(3, JSIterator::IteratorValue(thread, key_result2)->GetInt()); + JSHandle key_result3 = JSIterator::IteratorStep(thread, key_iter); + EXPECT_EQ(4, JSIterator::IteratorValue(thread, key_result3)->GetInt()); + JSHandle key(thread, JSTaggedValue(5)); + JSMap::Set(thread, map, key, key); + JSHandle key_result4 = JSIterator::IteratorStep(thread, key_iter); + + EXPECT_EQ(5, JSIterator::IteratorValue(thread, key_result4)->GetInt()); + JSHandle key_result5 = JSIterator::IteratorStep(thread, key_iter); + EXPECT_EQ(JSTaggedValue::False(), key_result5.GetTaggedValue()); +} + +} // namespace panda::test diff --git a/tests/runtime/common/js_object_test.cpp b/tests/runtime/common/js_object_test.cpp new file mode 100644 index 000000000..6c4a85adf --- /dev/null +++ b/tests/runtime/common/js_object_test.cpp @@ -0,0 +1,1291 @@ +#include "gtest/gtest.h" +#include "mem/c_string.h" +#include "test_helper.h" + +#include "plugins/ecmascript/runtime/ecma_string.h" +#include "include/coretypes/array.h" +#include "plugins/ecmascript/runtime/js_object.h" +#include "plugins/ecmascript/runtime/js_hclass.h" +#include "plugins/ecmascript/runtime/tagged_array-inl.h" +#include "plugins/ecmascript/runtime/js_handle.h" +#include "plugins/ecmascript/runtime/object_operator.h" +#include "include/runtime.h" +#include "plugins/ecmascript/runtime/object_factory.h" +#include "plugins/ecmascript/runtime/ecma_vm.h" +#include "plugins/ecmascript/runtime/base/builtins_base.h" +#include "plugins/ecmascript/runtime/ecma_runtime_call_info.h" +#include "plugins/ecmascript/runtime/base/builtins_base.h" +#include "plugins/ecmascript/runtime/js_thread.h" +#include "plugins/ecmascript/runtime/js_function.h" +#include "plugins/ecmascript/runtime/global_env.h" +#include "plugins/ecmascript/runtime/js_thread.h" +#include "plugins/ecmascript/runtime/tagged_dictionary.h" +#include "plugins/ecmascript/runtime/weak_vector-inl.h" +#include "plugins/ecmascript/runtime/ic/proto_change_details.h" + +using namespace panda::ecmascript; +using namespace panda::ecmascript::base; +using namespace panda::coretypes; + +namespace panda::test { +class JSObjectTest : public testing::Test { +public: + void SetUp() override + { + TestHelper::CreateEcmaVMWithScope(instance, thread, scope); + } + + void TearDown() override + { + TestHelper::DestroyEcmaVMWithScope(instance, scope); + } + + PandaVM *instance {nullptr}; + ecmascript::EcmaHandleScope *scope {nullptr}; + JSThread *thread; +}; + +static JSFunction *JSObjectTestCreate(JSThread *thread) +{ + JSHandle global_env = thread->GetEcmaVM()->GetGlobalEnv(); + return global_env->GetObjectFunction().GetObject(); +} + +TEST_F(JSObjectTest, Create) +{ + JSHandle obj_func(thread, JSObjectTestCreate(thread)); + JSHandle jsobject = + thread->GetEcmaVM()->GetFactory()->NewJSObjectByConstructor(JSHandle(obj_func), obj_func); + EXPECT_TRUE(*jsobject != nullptr); +} + +TEST_F(JSObjectTest, SetProperty) +{ + JSHandle obj_func(thread, JSObjectTestCreate(thread)); + JSHandle jsobject = + thread->GetEcmaVM()->GetFactory()->NewJSObjectByConstructor(JSHandle(obj_func), obj_func); + EXPECT_TRUE(*jsobject != nullptr); + + char array[] = "x"; + JSHandle key(thread->GetEcmaVM()->GetFactory()->NewFromString(array)); + JSHandle value(thread, JSTaggedValue(1)); + + JSObject::SetProperty(thread, JSHandle(jsobject), key, value); + EXPECT_EQ(JSObject::GetProperty(thread, JSHandle(jsobject), key).GetValue()->GetInt(), 1); + + JSHandle value2(thread, JSTaggedValue(2)); + JSObject::SetProperty(thread, JSHandle(jsobject), key, value2); + EXPECT_EQ(JSObject::GetProperty(thread, JSHandle(jsobject), key).GetValue()->GetInt(), 2); +} + +TEST_F(JSObjectTest, GetProperty) +{ + JSHandle obj_func(thread, JSObjectTestCreate(thread)); + JSHandle obj = + thread->GetEcmaVM()->GetFactory()->NewJSObjectByConstructor(JSHandle(obj_func), obj_func); + EXPECT_TRUE(*obj != nullptr); + + char array[] = "x"; + JSHandle key(thread->GetEcmaVM()->GetFactory()->NewFromString(array)); + JSHandle value(thread, JSTaggedValue(1)); + + EXPECT_TRUE(JSObject::GetProperty(thread, JSHandle(obj), key).GetValue()->IsUndefined()); + + JSObject::SetProperty(thread, JSHandle(obj), key, value); + EXPECT_EQ(JSObject::GetProperty(thread, JSHandle(obj), key).GetValue()->GetInt(), 1); +} + +TEST_F(JSObjectTest, DeleteProperty) +{ + JSHandle obj_func(thread, JSObjectTestCreate(thread)); + JSHandle obj = + thread->GetEcmaVM()->GetFactory()->NewJSObjectByConstructor(JSHandle(obj_func), obj_func); + EXPECT_TRUE(*obj != nullptr); + + char array[] = "print"; + JSHandle key(thread->GetEcmaVM()->GetFactory()->NewFromString(array)); + JSHandle value(thread, JSTaggedValue(1)); + + JSObject::DeleteProperty(thread, (obj), key); + EXPECT_TRUE(JSObject::GetProperty(thread, JSHandle(obj), key).GetValue()->IsUndefined()); + + JSObject::SetProperty(thread, JSHandle(obj), key, value); + EXPECT_EQ(JSObject::GetProperty(thread, JSHandle(obj), key).GetValue()->GetInt(), 1); + + JSHandle key2(thread->GetEcmaVM()->GetFactory()->NewFromString("print_test")); + JSObject::SetProperty(thread, JSHandle(obj), key2, + JSHandle(thread, JSTaggedValue(10))); + EXPECT_EQ(JSObject::GetProperty(thread, JSHandle(obj), key2).GetValue()->GetInt(), 10); + + JSObject::DeleteProperty(thread, (obj), key); + EXPECT_TRUE(JSObject::GetProperty(thread, JSHandle(obj), key).GetValue()->IsUndefined()); + EXPECT_EQ(JSObject::GetProperty(thread, JSHandle(obj), key2).GetValue()->GetInt(), 10); +} + +TEST_F(JSObjectTest, DeletePropertyGlobal) +{ + JSHandle global_env = thread->GetEcmaVM()->GetGlobalEnv(); + JSHandle global(thread, global_env->GetGlobalObject()); + JSHandle print_key(thread->GetEcmaVM()->GetFactory()->NewFromString("print")); + JSHandle print_test_key(thread->GetEcmaVM()->GetFactory()->NewFromString("print_test")); + + JSHandle value = JSObject::GetProperty(thread, global, print_key).GetValue(); + + JSObject::SetProperty(thread, global, print_test_key, value); + + JSTaggedValue val2 = JSObject::GetProperty(thread, global, print_test_key).GetValue().GetTaggedValue(); + EXPECT_EQ(val2, value.GetTaggedValue()); + JSTaggedValue::DeletePropertyOrThrow(thread, global, print_test_key); + JSTaggedValue val3 = JSObject::GetProperty(thread, global, print_key).GetValue().GetTaggedValue(); + EXPECT_NE(val3, JSTaggedValue::Undefined()); +} + +TEST_F(JSObjectTest, GetPropertyInPrototypeChain) +{ + JSHandle null_handle(thread, JSTaggedValue::Null()); + JSHandle grandfather = JSObject::ObjectCreate(thread, null_handle); + JSHandle father = JSObject::ObjectCreate(thread, grandfather); + JSHandle son = JSObject::ObjectCreate(thread, father); + + JSHandle son_key(thread->GetEcmaVM()->GetFactory()->NewFromString("key1")); + JSHandle father_key(thread->GetEcmaVM()->GetFactory()->NewFromString("key2")); + JSHandle grandfather_key(thread->GetEcmaVM()->GetFactory()->NewFromString("key3")); + JSHandle son_value(thread, JSTaggedValue(1)); + JSHandle father_value(thread, JSTaggedValue(2)); + JSHandle grandfather_value(thread, JSTaggedValue(3)); + + JSObject::SetProperty(thread, JSHandle(son), son_key, son_value); + JSObject::SetProperty(thread, JSHandle(father), father_key, father_value); + JSObject::SetProperty(thread, JSHandle(grandfather), grandfather_key, grandfather_value); + + EXPECT_EQ(son_value.GetTaggedValue(), + JSObject::GetProperty(thread, JSHandle(son), son_key).GetValue().GetTaggedValue()); + EXPECT_EQ(father_value.GetTaggedValue(), + JSObject::GetProperty(thread, JSHandle(son), father_key).GetValue().GetTaggedValue()); + EXPECT_EQ(grandfather_value.GetTaggedValue(), + JSObject::GetProperty(thread, JSHandle(son), grandfather_key).GetValue().GetTaggedValue()); +} + +TEST_F(JSObjectTest, PropertyAttribute) +{ + JSHandle constructor(thread, JSObjectTestCreate(thread)); + JSHandle obj1 = + thread->GetEcmaVM()->GetFactory()->NewJSObjectByConstructor(JSHandle(constructor), constructor); + JSHandle obj2 = + thread->GetEcmaVM()->GetFactory()->NewJSObjectByConstructor(JSHandle(constructor), constructor); + + JSHandle key1(thread->GetEcmaVM()->GetFactory()->NewFromString("key3")); + JSHandle key2(thread->GetEcmaVM()->GetFactory()->NewFromString("key3")); + + JSHandle value1(thread, JSTaggedValue(1)); + JSHandle value2(thread, JSTaggedValue(2)); + + // test set property + PropertyDescriptor desc(thread); + desc.SetValue(value1); + desc.SetWritable(false); + JSObject::DefineOwnProperty(thread, obj1, key1, desc); + JSObject::SetProperty(thread, JSHandle(obj1), key1, value2); + JSObject::SetProperty(thread, JSHandle(obj2), key1, value1); + JSObject::SetProperty(thread, JSHandle(obj2), key1, value2); + EXPECT_EQ(JSObject::GetProperty(thread, JSHandle(obj1), key1).GetValue().GetTaggedValue(), + value1.GetTaggedValue()); + EXPECT_EQ(JSObject::GetProperty(thread, JSHandle(obj2), key1).GetValue().GetTaggedValue(), + value2.GetTaggedValue()); + + // test delete property + PropertyDescriptor desc1(thread); + desc1.SetValue(value1); + desc1.SetConfigurable(false); + JSObject::DefineOwnProperty(thread, obj1, key2, desc1); + JSObject::SetProperty(thread, JSHandle(obj1), key2, value1); + JSObject::SetProperty(thread, JSHandle(obj2), key2, value1); + JSObject::DeleteProperty(thread, (obj1), key2); + JSObject::DeleteProperty(thread, (obj2), key2); + EXPECT_EQ(JSObject::GetProperty(thread, JSHandle(obj1), key2).GetValue().GetTaggedValue(), + value1.GetTaggedValue()); + EXPECT_EQ(JSObject::GetProperty(thread, JSHandle(obj2), key2).GetValue().GetTaggedValue(), + JSTaggedValue::Undefined()); +} + +TEST_F(JSObjectTest, CreateDataProperty) +{ + JSHandle obj_func(thread, JSObjectTestCreate(thread)); + JSHandle obj = + thread->GetEcmaVM()->GetFactory()->NewJSObjectByConstructor(JSHandle(obj_func), obj_func); + EXPECT_TRUE(*obj != nullptr); + + char array[] = "x"; + JSHandle key(thread->GetEcmaVM()->GetFactory()->NewFromString(array)); + JSHandle value(thread, JSTaggedValue(1)); + + bool success = JSObject::CreateDataProperty(thread, obj, key, value); + EXPECT_TRUE(success); + + success = JSTaggedValue::HasOwnProperty(thread, JSHandle::Cast(obj), key); + EXPECT_TRUE(success); + + PropertyDescriptor desc(thread); + success = JSObject::GetOwnProperty(thread, obj, key, desc); + EXPECT_TRUE(success); + EXPECT_EQ(true, desc.IsWritable()); + EXPECT_EQ(true, desc.IsEnumerable()); + EXPECT_EQ(true, desc.IsConfigurable()); +} + +TEST_F(JSObjectTest, CreateMethodProperty) +{ + JSHandle obj_func(thread, JSObjectTestCreate(thread)); + JSHandle obj = + thread->GetEcmaVM()->GetFactory()->NewJSObjectByConstructor(JSHandle(obj_func), obj_func); + EXPECT_TRUE(*obj != nullptr); + + char array[] = "x"; + JSHandle key(thread->GetEcmaVM()->GetFactory()->NewFromString(array)); + JSHandle value(thread, JSTaggedValue(1)); + + bool success = JSObject::CreateMethodProperty(thread, obj, key, value); + EXPECT_TRUE(success); + + success = JSTaggedValue::HasOwnProperty(thread, JSHandle::Cast(obj), key); + EXPECT_TRUE(success); + + PropertyDescriptor desc(thread); + success = JSObject::GetOwnProperty(thread, obj, key, desc); + EXPECT_TRUE(success); + EXPECT_EQ(true, desc.IsWritable()); + EXPECT_EQ(false, desc.IsEnumerable()); + EXPECT_EQ(true, desc.IsConfigurable()); +} + +TEST_F(JSObjectTest, DefinePropertyOrThrow) +{ + JSHandle obj_func(thread, JSObjectTestCreate(thread)); + JSHandle obj = + thread->GetEcmaVM()->GetFactory()->NewJSObjectByConstructor(JSHandle(obj_func), obj_func); + EXPECT_TRUE(*obj != nullptr); + + char array[] = "x"; + JSHandle key(thread->GetEcmaVM()->GetFactory()->NewFromString(array)); + + PropertyDescriptor desc1(thread, JSHandle(thread, JSTaggedValue(1)), true, true, true); + bool success = JSTaggedValue::DefinePropertyOrThrow(thread, JSHandle(obj), key, desc1); + EXPECT_TRUE(success); + PropertyDescriptor desc_res1(thread); + success = JSObject::GetOwnProperty(thread, obj, key, desc_res1); + EXPECT_TRUE(success); + EXPECT_EQ(1, desc_res1.GetValue()->GetInt()); + EXPECT_EQ(true, desc_res1.IsWritable()); + EXPECT_EQ(true, desc_res1.IsEnumerable()); + EXPECT_EQ(true, desc_res1.IsConfigurable()); + + PropertyDescriptor desc2(thread, false, true, true); + success = JSTaggedValue::DefinePropertyOrThrow(thread, JSHandle(obj), key, desc2); + EXPECT_TRUE(success); + PropertyDescriptor desc_res2(thread); + success = JSObject::GetOwnProperty(thread, obj, key, desc_res2); + EXPECT_TRUE(success); + EXPECT_EQ(1, desc_res2.GetValue()->GetInt()); + EXPECT_EQ(false, desc_res2.IsWritable()); + EXPECT_EQ(true, desc_res2.IsEnumerable()); + EXPECT_EQ(true, desc_res2.IsConfigurable()); + + PropertyDescriptor desc3(thread); + desc3.SetWritable(false); + desc3.SetEnumerable(false); + desc3.SetConfigurable(false); + success = JSTaggedValue::DefinePropertyOrThrow(thread, JSHandle(obj), key, desc3); + EXPECT_TRUE(success); + PropertyDescriptor desc_res3(thread); + success = JSObject::GetOwnProperty(thread, obj, key, desc_res3); + EXPECT_TRUE(success); + EXPECT_EQ(1, desc_res3.GetValue()->GetInt()); + EXPECT_EQ(false, desc_res3.IsWritable()); + EXPECT_EQ(false, desc_res3.IsEnumerable()); + EXPECT_EQ(false, desc_res3.IsConfigurable()); + + PropertyDescriptor desc4(thread, JSHandle(thread, JSTaggedValue(2))); + success = JSTaggedValue::DefinePropertyOrThrow(thread, JSHandle(obj), key, desc4); + EXPECT_FALSE(success); +} + +TEST_F(JSObjectTest, HasProperty) +{ + JSHandle obj_func(thread, JSObjectTestCreate(thread)); + JSHandle obj = + thread->GetEcmaVM()->GetFactory()->NewJSObjectByConstructor(JSHandle(obj_func), obj_func); + EXPECT_TRUE(*obj != nullptr); + + char array[] = "x"; + JSHandle key(thread->GetEcmaVM()->GetFactory()->NewFromString(array)); + JSHandle value(thread, JSTaggedValue(1)); + + bool flag = JSObject::HasProperty(thread, obj, key); + EXPECT_FALSE(flag); + + JSObject::SetProperty(thread, JSHandle(obj), key, value); + flag = JSObject::HasProperty(thread, obj, key); + EXPECT_TRUE(flag); + + JSObject::DeleteProperty(thread, (obj), key); + flag = JSObject::HasProperty(thread, obj, key); + EXPECT_FALSE(flag); +} + +TEST_F(JSObjectTest, HasPropertyWithProtoType) +{ + JSHandle null_handle(thread, JSTaggedValue::Null()); + JSHandle grandfather = JSObject::ObjectCreate(thread, null_handle); + JSHandle father = JSObject::ObjectCreate(thread, grandfather); + JSHandle son = JSObject::ObjectCreate(thread, father); + + auto test_grand = grandfather->GetPrototype(thread); + auto test_father = father->GetPrototype(thread); + auto test_son = son->GetPrototype(thread); + EXPECT_TRUE(test_son != test_father); + EXPECT_TRUE(test_grand != test_father); + JSHandle son_key(thread->GetEcmaVM()->GetFactory()->NewFromString("key1")); + JSHandle father_key(thread->GetEcmaVM()->GetFactory()->NewFromString("key2")); + JSHandle grandfather_key(thread->GetEcmaVM()->GetFactory()->NewFromString("key3")); + JSHandle son_value(thread, JSTaggedValue(1)); + JSHandle father_value(thread, JSTaggedValue(2)); + JSHandle grandfather_value(thread, JSTaggedValue(3)); + + JSObject::SetProperty(thread, JSHandle(son), son_key, son_value); + JSObject::SetProperty(thread, JSHandle(father), father_key, father_value); + JSObject::SetProperty(thread, JSHandle(grandfather), grandfather_key, grandfather_value); + + bool flag = JSObject::HasProperty(thread, son, son_key); + EXPECT_TRUE(flag); + flag = JSObject::HasProperty(thread, son, father_key); + EXPECT_TRUE(flag); + flag = JSObject::HasProperty(thread, son, grandfather_key); + EXPECT_TRUE(flag); +} + +TEST_F(JSObjectTest, HasOwnProperty) +{ + JSHandle null_handle(thread, JSTaggedValue::Null()); + JSHandle grandfather = JSObject::ObjectCreate(thread, null_handle); + JSHandle father = JSObject::ObjectCreate(thread, grandfather); + JSHandle son = JSObject::ObjectCreate(thread, father); + + JSHandle son_key(thread->GetEcmaVM()->GetFactory()->NewFromString("key1")); + JSHandle father_key(thread->GetEcmaVM()->GetFactory()->NewFromString("key2")); + JSHandle grandfather_key(thread->GetEcmaVM()->GetFactory()->NewFromString("key3")); + JSHandle son_value(thread, JSTaggedValue(1)); + JSHandle father_value(thread, JSTaggedValue(2)); + JSHandle grandfather_value(thread, JSTaggedValue(3)); + + JSObject::SetProperty(thread, JSHandle(son), son_key, son_value); + JSObject::SetProperty(thread, JSHandle(father), father_key, father_value); + JSObject::SetProperty(thread, JSHandle(grandfather), grandfather_key, grandfather_value); + + bool flag = JSTaggedValue::HasOwnProperty(thread, JSHandle::Cast(son), son_key); + EXPECT_TRUE(flag); + flag = JSTaggedValue::HasOwnProperty(thread, JSHandle::Cast(son), father_key); + EXPECT_FALSE(flag); + flag = JSTaggedValue::HasOwnProperty(thread, JSHandle::Cast(son), grandfather_key); + EXPECT_FALSE(flag); +} + +TEST_F(JSObjectTest, GetOwnPropertyKeys) +{ + JSHandle constructor(thread, JSObjectTestCreate(thread)); + JSHandle obj = + thread->GetEcmaVM()->GetFactory()->NewJSObjectByConstructor(JSHandle(constructor), constructor); + + JSHandle key1(thread->GetEcmaVM()->GetFactory()->NewFromString("x")); + JSHandle key2(thread->GetEcmaVM()->GetFactory()->NewFromString("y")); + JSHandle key3(thread->GetEcmaVM()->GetFactory()->NewFromString("3")); + JSHandle key4(thread->GetEcmaVM()->GetFactory()->NewFromString("4")); + JSHandle value1(thread, JSTaggedValue(1)); + JSHandle value2(thread, JSTaggedValue(2)); + JSHandle value3(thread, JSTaggedValue(3)); + JSHandle value4(thread, JSTaggedValue(4)); + + JSObject::SetProperty(thread, JSHandle(obj), key1, value1); + JSObject::SetProperty(thread, JSHandle(obj), key2, value2); + JSObject::SetProperty(thread, JSHandle(obj), key3, value3); + JSObject::SetProperty(thread, JSHandle(obj), key4, value4); + + JSHandle array = JSObject::GetOwnPropertyKeys(thread, obj); + int length = array->GetLength(); + EXPECT_EQ(length, 4); + int sum = 0; + for (int i = 0; i < length; i++) { + JSHandle key(thread, array->Get(i)); + sum += JSObject::GetProperty(thread, JSHandle(obj), key).GetValue()->GetInt(); + } + EXPECT_EQ(sum, 10); +} + +TEST_F(JSObjectTest, ObjectCreateMethod) +{ + JSHandle null_handle(thread, JSTaggedValue::Null()); + JSHandle grandfather = JSObject::ObjectCreate(thread, null_handle); + JSHandle father = JSObject::ObjectCreate(thread, grandfather); + JSHandle son = JSObject::ObjectCreate(thread, father); + + EXPECT_EQ(son->GetPrototype(thread), father.GetTaggedValue()); + EXPECT_EQ(father->GetPrototype(thread), grandfather.GetTaggedValue()); + EXPECT_EQ(grandfather->GetPrototype(thread), JSTaggedValue::Null()); +} + +TEST_F(JSObjectTest, GetMethod) +{ + JSHandle null_handle(thread, JSTaggedValue::Null()); + JSHandle obj = JSObject::ObjectCreate(thread, null_handle); + JSHandle env = thread->GetEcmaVM()->GetGlobalEnv(); + JSHandle func(thread->GetEcmaVM()->GetFactory()->NewJSFunction(env)); + EXPECT_TRUE(*func != nullptr); + JSHandle key(thread->GetEcmaVM()->GetFactory()->NewFromString("1")); + JSObject::SetProperty(thread, JSHandle(obj), key, func); + EXPECT_EQ(JSObject::GetProperty(thread, JSHandle(obj), key).GetValue().GetTaggedValue(), + func.GetTaggedValue()); +} + +TEST_F(JSObjectTest, EnumerableOwnNames) +{ + JSHandle obj_func(thread, JSObjectTestCreate(thread)); + JSHandle obj = + thread->GetEcmaVM()->GetFactory()->NewJSObjectByConstructor(JSHandle(obj_func), obj_func); + EXPECT_TRUE(*obj != nullptr); + + CString tag_c_str = "x"; + JSHandle tag_string = thread->GetEcmaVM()->GetFactory()->NewFromString(&tag_c_str[0]); + JSHandle key(tag_string); + + JSHandle value(thread, JSTaggedValue(1)); + + JSObject::SetProperty(thread, JSHandle(obj), key, value); + EXPECT_EQ(JSObject::GetProperty(thread, JSHandle(obj), key).GetValue()->GetInt(), 1); + + JSHandle names = JSObject::EnumerableOwnNames(thread, obj); + + JSHandle key_from_names(thread, JSTaggedValue(names->Get(0))); + EXPECT_EQ(JSObject::GetProperty(thread, JSHandle(obj), key_from_names).GetValue()->GetInt(), 1); + + PropertyDescriptor desc_no_enum(thread); + desc_no_enum.SetEnumerable(false); + JSTaggedValue::DefinePropertyOrThrow(thread, JSHandle(obj), key, desc_no_enum); + EXPECT_EQ(JSObject::GetProperty(thread, JSHandle(obj), key).GetValue()->GetInt(), 1); + + JSHandle names_no_enum = JSObject::EnumerableOwnNames(thread, obj); + EXPECT_TRUE(names_no_enum->GetLength() == 0); + + PropertyDescriptor desc_enum(thread); + desc_enum.SetConfigurable(false); + desc_enum.SetEnumerable(true); + JSTaggedValue::DefinePropertyOrThrow(thread, JSHandle(obj), key, desc_enum); + EXPECT_EQ(JSObject::GetProperty(thread, JSHandle(obj), key).GetValue()->GetInt(), 1); + + JSHandle names_no_config = JSObject::EnumerableOwnNames(thread, obj); + + JSHandle key_no_config(thread, JSTaggedValue(names_no_config->Get(0))); + EXPECT_EQ(JSObject::GetProperty(thread, JSHandle(obj), key_no_config).GetValue()->GetInt(), 1); +} + +TEST_F(JSObjectTest, SetIntegrityLevelSealed) +{ + JSHandle dynclass1(thread, JSObjectTestCreate(thread)); + JSHandle obj1 = + thread->GetEcmaVM()->GetFactory()->NewJSObjectByConstructor(JSHandle(dynclass1), dynclass1); + EXPECT_TRUE(*obj1 != nullptr); + CString undefined_c_str = "x"; + JSHandle key1(thread->GetEcmaVM()->GetFactory()->NewFromString(&undefined_c_str[0])); + JSHandle value1(thread, JSTaggedValue(1)); + JSObject::SetProperty(thread, JSHandle(obj1), key1, value1); + + // test SetIntegrityLevel::SEALED + JSHandle jsobject(obj1); + bool status1 = JSObject::SetIntegrityLevel(thread, jsobject, IntegrityLevel::SEALED); + EXPECT_TRUE(status1); + EXPECT_EQ(JSObject::GetProperty(thread, JSHandle(obj1), key1).GetValue().GetTaggedValue(), + value1.GetTaggedValue()); + PropertyDescriptor desc1(thread); + bool success1 = JSObject::GetOwnProperty(thread, jsobject, key1, desc1); + EXPECT_TRUE(success1); + EXPECT_EQ(true, desc1.IsWritable()); + EXPECT_EQ(true, desc1.IsEnumerable()); + EXPECT_EQ(false, desc1.IsConfigurable()); +} + +TEST_F(JSObjectTest, SetIntegrityLevelFrozen) +{ + JSHandle dynclass1(thread, JSObjectTestCreate(thread)); + JSHandle obj1 = + thread->GetEcmaVM()->GetFactory()->NewJSObjectByConstructor(JSHandle(dynclass1), dynclass1); + EXPECT_TRUE(*obj1 != nullptr); + + CString undefined_c_str = "x"; + JSHandle key1(thread->GetEcmaVM()->GetFactory()->NewFromString(&undefined_c_str[0])); + JSHandle value1(thread, JSTaggedValue(1)); + JSObject::SetProperty(thread, JSHandle(obj1), key1, value1); + + // test SetIntegrityLevel::FROZEN + bool status1 = JSObject::SetIntegrityLevel(thread, obj1, IntegrityLevel::FROZEN); + EXPECT_TRUE(status1); + EXPECT_EQ(JSObject::GetProperty(thread, JSHandle(obj1), key1).GetValue().GetTaggedValue(), + value1.GetTaggedValue()); + PropertyDescriptor desc1(thread); + bool success1 = JSObject::GetOwnProperty(thread, obj1, key1, desc1); + EXPECT_TRUE(success1); + EXPECT_EQ(false, desc1.IsWritable()); + EXPECT_EQ(true, desc1.IsEnumerable()); + EXPECT_EQ(false, desc1.IsConfigurable()); +} + +TEST_F(JSObjectTest, TestIntegrityLevelSealed) +{ + JSHandle dynclass1(thread, JSObjectTestCreate(thread)); + JSHandle obj1 = + thread->GetEcmaVM()->GetFactory()->NewJSObjectByConstructor(JSHandle(dynclass1), dynclass1); + CString undefined_c_str = "level"; + JSHandle key1(thread->GetEcmaVM()->GetFactory()->NewFromString(&undefined_c_str[0])); + JSHandle value1(thread, JSTaggedValue(1)); + JSObject::SetProperty(thread, JSHandle(obj1), key1, value1); + obj1->GetJSHClass()->SetExtensible(false); + + // test SetIntegrityLevel::SEALED + bool status1 = JSObject::SetIntegrityLevel(thread, obj1, IntegrityLevel::SEALED); + EXPECT_TRUE(status1); + EXPECT_EQ(JSObject::GetProperty(thread, JSHandle(obj1), key1).GetValue().GetTaggedValue(), + value1.GetTaggedValue()); + + PropertyDescriptor desc1(thread); + bool success1 = JSObject::GetOwnProperty(thread, obj1, key1, desc1); + EXPECT_TRUE(success1); + EXPECT_EQ(true, JSObject::TestIntegrityLevel(thread, obj1, IntegrityLevel::SEALED)); + EXPECT_EQ(false, JSObject::TestIntegrityLevel(thread, obj1, IntegrityLevel::FROZEN)); +} + +TEST_F(JSObjectTest, TestIntegrityLevelFrozen) +{ + JSHandle dynclass1(thread, JSObjectTestCreate(thread)); + JSHandle obj1 = + thread->GetEcmaVM()->GetFactory()->NewJSObjectByConstructor(JSHandle(dynclass1), dynclass1); + CString undefined_c_str = "level"; + JSHandle key1(thread->GetEcmaVM()->GetFactory()->NewFromString(&undefined_c_str[0])); + JSHandle value1(thread, JSTaggedValue(1)); + JSObject::SetProperty(thread, JSHandle(obj1), key1, value1); + obj1->GetJSHClass()->SetExtensible(false); + + // test SetIntegrityLevel::FROZEN + bool status1 = JSObject::SetIntegrityLevel(thread, obj1, IntegrityLevel::FROZEN); + EXPECT_TRUE(status1); + EXPECT_EQ(JSObject::GetProperty(thread, JSHandle(obj1), key1).GetValue().GetTaggedValue(), + value1.GetTaggedValue()); + + PropertyDescriptor desc1(thread); + bool success1 = JSObject::GetOwnProperty(thread, obj1, key1, desc1); + EXPECT_TRUE(success1); + EXPECT_EQ(true, JSObject::TestIntegrityLevel(thread, obj1, IntegrityLevel::SEALED)); + EXPECT_EQ(true, JSObject::TestIntegrityLevel(thread, obj1, IntegrityLevel::FROZEN)); +} + +TEST_F(JSObjectTest, TestIntegrityLevelWithoutProperty) +{ + JSHandle dynclass1(thread, JSObjectTestCreate(thread)); + JSHandle obj1( + thread->GetEcmaVM()->GetFactory()->NewJSObjectByConstructor(JSHandle(dynclass1), dynclass1)); + JSHandle::Cast(obj1)->GetJSHClass()->SetExtensible(false); + CString undefined_c_str = "level"; + JSHandle key1(thread->GetEcmaVM()->GetFactory()->NewFromString(&undefined_c_str[0])); + + // test SetIntegrityLevel::FROZEN + JSHandle jsobject(obj1); + bool status1 = JSObject::SetIntegrityLevel(thread, jsobject, IntegrityLevel::SEALED); + EXPECT_TRUE(status1); + + PropertyDescriptor desc1(thread); + bool success1 = JSObject::GetOwnProperty(thread, jsobject, key1, desc1); + EXPECT_TRUE(!success1); + EXPECT_EQ(true, JSObject::TestIntegrityLevel(thread, jsobject, IntegrityLevel::SEALED)); + EXPECT_EQ(true, JSObject::TestIntegrityLevel(thread, jsobject, IntegrityLevel::FROZEN)); +} + +JSTaggedValue TestGetter(EcmaRuntimeCallInfo *argv) +{ + auto thread = argv->GetThread(); + ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); + JSHandle obj(BuiltinsBase::GetThis(argv)); + JSHandle key(factory->NewFromString("y")); + JSTaggedValue value = JSObject::GetProperty(thread, JSHandle(obj), key).GetValue().GetTaggedValue(); + + return JSTaggedValue(value.GetInt() + 1); +} + +TEST_F(JSObjectTest, Getter) +{ + JSHandle dynclass1(thread, JSObjectTestCreate(thread)); + ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); + JSHandle obj = factory->NewJSObjectByConstructor(JSHandle(dynclass1), dynclass1); + JSHandle key1(factory->NewFromString("x")); + JSHandle key2(factory->NewFromString("y")); + JSHandle env = thread->GetEcmaVM()->GetGlobalEnv(); + JSHandle getter = + thread->GetEcmaVM()->GetFactory()->NewJSFunction(env, reinterpret_cast(TestGetter)); + + PropertyDescriptor desc1(thread); + desc1.SetGetter(JSHandle::Cast(getter)); + bool success1 = JSObject::DefineOwnProperty(thread, obj, key1, desc1); + EXPECT_TRUE(success1); + + PropertyDescriptor desc2(thread); + desc2.SetValue(JSHandle(thread, JSTaggedValue(1))); + success1 = JSObject::DefineOwnProperty(thread, obj, key2, desc2); + EXPECT_TRUE(success1); + + EXPECT_EQ(JSObject::GetProperty(thread, JSHandle(obj), key1).GetValue().GetTaggedValue(), + JSTaggedValue(2)); +} + +JSTaggedValue TestSetter(EcmaRuntimeCallInfo *argv) +{ + JSThread *thread = argv->GetThread(); + ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); + JSHandle obj(BuiltinsBase::GetThis(argv)); + JSHandle key(factory->NewFromString("y")); + JSTaggedValue value(JSObject::GetProperty(thread, JSHandle(obj), key).GetValue().GetTaggedValue()); + JSHandle value_handle(thread, JSTaggedValue(value.GetInt() + 1)); + JSObject::SetProperty(thread, JSHandle(obj), key, value_handle); + + return JSTaggedValue(JSTaggedValue::True()); +} + +TEST_F(JSObjectTest, Setter) +{ + JSHandle dynclass1(thread, JSObjectTestCreate(thread)); + ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); + JSHandle obj = factory->NewJSObjectByConstructor(JSHandle(dynclass1), dynclass1); + JSHandle key1(factory->NewFromString("x")); + JSHandle key2(factory->NewFromString("y")); + JSHandle env = thread->GetEcmaVM()->GetGlobalEnv(); + JSHandle setter = + thread->GetEcmaVM()->GetFactory()->NewJSFunction(env, reinterpret_cast(TestSetter)); + + PropertyDescriptor desc1(thread); + desc1.SetSetter(JSHandle::Cast(setter)); + bool success1 = JSObject::DefineOwnProperty(thread, obj, key1, desc1); + EXPECT_TRUE(success1); + + PropertyDescriptor desc2(thread, JSHandle(thread, JSTaggedValue(1)), true, true, true); + success1 = JSObject::DefineOwnProperty(thread, obj, key2, desc2); + EXPECT_TRUE(success1); + + JSHandle value_handle(thread, JSTaggedValue::Undefined()); + EXPECT_TRUE(JSObject::SetProperty(thread, JSHandle(obj), key1, value_handle)); + EXPECT_EQ(JSObject::GetProperty(thread, JSHandle(obj), key2).GetValue().GetTaggedValue(), + JSTaggedValue(2)); +} + +TEST_F(JSObjectTest, SpeciesConstructor) +{ + ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); + JSHandle env = thread->GetEcmaVM()->GetGlobalEnv(); + const GlobalEnvConstants *globalConst = thread->GlobalConstants(); + JSHandle constructor_func = + factory->NewJSFunction(env, static_cast(nullptr), FunctionKind::BASE_CONSTRUCTOR); + JSHandle constructor_func_value(constructor_func); + constructor_func->GetJSHClass()->SetExtensible(true); + JSFunction::NewJSFunctionPrototype(thread, factory, constructor_func); + + JSHandle null_handle(thread, JSTaggedValue::Null()); + JSHandle undefined_value(thread, JSTaggedValue::Undefined()); + JSHandle proto_obj = JSObject::ObjectCreate(thread, null_handle); + JSHandle proto_obj_value(proto_obj); + + JSHandle constructor_key = globalConst->GetHandledConstructorString(); + JSObject::SetProperty(thread, proto_obj_value, constructor_key, constructor_func_value); + + factory->NewJSObjectByConstructor(constructor_func, JSHandle::Cast(constructor_func)); + JSHandle species_construct = + factory->NewJSFunction(env, static_cast(nullptr), FunctionKind::BASE_CONSTRUCTOR); + JSHandle species_construct_value(species_construct); + constructor_func->GetJSHClass()->SetExtensible(true); + JSFunction::MakeConstructor(thread, species_construct, undefined_value); + + JSHandle species_symbol = env->GetSpeciesSymbol(); + JSObject::SetProperty(thread, constructor_func_value, species_symbol, species_construct_value); + + JSTaggedValue species_value = + JSObject::SpeciesConstructor(thread, proto_obj, constructor_func_value).GetTaggedValue(); + EXPECT_EQ(species_value, species_construct_value.GetTaggedValue()); +} + +JSTaggedValue TestUndefinedGetter([[maybe_unused]] EcmaRuntimeCallInfo *argv) +{ + return JSTaggedValue(10); +} + +JSTaggedValue TestUndefinedSetter([[maybe_unused]] EcmaRuntimeCallInfo *argv) +{ + return JSTaggedValue(10); +} + +TEST_F(JSObjectTest, GetterIsUndefined) +{ + JSHandle dynclass1(thread, JSObjectTestCreate(thread)); + ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); + JSHandle obj = factory->NewJSObjectByConstructor(JSHandle(dynclass1), dynclass1); + JSHandle key(factory->NewFromString("property")); + JSHandle env = thread->GetEcmaVM()->GetGlobalEnv(); + JSHandle getter = + thread->GetEcmaVM()->GetFactory()->NewJSFunction(env, reinterpret_cast(TestUndefinedGetter)); + JSHandle setter = + thread->GetEcmaVM()->GetFactory()->NewJSFunction(env, reinterpret_cast(TestUndefinedSetter)); + JSHandle un_getter(thread, JSTaggedValue::Undefined()); + + PropertyDescriptor desc1(thread); + desc1.SetGetter(JSHandle::Cast(getter)); + desc1.SetSetter(JSHandle::Cast(setter)); + desc1.SetConfigurable(true); + desc1.SetEnumerable(true); + bool success1 = JSObject::DefineOwnProperty(thread, obj, key, desc1); + EXPECT_TRUE(success1); + + PropertyDescriptor desc2(thread); + desc2.SetGetter(un_getter); + bool success2 = JSObject::DefineOwnProperty(thread, obj, key, desc2); + EXPECT_TRUE(success2); + + PropertyDescriptor desc(thread); + bool success = JSObject::GetOwnProperty(thread, obj, key, desc); + EXPECT_TRUE(success); + EXPECT_TRUE(desc.GetSetter()->IsJSFunction()); + EXPECT_TRUE(desc.GetGetter()->IsUndefined()); +} + +TEST_F(JSObjectTest, SetterIsUndefined) +{ + JSHandle dynclass1(thread, JSObjectTestCreate(thread)); + ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); + JSHandle obj = factory->NewJSObjectByConstructor(JSHandle(dynclass1), dynclass1); + JSHandle key(factory->NewFromString("property")); + JSHandle env = thread->GetEcmaVM()->GetGlobalEnv(); + JSHandle getter = + thread->GetEcmaVM()->GetFactory()->NewJSFunction(env, reinterpret_cast(TestUndefinedGetter)); + JSHandle setter = + thread->GetEcmaVM()->GetFactory()->NewJSFunction(env, reinterpret_cast(TestUndefinedSetter)); + JSHandle un_setter(thread, JSTaggedValue::Undefined()); + + PropertyDescriptor desc1(thread); + desc1.SetGetter(JSHandle::Cast(getter)); + desc1.SetSetter(JSHandle::Cast(setter)); + desc1.SetConfigurable(true); + desc1.SetEnumerable(true); + bool success1 = JSObject::DefineOwnProperty(thread, obj, key, desc1); + EXPECT_TRUE(success1); + + PropertyDescriptor desc2(thread); + desc2.SetSetter(un_setter); + bool success2 = JSObject::DefineOwnProperty(thread, obj, key, desc2); + EXPECT_TRUE(success2); + + PropertyDescriptor desc(thread); + bool success = JSObject::GetOwnProperty(thread, obj, key, desc); + EXPECT_TRUE(success); + EXPECT_TRUE(desc.GetSetter()->IsUndefined()); + + EXPECT_EQ(JSObject::GetProperty(thread, JSHandle(obj), key).GetValue().GetTaggedValue(), + JSTaggedValue(10)); +} + +TEST_F(JSObjectTest, HClass) +{ + ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); + JSHandle objFunc(thread, JSObjectTestCreate(thread)); + JSHandle obj1 = factory->NewJSObjectByConstructor(JSHandle(objFunc), objFunc); + JSHandle hc0(thread, obj1->GetJSHClass()); + + JSHandle key1(factory->NewFromCanBeCompressString("x")); + JSHandle key2(factory->NewFromCanBeCompressString("y")); + JSHandle key3(factory->NewFromCanBeCompressString("z")); + JSHandle value(thread, JSTaggedValue(1)); + + JSObject::SetProperty(thread, JSHandle(obj1), key1, value); + JSHandle hc1(thread, obj1->GetJSHClass()); + EXPECT_NE(hc0.GetTaggedValue(), hc1.GetTaggedValue()); + EXPECT_EQ(hc0.GetTaggedValue(), hc1->GetParent()); + + JSObject::SetProperty(thread, JSHandle(obj1), key2, value); + JSHandle hc2(thread, obj1->GetJSHClass()); + EXPECT_NE(hc1.GetTaggedValue(), hc2.GetTaggedValue()); + EXPECT_EQ(hc1.GetTaggedValue(), hc2->GetParent()); + + JSHandle obj2 = factory->NewJSObjectByConstructor(JSHandle(objFunc), objFunc); + EXPECT_EQ(hc0.GetTaggedValue().GetTaggedObject(), obj2->GetJSHClass()); + + JSObject::SetProperty(thread, JSHandle(obj2), key1, value); + EXPECT_EQ(hc1.GetTaggedValue().GetTaggedObject(), obj2->GetJSHClass()); + + JSObject::SetProperty(thread, JSHandle(obj2), key3, value); + JSHandle hc3(thread, obj2->GetJSHClass()); + EXPECT_NE(hc1.GetTaggedValue().GetTaggedObject(), obj2->GetJSHClass()); + EXPECT_EQ(hc1.GetTaggedValue(), obj2->GetJSHClass()->GetParent()); + + JSHandle obj3 = factory->NewJSObjectByConstructor(JSHandle(objFunc), objFunc); + EXPECT_EQ(hc0.GetTaggedValue().GetTaggedObject(), obj3->GetJSHClass()); + JSObject::SetProperty(thread, JSHandle(obj3), key1, value); + EXPECT_EQ(hc1.GetTaggedValue().GetTaggedObject(), obj3->GetJSHClass()); + + JSObject::SetProperty(thread, JSHandle(obj3), key2, value); + EXPECT_EQ(hc2.GetTaggedValue().GetTaggedObject(), obj3->GetJSHClass()); + + JSObject::SetProperty(thread, JSHandle(obj3), key3, value); + EXPECT_NE(hc3.GetTaggedValue().GetTaggedObject(), obj3->GetJSHClass()); + EXPECT_EQ(hc2.GetTaggedValue(), obj3->GetJSHClass()->GetParent()); +} + +TEST_F(JSObjectTest, FastToSlow) +{ + auto ecmaVM = thread->GetEcmaVM(); + ObjectFactory *factory = ecmaVM->GetFactory(); + + JSHandle objFunc(thread, JSObjectTestCreate(thread)); + JSHandle obj1 = factory->NewJSObjectByConstructor(JSHandle(objFunc), objFunc); + + JSMutableHandle key(factory->NewFromCanBeCompressString("x")); + JSMutableHandle number(thread, JSTaggedValue(0)); + JSMutableHandle newkey(thread, JSTaggedValue(0)); + JSHandle value(thread, JSTaggedValue(1)); + + ecmaVM->SetEnableForceGC(false); + for (uint32_t i = 0; i < PropertyAttributes::MAX_CAPACITY_OF_PROPERTIES; i++) { + number.Update(JSTaggedValue(i)); + number.Update(JSTaggedValue::ToString(thread, number).GetTaggedValue()); + EcmaString *newString = *factory->ConcatFromString(key, JSTaggedValue::ToString(thread, number)); + newkey.Update(JSTaggedValue(newString)); + JSObject::SetProperty(thread, JSHandle(obj1), newkey, value); + } + ecmaVM->SetEnableForceGC(true); + + EXPECT_FALSE(TaggedArray::Cast(obj1->GetProperties().GetTaggedObject())->IsDictionaryMode()); + + number.Update(JSTaggedValue(PropertyAttributes::MAX_CAPACITY_OF_PROPERTIES)); + number.Update(JSTaggedValue::ToString(thread, number).GetTaggedValue()); + EcmaString *newString = *factory->ConcatFromString(key, JSTaggedValue::ToString(thread, number)); + newkey.Update(JSTaggedValue(newString)); + JSObject::SetProperty(thread, JSHandle(obj1), newkey, value); + + EXPECT_TRUE(TaggedArray::Cast(obj1->GetProperties().GetTaggedObject())->IsDictionaryMode()); + NameDictionary *dict = NameDictionary::Cast(obj1->GetProperties().GetTaggedObject()); + EXPECT_EQ(dict->EntriesCount(), PropertyAttributes::MAX_CAPACITY_OF_PROPERTIES + 1); + EXPECT_EQ(dict->NextEnumerationIndex(thread), PropertyAttributes::MAX_CAPACITY_OF_PROPERTIES + 1); +} + +TEST_F(JSObjectTest, DeleteMiddle) +{ + auto ecmaVM = thread->GetEcmaVM(); + ObjectFactory *factory = ecmaVM->GetFactory(); + + JSHandle objFunc(thread, JSObjectTestCreate(thread)); + JSHandle obj1 = factory->NewJSObjectByConstructor(JSHandle(objFunc), objFunc); + + JSMutableHandle key(factory->NewFromCanBeCompressString("x")); + JSMutableHandle number(thread, JSTaggedValue(0)); + JSMutableHandle newkey(thread, JSTaggedValue(0)); + JSHandle value(thread, JSTaggedValue(1)); + + for (uint32_t i = 0; i < 10; i++) { + number.Update(JSTaggedValue(i)); + number.Update(JSTaggedValue::ToString(thread, number).GetTaggedValue()); + EcmaString *newString = *factory->ConcatFromString(key, JSTaggedValue::ToString(thread, number)); + newkey.Update(JSTaggedValue(newString)); + JSObject::SetProperty(thread, JSHandle(obj1), newkey, value); + } + + EXPECT_FALSE(TaggedArray::Cast(obj1->GetProperties().GetTaggedObject())->IsDictionaryMode()); + + JSMutableHandle key5(factory->NewFromCanBeCompressString("x5")); + JSObject::DeleteProperty(thread, (obj1), key5); + + EXPECT_TRUE(TaggedArray::Cast(obj1->GetProperties().GetTaggedObject())->IsDictionaryMode()); + NameDictionary *dict = NameDictionary::Cast(obj1->GetProperties().GetTaggedObject()); + EXPECT_EQ(dict->EntriesCount(), 9); + EXPECT_FALSE(JSObject::HasProperty(thread, obj1, key5)); +} + +TEST_F(JSObjectTest, ElementFastToSlow) +{ + ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); + JSHandle objFunc(thread, JSObjectTestCreate(thread)); + JSHandle key0(thread, JSTaggedValue(0)); + JSHandle key1(thread, JSTaggedValue(1)); + JSHandle key2(thread, JSTaggedValue(2)); + JSHandle key2000(thread, JSTaggedValue(2000)); + JSHandle keyStr(factory->NewFromCanBeCompressString("str")); + + // test dictionary [0,1,2,...,2000] + JSHandle obj1 = factory->NewJSObjectByConstructor(JSHandle(objFunc), objFunc); + EXPECT_TRUE(!TaggedArray::Cast(obj1->GetElements().GetTaggedObject())->IsDictionaryMode()); + JSObject::SetProperty(thread, JSHandle(obj1), keyStr, key2); + JSObject::SetProperty(thread, JSHandle(obj1), key0, key0); + EXPECT_TRUE(!TaggedArray::Cast(obj1->GetElements().GetTaggedObject())->IsDictionaryMode()); + JSHandle dynClass(thread, obj1->GetJSHClass()); + JSObject::SetProperty(thread, JSHandle(obj1), key1, key1); + EXPECT_TRUE(!TaggedArray::Cast(obj1->GetElements().GetTaggedObject())->IsDictionaryMode()); + EXPECT_EQ(obj1->GetJSHClass(), *dynClass); + + JSObject::SetProperty(thread, JSHandle(obj1), key2000, key2000); + EXPECT_TRUE(TaggedArray::Cast(obj1->GetElements().GetTaggedObject())->IsDictionaryMode()); + JSTaggedValue value = + JSObject::GetProperty(thread, JSHandle(obj1), keyStr).GetValue().GetTaggedValue(); + EXPECT_EQ(value, key2.GetTaggedValue()); + // test holey [0,,2] + JSHandle obj2 = factory->NewJSObjectByConstructor(JSHandle(objFunc), objFunc); + JSObject::SetProperty(thread, JSHandle(obj2), key0, key0); + EXPECT_TRUE(!TaggedArray::Cast(obj2->GetElements().GetTaggedObject())->IsDictionaryMode()); + JSHandle dynClass2(thread, obj2->GetJSHClass()); + JSObject::SetProperty(thread, JSHandle(obj2), key2, key2); + EXPECT_TRUE(!TaggedArray::Cast(obj2->GetElements().GetTaggedObject())->IsDictionaryMode()); + EXPECT_EQ(obj2->GetJSHClass(), *dynClass2); + // test change attr + JSHandle obj3 = factory->NewJSObjectByConstructor(JSHandle(objFunc), objFunc); + JSObject::SetProperty(thread, JSHandle(obj3), key0, key0); + JSObject::SetProperty(thread, JSHandle(obj3), key1, key1); + JSObject::SetProperty(thread, JSHandle(obj3), key2, key2); + EXPECT_TRUE(!TaggedArray::Cast(obj3->GetElements().GetTaggedObject())->IsDictionaryMode()); + PropertyDescriptor desc(thread); + desc.SetValue(key1); + desc.SetWritable(false); + JSObject::DefineOwnProperty(thread, obj3, key1, desc); + EXPECT_TRUE(TaggedArray::Cast(obj3->GetElements().GetTaggedObject())->IsDictionaryMode()); + // test delete element + JSHandle obj4 = factory->NewJSObjectByConstructor(JSHandle(objFunc), objFunc); + JSObject::SetProperty(thread, JSHandle(obj4), key0, key0); + JSObject::SetProperty(thread, JSHandle(obj4), key1, key1); + JSObject::SetProperty(thread, JSHandle(obj4), key2, key2); + EXPECT_TRUE(!TaggedArray::Cast(obj4->GetElements().GetTaggedObject())->IsDictionaryMode()); + JSObject::DeleteProperty(thread, (obj4), key1); + EXPECT_TRUE(TaggedArray::Cast(obj4->GetElements().GetTaggedObject())->IsDictionaryMode()); + + JSHandle value1001(thread, JSTaggedValue(1001)); + JSHandle obj100 = factory->NewJSObjectByConstructor(JSHandle(objFunc), objFunc); + PropertyDescriptor desc1(thread); + desc1.SetValue(value1001); + desc1.SetWritable(false); + desc1.SetEnumerable(false); + desc1.SetConfigurable(false); + JSObject::SetProperty(thread, JSHandle(obj100), key0, key1); + JSObject::DefineOwnProperty(thread, obj100, key0, desc1); + JSTaggedValue result1001 = + JSObject::GetProperty(thread, JSHandle(obj100), key0).GetValue().GetTaggedValue(); + EXPECT_EQ(result1001, value1001.GetTaggedValue()); +} + +TEST_F(JSObjectTest, EnableProtoChangeMarker) +{ + JSHandle nullHandle(thread, JSTaggedValue::Null()); + JSHandle obj1 = JSObject::ObjectCreate(thread, nullHandle); + JSHandle obj2 = JSObject::ObjectCreate(thread, obj1); + JSHandle obj3 = JSObject::ObjectCreate(thread, obj2); + + JSHandle obj1Key(thread->GetEcmaVM()->GetFactory()->NewFromCanBeCompressString("key1")); + JSHandle obj2Key(thread->GetEcmaVM()->GetFactory()->NewFromCanBeCompressString("key2")); + JSHandle obj3Key(thread->GetEcmaVM()->GetFactory()->NewFromCanBeCompressString("key3")); + JSHandle obj1Value(thread, JSTaggedValue(1)); + JSHandle obj2Value(thread, JSTaggedValue(2)); + JSHandle obj3Value(thread, JSTaggedValue(3)); + + JSObject::SetProperty(thread, JSHandle(obj1), obj1Key, obj1Value); + JSObject::SetProperty(thread, JSHandle(obj2), obj2Key, obj2Value); + JSObject::SetProperty(thread, JSHandle(obj3), obj3Key, obj3Value); + JSHandle obj3Dynclass(thread, obj3->GetJSHClass()); + JSHandle resultMarker = JSHClass::EnableProtoChangeMarker(thread, obj3Dynclass); + EXPECT_TRUE(resultMarker->IsProtoChangeMarker()); + bool hasChanged = ProtoChangeMarker::Cast(resultMarker->GetTaggedObject())->GetHasChanged(); + EXPECT_TRUE(!hasChanged); + + JSHandle obj1Dynclass(thread, obj1->GetJSHClass()); + JSHandle obj2Dynclass(thread, obj2->GetJSHClass()); + JSTaggedValue obj2Marker = obj2Dynclass->GetProtoChangeMarker(); + EXPECT_TRUE(obj2Marker.IsProtoChangeMarker()); + bool hasChanged2 = ProtoChangeMarker::Cast(obj2Marker.GetTaggedObject())->GetHasChanged(); + EXPECT_TRUE(!hasChanged2); + + JSTaggedValue obj1Marker = obj1Dynclass->GetProtoChangeMarker(); + EXPECT_TRUE(!obj1Marker.IsProtoChangeMarker()); + + JSTaggedValue protoDetails2 = obj2Dynclass->GetProtoChangeDetails(); + EXPECT_TRUE(protoDetails2.IsProtoChangeDetails()); + JSTaggedValue protoDetails1 = obj1Dynclass->GetProtoChangeDetails(); + EXPECT_TRUE(protoDetails1.IsProtoChangeDetails()); + JSTaggedValue listeners1 = ProtoChangeDetails::Cast(protoDetails1.GetTaggedObject())->GetChangeListener(); + EXPECT_TRUE(listeners1 != JSTaggedValue(0)); + JSTaggedValue listeners2 = ProtoChangeDetails::Cast(protoDetails2.GetTaggedObject())->GetChangeListener(); + EXPECT_TRUE(listeners2 == JSTaggedValue(0)); + JSTaggedValue index = ProtoChangeDetails::Cast(protoDetails2.GetTaggedObject())->GetRegisterIndex(); + JSTaggedValue listenersResult = ChangeListener::Cast(listeners1.GetTaggedObject())->Get(index.GetArrayLength()); + EXPECT_TRUE(listenersResult == obj2Dynclass.GetTaggedValue()); +} + +TEST_F(JSObjectTest, BuildRegisterTree) +{ + JSHandle nullHandle(thread, JSTaggedValue::Null()); + JSHandle obj1 = JSObject::ObjectCreate(thread, nullHandle); + JSHandle obj2 = JSObject::ObjectCreate(thread, obj1); + JSHandle obj3 = JSObject::ObjectCreate(thread, obj2); + JSHandle obj4 = JSObject::ObjectCreate(thread, obj2); + JSHandle obj5 = JSObject::ObjectCreate(thread, obj4); + JSHandle obj6 = JSObject::ObjectCreate(thread, obj2); + JSHandle obj7 = JSObject::ObjectCreate(thread, obj6); + + JSHandle obj1Key(thread->GetEcmaVM()->GetFactory()->NewFromCanBeCompressString("key1")); + JSHandle obj2Key(thread->GetEcmaVM()->GetFactory()->NewFromCanBeCompressString("key2")); + JSHandle obj3Key(thread->GetEcmaVM()->GetFactory()->NewFromCanBeCompressString("key3")); + JSHandle obj4Key(thread->GetEcmaVM()->GetFactory()->NewFromCanBeCompressString("key4")); + JSHandle obj5Key(thread->GetEcmaVM()->GetFactory()->NewFromCanBeCompressString("key5")); + JSHandle obj6Key(thread->GetEcmaVM()->GetFactory()->NewFromCanBeCompressString("key6")); + JSHandle obj7Key(thread->GetEcmaVM()->GetFactory()->NewFromCanBeCompressString("key7")); + + JSHandle obj1Value(thread, JSTaggedValue(1)); + JSHandle obj2Value(thread, JSTaggedValue(2)); + JSHandle obj3Value(thread, JSTaggedValue(3)); + JSHandle obj4Value(thread, JSTaggedValue(4)); + JSHandle obj5Value(thread, JSTaggedValue(5)); + JSHandle obj6Value(thread, JSTaggedValue(6)); + JSHandle obj7Value(thread, JSTaggedValue(7)); + + JSObject::SetProperty(thread, JSHandle(obj1), obj1Key, obj1Value); + JSObject::SetProperty(thread, JSHandle(obj2), obj2Key, obj2Value); + JSObject::SetProperty(thread, JSHandle(obj3), obj3Key, obj3Value); + JSObject::SetProperty(thread, JSHandle(obj4), obj4Key, obj4Value); + JSObject::SetProperty(thread, JSHandle(obj5), obj5Key, obj5Value); + JSObject::SetProperty(thread, JSHandle(obj6), obj6Key, obj6Value); + JSObject::SetProperty(thread, JSHandle(obj7), obj7Key, obj7Value); + + JSHandle obj1Dynclass(thread, obj1->GetJSHClass()); + JSHandle obj2Dynclass(thread, obj2->GetJSHClass()); + JSHandle obj3Dynclass(thread, obj3->GetJSHClass()); + JSHandle obj4Dynclass(thread, obj4->GetJSHClass()); + JSHandle obj5Dynclass(thread, obj5->GetJSHClass()); + JSHandle obj6Dynclass(thread, obj6->GetJSHClass()); + JSHandle obj7Dynclass(thread, obj7->GetJSHClass()); + + JSHandle result3Marker = JSHClass::EnableProtoChangeMarker(thread, obj3Dynclass); + JSHandle result5Marker = JSHClass::EnableProtoChangeMarker(thread, obj5Dynclass); + EXPECT_TRUE(result3Marker->IsProtoChangeMarker()); + EXPECT_TRUE(!(ProtoChangeMarker::Cast(result3Marker->GetTaggedObject())->GetHasChanged())); + EXPECT_TRUE(result5Marker->IsProtoChangeMarker()); + EXPECT_TRUE(!(ProtoChangeMarker::Cast(result5Marker->GetTaggedObject())->GetHasChanged())); + + EXPECT_TRUE(obj4Dynclass->GetProtoChangeMarker().IsProtoChangeMarker()); + EXPECT_TRUE(!obj6Dynclass->GetProtoChangeMarker().IsProtoChangeMarker()); + + JSHandle result7Marker = JSHClass::EnableProtoChangeMarker(thread, obj7Dynclass); + EXPECT_TRUE(result7Marker->IsProtoChangeMarker()); + EXPECT_TRUE(!(ProtoChangeMarker::Cast(result7Marker->GetTaggedObject())->GetHasChanged())); + + JSTaggedValue protoDetails1 = obj1Dynclass->GetProtoChangeDetails(); + EXPECT_TRUE(protoDetails1.IsProtoChangeDetails()); + JSTaggedValue listeners1Value = ProtoChangeDetails::Cast(protoDetails1.GetTaggedObject())->GetChangeListener(); + EXPECT_TRUE(listeners1Value != JSTaggedValue(0)); + JSHandle listeners1(thread, listeners1Value.GetTaggedObject()); + JSTaggedValue protoDetails2 = obj2Dynclass->GetProtoChangeDetails(); + EXPECT_TRUE(protoDetails2.IsProtoChangeDetails()); + JSTaggedValue index2 = ProtoChangeDetails::Cast(protoDetails2.GetTaggedObject())->GetRegisterIndex(); + EXPECT_TRUE(listeners1->Get(index2.GetArrayLength()) == obj2Dynclass.GetTaggedValue()); + + JSTaggedValue listeners2Value = ProtoChangeDetails::Cast(protoDetails2.GetTaggedObject())->GetChangeListener(); + EXPECT_TRUE(listeners2Value != JSTaggedValue(0)); + JSHandle listeners2(thread, listeners2Value.GetTaggedObject()); + JSTaggedValue protoDetails4 = obj4Dynclass->GetProtoChangeDetails(); + JSTaggedValue protoDetails6 = obj6Dynclass->GetProtoChangeDetails(); + EXPECT_TRUE(protoDetails4.IsProtoChangeDetails()); + EXPECT_TRUE(protoDetails6.IsProtoChangeDetails()); + JSTaggedValue index4 = ProtoChangeDetails::Cast(protoDetails4.GetTaggedObject())->GetRegisterIndex(); + EXPECT_TRUE(listeners2->Get(index4.GetArrayLength()) == obj4Dynclass.GetTaggedValue()); + JSTaggedValue index6 = ProtoChangeDetails::Cast(protoDetails6.GetTaggedObject())->GetRegisterIndex(); + EXPECT_TRUE(listeners2->Get(index6.GetArrayLength()) == obj6Dynclass.GetTaggedValue()); + + EXPECT_TRUE(listeners1->GetEnd() == 1); + EXPECT_TRUE(listeners2->GetEnd() == 2); +} + +TEST_F(JSObjectTest, NoticeThroughChain) +{ + JSHandle nullHandle(thread, JSTaggedValue::Null()); + JSHandle obj1 = JSObject::ObjectCreate(thread, nullHandle); + JSHandle obj2 = JSObject::ObjectCreate(thread, obj1); + JSHandle obj3 = JSObject::ObjectCreate(thread, obj2); + JSHandle obj4 = JSObject::ObjectCreate(thread, obj2); + JSHandle obj5 = JSObject::ObjectCreate(thread, obj4); + JSHandle obj6 = JSObject::ObjectCreate(thread, obj2); + JSHandle obj7 = JSObject::ObjectCreate(thread, obj6); + + JSHandle obj1Key(thread->GetEcmaVM()->GetFactory()->NewFromCanBeCompressString("key1")); + JSHandle obj2Key(thread->GetEcmaVM()->GetFactory()->NewFromCanBeCompressString("key2")); + JSHandle obj3Key(thread->GetEcmaVM()->GetFactory()->NewFromCanBeCompressString("key3")); + JSHandle obj4Key(thread->GetEcmaVM()->GetFactory()->NewFromCanBeCompressString("key4")); + JSHandle obj5Key(thread->GetEcmaVM()->GetFactory()->NewFromCanBeCompressString("key5")); + JSHandle obj6Key(thread->GetEcmaVM()->GetFactory()->NewFromCanBeCompressString("key6")); + JSHandle obj7Key(thread->GetEcmaVM()->GetFactory()->NewFromCanBeCompressString("key7")); + + JSHandle obj1Value(thread, JSTaggedValue(1)); + JSHandle obj2Value(thread, JSTaggedValue(2)); + JSHandle obj3Value(thread, JSTaggedValue(3)); + JSHandle obj4Value(thread, JSTaggedValue(4)); + JSHandle obj5Value(thread, JSTaggedValue(5)); + JSHandle obj6Value(thread, JSTaggedValue(6)); + JSHandle obj7Value(thread, JSTaggedValue(7)); + + JSObject::SetProperty(thread, JSHandle(obj1), obj1Key, obj1Value); + JSObject::SetProperty(thread, JSHandle(obj2), obj2Key, obj2Value); + JSObject::SetProperty(thread, JSHandle(obj3), obj3Key, obj3Value); + JSObject::SetProperty(thread, JSHandle(obj4), obj4Key, obj4Value); + JSObject::SetProperty(thread, JSHandle(obj5), obj5Key, obj5Value); + JSObject::SetProperty(thread, JSHandle(obj6), obj6Key, obj6Value); + JSObject::SetProperty(thread, JSHandle(obj7), obj7Key, obj7Value); + + JSHandle obj1Dynclass(thread, obj1->GetJSHClass()); + JSHandle obj2Dynclass(thread, obj2->GetJSHClass()); + JSHandle obj3Dynclass(thread, obj3->GetJSHClass()); + JSHandle obj4Dynclass(thread, obj4->GetJSHClass()); + JSHandle obj5Dynclass(thread, obj5->GetJSHClass()); + JSHandle obj6Dynclass(thread, obj6->GetJSHClass()); + JSHandle obj7Dynclass(thread, obj7->GetJSHClass()); + + JSHClass::EnableProtoChangeMarker(thread, obj3Dynclass); + JSHClass::EnableProtoChangeMarker(thread, obj7Dynclass); + JSHClass::EnableProtoChangeMarker(thread, obj5Dynclass); + + JSHClass::NoticeThroughChain(thread, obj2Dynclass); + JSHClass::UnregisterOnProtoChain(thread, obj2Dynclass); + JSTaggedValue protoDetails1 = obj1Dynclass->GetProtoChangeDetails(); + EXPECT_TRUE(protoDetails1.IsProtoChangeDetails()); + JSTaggedValue listeners1Value = ProtoChangeDetails::Cast(protoDetails1.GetTaggedObject())->GetChangeListener(); + EXPECT_TRUE(listeners1Value != JSTaggedValue(0)); + JSHandle listeners1(thread, listeners1Value.GetTaggedObject()); + uint32_t holeIndex = ChangeListener::CheckHole(listeners1); + EXPECT_TRUE(holeIndex == 0); + + JSTaggedValue protoDetails2 = obj2Dynclass->GetProtoChangeDetails(); + EXPECT_TRUE(protoDetails2.IsProtoChangeDetails()); + JSTaggedValue listeners2Value = ProtoChangeDetails::Cast(protoDetails2.GetTaggedObject())->GetChangeListener(); + EXPECT_TRUE(listeners2Value != JSTaggedValue(0)); + JSTaggedValue index2 = ProtoChangeDetails::Cast(protoDetails2.GetTaggedObject())->GetRegisterIndex(); + EXPECT_TRUE(listeners1->Get(index2.GetArrayLength()) == JSTaggedValue::Hole()); + + JSTaggedValue obj6Marker = obj6Dynclass->GetProtoChangeMarker(); + EXPECT_TRUE(obj6Marker.IsProtoChangeMarker()); + bool hasChanged6 = ProtoChangeMarker::Cast(obj6Marker.GetTaggedObject())->GetHasChanged(); + EXPECT_TRUE(hasChanged6); + + JSTaggedValue obj4Marker = obj4Dynclass->GetProtoChangeMarker(); + EXPECT_TRUE(obj4Marker.IsProtoChangeMarker()); + bool hasChanged4 = ProtoChangeMarker::Cast(obj4Marker.GetTaggedObject())->GetHasChanged(); + EXPECT_TRUE(hasChanged4); +} + +TEST_F(JSObjectTest, ChangeProtoAndNoticeTheChain) +{ + JSHandle nullHandle(thread, JSTaggedValue::Null()); + JSHandle obj1 = JSObject::ObjectCreate(thread, nullHandle); + JSHandle obj2 = JSObject::ObjectCreate(thread, obj1); + JSHandle obj3 = JSObject::ObjectCreate(thread, obj1); + JSHandle obj4 = JSObject::ObjectCreate(thread, obj2); + JSHandle obj5 = JSObject::ObjectCreate(thread, obj4); + JSHandle obj6 = JSObject::ObjectCreate(thread, obj2); + JSHandle obj7 = JSObject::ObjectCreate(thread, obj6); + + JSHandle obj1Key(thread->GetEcmaVM()->GetFactory()->NewFromCanBeCompressString("key1")); + JSHandle obj2Key(thread->GetEcmaVM()->GetFactory()->NewFromCanBeCompressString("key2")); + JSHandle obj3Key(thread->GetEcmaVM()->GetFactory()->NewFromCanBeCompressString("key3")); + JSHandle obj4Key(thread->GetEcmaVM()->GetFactory()->NewFromCanBeCompressString("key4")); + JSHandle obj5Key(thread->GetEcmaVM()->GetFactory()->NewFromCanBeCompressString("key5")); + JSHandle obj6Key(thread->GetEcmaVM()->GetFactory()->NewFromCanBeCompressString("key6")); + JSHandle obj7Key(thread->GetEcmaVM()->GetFactory()->NewFromCanBeCompressString("key7")); + + JSHandle obj1Value(thread, JSTaggedValue(1)); + JSHandle obj2Value(thread, JSTaggedValue(2)); + JSHandle obj3Value(thread, JSTaggedValue(3)); + JSHandle obj4Value(thread, JSTaggedValue(4)); + JSHandle obj5Value(thread, JSTaggedValue(5)); + JSHandle obj6Value(thread, JSTaggedValue(6)); + JSHandle obj7Value(thread, JSTaggedValue(7)); + + JSObject::SetProperty(thread, JSHandle(obj1), obj1Key, obj1Value); + JSObject::SetProperty(thread, JSHandle(obj2), obj2Key, obj2Value); + JSObject::SetProperty(thread, JSHandle(obj3), obj3Key, obj3Value); + JSObject::SetProperty(thread, JSHandle(obj4), obj4Key, obj4Value); + JSObject::SetProperty(thread, JSHandle(obj5), obj5Key, obj5Value); + JSObject::SetProperty(thread, JSHandle(obj6), obj6Key, obj6Value); + JSObject::SetProperty(thread, JSHandle(obj7), obj7Key, obj7Value); + + JSHandle obj5Dynclass(thread, obj5->GetJSHClass()); + JSHandle obj7Dynclass(thread, obj7->GetJSHClass()); + + JSHClass::EnableProtoChangeMarker(thread, obj7Dynclass); + JSHClass::EnableProtoChangeMarker(thread, obj5Dynclass); + + JSObject::SetPrototype(thread, obj2, JSHandle(obj3)); + + JSHandle obj1Dynclass(thread, obj1->GetJSHClass()); + JSHandle obj2Dynclass(thread, obj2->GetJSHClass()); + JSHandle obj3Dynclass(thread, obj3->GetJSHClass()); + JSHandle obj4Dynclass(thread, obj4->GetJSHClass()); + JSHandle obj6Dynclass(thread, obj6->GetJSHClass()); + + JSTaggedValue obj6Marker = obj6Dynclass->GetProtoChangeMarker(); + EXPECT_TRUE(obj6Marker.IsProtoChangeMarker()); + bool hasChanged6 = ProtoChangeMarker::Cast(obj6Marker.GetTaggedObject())->GetHasChanged(); + EXPECT_TRUE(hasChanged6); + + JSTaggedValue obj4Marker = obj4Dynclass->GetProtoChangeMarker(); + EXPECT_TRUE(obj4Marker.IsProtoChangeMarker()); + bool hasChanged4 = ProtoChangeMarker::Cast(obj4Marker.GetTaggedObject())->GetHasChanged(); + EXPECT_TRUE(hasChanged4); + + JSTaggedValue protoDetails1 = obj1Dynclass->GetProtoChangeDetails(); + EXPECT_TRUE(protoDetails1.IsProtoChangeDetails()); + JSTaggedValue protoDetails2 = obj2Dynclass->GetProtoChangeDetails(); + EXPECT_TRUE(protoDetails2.IsProtoChangeDetails()); + JSTaggedValue protoDetails3 = obj3Dynclass->GetProtoChangeDetails(); + EXPECT_TRUE(protoDetails3.IsProtoChangeDetails()); + JSTaggedValue protoDetails4 = obj4Dynclass->GetProtoChangeDetails(); + EXPECT_TRUE(protoDetails4.IsProtoChangeDetails()); + JSTaggedValue protoDetails6 = obj6Dynclass->GetProtoChangeDetails(); + EXPECT_TRUE(protoDetails6.IsProtoChangeDetails()); + + JSTaggedValue listeners1 = ProtoChangeDetails::Cast(protoDetails1.GetTaggedObject())->GetChangeListener(); + EXPECT_TRUE(listeners1 != JSTaggedValue(0)); + JSTaggedValue listeners2 = ProtoChangeDetails::Cast(protoDetails2.GetTaggedObject())->GetChangeListener(); + EXPECT_TRUE(listeners2 != JSTaggedValue(0)); + JSTaggedValue listeners3 = ProtoChangeDetails::Cast(protoDetails3.GetTaggedObject())->GetChangeListener(); + EXPECT_TRUE(listeners3 != JSTaggedValue(0)); + + JSTaggedValue index2 = ProtoChangeDetails::Cast(protoDetails2.GetTaggedObject())->GetRegisterIndex(); + JSTaggedValue index3 = ProtoChangeDetails::Cast(protoDetails3.GetTaggedObject())->GetRegisterIndex(); + JSTaggedValue index4 = ProtoChangeDetails::Cast(protoDetails4.GetTaggedObject())->GetRegisterIndex(); + JSTaggedValue index6 = ProtoChangeDetails::Cast(protoDetails6.GetTaggedObject())->GetRegisterIndex(); + + JSTaggedValue result2 = ChangeListener::Cast(listeners3.GetTaggedObject())->Get(index2.GetArrayLength()); + JSTaggedValue result3 = ChangeListener::Cast(listeners1.GetTaggedObject())->Get(index3.GetArrayLength()); + JSTaggedValue result4 = ChangeListener::Cast(listeners2.GetTaggedObject())->Get(index4.GetArrayLength()); + JSTaggedValue result6 = ChangeListener::Cast(listeners2.GetTaggedObject())->Get(index6.GetArrayLength()); + + EXPECT_TRUE(result2 == obj2Dynclass.GetTaggedValue()); + EXPECT_TRUE(result3 == obj3Dynclass.GetTaggedValue()); + EXPECT_TRUE(result4 == obj4Dynclass.GetTaggedValue()); + EXPECT_TRUE(result6 == obj6Dynclass.GetTaggedValue()); +} + +TEST_F(JSObjectTest, NativePointerField) +{ + ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); + JSHandle objFunc(thread, JSObjectTestCreate(thread)); + JSHandle obj = factory->NewJSObjectByConstructor(JSHandle(objFunc), objFunc); + obj->SetHash(87); + EXPECT_TRUE(obj->GetHash() == 87); + + obj->SetNativePointerFieldCount(1); + char array[] = "Hello World!"; + obj->SetNativePointerField(0, array, nullptr, nullptr); + int32_t count = obj->GetNativePointerFieldCount(); + EXPECT_TRUE(count == 1); + void *pointer = obj->GetNativePointerField(0); + EXPECT_TRUE(pointer == array); +} +} // namespace panda::test diff --git a/tests/runtime/common/js_primitive_ref_test.cpp b/tests/runtime/common/js_primitive_ref_test.cpp new file mode 100644 index 000000000..92c2adb9d --- /dev/null +++ b/tests/runtime/common/js_primitive_ref_test.cpp @@ -0,0 +1,69 @@ +/* + * Copyright (c) 2021-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 "gtest/gtest.h" +#include "test_helper.h" +#include "plugins/ecmascript/runtime/js_primitive_ref.h" + +#include "plugins/ecmascript/runtime/ecma_string.h" +#include "include/coretypes/array.h" +#include "plugins/ecmascript/runtime/js_object.h" +#include "plugins/ecmascript/runtime/js_hclass.h" +#include "plugins/ecmascript/runtime/js_tagged_value-inl.h" +#include "plugins/ecmascript/runtime/js_handle.h" +#include "plugins/ecmascript/runtime/object_operator.h" +#include "include/runtime.h" +#include "plugins/ecmascript/runtime/object_factory.h" +#include "plugins/ecmascript/runtime/ecma_vm.h" + +using namespace panda::ecmascript; +using namespace panda::coretypes; + +namespace panda::test { +class JSPrimitiveRefTest : public testing::Test { +public: + void SetUp() override + { + TestHelper::CreateEcmaVMWithScope(instance, thread, scope); + } + + void TearDown() override + { + TestHelper::DestroyEcmaVMWithScope(instance, scope); + } + + PandaVM *instance {nullptr}; + ecmascript::EcmaHandleScope *scope {nullptr}; + JSThread *thread; +}; + +TEST_F(JSPrimitiveRefTest, DISABLED_StringCreate) // issue #5368 +{ + JSHandle hello(thread->GetEcmaVM()->GetFactory()->NewFromString("hello")); + JSHandle str(JSPrimitiveRef::StringCreate(thread, hello)); + + JSHandle idx(thread->GetEcmaVM()->GetFactory()->NewFromString("0")); + bool status = JSPrimitiveRef::HasProperty(thread, str, idx); + ASSERT_TRUE(status); + + PropertyDescriptor desc(thread); + status = JSPrimitiveRef::GetOwnProperty(thread, str, idx, desc); + ASSERT_TRUE(status); + JSHandle h = thread->GetEcmaVM()->GetFactory()->NewFromString("h"); + JSHandle h2 = JSTaggedValue::ToString(thread, desc.GetValue()); + ASSERT_TRUE(h->Compare(*h2) == 0); +} + +} // namespace panda::test diff --git a/tests/runtime/common/js_promise_test.cpp b/tests/runtime/common/js_promise_test.cpp new file mode 100644 index 000000000..0387dbd71 --- /dev/null +++ b/tests/runtime/common/js_promise_test.cpp @@ -0,0 +1,128 @@ +/* + * Copyright (c) 2021-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 "gtest/gtest.h" + +#include "test_helper.h" + +#include "include/runtime.h" +#include "plugins/ecmascript/runtime/internal_call_params.h" +#include "plugins/ecmascript/runtime/object_factory.h" +#include "plugins/ecmascript/runtime/ecma_vm.h" +#include "plugins/ecmascript/runtime/base/builtins_base.h" +#include "plugins/ecmascript/runtime/ecma_runtime_call_info.h" +#include "plugins/ecmascript/runtime/js_thread.h" +#include "plugins/ecmascript/runtime/js_function.h" +#include "plugins/ecmascript/runtime/global_env.h" +#include "plugins/ecmascript/runtime/js_promise.h" +#include "include/runtime_options.h" + +using namespace panda::ecmascript; +using namespace panda::ecmascript::base; +using namespace panda::coretypes; + +namespace panda::test { +class JSPromiseTest : public testing::Test { +public: + void SetUp() override + { + TestHelper::CreateEcmaVMWithScope(instance, thread, scope); + } + + void TearDown() override + { + TestHelper::DestroyEcmaVMWithScope(instance, scope); + } + + JSThread *thread {nullptr}; + PandaVM *instance {nullptr}; + ecmascript::EcmaHandleScope *scope {nullptr}; +}; + +TEST_F(JSPromiseTest, CreateResolvingFunctions) +{ + EcmaVM *ecmaVM = thread->GetEcmaVM(); + JSHandle env = ecmaVM->GetGlobalEnv(); + ObjectFactory *factory = ecmaVM->GetFactory(); + + JSHandle promiseFunc = env->GetPromiseFunction(); + JSHandle jsPromise = + JSHandle::Cast(factory->NewJSObjectByConstructor(JSHandle(promiseFunc), promiseFunc)); + JSHandle reactions = JSPromise::CreateResolvingFunctions(thread, jsPromise); + JSHandle resolve(thread, reactions->GetResolveFunction()); + JSHandle reject(thread, reactions->GetRejectFunction()); + EXPECT_EQ(resolve->IsCallable(), true); + EXPECT_EQ(reject->IsCallable(), true); +} + +TEST_F(JSPromiseTest, NewPromiseCapability) +{ + EcmaVM *ecmaVM = thread->GetEcmaVM(); + JSHandle env = ecmaVM->GetGlobalEnv(); + JSHandle promise = env->GetPromiseFunction(); + + JSHandle capbility = JSPromise::NewPromiseCapability(thread, promise); + JSHandle newPromise(thread, capbility->GetPromise()); + EXPECT_EQ(static_cast(newPromise->GetPromiseState().GetInt()), PromiseStatus::PENDING); + + JSHandle resolve(thread, capbility->GetResolve()); + JSHandle reject(thread, capbility->GetReject()); + EXPECT_EQ(resolve.GetTaggedValue().IsCallable(), true); + EXPECT_EQ(resolve.GetTaggedValue().IsCallable(), true); + + JSHandle resolve_promise(thread, resolve->GetPromise()); + JSHandle reject_promise(thread, reject->GetPromise()); + EXPECT_EQ(JSTaggedValue::SameValue(newPromise.GetTaggedValue(), resolve_promise.GetTaggedValue()), true); + EXPECT_EQ(JSTaggedValue::SameValue(newPromise.GetTaggedValue(), reject_promise.GetTaggedValue()), true); +} + +TEST_F(JSPromiseTest, FullFillPromise) +{ + EcmaVM *ecmaVM = thread->GetEcmaVM(); + JSHandle env = ecmaVM->GetGlobalEnv(); + JSHandle promise = env->GetPromiseFunction(); + JSHandle capbility = JSPromise::NewPromiseCapability(thread, promise); + JSHandle newPromise(thread, capbility->GetPromise()); + EXPECT_EQ(static_cast(newPromise->GetPromiseState().GetInt()), PromiseStatus::PENDING); + EXPECT_EQ(newPromise->GetPromiseResult().IsUndefined(), true); + + JSHandle resolve(thread, capbility->GetResolve()); + JSHandle undefined(thread, JSTaggedValue::Undefined()); + InternalCallParams *arguments = thread->GetInternalCallParams(); + arguments->MakeArgv(JSTaggedValue(33)); + JSFunction::Call(thread, resolve, undefined, 1, arguments->GetArgv()); + EXPECT_EQ(static_cast(newPromise->GetPromiseState().GetInt()), PromiseStatus::FULFILLED); + EXPECT_EQ(JSTaggedValue::SameValue(newPromise->GetPromiseResult(), JSTaggedValue(33)), true); +} + +TEST_F(JSPromiseTest, RejectPromise) +{ + EcmaVM *ecmaVM = thread->GetEcmaVM(); + JSHandle env = ecmaVM->GetGlobalEnv(); + JSHandle promise = env->GetPromiseFunction(); + JSHandle capbility = JSPromise::NewPromiseCapability(thread, promise); + JSHandle newPromise(thread, capbility->GetPromise()); + EXPECT_EQ(static_cast(newPromise->GetPromiseState().GetInt()), PromiseStatus::PENDING); + EXPECT_EQ(newPromise->GetPromiseResult().IsUndefined(), true); + + JSHandle reject(thread, capbility->GetReject()); + JSHandle undefined(thread, JSTaggedValue::Undefined()); + InternalCallParams *arguments = thread->GetInternalCallParams(); + arguments->MakeArgv(JSTaggedValue(44)); + JSFunction::Call(thread, reject, undefined, 1, arguments->GetArgv()); + EXPECT_EQ(static_cast(newPromise->GetPromiseState().GetInt()), PromiseStatus::REJECTED); + EXPECT_EQ(JSTaggedValue::SameValue(newPromise->GetPromiseResult(), JSTaggedValue(44)), true); +} +} // namespace panda::test diff --git a/tests/runtime/common/js_proxy_test.cpp b/tests/runtime/common/js_proxy_test.cpp new file mode 100644 index 000000000..3b1dfd2e8 --- /dev/null +++ b/tests/runtime/common/js_proxy_test.cpp @@ -0,0 +1,634 @@ +/* + * Copyright (c) 2021-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 "gtest/gtest.h" +#include "test_helper.h" + +#include "plugins/ecmascript/runtime/ecma_string.h" +#include "include/coretypes/array.h" +#include "plugins/ecmascript/runtime/js_proxy.h" +#include "plugins/ecmascript/runtime/js_hclass.h" +#include "plugins/ecmascript/runtime/js_tagged_value-inl.h" +#include "plugins/ecmascript/runtime/js_handle.h" +#include "include/runtime.h" +#include "plugins/ecmascript/runtime/object_factory.h" +#include "plugins/ecmascript/runtime/ecma_vm.h" +#include "plugins/ecmascript/runtime/base/builtins_base.h" +#include "plugins/ecmascript/runtime/ecma_runtime_call_info.h" +#include "plugins/ecmascript/runtime/js_function.h" +#include "plugins/ecmascript/runtime/global_env.h" + +using namespace panda::ecmascript; +using namespace panda::ecmascript::base; +using namespace panda::coretypes; + +namespace panda::test { +class JSProxyTest : public testing::Test { +public: + void SetUp() override + { + TestHelper::CreateEcmaVMWithScope(instance, thread, scope); + } + + void TearDown() override + { + TestHelper::DestroyEcmaVMWithScope(instance, scope); + } + + PandaVM *instance {nullptr}; + ecmascript::EcmaHandleScope *scope {nullptr}; + JSThread *thread; +}; + +static JSFunction *JSObjectTestCreate(JSThread *thread) +{ + EcmaVM *ecma_vm = thread->GetEcmaVM(); + JSHandle global_env = ecma_vm->GetGlobalEnv(); + return global_env->GetObjectFunction().GetObject(); +} + +TEST_F(JSProxyTest, ProxyCreate) +{ + JSHandle dynclass(thread, JSObjectTestCreate(thread)); + JSHandle target_handle( + thread->GetEcmaVM()->GetFactory()->NewJSObjectByConstructor(JSHandle::Cast(dynclass), dynclass)); + + JSHandle key(thread->GetEcmaVM()->GetFactory()->NewFromString("x")); + JSHandle value(thread, JSTaggedValue(1)); + JSObject::SetProperty(thread, target_handle, key, value); + EXPECT_EQ(JSObject::GetProperty(thread, target_handle, key).GetValue()->GetInt(), 1); + + JSHandle handler_handle( + thread->GetEcmaVM()->GetFactory()->NewJSObjectByConstructor(JSHandle::Cast(dynclass), dynclass)); + EXPECT_TRUE(handler_handle->IsECMAObject()); + + JSHandle proxy_handle = JSProxy::ProxyCreate(thread, target_handle, handler_handle); + EXPECT_TRUE(*proxy_handle != nullptr); + + EXPECT_EQ(JSProxy::GetProperty(thread, proxy_handle, key).GetValue()->GetInt(), 1); + PropertyDescriptor desc(thread); + JSProxy::GetOwnProperty(thread, proxy_handle, key, desc); + EXPECT_EQ(desc.GetValue()->GetInt(), 1); +} + +// ES6 9.5.8 [[Get]] (P, Receiver) +JSTaggedValue HandlerGetProperty([[maybe_unused]] EcmaRuntimeCallInfo *argv) +{ + return JSTaggedValue(10); +} + +TEST_F(JSProxyTest, GetProperty) +{ + ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); + // 1. handler has no "get" + JSHandle dynclass(thread, JSObjectTestCreate(thread)); + JSHandle target_handle(factory->NewJSObjectByConstructor(JSHandle(dynclass), dynclass)); + EXPECT_TRUE(target_handle->IsECMAObject()); + + JSHandle key(factory->NewFromString("x")); + JSHandle value(thread, JSTaggedValue(1)); + JSObject::SetProperty(thread, target_handle, key, value); + EXPECT_EQ(JSObject::GetProperty(thread, target_handle, key).GetValue()->GetInt(), 1); + + JSHandle handler_handle(factory->NewJSObjectByConstructor(JSHandle(dynclass), dynclass)); + EXPECT_TRUE(handler_handle->IsECMAObject()); + + JSHandle proxy_handle = JSProxy::ProxyCreate(thread, target_handle, handler_handle); + EXPECT_TRUE(*proxy_handle != nullptr); + + EXPECT_EQ(JSProxy::GetProperty(thread, proxy_handle, key).GetValue()->GetInt(), 1); + + // 2. handler has "get" + EcmaVM *vm = thread->GetEcmaVM(); + JSHandle env = vm->GetGlobalEnv(); + JSHandle get_key = thread->GlobalConstants()->GetHandledGetString(); + JSHandle get_handle(factory->NewJSFunction(env, reinterpret_cast(HandlerGetProperty))); + JSObject::SetProperty(thread, JSHandle(handler_handle), get_key, get_handle); + + JSHandle proxy_handle2(JSProxy::ProxyCreate(thread, target_handle, handler_handle)); + EXPECT_TRUE(*proxy_handle2 != nullptr); + JSHandle key2(factory->NewFromString("y")); + EXPECT_EQ(JSProxy::GetProperty(thread, proxy_handle2, key2).GetValue()->GetInt(), 10); +} + +// ES6 9.5.5 [[GetOwnProperty]] (P) +JSTaggedValue HandlerGetOwnProperty([[maybe_unused]] EcmaRuntimeCallInfo *argv) +{ + return JSTaggedValue(JSTaggedValue::Undefined()); +} + +TEST_F(JSProxyTest, GetOwnProperty) +{ + ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); + // 1. handler has no "get" + JSHandle dynclass(thread, JSObjectTestCreate(thread)); + JSHandle target_handle(factory->NewJSObjectByConstructor(JSHandle(dynclass), dynclass)); + EXPECT_TRUE(target_handle->IsECMAObject()); + + JSHandle key(factory->NewFromString("x")); + JSHandle value(thread, JSTaggedValue(1)); + JSObject::SetProperty(thread, target_handle, key, value); + EXPECT_EQ(JSObject::GetProperty(thread, target_handle, key).GetValue()->GetInt(), 1); + + JSHandle handler_handle(factory->NewJSObjectByConstructor(JSHandle(dynclass), dynclass)); + EXPECT_TRUE(handler_handle->IsECMAObject()); + + JSHandle proxy_handle = JSProxy::ProxyCreate(thread, target_handle, handler_handle); + EXPECT_TRUE(*proxy_handle != nullptr); + + PropertyDescriptor desc(thread); + JSProxy::GetOwnProperty(thread, proxy_handle, key, desc); + EXPECT_EQ(desc.GetValue()->GetInt(), 1); + + // 2. handler has "get" + EcmaVM *vm = thread->GetEcmaVM(); + JSHandle env = vm->GetGlobalEnv(); + JSHandle define_key = thread->GlobalConstants()->GetHandledGetOwnPropertyDescriptorString(); + JSHandle define_handle(factory->NewJSFunction(env, reinterpret_cast(HandlerGetOwnProperty))); + JSObject::SetProperty(thread, JSHandle(handler_handle), define_key, define_handle); + + JSHandle proxy_handle2 = JSProxy::ProxyCreate(thread, target_handle, handler_handle); + EXPECT_TRUE(*proxy_handle2 != nullptr); + JSHandle key2(factory->NewFromString("y")); + PropertyDescriptor desc2(thread); + EXPECT_FALSE(JSProxy::GetOwnProperty(thread, proxy_handle2, key2, desc2)); +} + +// ES6 9.5.9 [[Set]] ( P, V, Receiver) +JSTaggedValue HandlerSetProperty([[maybe_unused]] EcmaRuntimeCallInfo *argv) +{ + return JSTaggedValue(JSTaggedValue::False()); +} + +TEST_F(JSProxyTest, SetProperty) +{ + // 1. handler has no "get" + ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); + // 1. handler has no "get" + JSHandle dynclass(thread, JSObjectTestCreate(thread)); + JSHandle target_handle(factory->NewJSObjectByConstructor(JSHandle(dynclass), dynclass)); + EXPECT_TRUE(target_handle->IsECMAObject()); + + JSHandle key(factory->NewFromString("x")); + JSHandle value(thread, JSTaggedValue(1)); + + JSHandle handler_handle(factory->NewJSObjectByConstructor(JSHandle(dynclass), dynclass)); + EXPECT_TRUE(handler_handle->IsECMAObject()); + + JSHandle proxy_handle = JSProxy::ProxyCreate(thread, target_handle, handler_handle); + EXPECT_TRUE(*proxy_handle != nullptr); + + EXPECT_TRUE(JSProxy::SetProperty(thread, proxy_handle, key, value)); + EXPECT_EQ(JSProxy::GetProperty(thread, proxy_handle, key).GetValue()->GetInt(), 1); + EXPECT_EQ(JSObject::GetProperty(thread, target_handle, key).GetValue()->GetInt(), 1); + + // 2. handler has "set" + EcmaVM *vm = thread->GetEcmaVM(); + JSHandle env = vm->GetGlobalEnv(); + JSHandle set_key = thread->GlobalConstants()->GetHandledSetString(); + JSHandle set_handle(factory->NewJSFunction(env, reinterpret_cast(HandlerSetProperty))); + JSObject::SetProperty(thread, JSHandle(handler_handle), set_key, set_handle); + + JSHandle proxy_handle2(JSProxy::ProxyCreate(thread, target_handle, handler_handle)); + EXPECT_TRUE(*proxy_handle2 != nullptr); + JSHandle value2(thread, JSTaggedValue(10)); + EXPECT_FALSE(JSProxy::SetProperty(thread, proxy_handle2, key, value2)); + EXPECT_EQ(JSProxy::GetProperty(thread, proxy_handle2, key).GetValue()->GetInt(), 1); +} + +// ES6 9.5.6 [[DefineOwnProperty]] (P, Desc) +JSTaggedValue HandlerDefineOwnProperty([[maybe_unused]] EcmaRuntimeCallInfo *argv) +{ + return JSTaggedValue(JSTaggedValue::False()); +} + +TEST_F(JSProxyTest, DefineOwnProperty) +{ + ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); + // 1. handler has no "defineProperty" + JSHandle dynclass(thread, JSObjectTestCreate(thread)); + JSHandle target_handle(factory->NewJSObjectByConstructor(JSHandle(dynclass), dynclass)); + EXPECT_TRUE(target_handle->IsECMAObject()); + + JSHandle key(factory->NewFromString("x")); + JSHandle value(thread, JSTaggedValue(1)); + + JSHandle handler_handle(factory->NewJSObjectByConstructor(JSHandle(dynclass), dynclass)); + EXPECT_TRUE(handler_handle->IsECMAObject()); + + JSHandle proxy_handle = JSProxy::ProxyCreate(thread, target_handle, handler_handle); + EXPECT_TRUE(*proxy_handle != nullptr); + + PropertyDescriptor desc(thread, JSHandle(thread, JSTaggedValue(1))); + EXPECT_TRUE(JSProxy::DefineOwnProperty(thread, proxy_handle, key, desc)); + EXPECT_EQ(JSProxy::GetProperty(thread, proxy_handle, key).GetValue()->GetInt(), 1); + EXPECT_EQ(JSObject::GetProperty(thread, target_handle, key).GetValue()->GetInt(), 1); + + // 2. handler has "defineProperty" + EcmaVM *vm = thread->GetEcmaVM(); + JSHandle env = vm->GetGlobalEnv(); + JSHandle set_key = thread->GlobalConstants()->GetHandledDefinePropertyString(); + JSHandle set_handle(factory->NewJSFunction(env, reinterpret_cast(HandlerDefineOwnProperty))); + JSObject::SetProperty(thread, JSHandle(handler_handle), set_key, set_handle); + + JSHandle proxy_handle2 = JSProxy::ProxyCreate(thread, target_handle, handler_handle); + EXPECT_TRUE(*proxy_handle2 != nullptr); + PropertyDescriptor desc2(thread, JSHandle(thread, JSTaggedValue(10))); + EXPECT_FALSE(JSProxy::DefineOwnProperty(thread, proxy_handle, key, desc2)); + EXPECT_EQ(JSProxy::GetProperty(thread, proxy_handle2, key).GetValue()->GetInt(), 1); +} + +JSTaggedValue HandlerDeleteProperty([[maybe_unused]] EcmaRuntimeCallInfo *argv) +{ + return JSTaggedValue(JSTaggedValue::False()); +} + +// ES6 9.5.10 [[Delete]] (P) +TEST_F(JSProxyTest, DeleteProperty) +{ + ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); + // 1. handler has no "deleteProperty" + JSHandle dynclass(thread, JSObjectTestCreate(thread)); + JSHandle target_handle(factory->NewJSObjectByConstructor(JSHandle(dynclass), dynclass)); + EXPECT_TRUE(target_handle->IsECMAObject()); + + JSHandle key(factory->NewFromString("x")); + JSHandle value(thread, JSTaggedValue(1)); + + JSHandle handler_handle(factory->NewJSObjectByConstructor(JSHandle(dynclass), dynclass)); + EXPECT_TRUE(handler_handle->IsECMAObject()); + + JSHandle proxy_handle = JSProxy::ProxyCreate(thread, target_handle, handler_handle); + EXPECT_TRUE(*proxy_handle != nullptr); + + PropertyDescriptor desc(thread, JSHandle(thread, JSTaggedValue(1)), true, true, true); + EXPECT_TRUE(JSProxy::DefineOwnProperty(thread, proxy_handle, key, desc)); + EXPECT_EQ(JSProxy::GetProperty(thread, proxy_handle, key).GetValue()->GetInt(), 1); + EXPECT_EQ(JSObject::GetProperty(thread, target_handle, key).GetValue()->GetInt(), 1); + EXPECT_TRUE(JSProxy::DeleteProperty(thread, proxy_handle, key)); + PropertyDescriptor res_desc(thread); + JSProxy::GetOwnProperty(thread, proxy_handle, key, res_desc); + EXPECT_TRUE(JSTaggedValue::SameValue(res_desc.GetValue().GetTaggedValue(), JSTaggedValue::Undefined())); + + // 2. handler has "deleteProperty" + EcmaVM *vm = thread->GetEcmaVM(); + JSHandle env = vm->GetGlobalEnv(); + JSHandle func_key = thread->GlobalConstants()->GetHandledDeletePropertyString(); + JSHandle func_handle(factory->NewJSFunction(env, reinterpret_cast(HandlerDeleteProperty))); + JSObject::SetProperty(thread, JSHandle(handler_handle), func_key, func_handle); + + JSHandle proxy_handle2 = JSProxy::ProxyCreate(thread, target_handle, handler_handle); + EXPECT_TRUE(*proxy_handle2 != nullptr); + PropertyDescriptor desc2(thread, JSHandle(thread, JSTaggedValue(1)), true, true, true); + EXPECT_TRUE(JSProxy::DefineOwnProperty(thread, proxy_handle2, key, desc2)); + EXPECT_EQ(JSProxy::GetProperty(thread, proxy_handle2, key).GetValue()->GetInt(), 1); + EXPECT_FALSE(JSProxy::DeleteProperty(thread, proxy_handle2, key)); +} + +JSTaggedValue HandlerGetPrototype([[maybe_unused]] EcmaRuntimeCallInfo *argv) +{ + return JSTaggedValue(JSTaggedValue::Null()); +} + +// ES6 9.5.1 [[GetPrototypeOf]] ( ) +TEST_F(JSProxyTest, GetPrototypeOf) +{ + ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); + // 1. handler has no "GetPrototypeOf" + JSHandle dynclass(thread, JSObjectTestCreate(thread)); + JSHandle proto(factory->NewJSObjectByConstructor(JSHandle(dynclass), dynclass)); + JSHandle target_handle(factory->NewJSObjectByConstructor(JSHandle(dynclass), dynclass)); + EXPECT_TRUE(target_handle->IsECMAObject()); + JSObject::SetPrototype(thread, JSHandle(target_handle), proto); + EXPECT_TRUE( + JSTaggedValue::SameValue(JSHandle(target_handle)->GetPrototype(thread), proto.GetTaggedValue())); + + JSHandle handler_handle(factory->NewJSObjectByConstructor(JSHandle(dynclass), dynclass)); + EXPECT_TRUE(handler_handle->IsECMAObject()); + + JSHandle proxy_handle = JSProxy::ProxyCreate(thread, target_handle, handler_handle); + EXPECT_TRUE(*proxy_handle != nullptr); + + EXPECT_TRUE(JSTaggedValue::SameValue(JSProxy::GetPrototype(thread, proxy_handle), proto.GetTaggedValue())); + + // 2. handler has "GetPrototypeOf" + EcmaVM *vm = thread->GetEcmaVM(); + JSHandle env = vm->GetGlobalEnv(); + JSHandle func_key = thread->GlobalConstants()->GetHandledGetPrototypeOfString(); + JSHandle func_handle(factory->NewJSFunction(env, reinterpret_cast(HandlerGetPrototype))); + JSObject::SetProperty(thread, JSHandle(handler_handle), func_key, func_handle); + + JSHandle proxy_handle2 = JSProxy::ProxyCreate(thread, target_handle, handler_handle); + EXPECT_TRUE(*proxy_handle2 != nullptr); + EXPECT_TRUE(JSTaggedValue::SameValue(JSProxy::GetPrototype(thread, proxy_handle2), JSTaggedValue::Null())); +} + +JSTaggedValue HandlerSetPrototype([[maybe_unused]] EcmaRuntimeCallInfo *argv) +{ + return JSTaggedValue(JSTaggedValue::False()); +} + +// ES6 9.5.2 [[SetPrototypeOf]] (V) +TEST_F(JSProxyTest, SetPrototypeOf) +{ + ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); + // 1. handler has no "SetPrototypeOf" + JSHandle dynclass(thread, JSObjectTestCreate(thread)); + JSHandle proto(factory->NewJSObjectByConstructor(JSHandle(dynclass), dynclass)); + JSHandle target_handle(factory->NewJSObjectByConstructor(JSHandle(dynclass), dynclass)); + EXPECT_TRUE(target_handle->IsECMAObject()); + + JSHandle handler_handle(factory->NewJSObjectByConstructor(JSHandle(dynclass), dynclass)); + EXPECT_TRUE(handler_handle->IsECMAObject()); + + JSHandle proxy_handle = JSProxy::ProxyCreate(thread, target_handle, handler_handle); + EXPECT_TRUE(*proxy_handle != nullptr); + + JSProxy::SetPrototype(thread, proxy_handle, proto); + EXPECT_TRUE( + JSTaggedValue::SameValue(JSHandle(target_handle)->GetPrototype(thread), proto.GetTaggedValue())); + + // 2. handler has "SetPrototypeOf" + EcmaVM *vm = thread->GetEcmaVM(); + JSHandle env = vm->GetGlobalEnv(); + JSHandle func_key = thread->GlobalConstants()->GetHandledSetPrototypeOfString(); + JSHandle func_handle(factory->NewJSFunction(env, reinterpret_cast(HandlerSetPrototype))); + JSObject::SetProperty(thread, JSHandle(handler_handle), func_key, func_handle); + + JSHandle proxy_handle2 = JSProxy::ProxyCreate(thread, target_handle, handler_handle); + EXPECT_TRUE(*proxy_handle2 != nullptr); + EXPECT_FALSE(JSProxy::SetPrototype(thread, proxy_handle2, proto)); +} + +JSTaggedValue HandlerIsExtensible([[maybe_unused]] EcmaRuntimeCallInfo *argv) +{ + return JSTaggedValue(JSTaggedValue::False()); +} + +// ES6 9.5.3 [[IsExtensible]] ( ) +TEST_F(JSProxyTest, IsExtensible) +{ + ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); + // 1. handler has no "IsExtensible" + JSHandle dynclass(thread, JSObjectTestCreate(thread)); + JSHandle target_handle(factory->NewJSObjectByConstructor(JSHandle(dynclass), dynclass)); + EXPECT_TRUE(target_handle->IsECMAObject()); + + JSHandle handler_handle(factory->NewJSObjectByConstructor(JSHandle(dynclass), dynclass)); + EXPECT_TRUE(handler_handle->IsECMAObject()); + + JSHandle proxy_handle = JSProxy::ProxyCreate(thread, target_handle, handler_handle); + EXPECT_TRUE(*proxy_handle != nullptr); + + bool status1 = JSProxy::IsExtensible(thread, proxy_handle); + bool status2 = JSHandle::Cast(target_handle)->IsExtensible(); + EXPECT_TRUE(status1 == status2); + + // 2. handler has "IsExtensible" + EcmaVM *vm = thread->GetEcmaVM(); + JSHandle env = vm->GetGlobalEnv(); + JSHandle func_key = thread->GlobalConstants()->GetHandledIsExtensibleString(); + JSHandle func_handle(factory->NewJSFunction(env, reinterpret_cast(HandlerIsExtensible))); + JSObject::SetProperty(thread, JSHandle(handler_handle), func_key, func_handle); + + JSHandle proxy_handle2 = JSProxy::ProxyCreate(thread, target_handle, handler_handle); + EXPECT_TRUE(*proxy_handle2 != nullptr); + EXPECT_FALSE(JSProxy::IsExtensible(thread, proxy_handle2)); +} + +JSTaggedValue HandlerPreventExtensions([[maybe_unused]] EcmaRuntimeCallInfo *argv) +{ + return JSTaggedValue(JSTaggedValue::False()); +} + +// ES6 9.5.4 [[PreventExtensions]] ( ) +TEST_F(JSProxyTest, PreventExtensions) +{ + ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); + // 1. handler has no "PreventExtensions" + JSHandle dynclass(thread, JSObjectTestCreate(thread)); + JSHandle target_handle(factory->NewJSObjectByConstructor(JSHandle(dynclass), dynclass)); + EXPECT_TRUE(target_handle->IsECMAObject()); + + JSHandle handler_handle(factory->NewJSObjectByConstructor(JSHandle(dynclass), dynclass)); + EXPECT_TRUE(handler_handle->IsECMAObject()); + + JSHandle proxy_handle = JSProxy::ProxyCreate(thread, target_handle, handler_handle); + EXPECT_TRUE(*proxy_handle != nullptr); + + bool status1 = JSProxy::PreventExtensions(thread, proxy_handle); + EXPECT_TRUE(status1); + bool status2 = JSHandle::Cast(target_handle)->IsExtensible(); + EXPECT_FALSE(status2); + + // 2. handler has "PreventExtensions" + EcmaVM *vm = thread->GetEcmaVM(); + JSHandle env = vm->GetGlobalEnv(); + JSHandle func_key = thread->GlobalConstants()->GetHandledPreventExtensionsString(); + JSHandle func_handle( + factory->NewJSFunction(env, reinterpret_cast(HandlerPreventExtensions))); + JSObject::SetProperty(thread, JSHandle(handler_handle), func_key, func_handle); + + JSHandle proxy_handle2 = JSProxy::ProxyCreate(thread, target_handle, handler_handle); + EXPECT_TRUE(*proxy_handle2 != nullptr); + EXPECT_FALSE(JSProxy::PreventExtensions(thread, proxy_handle2)); +} + +JSTaggedValue HandlerHasProperty([[maybe_unused]] EcmaRuntimeCallInfo *argv) +{ + return JSTaggedValue(JSTaggedValue::False()); +} + +// ES6 9.5.7 [[HasProperty]] (P) +TEST_F(JSProxyTest, HasProperty) +{ + ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); + // 1. handler has no "HasProperty" + JSHandle dynclass(thread, JSObjectTestCreate(thread)); + JSHandle target_handle(factory->NewJSObjectByConstructor(JSHandle(dynclass), dynclass)); + EXPECT_TRUE(target_handle->IsECMAObject()); + + JSHandle key(factory->NewFromString("x")); + PropertyDescriptor desc(thread, JSHandle(thread, JSTaggedValue(1))); + JSObject::DefineOwnProperty(thread, JSHandle::Cast(target_handle), key, desc); + + JSHandle handler_handle(factory->NewJSObjectByConstructor(JSHandle(dynclass), dynclass)); + EXPECT_TRUE(handler_handle->IsECMAObject()); + + JSHandle proxy_handle = JSProxy::ProxyCreate(thread, target_handle, handler_handle); + EXPECT_TRUE(*proxy_handle != nullptr); + + EXPECT_TRUE(JSProxy::HasProperty(thread, proxy_handle, key)); + + // 2. handler has "HasProperty" + EcmaVM *vm = thread->GetEcmaVM(); + JSHandle env = vm->GetGlobalEnv(); + JSHandle func_key = thread->GlobalConstants()->GetHandledHasString(); + JSHandle func_handle(factory->NewJSFunction(env, reinterpret_cast(HandlerHasProperty))); + JSObject::SetProperty(thread, JSHandle(handler_handle), func_key, func_handle); + + JSHandle proxy_handle2 = JSProxy::ProxyCreate(thread, target_handle, handler_handle); + EXPECT_TRUE(*proxy_handle2 != nullptr); + EXPECT_FALSE(JSProxy::HasProperty(thread, proxy_handle2, key)); +} + +JSTaggedValue HandlerOwnPropertyKeys([[maybe_unused]] EcmaRuntimeCallInfo *argv) +{ + auto thread = argv->GetThread(); + ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); + JSHandle arr = factory->NewJSArray(); + return JSTaggedValue(arr.GetTaggedValue()); +} + +// ES6 9.5.12 [[OwnPropertyKeys]] () +TEST_F(JSProxyTest, OwnPropertyKeys) +{ + ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); + // 1. handler has no "OwnPropertyKeys" + JSHandle dynclass(thread, JSObjectTestCreate(thread)); + JSHandle target_handle(factory->NewJSObjectByConstructor(JSHandle(dynclass), dynclass)); + EXPECT_TRUE(target_handle->IsECMAObject()); + + JSHandle key(factory->NewFromString("x")); + PropertyDescriptor desc(thread, JSHandle(thread, JSTaggedValue(1))); + JSObject::DefineOwnProperty(thread, JSHandle::Cast(target_handle), key, desc); + + JSHandle handler_handle(factory->NewJSObjectByConstructor(JSHandle(dynclass), dynclass)); + EXPECT_TRUE(handler_handle->IsECMAObject()); + + JSHandle proxy_handle = JSProxy::ProxyCreate(thread, target_handle, handler_handle); + EXPECT_TRUE(*proxy_handle != nullptr); + JSHandle res = JSProxy::OwnPropertyKeys(thread, proxy_handle); + + EXPECT_TRUE(JSTaggedValue::SameValue(res->Get(0), key.GetTaggedValue())); + + // 2. handler has "OwnPropertyKeys" + // create new empty target so we wont throw TypeError at (18. - 21.) + JSHandle target_handle2(factory->NewJSObjectByConstructor(JSHandle(dynclass), dynclass)); + EXPECT_TRUE(target_handle2->IsECMAObject()); + EcmaVM *vm = thread->GetEcmaVM(); + JSHandle env = vm->GetGlobalEnv(); + JSHandle func_key = thread->GlobalConstants()->GetHandledOwnKeysString(); + JSHandle func_handle(factory->NewJSFunction(env, reinterpret_cast(HandlerOwnPropertyKeys))); + JSObject::SetProperty(thread, JSHandle(handler_handle), func_key, func_handle); + + JSHandle proxy_handle2 = JSProxy::ProxyCreate(thread, target_handle2, handler_handle); + EXPECT_TRUE(*proxy_handle2 != nullptr); + JSHandle res2 = JSProxy::OwnPropertyKeys(thread, proxy_handle2); + EXPECT_TRUE(res2->GetLength() == 0 || !JSTaggedValue::SameValue(res2->Get(0), key.GetTaggedValue())); +} + +JSTaggedValue HandlerCall([[maybe_unused]] EcmaRuntimeCallInfo *argv) +{ + return JSTaggedValue(JSTaggedValue::False()); +} +JSTaggedValue HandlerFunction([[maybe_unused]] EcmaRuntimeCallInfo *argv) +{ + return JSTaggedValue(JSTaggedValue::True()); +} + +// ES6 9.5.13 [[Call]] (thisArgument, argumentsList) +TEST_F(JSProxyTest, Call) +{ + ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); + JSHandle env = thread->GetEcmaVM()->GetGlobalEnv(); + // 1. handler has no "Call" + JSHandle dynclass(thread, JSObjectTestCreate(thread)); + JSHandle targetHandle(factory->NewJSFunction(env, reinterpret_cast(HandlerFunction))); + EXPECT_TRUE(targetHandle->IsECMAObject()); + + JSHandle handlerHandle(factory->NewJSObjectByConstructor(JSHandle(dynclass), dynclass)); + EXPECT_TRUE(handlerHandle->IsECMAObject()); + + JSHandle proxyHandle = JSProxy::ProxyCreate(thread, targetHandle, handlerHandle); + EXPECT_TRUE(*proxyHandle != nullptr); + JSTaggedValue res = + JSProxy::CallInternal(thread, proxyHandle, JSHandle::Cast(proxyHandle), 0, nullptr); + JSHandle taggedRes(thread, res); + + EXPECT_TRUE(JSTaggedValue::SameValue(taggedRes.GetTaggedValue(), JSTaggedValue::True())); + + // 2. handler has "Call" + JSHandle funcKey = thread->GlobalConstants()->GetHandledApplyString(); + JSHandle funcHandle(factory->NewJSFunction(env, reinterpret_cast(HandlerCall))); + JSObject::SetProperty(thread, JSHandle(handlerHandle), funcKey, funcHandle); + + JSHandle proxyHandle2 = JSProxy::ProxyCreate(thread, targetHandle, handlerHandle); + EXPECT_TRUE(*proxyHandle2 != nullptr); + JSTaggedValue res2 = + JSProxy::CallInternal(thread, proxyHandle2, JSHandle::Cast(proxyHandle2), 0, nullptr); + JSHandle taggedRes2(thread, res2); + + EXPECT_TRUE(JSTaggedValue::SameValue(taggedRes2.GetTaggedValue(), JSTaggedValue::False())); +} + +JSTaggedValue HandlerConstruct([[maybe_unused]] EcmaRuntimeCallInfo *argv) +{ + auto thread = argv->GetThread(); + ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); + JSHandle dynclass(thread, JSObjectTestCreate(thread)); + JSHandle obj(factory->NewJSObjectByConstructor(JSHandle(dynclass), dynclass)); + + JSHandle key(factory->NewFromString("x")); + PropertyDescriptor desc(thread, JSHandle(thread, JSTaggedValue(2))); + JSObject::DefineOwnProperty(argv->GetThread(), JSHandle::Cast(obj), key, desc); + return JSTaggedValue(obj.GetTaggedValue()); +} +JSTaggedValue HandlerConFunc([[maybe_unused]] EcmaRuntimeCallInfo *argv) +{ + auto thread = argv->GetThread(); + ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); + JSHandle dynclass(thread, JSObjectTestCreate(thread)); + JSHandle obj(factory->NewJSObjectByConstructor(JSHandle(dynclass), dynclass)); + + JSHandle key(factory->NewFromString("x")); + PropertyDescriptor desc(thread, JSHandle(thread, JSTaggedValue(1))); + JSObject::DefineOwnProperty(argv->GetThread(), JSHandle::Cast(obj), key, desc); + return JSTaggedValue(obj.GetTaggedValue()); +} + +// ES6 9.5.14 [[Construct]] ( argumentsList, newTarget) +TEST_F(JSProxyTest, Construct) +{ + ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); + JSHandle env = thread->GetEcmaVM()->GetGlobalEnv(); + // 1. handler has no "Construct" + JSHandle dynclass(thread, JSObjectTestCreate(thread)); + JSHandle targetHandle(factory->NewJSFunction(env, reinterpret_cast(HandlerConFunc))); + JSHandle::Cast(targetHandle)->GetJSHClass()->SetConstructor(true); + EXPECT_TRUE(targetHandle->IsECMAObject()); + + JSHandle handlerHandle(factory->NewJSObjectByConstructor(JSHandle(dynclass), dynclass)); + EXPECT_TRUE(handlerHandle->IsECMAObject()); + + JSHandle proxyHandle = JSProxy::ProxyCreate(thread, targetHandle, handlerHandle); + EXPECT_TRUE(*proxyHandle != nullptr); + JSTaggedValue res = JSProxy::ConstructInternal(thread, proxyHandle, 0, nullptr, targetHandle); + JSHandle taggedRes(thread, res); + JSHandle key(factory->NewFromCanBeCompressString("x")); + EXPECT_EQ(JSObject::GetProperty(thread, taggedRes, key).GetValue()->GetInt(), 1); + + // 2. handler has "Construct" + JSHandle funcKey = thread->GlobalConstants()->GetHandledProxyConstructString(); + JSHandle funcHandle(factory->NewJSFunction(env, reinterpret_cast(HandlerConstruct))); + JSObject::SetProperty(thread, JSHandle(handlerHandle), funcKey, funcHandle); + + JSHandle proxyHandle2 = JSProxy::ProxyCreate(thread, targetHandle, handlerHandle); + EXPECT_TRUE(*proxyHandle2 != nullptr); + JSTaggedValue res2 = JSProxy::ConstructInternal(thread, proxyHandle2, 0, nullptr, targetHandle); + JSHandle taggedRes2(thread, res2); + EXPECT_EQ(JSObject::GetProperty(thread, taggedRes2, key).GetValue()->GetInt(), 2); +} +} // namespace panda::test diff --git a/tests/runtime/common/js_serializer_test.cpp b/tests/runtime/common/js_serializer_test.cpp new file mode 100644 index 000000000..07499c604 --- /dev/null +++ b/tests/runtime/common/js_serializer_test.cpp @@ -0,0 +1,992 @@ +/* + * Copyright (c) 2021-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 + +#include "plugins/ecmascript/runtime/builtins/builtins_arraybuffer.h" +#include "plugins/ecmascript/runtime/ecma_language_context.h" +#include "plugins/ecmascript/runtime/ecma_vm.h" +#include "plugins/ecmascript/runtime/global_env.h" +#include "plugins/ecmascript/runtime/js_array.h" +#include "plugins/ecmascript/runtime/js_arraybuffer.h" +#include "plugins/ecmascript/runtime/js_hclass.h" +#include "plugins/ecmascript/runtime/js_regexp.h" +#include "plugins/ecmascript/runtime/js_serializer.h" +#include "plugins/ecmascript/runtime/js_set.h" +#include "plugins/ecmascript/runtime/js_thread.h" +#include "plugins/ecmascript/runtime/js_typed_array.h" +#include "plugins/ecmascript/runtime/linked_hash_table-inl.h" +#include "plugins/ecmascript/runtime/object_factory.h" +#include "plugins/ecmascript/tests/runtime/common/test_helper.h" + +using namespace panda::ecmascript; +using namespace panda::ecmascript::builtins; + +namespace panda::test { +class JSDeserializerTest { +public: + JSDeserializerTest() : ecmaVm(nullptr), scope(nullptr), thread(nullptr) {} + void Init() + { + JSRuntimeOptions options; + options.SetShouldLoadBootPandaFiles(false); + options.SetShouldInitializeIntrinsics(false); + options.SetBootIntrinsicSpaces({"ecmascript"}); + options.SetBootClassSpaces({"ecmascript"}); + options.SetRuntimeType("ecmascript"); + options.SetEnableForceGC(true); + ecmaVm = EcmaVM::Create(options); + ecmaVm->SetEnableForceGC(true); + EXPECT_TRUE(ecmaVm != nullptr) << "Cannot create Runtime"; + thread = ecmaVm->GetJSThread(); + scope = new EcmaHandleScope(thread); + } + void Destroy() + { + delete scope; + scope = nullptr; + ecmaVm->SetEnableForceGC(false); + thread->ClearException(); + [[maybe_unused]] bool success = EcmaVM::Destroy(ecmaVm); + EXPECT_TRUE(success) << "Cannot destroy Runtime"; + } + void JSSpecialValueTest(std::pair data) + { + Init(); + JSHandle jsTrue(thread, JSTaggedValue::True()); + JSHandle jsFalse(thread, JSTaggedValue::False()); + JSHandle jsUndefined(thread, JSTaggedValue::Undefined()); + JSHandle jsNull(thread, JSTaggedValue::Null()); + JSHandle jsHole(thread, JSTaggedValue::Hole()); + + JSDeserializer deserializer(thread, data.first, data.second); + JSHandle retTrue = deserializer.DeserializeJSTaggedValue(); + EXPECT_TRUE(JSTaggedValue::SameValue(jsTrue, retTrue)) << "Not same value for JS_TRUE"; + JSHandle retFalse = deserializer.DeserializeJSTaggedValue(); + EXPECT_TRUE(JSTaggedValue::SameValue(jsFalse, retFalse)) << "Not same value for JS_FALSE"; + JSHandle retUndefined = deserializer.DeserializeJSTaggedValue(); + JSHandle retNull = deserializer.DeserializeJSTaggedValue(); + JSHandle retHole = deserializer.DeserializeJSTaggedValue(); + + EXPECT_TRUE(JSTaggedValue::SameValue(jsUndefined, retUndefined)) << "Not same value for JS_UNDEFINED"; + EXPECT_TRUE(JSTaggedValue::SameValue(jsNull, retNull)) << "Not same value for JS_NULL"; + EXPECT_TRUE(JSTaggedValue::SameValue(jsHole, retHole)) << "Not same value for JS_HOLE"; + Destroy(); + } + + void JSPlainObjectTest(std::pair data) + { + Init(); + JSDeserializer deserializer(thread, data.first, data.second); + JSHandle objValue1 = deserializer.DeserializeJSTaggedValue(); + JSHandle objValue2 = deserializer.DeserializeJSTaggedValue(); + + JSHandle retObj1 = JSHandle::Cast(objValue1); + JSHandle retObj2 = JSHandle::Cast(objValue2); + EXPECT_FALSE(retObj1.IsEmpty()); + EXPECT_FALSE(retObj2.IsEmpty()); + + JSHandle array1 = JSObject::GetOwnPropertyKeys(thread, retObj1); + int length1 = array1->GetLength(); + EXPECT_EQ(length1, 4); // 4 : test case + double sum1 = 0.0; + for (int i = 0; i < length1; i++) { + JSHandle key(thread, array1->Get(i)); + double a = JSObject::GetProperty(thread, JSHandle(retObj1), key).GetValue()->GetNumber(); + sum1 += a; + } + EXPECT_EQ(sum1, 10); // 10 : test case + + JSHandle array2 = JSObject::GetOwnPropertyKeys(thread, retObj2); + int length2 = array2->GetLength(); + EXPECT_EQ(length2, 4); // 4 : test case + double sum2 = 0.0; + for (int i = 0; i < length2; i++) { + JSHandle key(thread, array2->Get(i)); + double a = JSObject::GetProperty(thread, JSHandle(retObj2), key).GetValue()->GetNumber(); + sum2 += a; + } + EXPECT_EQ(sum2, 26); // 26 : test case + Destroy(); + } + + void DescriptionTest(std::pair data) + { + Init(); + ObjectFactory *factory = ecmaVm->GetFactory(); + JSHandle key1(factory->NewFromCanBeCompressString("x")); + JSHandle key2(thread->GetEcmaVM()->GetFactory()->NewFromCanBeCompressString("y")); + JSHandle value1(thread, JSTaggedValue(1)); + JSHandle value2(thread, JSTaggedValue(2)); // 2 : test case + + JSDeserializer deserializer(thread, data.first, data.second); + JSHandle objValue = deserializer.DeserializeJSTaggedValue(); + JSHandle retObj = JSHandle::Cast(objValue); + + PropertyDescriptor desc3(thread); + PropertyDescriptor desc4(thread); + + JSHandle array1 = JSObject::GetOwnPropertyKeys(thread, retObj); + JSHandle retKey1(thread, array1->Get(0)); + JSHandle retKey2(thread, array1->Get(1)); + JSObject::GetOwnProperty(thread, retObj, retKey1, desc3); + JSObject::GetOwnProperty(thread, retObj, retKey2, desc4); + EXPECT_EQ(key1.GetTaggedValue().GetRawData(), retKey1.GetTaggedValue().GetRawData()); + + EXPECT_EQ(desc3.GetValue().GetTaggedValue().GetRawData(), value1.GetTaggedValue().GetRawData()); + EXPECT_TRUE(desc3.IsWritable()); + EXPECT_FALSE(desc3.IsEnumerable()); + EXPECT_TRUE(desc3.IsConfigurable()); + + EXPECT_EQ(desc4.GetValue().GetTaggedValue().GetRawData(), value2.GetTaggedValue().GetRawData()); + EXPECT_FALSE(desc4.IsWritable()); + EXPECT_TRUE(desc4.IsEnumerable()); + EXPECT_FALSE(desc4.IsConfigurable()); + Destroy(); + } + + void JSSetTest(std::pair data) + { + Init(); + ObjectFactory *factory = ecmaVm->GetFactory(); + JSHandle value1(thread, JSTaggedValue(7)); // 7 : test case + JSHandle value2(thread, JSTaggedValue(9)); // 9 : test case + JSHandle value3(factory->NewFromCanBeCompressString("x")); + JSHandle value4(factory->NewFromCanBeCompressString("y")); + + JSDeserializer deserializer(thread, data.first, data.second); + JSHandle setValue = deserializer.DeserializeJSTaggedValue(); + EXPECT_TRUE(!setValue.IsEmpty()); + JSHandle retSet = JSHandle::Cast(setValue); + JSHandle array = JSObject::GetOwnPropertyKeys(thread, JSHandle::Cast(retSet)); + int propertyLength = array->GetLength(); + EXPECT_EQ(propertyLength, 2); // 2 : test case + int sum = 0; + for (int i = 0; i < propertyLength; i++) { + JSHandle key(thread, array->Get(i)); + double a = JSObject::GetProperty(thread, JSHandle(retSet), key).GetValue()->GetNumber(); + sum += a; + } + EXPECT_EQ(sum, 16); // 16 : test case + + EXPECT_EQ(retSet->GetSize(), 4); // 4 : test case + EXPECT_TRUE(retSet->Has(value1.GetTaggedValue())); + EXPECT_TRUE(retSet->Has(value2.GetTaggedValue())); + EXPECT_TRUE(retSet->Has(value3.GetTaggedValue())); + EXPECT_TRUE(retSet->Has(value4.GetTaggedValue())); + Destroy(); + } + + void JSArrayTest(std::pair data) + { + Init(); + JSDeserializer deserializer(thread, data.first, data.second); + JSHandle arrayValue = deserializer.DeserializeJSTaggedValue(); + EXPECT_TRUE(!arrayValue.IsEmpty()); + + JSHandle retArray = JSHandle::Cast(arrayValue); + + JSHandle keyArray = JSObject::GetOwnPropertyKeys(thread, JSHandle(retArray)); + int propertyLength = keyArray->GetLength(); + EXPECT_EQ(propertyLength, 23); // 23 : test case + int sum = 0; + for (int i = 0; i < propertyLength; i++) { + JSHandle key(thread, keyArray->Get(i)); + double a = JSObject::GetProperty(thread, JSHandle(retArray), key).GetValue()->GetNumber(); + sum += a; + } + EXPECT_EQ(sum, 226); // 226 : test case + + // test get value from array + for (int i = 0; i < 20; i++) { // 20 : test case + JSHandle value = JSArray::FastGetPropertyByValue(thread, arrayValue, i); + EXPECT_EQ(i, value.GetTaggedValue().GetInt()); + } + Destroy(); + } + + void ObjectsPropertyReferenceTest(std::pair data) + { + Init(); + JSDeserializer deserializer(thread, data.first, data.second); + JSHandle objValue1 = deserializer.DeserializeJSTaggedValue(); + JSHandle objValue2 = deserializer.DeserializeJSTaggedValue(); + EXPECT_TRUE(!objValue1.IsEmpty()) << "[Empty] Deserialize obj1 fail"; + EXPECT_TRUE(!objValue2.IsEmpty()) << "[Empty] Deserialize obj2 fail"; + Destroy(); + } + + void EcmaStringTest1(std::pair data) + { + Init(); + const char *rawStr = "this is a test ecmaString"; + JSHandle ecmaString = thread->GetEcmaVM()->GetFactory()->NewFromCanBeCompressString(rawStr); + + JSDeserializer deserializer(thread, data.first, data.second); + JSHandle res = deserializer.DeserializeJSTaggedValue(); + EXPECT_TRUE(!res.IsEmpty()) << "[Empty] Deserialize ecmaString fail"; + EXPECT_TRUE(res->IsString()) << "[NotString] Deserialize ecmaString fail"; + JSHandle resEcmaString = JSHandle::Cast(res); + EXPECT_TRUE(ecmaString->GetHashcode() == resEcmaString->GetHashcode()) << "Not same HashCode"; + EXPECT_TRUE(EcmaString::StringsAreEqual(*ecmaString, *resEcmaString)) << "Not same EcmaString"; + Destroy(); + } + + void EcmaStringTest2(std::pair data) + { + Init(); + const char *rawStr = "ssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssss"\ + "sssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssss"\ + "sssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssss"\ + "ssssss"; + JSHandle ecmaString = thread->GetEcmaVM()->GetFactory()->NewFromCanBeCompressString(rawStr); + + JSDeserializer deserializer(thread, data.first, data.second); + JSHandle res = deserializer.DeserializeJSTaggedValue(); + EXPECT_TRUE(!res.IsEmpty()) << "[Empty] Deserialize ecmaString fail"; + EXPECT_TRUE(res->IsString()) << "[NotString] Deserialize ecmaString fail"; + JSHandle resEcmaString = JSHandle::Cast(res); + EXPECT_TRUE(ecmaString->GetHashcode() == resEcmaString->GetHashcode()) << "Not same HashCode"; + EXPECT_TRUE(EcmaString::StringsAreEqual(*ecmaString, *resEcmaString)) << "Not same EcmaString"; + Destroy(); + } + + void Int32Test(std::pair data) + { + Init(); + int32_t a = 64; + int32_t min = -2147483648; + int32_t b = -63; + JSDeserializer deserializer(thread, data.first, data.second); + JSHandle resA = deserializer.DeserializeJSTaggedValue(); + JSHandle resMin = deserializer.DeserializeJSTaggedValue(); + JSHandle resB = deserializer.DeserializeJSTaggedValue(); + EXPECT_TRUE(!resA.IsEmpty() && !resMin.IsEmpty() && !resB.IsEmpty()) << "[Empty] Deserialize Int32 fail"; + EXPECT_TRUE(resA->IsInt() && resMin->IsInt() && resB->IsInt()) << "[NotInt] Deserialize Int32 fail"; + EXPECT_TRUE(JSTaggedValue::ToInt32(thread, resA) == a) << "Not Same Value"; + EXPECT_TRUE(JSTaggedValue::ToInt32(thread, resMin) == min) << "Not Same Value"; + EXPECT_TRUE(JSTaggedValue::ToInt32(thread, resB) == b) << "Not Same Value"; + Destroy(); + } + + void DoubleTest(std::pair data) + { + Init(); + double a = 3.1415926535; + double b = -3.1415926535; + JSDeserializer deserializer(thread, data.first, data.second); + JSHandle resA = deserializer.DeserializeJSTaggedValue(); + JSHandle resB = deserializer.DeserializeJSTaggedValue(); + EXPECT_TRUE(!resA.IsEmpty() && !resB.IsEmpty()) << "[Empty] Deserialize double fail"; + EXPECT_TRUE(resA->IsDouble() && resB->IsDouble()) << "[NotInt] Deserialize double fail"; + EXPECT_TRUE(resA->GetDouble() == a) << "Not Same Value"; + EXPECT_TRUE(resB->GetDouble() == b) << "Not Same Value"; + Destroy(); + } + + void JSDateTest(std::pair data) + { + Init(); + double tm = 28 * 60 * 60 * 1000; // 28 * 60 * 60 * 1000 : test case + JSDeserializer deserializer(thread, data.first, data.second); + JSHandle res = deserializer.DeserializeJSTaggedValue(); + EXPECT_TRUE(!res.IsEmpty()) << "[Empty] Deserialize JSDate fail"; + EXPECT_TRUE(res->IsDate()) << "[NotJSDate] Deserialize JSDate fail"; + JSHandle resDate = JSHandle(res); + EXPECT_TRUE(resDate->GetTimeValue() == JSTaggedValue(tm)) << "Not Same Time Value"; + Destroy(); + } + + void JSMapTest(std::pair data, const JSHandle &originMap) + { + Init(); + JSDeserializer deserializer(thread, data.first, data.second); + JSHandle res = deserializer.DeserializeJSTaggedValue(); + EXPECT_TRUE(!res.IsEmpty()) << "[Empty] Deserialize JSMap fail"; + EXPECT_TRUE(res->IsJSMap()) << "[NotJSMap] Deserialize JSMap fail"; + JSHandle resMap = JSHandle::Cast(res); + EXPECT_TRUE(originMap->GetSize() == resMap->GetSize()) << "the map size Not equal"; + uint32_t resSize = resMap->GetSize(); + for (uint32_t i = 0; i < resSize; i++) { + JSHandle resKey(thread, resMap->GetKey(i)); + JSHandle resValue(thread, resMap->GetValue(i)); + JSHandle key(thread, originMap->GetKey(i)); + JSHandle value(thread, originMap->GetValue(i)); + + JSHandle resKeyStr = JSHandle::Cast(resKey); + JSHandle keyStr = JSHandle::Cast(key); + EXPECT_TRUE(EcmaString::StringsAreEqual(*resKeyStr, *keyStr)) << "Not same map key"; + EXPECT_TRUE(JSTaggedValue::ToInt32(thread, resValue) == JSTaggedValue::ToInt32(thread, value)) + << "Not same map value"; + } + Destroy(); + } + + void JSArrayBufferTest(std::pair data, + const JSHandle &originArrayBuffer, int32_t byteLength, const char *msg) + { + Init(); + JSDeserializer deserializer(thread, data.first, data.second); + JSHandle res = deserializer.DeserializeJSTaggedValue(); + EXPECT_TRUE(!res.IsEmpty()) << "[Empty] Deserialize JSArrayBuffer fail"; + EXPECT_TRUE(res->IsArrayBuffer()) << "[NotJSArrayBuffer] Deserialize JSArrayBuffer fail"; + JSHandle resJSArrayBuffer = JSHandle::Cast(res); + JSTaggedValue resTaggedLength = resJSArrayBuffer->GetArrayBufferByteLength(); + int32_t resByteLength = static_cast(resTaggedLength.GetInt()); + EXPECT_TRUE(resByteLength == byteLength) << "Not Same ByteLength"; // 10 : test case + + JSHandle bufferData(thread, originArrayBuffer->GetArrayBufferData()); + auto np = JSHandle::Cast(bufferData); + void *buffer = np->GetExternalPointer(); + ASSERT_NE(buffer, nullptr); + JSHandle resBufferData(thread, resJSArrayBuffer->GetArrayBufferData()); + JSHandle resNp = JSHandle::Cast(resBufferData); + void *resBuffer = resNp->GetExternalPointer(); + ASSERT_NE(resBuffer, nullptr); + + for (int32_t i = 0; i < resByteLength; i++) { + EXPECT_TRUE(static_cast(resBuffer)[i] == static_cast(buffer)[i]) << "Not Same Buffer"; + } + + if (msg != nullptr) { + if (memcpy_s(resBuffer, byteLength, msg, byteLength) != EOK) { + EXPECT_TRUE(false) << " memcpy error!"; + } + } + Destroy(); + } + + void JSRegexpTest(std::pair data) + { + Init(); + JSHandle pattern = thread->GetEcmaVM()->GetFactory()->NewFromCanBeCompressString("key2"); + JSHandle flags = thread->GetEcmaVM()->GetFactory()->NewFromCanBeCompressString("i"); + char buffer[] = "1234567"; // use char buffer to simulate byteCodeBuffer + uint32_t bufferSize = 7; + + JSDeserializer deserializer(thread, data.first, data.second); + JSHandle res = deserializer.DeserializeJSTaggedValue(); + EXPECT_TRUE(!res.IsEmpty()) << "[Empty] Deserialize JSRegExp fail"; + EXPECT_TRUE(res->IsJSRegExp()) << "[NotJSRegexp] Deserialize JSRegExp fail"; + JSHandle resJSRegexp(res); + + uint32_t resBufferSize = static_cast(resJSRegexp->GetLength().GetInt()); + EXPECT_TRUE(resBufferSize == bufferSize) << "Not Same Length"; + JSHandle originalSource(thread, resJSRegexp->GetOriginalSource()); + EXPECT_TRUE(originalSource->IsString()); + JSHandle originalFlags(thread, resJSRegexp->GetOriginalFlags()); + EXPECT_TRUE(originalFlags->IsString()); + EXPECT_TRUE(EcmaString::StringsAreEqual(*JSHandle(originalSource), *pattern)); + EXPECT_TRUE(EcmaString::StringsAreEqual(*JSHandle(originalFlags), *flags)); + + JSHandle resBufferData(thread, resJSRegexp->GetByteCodeBuffer()); + JSHandle resNp = JSHandle::Cast(resBufferData); + void *resBuffer = resNp->GetExternalPointer(); + ASSERT_NE(resBuffer, nullptr); + + for (uint32_t i = 0; i < resBufferSize; i++) { + EXPECT_TRUE(static_cast(resBuffer)[i] == buffer[i]) << "Not Same ByteCode"; + } + Destroy(); + } + + void TypedArrayTest(std::pair data, const JSHandle &originTypedArray) + { + Init(); + JSHandle originTypedArrayName(thread, originTypedArray->GetTypedArrayName()); + JSDeserializer deserializer(thread, data.first, data.second); + JSHandle res = deserializer.DeserializeJSTaggedValue(); + EXPECT_TRUE(!res.IsEmpty()) << "[Empty] Deserialize TypedArray fail"; + EXPECT_TRUE(res->IsJSInt8Array()) << "[NotJSInt8Array] Deserialize TypedArray fail"; + JSHandle resJSInt8Array = JSHandle::Cast(res); + + JSHandle typedArrayName(thread, resJSInt8Array->GetTypedArrayName()); + JSTaggedValue byteLength = resJSInt8Array->GetByteLength(); + JSTaggedValue byteOffset = resJSInt8Array->GetByteOffset(); + JSTaggedValue arrayLength = resJSInt8Array->GetArrayLength(); + JSHandle viewedArrayBuffer(thread, resJSInt8Array->GetViewedArrayBuffer()); + + EXPECT_TRUE(typedArrayName->IsString()); + EXPECT_TRUE(EcmaString::StringsAreEqual(*JSHandle(typedArrayName), + *JSHandle(originTypedArrayName))); + EXPECT_TRUE(byteLength.GetInt() == originTypedArray->GetByteLength().GetInt()) << "Not Same ByteLength"; + EXPECT_TRUE(byteOffset.GetInt() == originTypedArray->GetByteOffset().GetInt()) << "Not Same ByteOffset"; + EXPECT_TRUE(arrayLength.GetInt() == originTypedArray->GetArrayLength().GetInt()) << "Not Same ArrayLength"; + + // check arrayBuffer + JSHandle resJSArrayBuffer(viewedArrayBuffer); + JSHandle originArrayBuffer(thread, originTypedArray->GetViewedArrayBuffer()); + JSTaggedValue resTaggedLength = resJSArrayBuffer->GetArrayBufferByteLength(); + JSTaggedValue originTaggedLength = originArrayBuffer->GetArrayBufferByteLength(); + EXPECT_TRUE(resTaggedLength.GetInt() == originTaggedLength.GetInt()) << "Not same viewedBuffer length"; + JSHandle bufferData(thread, originArrayBuffer->GetArrayBufferData()); + JSHandle np = JSHandle::Cast(bufferData); + void *buffer = np->GetExternalPointer(); + JSHandle resBufferData(thread, resJSArrayBuffer->GetArrayBufferData()); + JSHandle resNp = JSHandle::Cast(resBufferData); + void *resBuffer = resNp->GetExternalPointer(); + for (int32_t i = 0; i < resTaggedLength.GetInt(); i++) { + EXPECT_TRUE(static_cast(resBuffer)[i] == static_cast(buffer)[i]) << "Not same viewedBuffer"; + } + Destroy(); + } + +private: + EcmaVM *ecmaVm = nullptr; + EcmaHandleScope *scope = nullptr; + JSThread *thread = nullptr; +}; + +class JSSerializerTest : public testing::Test { +public: + static void SetUpTestCase() + { + GTEST_LOG_(INFO) << "SetUpTestCase"; + } + + static void TearDownTestCase() + { + GTEST_LOG_(INFO) << "TearDownCase"; + } + + void SetUp() override + { + TestHelper::CreateEcmaVMWithScope(instance, thread, scope); + ecmaVm = EcmaVM::Cast(instance); + } + + void TearDown() override + { + TestHelper::DestroyEcmaVMWithScope(instance, scope); + } + + JSThread *thread {nullptr}; + PandaVM *instance {nullptr}; + EcmaVM *ecmaVm {nullptr}; + EcmaHandleScope *scope {nullptr}; +}; + +TEST_F(JSSerializerTest, SerializeJSSpecialValue) +{ + JSSerializer *serializer = new JSSerializer(thread); + JSHandle jsTrue(thread, JSTaggedValue::True()); + JSHandle jsFalse(thread, JSTaggedValue::False()); + JSHandle jsUndefined(thread, JSTaggedValue::Undefined()); + JSHandle jsNull(thread, JSTaggedValue::Null()); + JSHandle jsHole(thread, JSTaggedValue::Hole()); + serializer->SerializeJSTaggedValue(jsTrue); + serializer->SerializeJSTaggedValue(jsFalse); + serializer->SerializeJSTaggedValue(jsUndefined); + serializer->SerializeJSTaggedValue(jsNull); + serializer->SerializeJSTaggedValue(jsHole); + std::pair data = serializer->ReleaseBuffer(); + JSDeserializerTest jsDeserializerTest; + std::thread t1(&JSDeserializerTest::JSSpecialValueTest, jsDeserializerTest, data); + t1.join(); + delete serializer; +} + +TEST_F(JSSerializerTest, SerializeJSPlainObject) +{ + ObjectFactory *factory = ecmaVm->GetFactory(); + JSHandle obj1 = factory->NewEmptyJSObject(); + JSHandle obj2 = factory->NewEmptyJSObject(); + + JSHandle key1(factory->NewFromCanBeCompressString("2")); + JSHandle key2(factory->NewFromCanBeCompressString("3")); + JSHandle key3(factory->NewFromCanBeCompressString("x")); + JSHandle key4(factory->NewFromCanBeCompressString("y")); + JSHandle key5(factory->NewFromCanBeCompressString("a")); + JSHandle key6(factory->NewFromCanBeCompressString("b")); + JSHandle key7(factory->NewFromCanBeCompressString("5")); + JSHandle key8(factory->NewFromCanBeCompressString("6")); + JSHandle value1(thread, JSTaggedValue(1)); + JSHandle value2(thread, JSTaggedValue(2)); + JSHandle value3(thread, JSTaggedValue(3)); + JSHandle value4(thread, JSTaggedValue(4)); + JSHandle value5(thread, JSTaggedValue(5)); + JSHandle value6(thread, JSTaggedValue(6)); + JSHandle value7(thread, JSTaggedValue(7)); + JSHandle value8(thread, JSTaggedValue(8)); + + JSObject::SetProperty(thread, JSHandle(obj1), key1, value1); + JSObject::SetProperty(thread, JSHandle(obj1), key2, value2); + JSObject::SetProperty(thread, JSHandle(obj1), key3, value3); + JSObject::SetProperty(thread, JSHandle(obj1), key4, value4); + JSObject::SetProperty(thread, JSHandle(obj2), key5, value5); + JSObject::SetProperty(thread, JSHandle(obj2), key6, value6); + JSObject::SetProperty(thread, JSHandle(obj2), key7, value7); + JSObject::SetProperty(thread, JSHandle(obj2), key8, value8); + + JSSerializer *serializer = new JSSerializer(thread); + bool success1 = serializer->SerializeJSTaggedValue(JSHandle::Cast(obj1)); + bool success2 = serializer->SerializeJSTaggedValue(JSHandle::Cast(obj2)); + + EXPECT_TRUE(success1); + EXPECT_TRUE(success2); + std::pair data = serializer->ReleaseBuffer(); + JSDeserializerTest jsDeserializerTest; + std::thread t1(&JSDeserializerTest::JSPlainObjectTest, jsDeserializerTest, data); + t1.join(); + delete serializer; +} + +TEST_F(JSSerializerTest, TestSerializeDescription) +{ + ObjectFactory *factory = ecmaVm->GetFactory(); + JSHandle obj = factory->NewEmptyJSObject(); + + JSHandle key1(factory->NewFromCanBeCompressString("x")); + JSHandle key2(thread->GetEcmaVM()->GetFactory()->NewFromCanBeCompressString("y")); + + PropertyDescriptor desc1(thread); + JSHandle value1(thread, JSTaggedValue(1)); + desc1.SetValue(value1); + desc1.SetWritable(true); + desc1.SetEnumerable(false); + desc1.SetConfigurable(true); + JSObject::DefineOwnProperty(thread, obj, key1, desc1); + + PropertyDescriptor desc2(thread); + JSHandle value2(thread, JSTaggedValue(2)); + desc2.SetValue(value2); + desc2.SetWritable(false); + desc2.SetEnumerable(true); + desc2.SetConfigurable(false); + JSObject::DefineOwnProperty(thread, obj, key2, desc2); + + JSSerializer *serializer = new JSSerializer(thread); + bool success = serializer->SerializeJSTaggedValue(JSHandle::Cast(obj)); + EXPECT_TRUE(success); + std::pair data = serializer->ReleaseBuffer(); + JSDeserializerTest jsDeserializerTest; + std::thread t1(&JSDeserializerTest::DescriptionTest, jsDeserializerTest, data); + t1.join(); + delete serializer; +} + +TEST_F(JSSerializerTest, TestSerializeJSSet) +{ + ObjectFactory *factory = ecmaVm->GetFactory(); + JSHandle env = thread->GetEcmaVM()->GetGlobalEnv(); + + JSHandle constructor = env->GetBuiltinsSetFunction(); + JSHandle set = + JSHandle::Cast(factory->NewJSObjectByConstructor(JSHandle(constructor), constructor)); + JSHandle linkedSet = LinkedHashSet::Create(thread); + set->SetLinkedSet(thread, linkedSet); + // set property to set + JSHandle value1(thread, JSTaggedValue(7)); + JSHandle value2(thread, JSTaggedValue(9)); + JSHandle value3(factory->NewFromCanBeCompressString("x")); + JSHandle value4(factory->NewFromCanBeCompressString("y")); + + JSSet::Add(thread, set, value1); + JSSet::Add(thread, set, value2); + JSSet::Add(thread, set, value3); + JSSet::Add(thread, set, value4); + + // set property to object + JSHandle key1(factory->NewFromCanBeCompressString("5")); + JSHandle key2(factory->NewFromCanBeCompressString("6")); + + JSObject::SetProperty(thread, JSHandle(set), key1, value1); + JSObject::SetProperty(thread, JSHandle(set), key2, value2); + + JSSerializer *serializer = new JSSerializer(thread); + bool success = serializer->SerializeJSTaggedValue(JSHandle::Cast(set)); + EXPECT_TRUE(success); + std::pair data = serializer->ReleaseBuffer(); + JSDeserializerTest jsDeserializerTest; + std::thread t1(&JSDeserializerTest::JSSetTest, jsDeserializerTest, data); + t1.join(); + delete serializer; +} + +TEST_F(JSSerializerTest, TestSerializeJSArray) +{ + ObjectFactory *factory = ecmaVm->GetFactory(); + JSHandle array = factory->NewJSArray(); + + // set property to object + JSHandle key1(factory->NewFromCanBeCompressString("abasd")); + JSHandle key2(factory->NewFromCanBeCompressString("qweqwedasd")); + + JSHandle value1(thread, JSTaggedValue(7)); + JSHandle value2(thread, JSTaggedValue(9)); + + JSObject::SetProperty(thread, JSHandle(array), key1, value1); + JSObject::SetProperty(thread, JSHandle(array), key2, value2); + + // set value to array + array->SetArrayLength(thread, 20); + for (int i = 0; i < 20; i++) { + JSHandle data(thread, JSTaggedValue(i)); + JSArray::FastSetPropertyByValue(thread, JSHandle::Cast(array), i, data); + } + + JSSerializer *serializer = new JSSerializer(thread); + bool success = serializer->SerializeJSTaggedValue(JSHandle::Cast(array)); + EXPECT_TRUE(success); + std::pair data = serializer->ReleaseBuffer(); + JSDeserializerTest jsDeserializerTest; + std::thread t1(&JSDeserializerTest::JSArrayTest, jsDeserializerTest, data); + t1.join(); + delete serializer; +} + +// Test the situation that Objects' properties stores values that reference with each other +TEST_F(JSSerializerTest, TestObjectsPropertyReference) +{ + ObjectFactory *factory = ecmaVm->GetFactory(); + JSHandle obj1 = factory->NewEmptyJSObject(); + JSHandle obj2 = factory->NewEmptyJSObject(); + [[maybe_unused]] JSHandle obj3 = factory->NewEmptyJSObject(); + + JSHandle key1(factory->NewFromCanBeCompressString("abc")); + JSHandle key2(factory->NewFromCanBeCompressString("def")); + JSHandle key3(factory->NewFromCanBeCompressString("dgsdgf")); + JSHandle key4(factory->NewFromCanBeCompressString("qwjhrf")); + + JSHandle value3(thread, JSTaggedValue(10)); + JSHandle value4(thread, JSTaggedValue(5)); + + // set property to obj1 + JSObject::SetProperty(thread, JSHandle::Cast(obj1), key1, JSHandle::Cast(obj2)); + JSObject::SetProperty(thread, JSHandle::Cast(obj1), key3, value3); + + // set property to obj2 + JSObject::SetProperty(thread, JSHandle::Cast(obj2), key2, JSHandle::Cast(obj1)); + JSObject::SetProperty(thread, JSHandle::Cast(obj2), key4, value4); + + JSSerializer *serializer = new JSSerializer(thread); + bool success1 = serializer->SerializeJSTaggedValue(JSHandle::Cast(obj1)); + bool success2 = serializer->SerializeJSTaggedValue(JSHandle::Cast(obj2)); + EXPECT_TRUE(success1) << "Serialize obj1 fail"; + EXPECT_TRUE(success2) << "Serialize obj2 fail"; + std::pair data = serializer->ReleaseBuffer(); + JSDeserializerTest jsDeserializerTest; + std::thread t1(&JSDeserializerTest::ObjectsPropertyReferenceTest, jsDeserializerTest, data); + t1.join(); + delete serializer; +} + +TEST_F(JSSerializerTest, SerializeEcmaString1) +{ + const char *rawStr = "this is a test ecmaString"; + JSHandle ecmaString = thread->GetEcmaVM()->GetFactory()->NewFromCanBeCompressString(rawStr); + JSSerializer *serializer = new JSSerializer(thread); + bool success = serializer->SerializeJSTaggedValue(JSHandle(ecmaString)); + EXPECT_TRUE(success) << "Serialize EcmaString fail"; + std::pair data = serializer->ReleaseBuffer(); + JSDeserializerTest jsDeserializerTest; + std::thread t1(&JSDeserializerTest::EcmaStringTest1, jsDeserializerTest, data); + t1.join(); + delete serializer; +} + +TEST_F(JSSerializerTest, SerializeEcmaString2) +{ + const char *rawStr = "ssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssss"\ + "sssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssss"\ + "sssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssss"\ + "ssssss"; + JSHandle ecmaString = thread->GetEcmaVM()->GetFactory()->NewFromCanBeCompressString(rawStr); + JSSerializer *serializer = new JSSerializer(thread); + bool success = serializer->SerializeJSTaggedValue(JSHandle(ecmaString)); + EXPECT_TRUE(success) << "Serialize EcmaString fail"; + std::pair data = serializer->ReleaseBuffer(); + JSDeserializerTest jsDeserializerTest; + std::thread t1(&JSDeserializerTest::EcmaStringTest2, jsDeserializerTest, data); + t1.join(); + delete serializer; +} + +TEST_F(JSSerializerTest, SerializeInt32_t) +{ + JSSerializer *serializer = new JSSerializer(thread); + int32_t a = 64, min = -2147483648, b = -63; + JSTaggedValue aTag(a), minTag(min), bTag(b); + bool success1 = serializer->SerializeJSTaggedValue(JSHandle(thread, aTag)); + bool success2 = serializer->SerializeJSTaggedValue(JSHandle(thread, minTag)); + bool success3 = serializer->SerializeJSTaggedValue(JSHandle(thread, bTag)); + EXPECT_TRUE(success1 && success2 && success3) << "Serialize Int32 fail"; + std::pair data = serializer->ReleaseBuffer(); + JSDeserializerTest jsDeserializerTest; + std::thread t1(&JSDeserializerTest::Int32Test, jsDeserializerTest, data); + t1.join(); + delete serializer; +} + +TEST_F(JSSerializerTest, SerializeDouble) +{ + JSSerializer *serializer = new JSSerializer(thread); + double a = 3.1415926535, b = -3.1415926535; + JSTaggedValue aTag(a), bTag(b); + bool success1 = serializer->SerializeJSTaggedValue(JSHandle(thread, aTag)); + bool success2 = serializer->SerializeJSTaggedValue(JSHandle(thread, bTag)); + EXPECT_TRUE(success1 && success2) << "Serialize Double fail"; + std::pair data = serializer->ReleaseBuffer(); + JSDeserializerTest jsDeserializerTest; + std::thread t1(&JSDeserializerTest::DoubleTest, jsDeserializerTest, data); + t1.join(); + delete serializer; +} + +JSDate *JSDateCreate(EcmaVM *ecmaVM) +{ + ObjectFactory *factory = ecmaVM->GetFactory(); + JSHandle globalEnv = ecmaVM->GetGlobalEnv(); + JSHandle dateFunction = globalEnv->GetDateFunction(); + JSHandle dateObject = + JSHandle::Cast(factory->NewJSObjectByConstructor(JSHandle(dateFunction), dateFunction)); + return *dateObject; +} + +TEST_F(JSSerializerTest, SerializeDate) +{ + double tm = 28 * 60 * 60 * 1000; + JSHandle jsDate(thread, JSDateCreate(ecmaVm)); + jsDate->SetTimeValue(thread, JSTaggedValue(tm)); + JSSerializer *serializer = new JSSerializer(thread); + bool success = serializer->SerializeJSTaggedValue(JSHandle::Cast(jsDate)); + EXPECT_TRUE(success) << "Serialize JSDate fail"; + std::pair data = serializer->ReleaseBuffer(); + JSDeserializerTest jsDeserializerTest; + std::thread t1(&JSDeserializerTest::JSDateTest, jsDeserializerTest, data); + t1.join(); + delete serializer; +} + +JSMap *CreateMap(JSThread *thread) +{ + ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); + JSHandle env = thread->GetEcmaVM()->GetGlobalEnv(); + JSHandle constructor = env->GetBuiltinsMapFunction(); + JSHandle map = + JSHandle::Cast(factory->NewJSObjectByConstructor(JSHandle(constructor), constructor)); + JSHandle linkedMap = LinkedHashMap::Create(thread); + map->SetLinkedMap(thread, linkedMap); + return *map; +} + +TEST_F(JSSerializerTest, SerializeJSMap) +{ + JSHandle map(thread, CreateMap(thread)); + ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); + JSHandle key1(factory->NewFromCanBeCompressString("3")); + JSHandle value1(thread, JSTaggedValue(12345)); + JSMap::Set(thread, map, key1, value1); + JSHandle key2(factory->NewFromCanBeCompressString("key1")); + JSHandle value2(thread, JSTaggedValue(34567)); + JSMap::Set(thread, map, key2, value2); + + JSSerializer *serializer = new JSSerializer(thread); + bool success = serializer->SerializeJSTaggedValue(JSHandle::Cast(map)); + EXPECT_TRUE(success) << "Serialize JSMap fail"; + std::pair data = serializer->ReleaseBuffer(); + JSDeserializerTest jsDeserializerTest; + std::thread t1(&JSDeserializerTest::JSMapTest, jsDeserializerTest, data, map); + t1.join(); + delete serializer; +} + +JSArrayBuffer *CreateJSArrayBuffer(JSThread *thread) +{ + ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); + JSHandle env = thread->GetEcmaVM()->GetGlobalEnv(); + JSHandle target = env->GetArrayBufferFunction(); + JSHandle jsArrayBuffer = + JSHandle::Cast(factory->NewJSObjectByConstructor(JSHandle(target), target)); + return *jsArrayBuffer; +} + +TEST_F(JSSerializerTest, SerializeJSArrayBuffer) +{ + JSHandle jsArrayBuffer(thread, CreateJSArrayBuffer(thread)); + int32_t byteLength = 10; + thread->GetEcmaVM()->GetFactory()->NewJSArrayBufferData(jsArrayBuffer, byteLength); + jsArrayBuffer->SetArrayBufferByteLength(thread, JSTaggedValue(static_cast(byteLength))); + JSHandle obj = JSHandle(jsArrayBuffer); + BuiltinsArrayBuffer::SetValueInBuffer(obj.GetTaggedValue(), 1, DataViewType::UINT8, JSTaggedNumber(7), true); + BuiltinsArrayBuffer::SetValueInBuffer(obj.GetTaggedValue(), 3, DataViewType::UINT8, JSTaggedNumber(17), true); + + JSSerializer *serializer = new JSSerializer(thread); + bool success = serializer->SerializeJSTaggedValue(JSHandle::Cast(jsArrayBuffer)); + EXPECT_TRUE(success) << "Serialize JSArrayBuffer fail"; + std::pair data = serializer->ReleaseBuffer(); + JSDeserializerTest jsDeserializerTest; + std::thread t1(&JSDeserializerTest::JSArrayBufferTest, jsDeserializerTest, data, jsArrayBuffer, 10, nullptr); + t1.join(); + delete serializer; +} + +TEST_F(JSSerializerTest, SerializeJSArrayBufferShared) +{ + std::string msg = "hello world"; + int msgBufferLen = msg.length() + 1; + char* msgBuffer = new char[msgBufferLen] { 0 }; + if (memcpy_s(msgBuffer, msgBufferLen, msg.c_str(), msgBufferLen) != EOK) { + delete[] msgBuffer; + EXPECT_TRUE(false) << " memcpy error"; + } + + ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); + JSHandle jsArrayBuffer = factory->NewJSArrayBuffer(msgBuffer, msgBufferLen, nullptr, nullptr); + + JSSerializer *serializer = new JSSerializer(thread); + bool success = serializer->SerializeJSTaggedValue(JSHandle::Cast(jsArrayBuffer)); + EXPECT_TRUE(success) << "Serialize JSArrayBuffer fail"; + std::pair data = serializer->ReleaseBuffer(); + JSDeserializerTest jsDeserializerTest; + std::thread t1(&JSDeserializerTest::JSArrayBufferTest, jsDeserializerTest, data, jsArrayBuffer, 12, nullptr); + t1.join(); + delete serializer; +} + +TEST_F(JSSerializerTest, SerializeJSArrayBufferShared2) +{ + std::string msg = "hello world"; + int msgBufferLen = msg.length() + 1; + char* msgBuffer = new char[msgBufferLen] { 0 }; + if (memcpy_s(msgBuffer, msgBufferLen, msg.c_str(), msgBufferLen) != EOK) { + delete[] msgBuffer; + EXPECT_TRUE(false) << " memcpy error"; + } + + ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); + JSHandle jsArrayBuffer = factory->NewJSArrayBuffer(msgBuffer, msgBufferLen, nullptr, nullptr, true); + + JSSerializer *serializer = new JSSerializer(thread); + bool success = serializer->SerializeJSTaggedValue(JSHandle::Cast(jsArrayBuffer)); + EXPECT_TRUE(success) << "Serialize JSArrayBuffer fail"; + std::pair data = serializer->ReleaseBuffer(); + JSDeserializerTest jsDeserializerTest; + std::string changeStr = "world hello"; + std::thread t1(&JSDeserializerTest::JSArrayBufferTest, + jsDeserializerTest, data, jsArrayBuffer, 12, changeStr.c_str()); + t1.join(); + EXPECT_TRUE(strcmp(msgBuffer, "world hello") == 0) << "Serialize JSArrayBuffer fail"; + delete serializer; +} + +TEST_F(JSSerializerTest, SerializeJSRegExp) +{ + ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); + JSHandle env = thread->GetEcmaVM()->GetGlobalEnv(); + JSHandle target = env->GetRegExpFunction(); + JSHandle jsRegexp = + JSHandle::Cast(factory->NewJSObjectByConstructor(JSHandle(target), target)); + JSHandle pattern = thread->GetEcmaVM()->GetFactory()->NewFromCanBeCompressString("key2"); + JSHandle flags = thread->GetEcmaVM()->GetFactory()->NewFromCanBeCompressString("i"); + char buffer[] = "1234567"; // use char to simulate bytecode + uint32_t bufferSize = 7; + factory->NewJSRegExpByteCodeData(jsRegexp, static_cast(buffer), bufferSize); + jsRegexp->SetOriginalSource(thread, JSHandle(pattern)); + jsRegexp->SetOriginalFlags(thread, JSHandle(flags)); + + JSSerializer *serializer = new JSSerializer(thread); + bool success = serializer->SerializeJSTaggedValue(JSHandle::Cast(jsRegexp)); + EXPECT_TRUE(success) << "Serialize JSRegExp fail"; + std::pair data = serializer->ReleaseBuffer(); + JSDeserializerTest jsDeserializerTest; + std::thread t1(&JSDeserializerTest::JSRegexpTest, jsDeserializerTest, data); + t1.join(); + delete serializer; +} + +JSArrayBuffer *CreateTestJSArrayBuffer(JSThread *thread) +{ + JSHandle jsArrayBuffer(thread, CreateJSArrayBuffer(thread)); + int32_t byteLength = 10; + thread->GetEcmaVM()->GetFactory()->NewJSArrayBufferData(jsArrayBuffer, byteLength); + jsArrayBuffer->SetArrayBufferByteLength(thread, JSTaggedValue(static_cast(byteLength))); + JSHandle obj = JSHandle(jsArrayBuffer); + // 7 : test case + BuiltinsArrayBuffer::SetValueInBuffer(obj.GetTaggedValue(), 1, DataViewType::UINT8, JSTaggedNumber(7), true); + // 3, 17 : test case + BuiltinsArrayBuffer::SetValueInBuffer(obj.GetTaggedValue(), 3, DataViewType::UINT8, JSTaggedNumber(17), true); + return *jsArrayBuffer; +} + +TEST_F(JSSerializerTest, SerializeJSTypedArray) +{ + ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); + JSHandle env = thread->GetEcmaVM()->GetGlobalEnv(); + JSHandle target = env->GetInt8ArrayFunction(); + JSHandle int8Array = + JSHandle::Cast(factory->NewJSObjectByConstructor(JSHandle(target), target)); + JSHandle viewedArrayBuffer(thread, CreateTestJSArrayBuffer(thread)); + int8Array->SetViewedArrayBuffer(thread, viewedArrayBuffer); + int byteLength = 10; + int byteOffset = 0; + int arrayLength = (byteLength - byteOffset) / (sizeof(int8_t)); + int8Array->SetByteLength(thread, JSTaggedValue(byteLength)); + int8Array->SetByteOffset(thread, JSTaggedValue(byteOffset)); + int8Array->SetTypedArrayName(thread, thread->GlobalConstants()->GetInt8ArrayString()); + int8Array->SetArrayLength(thread, JSTaggedValue(arrayLength)); + JSSerializer *serializer = new JSSerializer(thread); + bool success = serializer->SerializeJSTaggedValue(JSHandle::Cast(int8Array)); + EXPECT_TRUE(success); + std::pair data = serializer->ReleaseBuffer(); + JSDeserializerTest jsDeserializerTest; + std::thread t1(&JSDeserializerTest::TypedArrayTest, jsDeserializerTest, data, int8Array); + t1.join(); + delete serializer; +} + +// not support function +TEST_F(JSSerializerTest, SerializeObjectWithFunction) +{ + ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); + JSHandle env = thread->GetEcmaVM()->GetGlobalEnv(); + JSHandle function = env->GetRegExpFunction(); + EXPECT_TRUE(function->IsJSFunction()); + JSHandle key(factory->NewFromCanBeCompressString("2")); + JSHandle obj = factory->NewEmptyJSObject(); + JSObject::SetProperty(thread, JSHandle(obj), key, function); + + JSSerializer *serializer = new JSSerializer(thread); + bool success = serializer->SerializeJSTaggedValue(JSHandle(obj)); + EXPECT_FALSE(success); + std::pair data = serializer->ReleaseBuffer(); + JSDeserializer deserializer(thread, data.first, data.second); + JSHandle ret = deserializer.DeserializeJSTaggedValue(); + EXPECT_TRUE(ret.IsEmpty()); + delete serializer; +} + +// not support symbol +TEST_F(JSSerializerTest, SerializeSymbolWithProperty) +{ + ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); + JSHandle jsSymbol = factory->NewJSSymbol(); + JSHandle key1(factory->NewFromCanBeCompressString("2")); + JSHandle key2(factory->NewFromCanBeCompressString("x")); + JSHandle value1(thread, JSTaggedValue(1)); + JSHandle value2(thread, JSTaggedValue(8)); + JSObject::SetProperty(thread, JSHandle(jsSymbol), key1, value1); + JSObject::SetProperty(thread, JSHandle(jsSymbol), key2, value2); + + JSSerializer *serializer = new JSSerializer(thread); + bool success = serializer->SerializeJSTaggedValue(JSHandle(jsSymbol)); + EXPECT_FALSE(success); + std::pair data = serializer->ReleaseBuffer(); + JSDeserializer deserializer(thread, data.first, data.second); + JSHandle ret = deserializer.DeserializeJSTaggedValue(); + EXPECT_TRUE(ret.IsEmpty()); + delete serializer; +} +} // namespace panda::test diff --git a/tests/runtime/common/js_set_iterator_test.cpp b/tests/runtime/common/js_set_iterator_test.cpp new file mode 100644 index 000000000..035d74223 --- /dev/null +++ b/tests/runtime/common/js_set_iterator_test.cpp @@ -0,0 +1,149 @@ +/* + * Copyright (c) 2021-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 "plugins/ecmascript/runtime/js_set_iterator.h" +#include "plugins/ecmascript/runtime/js_set.h" +#include "plugins/ecmascript/runtime/global_env.h" +#include "plugins/ecmascript/runtime/linked_hash_table-inl.h" +#include "plugins/ecmascript/runtime/object_factory.h" +#include "plugins/ecmascript/tests/runtime/common/test_helper.h" + +using namespace panda::ecmascript; + +namespace panda::test { +class JSSetIteratorTest : public testing::Test { +public: + static void SetUpTestCase() + { + GTEST_LOG_(INFO) << "SetUpTestCase"; + } + + static void TearDownTestCase() + { + GTEST_LOG_(INFO) << "TearDownCase"; + } + + void SetUp() override + { + TestHelper::CreateEcmaVMWithScope(instance, thread, scope); + } + + void TearDown() override + { + TestHelper::DestroyEcmaVMWithScope(instance, scope); + } + ecmascript::EcmaHandleScope *scope {nullptr}; + PandaVM *instance {nullptr}; + JSThread *thread {nullptr}; +}; + +JSSet *CreateSet(JSThread *thread) +{ + ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); + JSHandle env = thread->GetEcmaVM()->GetGlobalEnv(); + + JSHandle constructor = env->GetBuiltinsSetFunction(); + JSHandle set = + JSHandle::Cast(factory->NewJSObjectByConstructor(JSHandle(constructor), constructor)); + JSHandle hashSet = LinkedHashSet::Create(thread); + set->SetLinkedSet(thread, hashSet); + return JSSet::Cast(set.GetTaggedValue().GetTaggedObject()); +} + +/* + * Feature: JSSetIterator + * Function: CreateSetIterator + * SubFunction: GetIterationKind,GetNextIndex + * FunctionPoints: Create SetIterator + * CaseDescription: Check whether the returned value through "CreateSetIterator" function is within expectations. + */ +TEST_F(JSSetIteratorTest, CreateSetIterator) +{ + JSHandle jsSet(thread, CreateSet(thread)); + EXPECT_TRUE(*jsSet != nullptr); + + JSHandle setIteratorValue1 = + JSSetIterator::CreateSetIterator(thread, JSHandle(jsSet), IterationKind::KEY); + + EXPECT_EQ(setIteratorValue1->IsJSSetIterator(), true); + JSHandle setIterator1(setIteratorValue1); + EXPECT_EQ(JSTaggedValue::SameValue(setIterator1->GetIteratedSet(), jsSet->GetLinkedSet()), true); + EXPECT_EQ(setIterator1->GetNextIndex().GetInt(), 0); + + JSTaggedValue iterationKind1 = setIterator1->GetIterationKind(); + EXPECT_EQ(JSTaggedValue::SameValue(iterationKind1, JSTaggedValue(static_cast(IterationKind::KEY))), true); + + JSHandle setIteratorValue2 = + JSSetIterator::CreateSetIterator(thread, JSHandle(jsSet), IterationKind::VALUE); + + EXPECT_EQ(setIteratorValue2->IsJSSetIterator(), true); + JSHandle setIterator2(setIteratorValue2); + EXPECT_EQ(JSTaggedValue::SameValue(setIterator2->GetIteratedSet(), jsSet->GetLinkedSet()), true); + EXPECT_EQ(setIterator2->GetNextIndex().GetInt(), 0); + + JSTaggedValue iterationKind2 = setIterator2->GetIterationKind(); + EXPECT_EQ(JSTaggedValue::SameValue(iterationKind2, JSTaggedValue(static_cast(IterationKind::VALUE))), true); +} + +/* + * Feature: JSSetIterator + * Function: next + * SubFunction: IteratorValue + * FunctionPoints: get the next value in setiterator + * CaseDescription: Check whether the return value obtained by the function is the next value in the array element. + */ +TEST_F(JSSetIteratorTest, Next) +{ + JSHandle jsSet(thread, CreateSet(thread)); + EXPECT_TRUE(*jsSet != nullptr); + + for (int i = 0; i < 3; i++) { + JSHandle key(thread, JSTaggedValue(i)); + JSSet::Add(thread, jsSet, key); + } + + // set IterationKind(key or value) + JSHandle setIteratorValue = + JSSetIterator::CreateSetIterator(thread, JSHandle(jsSet), IterationKind::KEY); + JSHandle setIterator(setIteratorValue); + + auto ecmaRuntimeCallInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread, JSTaggedValue::Undefined(), 6); + ecmaRuntimeCallInfo->SetFunction(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetThis(setIteratorValue.GetTaggedValue()); + ecmaRuntimeCallInfo->SetCallArg(0, JSTaggedValue::Undefined()); + [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread, ecmaRuntimeCallInfo.get()); + + JSTaggedValue result1 = JSSetIterator::Next(ecmaRuntimeCallInfo.get()); + EXPECT_EQ(setIterator->GetNextIndex().GetInt(), 1); + JSHandle resultObj1(thread, result1); + EXPECT_EQ(0, JSIterator::IteratorValue(thread, resultObj1)->GetInt()); + + JSTaggedValue result2 = JSSetIterator::Next(ecmaRuntimeCallInfo.get()); + EXPECT_EQ(setIterator->GetNextIndex().GetInt(), 2); + JSHandle resultObj2(thread, result2); + EXPECT_EQ(1, JSIterator::IteratorValue(thread, resultObj2)->GetInt()); + + JSTaggedValue result3 = JSSetIterator::Next(ecmaRuntimeCallInfo.get()); + EXPECT_EQ(setIterator->GetNextIndex().GetInt(), 3); + JSHandle resultObj3(thread, result3); + EXPECT_EQ(2, JSIterator::IteratorValue(thread, resultObj3)->GetInt()); + + JSTaggedValue result4 = JSSetIterator::Next(ecmaRuntimeCallInfo.get()); + JSHandle resultObj4(thread, result4); + EXPECT_EQ(JSIterator::IteratorValue(thread, resultObj4).GetTaggedValue(), JSTaggedValue::Undefined()); + + TestHelper::TearDownFrame(thread, prev); +} +} // namespace panda::test diff --git a/tests/runtime/common/js_set_test.cpp b/tests/runtime/common/js_set_test.cpp new file mode 100644 index 000000000..ce41c922c --- /dev/null +++ b/tests/runtime/common/js_set_test.cpp @@ -0,0 +1,151 @@ +/* + * Copyright (c) 2021-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 "gtest/gtest.h" +#include "test_helper.h" +#include "include/coretypes/dyn_objects.h" +#include "plugins/ecmascript/runtime/linked_hash_table-inl.h" +#include "plugins/ecmascript/runtime/ecma_string.h" +#include "include/coretypes/tagged_value.h" +#include "include/runtime.h" +#include "include/runtime_options.h" +#include "plugins/ecmascript/runtime/tagged_hash_table-inl.h" +#include "plugins/ecmascript/runtime/js_object.h" +#include "plugins/ecmascript/runtime/js_handle.h" +#include "plugins/ecmascript/runtime/object_factory.h" +#include "plugins/ecmascript/runtime/ecma_vm.h" +#include "plugins/ecmascript/runtime/js_set.h" +#include "plugins/ecmascript/runtime/js_function.h" +#include "plugins/ecmascript/runtime/global_env.h" +#include "plugins/ecmascript/runtime/js_iterator.h" +#include "plugins/ecmascript/runtime/js_set_iterator.h" + +using namespace panda; + +using namespace panda::ecmascript; +using namespace panda::coretypes; + +namespace panda::test { +class JSSetTest : public testing::Test { +public: + void SetUp() override + { + TestHelper::CreateEcmaVMWithScope(instance, thread, scope); + } + + void TearDown() override + { + TestHelper::DestroyEcmaVMWithScope(instance, scope); + } + + PandaVM *instance {nullptr}; + EcmaHandleScope *scope {nullptr}; + JSThread *thread; + +protected: + JSSet *CreateSet() + { + ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); + JSHandle env = thread->GetEcmaVM()->GetGlobalEnv(); + + JSHandle constructor = env->GetBuiltinsSetFunction(); + JSHandle set = + JSHandle::Cast(factory->NewJSObjectByConstructor(JSHandle(constructor), constructor)); + JSHandle hashSet = LinkedHashSet::Create(thread); + set->SetLinkedSet(thread, hashSet); + return JSSet::Cast(set.GetTaggedValue().GetTaggedObject()); + } +}; + +TEST_F(JSSetTest, SetCreate) +{ + JSSet *set = CreateSet(); + EXPECT_TRUE(set != nullptr); +} + +TEST_F(JSSetTest, AddAndHas) +{ + ObjectFactory *factory_ = thread->GetEcmaVM()->GetFactory(); + // create js_set + JSHandle set(thread, CreateSet()); + + // + JSHandle key(factory_->NewFromString("key")); + JSSet::Add(thread, set, key); + EXPECT_TRUE(set->Has(key.GetTaggedValue())); +} + +TEST_F(JSSetTest, DeleteAndGet) +{ + ObjectFactory *factory_ = thread->GetEcmaVM()->GetFactory(); + // create js_set + JSHandle set(thread, CreateSet()); + + // add 40 keys + char key_array[] = "key0"; + for (int i = 0; i < 40; i++) { + key_array[3] = '1' + i; + JSHandle key(factory_->NewFromString(key_array)); + JSSet::Add(thread, set, key); + EXPECT_TRUE(set->Has(key.GetTaggedValue())); + } + EXPECT_EQ(set->GetSize(), 40); + // whether js_set has delete key + key_array[3] = '1' + 8; + JSHandle delete_key(factory_->NewFromString(key_array)); + JSSet::Delete(thread, set, delete_key); + EXPECT_FALSE(set->Has(delete_key.GetTaggedValue())); + EXPECT_EQ(set->GetSize(), 39); +} + +TEST_F(JSSetTest, Iterator) +{ + ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); + JSHandle set(thread, CreateSet()); + for (int i = 0; i < 5; i++) { + JSHandle key(thread, JSTaggedValue(i)); + JSSet::Add(thread, set, key); + } + + JSHandle key_iter(factory->NewJSSetIterator(set, IterationKind::KEY)); + JSHandle value_iter(factory->NewJSSetIterator(set, IterationKind::VALUE)); + + JSHandle key_result0 = JSIterator::IteratorStep(thread, key_iter); + JSHandle value_result0 = JSIterator::IteratorStep(thread, value_iter); + + EXPECT_EQ(0, JSIterator::IteratorValue(thread, key_result0)->GetInt()); + EXPECT_EQ(0, JSIterator::IteratorValue(thread, value_result0)->GetInt()); + + JSHandle key_result1 = JSIterator::IteratorStep(thread, key_iter); + EXPECT_EQ(1, JSIterator::IteratorValue(thread, key_result1)->GetInt()); + + for (int i = 0; i < 3; i++) { + JSHandle key(thread, JSTaggedValue(i)); + JSSet::Delete(thread, set, key); + } + + JSHandle key_result2 = JSIterator::IteratorStep(thread, key_iter); + EXPECT_EQ(3, JSIterator::IteratorValue(thread, key_result2)->GetInt()); + JSHandle key_result3 = JSIterator::IteratorStep(thread, key_iter); + EXPECT_EQ(4, JSIterator::IteratorValue(thread, key_result3)->GetInt()); + JSHandle key(thread, JSTaggedValue(5)); + JSSet::Add(thread, set, key); + JSHandle key_result4 = JSIterator::IteratorStep(thread, key_iter); + EXPECT_EQ(5, JSIterator::IteratorValue(thread, key_result4)->GetInt()); + JSHandle key_result5 = JSIterator::IteratorStep(thread, key_iter); + EXPECT_EQ(JSTaggedValue::False(), key_result5.GetTaggedValue()); +} + +} // namespace panda::test diff --git a/tests/runtime/common/js_symbol_test.cpp b/tests/runtime/common/js_symbol_test.cpp new file mode 100644 index 000000000..40c2819cf --- /dev/null +++ b/tests/runtime/common/js_symbol_test.cpp @@ -0,0 +1,54 @@ +/* + * Copyright (c) 2021-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 "gtest/gtest.h" +#include "test_helper.h" +#include "plugins/ecmascript/runtime/ecma_string.h" +#include "plugins/ecmascript/runtime/js_object.h" +#include "plugins/ecmascript/runtime/js_hclass.h" +#include "plugins/ecmascript/runtime/js_function.h" +#include "plugins/ecmascript/runtime/js_tagged_value-inl.h" +#include "include/runtime.h" +#include "plugins/ecmascript/runtime/object_factory.h" + +using DynClass = panda::coretypes::DynClass; +using JSSymbol = panda::ecmascript::JSSymbol; +using JSTaggedValue = panda::ecmascript::JSTaggedValue; +using LexicalEnv = panda::ecmascript::LexicalEnv; +using JSHClass = panda::ecmascript::JSHClass; +using ObjectFactory = panda::ecmascript::ObjectFactory; + +template +using JSHandle = panda::ecmascript::JSHandle; + +namespace panda::test { +class JSSymbolTest : public testing::Test { +public: + void SetUp() override + { + TestHelper::CreateEcmaVMWithScope(instance, thread, scope); + } + + void TearDown() override + { + TestHelper::DestroyEcmaVMWithScope(instance, scope); + } + + PandaVM *instance {nullptr}; + ecmascript::EcmaHandleScope *scope {nullptr}; + JSThread *thread; +}; + +} // namespace panda::test diff --git a/tests/runtime/common/js_tagged_queue_test.cpp b/tests/runtime/common/js_tagged_queue_test.cpp new file mode 100644 index 000000000..b50a1c8ea --- /dev/null +++ b/tests/runtime/common/js_tagged_queue_test.cpp @@ -0,0 +1,110 @@ +/* + * Copyright (c) 2021-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 "gtest/gtest.h" +#include "test_helper.h" + +#include "plugins/ecmascript/runtime/tagged_queue.h" +#include "plugins/ecmascript/runtime/tagged_queue-inl.h" +#include "include/runtime.h" +#include "plugins/ecmascript/runtime/ecma_vm.h" +#include "plugins/ecmascript/runtime/js_handle.h" +#include "plugins/ecmascript/runtime/object_factory.h" +#include "plugins/ecmascript/runtime/global_env.h" +#include "plugins/ecmascript/runtime/snapshot/mem/snapshot.h" + +using namespace panda::ecmascript; +using namespace panda::coretypes; + +namespace panda::test { +class JSTaggedQueueTest : public testing::Test { +public: + void SetUp() override + { + TestHelper::CreateEcmaVMWithScope(instance, thread, scope); + } + + void TearDown() override + { + TestHelper::DestroyEcmaVMWithScope(instance, scope); + } + + PandaVM *instance {nullptr}; + ecmascript::EcmaHandleScope *scope {nullptr}; + JSThread *thread; +}; + +TEST_F(JSTaggedQueueTest, Create) +{ + JSHandle queue = thread->GetEcmaVM()->GetFactory()->NewTaggedQueue(0); + EXPECT_TRUE(*queue != nullptr); + EXPECT_TRUE(queue->Empty()); + EXPECT_TRUE(queue->Size() == 0); + EXPECT_TRUE(queue->Front() == JSTaggedValue::Hole()); + EXPECT_TRUE(queue->Back() == JSTaggedValue::Hole()); +} + +TEST_F(JSTaggedQueueTest, PopAndPush) +{ + JSHandle queue = thread->GetEcmaVM()->GetFactory()->NewTaggedQueue(0); + EXPECT_TRUE(queue->Empty()); + + JSHandle queue2(thread, + TaggedQueue::Push(thread, queue, JSHandle(thread, JSTaggedValue(0)))); + EXPECT_FALSE(queue2->Empty()); + EXPECT_EQ(queue2->Size(), 1); + EXPECT_EQ(queue2->Front(), JSTaggedValue(0)); + EXPECT_EQ(queue2->Back(), JSTaggedValue(0)); + + JSHandle queue3(thread, + TaggedQueue::Push(thread, queue2, JSHandle(thread, JSTaggedValue(1)))); + EXPECT_EQ(queue3->Size(), 2); + EXPECT_EQ(queue3->Front(), JSTaggedValue(0)); + EXPECT_EQ(queue3->Back(), JSTaggedValue(1)); + EXPECT_NE(queue3.GetTaggedValue(), queue2.GetTaggedValue()); + + JSHandle queue4(thread, + TaggedQueue::Push(thread, queue3, JSHandle(thread, JSTaggedValue(2)))); + EXPECT_EQ(queue4->Size(), 3); + EXPECT_EQ(queue4->Front(), JSTaggedValue(0)); + EXPECT_EQ(queue4->Back(), JSTaggedValue(2)); + EXPECT_NE(queue4.GetTaggedValue(), queue3.GetTaggedValue()); + + JSHandle queue5(thread, + TaggedQueue::Push(thread, queue4, JSHandle(thread, JSTaggedValue(3)))); + EXPECT_EQ(queue5->Size(), 4); + EXPECT_EQ(queue5->Front(), JSTaggedValue(0)); + EXPECT_EQ(queue5->Back(), JSTaggedValue(3)); + EXPECT_NE(queue5.GetTaggedValue(), queue4.GetTaggedValue()); + + EXPECT_EQ(queue5->Pop(thread), JSTaggedValue(0)); + EXPECT_EQ(queue5->Size(), 3); + EXPECT_EQ(queue5->Front(), JSTaggedValue(1)); + + EXPECT_EQ(queue5->Pop(thread), JSTaggedValue(1)); + EXPECT_EQ(queue5->Size(), 2); + EXPECT_EQ(queue5->Front(), JSTaggedValue(2)); + + EXPECT_EQ(queue5->Pop(thread), JSTaggedValue(2)); + EXPECT_EQ(queue5->Size(), 1); + EXPECT_EQ(queue5->Front(), JSTaggedValue(3)); + + EXPECT_EQ(queue5->Pop(thread), JSTaggedValue(3)); + EXPECT_EQ(queue5->Size(), 0); + EXPECT_EQ(queue5->Front(), JSTaggedValue::Hole()); + EXPECT_TRUE(queue5->Empty()); +} + +} // namespace panda::test diff --git a/tests/runtime/common/js_typed_array_test.cpp b/tests/runtime/common/js_typed_array_test.cpp new file mode 100644 index 000000000..bf7ab7529 --- /dev/null +++ b/tests/runtime/common/js_typed_array_test.cpp @@ -0,0 +1,1344 @@ +/* + * Copyright (c) 2021-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 "plugins/ecmascript/runtime/base/typed_array_helper-inl.h" +#include "plugins/ecmascript/tests/runtime/common/test_helper.h" + +using namespace panda::ecmascript; + +namespace panda::test { +class JSTypedArrayTest : public testing::Test { +public: + static void SetUpTestCase() + { + GTEST_LOG_(INFO) << "SetUpTestCase"; + } + + static void TearDownTestCase() + { + GTEST_LOG_(INFO) << "TearDownCase"; + } + + void SetUp() override + { + TestHelper::CreateEcmaVMWithScope(instance, thread, scope); + } + + void TearDown() override + { + TestHelper::DestroyEcmaVMWithScope(instance, scope); + } + PandaVM *instance {nullptr}; + EcmaHandleScope *scope {nullptr}; + JSThread *thread {nullptr}; + + const CVector cVecJSType {JSType::JS_INT8_ARRAY, JSType::JS_UINT8_ARRAY, JSType::JS_UINT8_CLAMPED_ARRAY, + JSType::JS_INT16_ARRAY, JSType::JS_UINT16_ARRAY, JSType::JS_INT32_ARRAY, + JSType::JS_UINT32_ARRAY, JSType::JS_FLOAT32_ARRAY, JSType::JS_FLOAT64_ARRAY}; + + // CVector pushed with JSTaggedValue made from compatible input value for the JSType + const CVector cVecHandleTagValValueForTypedArray { + // Use "(S)(...)" cast to make v in "JSTaggedValue(T v) : coretypes::TaggedValue(v) {}" compatible with S + JSTaggedValue((int8_t)(-111)), JSTaggedValue((uint8_t)(222)), JSTaggedValue((uint8_t)(222)), + JSTaggedValue((int16_t)(-31111)), JSTaggedValue((uint16_t)(61111)), + // int32 : -2147483648->2147483647, uint32 : 0->4294967295 + JSTaggedValue((int32_t)(2111111111)), JSTaggedValue((uint32_t)(4111111111)), JSTaggedValue((float)(4321.1234)), + JSTaggedValue((double)(987654321.123456789))}; +}; + +JSHandle CreateNumberTypedArray(JSThread *thread, JSType jsType) +{ + JSHandle env = thread->GetEcmaVM()->GetGlobalEnv(); + ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); + + JSHandle handleTagValFunc = env->GetInt8ArrayFunction(); + switch (jsType) { + case JSType::JS_INT8_ARRAY: + break; + case JSType::JS_UINT8_ARRAY: + handleTagValFunc = env->GetUint8ArrayFunction(); + break; + case JSType::JS_UINT8_CLAMPED_ARRAY: + handleTagValFunc = env->GetUint8ClampedArrayFunction(); + break; + case JSType::JS_INT16_ARRAY: + handleTagValFunc = env->GetInt16ArrayFunction(); + break; + case JSType::JS_UINT16_ARRAY: + handleTagValFunc = env->GetUint16ArrayFunction(); + break; + case JSType::JS_INT32_ARRAY: + handleTagValFunc = env->GetInt32ArrayFunction(); + break; + case JSType::JS_UINT32_ARRAY: + handleTagValFunc = env->GetUint32ArrayFunction(); + break; + case JSType::JS_FLOAT32_ARRAY: + handleTagValFunc = env->GetFloat32ArrayFunction(); + break; + case JSType::JS_FLOAT64_ARRAY: + handleTagValFunc = env->GetFloat64ArrayFunction(); + break; + default: + ASSERT_PRINT(false, "the second argument is a wrong JSType for CreateNumberTypedArray function"); + break; + } + + return JSHandle::Cast( + factory->NewJSObjectByConstructor(JSHandle::Cast(handleTagValFunc), handleTagValFunc)); +} + +/* + * Feature: JSTypedArray + * Function: ToPropKey + * SubFunction: EcmaString::GetCString + * FunctionPoints: TaggedType Signs To EcmaString Signs + * CaseDescription: Check whether the EcmaStrings transformed through calling ToPropKey function from TaggedTypes are + * within expectations. + */ +TEST_F(JSTypedArrayTest, ToPropKey_001) +{ + JSHandle handleUndefined(thread, JSTaggedValue::Undefined()); + JSHandle handleHole(thread, JSTaggedValue::Hole()); + JSHandle hnadleTagValEcmaStrPropKeyTo1 = JSTypedArray::ToPropKey(thread, handleUndefined); + JSHandle hnadleTagValEcmaStrPropKeyTo2 = JSTypedArray::ToPropKey(thread, handleHole); + JSHandle handleEcmaStrPropKeyTo1 = JSHandle::Cast(hnadleTagValEcmaStrPropKeyTo1); + JSHandle handleEcmaStrPropKeyTo2 = JSHandle::Cast(hnadleTagValEcmaStrPropKeyTo2); + EXPECT_NE(0, sizeof(handleUndefined)); + EXPECT_NE(0, sizeof(handleHole)); + std::unique_ptr uniCharArrTo1(handleEcmaStrPropKeyTo1->GetCString()); + std::unique_ptr uniCharArrTo2(handleEcmaStrPropKeyTo2->GetCString()); + EXPECT_EQ(uniCharArrTo1[0], 'u'); + EXPECT_EQ(uniCharArrTo1[1], 'n'); + EXPECT_EQ(uniCharArrTo1[2], 'd'); + EXPECT_EQ(uniCharArrTo1[3], 'e'); + EXPECT_EQ(uniCharArrTo1[4], 'f'); + EXPECT_EQ(uniCharArrTo1[5], 'i'); + EXPECT_EQ(uniCharArrTo1[6], 'n'); + EXPECT_EQ(uniCharArrTo1[7], 'e'); + EXPECT_EQ(uniCharArrTo1[8], 'd'); + EXPECT_EQ(uniCharArrTo1[9], 0); // "undefined" + EXPECT_EQ(uniCharArrTo2[0], 0); // "" +} + +/* + * Feature: JSTypedArray + * Function: ToPropKey + * SubFunction: EcmaString::GetCString + * FunctionPoints: Number Signs To EcmaString Signs + * CaseDescription: Check whether the EcmaStrings transformed through calling ToPropKey function from Numbers are + * within expectations. + */ +TEST_F(JSTypedArrayTest, ToPropKey_002) +{ + JSHandle handleTagVal1(thread, JSTaggedValue(0)); + JSHandle handleTagVal2(thread, JSTaggedValue(-1)); + JSHandle handleTagVal3(thread, JSTaggedValue(1.789)); + JSHandle handleTagVal4(thread, JSTaggedValue(-789.1)); + JSHandle hnadleTagValEcmaStrPropKeyTo1 = JSTypedArray::ToPropKey(thread, handleTagVal1); + JSHandle hnadleTagValEcmaStrPropKeyTo2 = JSTypedArray::ToPropKey(thread, handleTagVal2); + JSHandle hnadleTagValEcmaStrPropKeyTo3 = JSTypedArray::ToPropKey(thread, handleTagVal3); + JSHandle hnadleTagValEcmaStrPropKeyTo4 = JSTypedArray::ToPropKey(thread, handleTagVal4); + JSHandle handleEcmaStrPropKeyTo1 = JSHandle::Cast(hnadleTagValEcmaStrPropKeyTo1); + JSHandle handleEcmaStrPropKeyTo2 = JSHandle::Cast(hnadleTagValEcmaStrPropKeyTo2); + JSHandle handleEcmaStrPropKeyTo3 = JSHandle::Cast(hnadleTagValEcmaStrPropKeyTo3); + JSHandle handleEcmaStrPropKeyTo4 = JSHandle::Cast(hnadleTagValEcmaStrPropKeyTo4); + std::unique_ptr uniCharArrTo1(handleEcmaStrPropKeyTo1->GetCString()); + std::unique_ptr uniCharArrTo2(handleEcmaStrPropKeyTo2->GetCString()); + std::unique_ptr uniCharArrTo3(handleEcmaStrPropKeyTo3->GetCString()); + std::unique_ptr uniCharArrTo4(handleEcmaStrPropKeyTo4->GetCString()); + EXPECT_EQ(uniCharArrTo1[0], '0'); + EXPECT_EQ(uniCharArrTo1[1], 0); // "0" + EXPECT_EQ(uniCharArrTo2[0], '-'); + EXPECT_EQ(uniCharArrTo2[1], '1'); + EXPECT_EQ(uniCharArrTo2[2], 0); // "-1" + EXPECT_EQ(uniCharArrTo3[0], '1'); + EXPECT_EQ(uniCharArrTo3[1], '.'); + EXPECT_EQ(uniCharArrTo3[2], '7'); + EXPECT_EQ(uniCharArrTo3[3], '8'); + EXPECT_EQ(uniCharArrTo3[4], '9'); + EXPECT_EQ(uniCharArrTo3[5], 0); // "1.789" + EXPECT_EQ(uniCharArrTo4[0], '-'); + EXPECT_EQ(uniCharArrTo4[1], '7'); + EXPECT_EQ(uniCharArrTo4[2], '8'); + EXPECT_EQ(uniCharArrTo4[3], '9'); + EXPECT_EQ(uniCharArrTo4[4], '.'); + EXPECT_EQ(uniCharArrTo4[5], '1'); + EXPECT_EQ(uniCharArrTo4[6], 0); // "-789.1" +} + +/* + * Feature: JSTypedArray + * Function: CreateJSTypedArray(GlobalEnv::GetInt8ArrayFunction.../ObjectFactory::NewJSObjectByConstructor) + * SubFunction: JSTaggedValue::IsTypedArray/IsJSInt8Array... + * FunctionPoints: Create JSTypedArray(JSInt8Array...) + * CaseDescription: Check whether the bools returned through calling IsTypedArray/IsInt8Array... functions from the + * JSTypedArrays created through calling NewJSObjectByConstructor function are within expectations. + */ +TEST_F(JSTypedArrayTest, TypedArrayCreate) +{ + JSHandle handleInt8Array = CreateNumberTypedArray(thread, JSType::JS_INT8_ARRAY); + JSHandle handleTagValInt8Array = JSHandle::Cast(handleInt8Array); + EXPECT_TRUE(handleTagValInt8Array->IsJSInt8Array() && handleTagValInt8Array->IsTypedArray()); + + JSHandle handleUint8Array = CreateNumberTypedArray(thread, JSType::JS_UINT8_ARRAY); + JSHandle handleTagValUint8Array = JSHandle::Cast(handleUint8Array); + EXPECT_TRUE(handleTagValUint8Array->IsJSUint8Array() && handleTagValUint8Array->IsTypedArray()); + + JSHandle handleUint8ClampedArray = CreateNumberTypedArray(thread, JSType::JS_UINT8_CLAMPED_ARRAY); + JSHandle handleTagValUint8ClampedArray = JSHandle::Cast(handleUint8ClampedArray); + EXPECT_TRUE(handleTagValUint8ClampedArray->IsJSUint8ClampedArray() && + handleTagValUint8ClampedArray->IsTypedArray()); + + JSHandle handleInt16Array = CreateNumberTypedArray(thread, JSType::JS_INT16_ARRAY); + JSHandle handleTagValInt16Array = JSHandle::Cast(handleInt16Array); + EXPECT_TRUE(handleTagValInt16Array->IsJSInt16Array() && handleTagValInt16Array->IsTypedArray()); + + JSHandle handleUint16Array = CreateNumberTypedArray(thread, JSType::JS_UINT16_ARRAY); + JSHandle handleTagValUint16Array = JSHandle::Cast(handleUint16Array); + EXPECT_TRUE(handleTagValUint16Array->IsJSUint16Array() && handleTagValUint16Array->IsTypedArray()); + + JSHandle handleInt32Array = CreateNumberTypedArray(thread, JSType::JS_INT32_ARRAY); + JSHandle handleTagValInt32Array = JSHandle::Cast(handleInt32Array); + EXPECT_TRUE(handleTagValInt32Array->IsJSInt32Array() && handleTagValInt32Array->IsTypedArray()); + + JSHandle handleUint32Array = CreateNumberTypedArray(thread, JSType::JS_UINT32_ARRAY); + JSHandle handleTagValUint32Array = JSHandle::Cast(handleUint32Array); + EXPECT_TRUE(handleTagValUint32Array->IsJSUint32Array() && handleTagValUint32Array->IsTypedArray()); + + JSHandle handleFloat32Array = CreateNumberTypedArray(thread, JSType::JS_FLOAT32_ARRAY); + JSHandle handleTagValFloat32Array = JSHandle::Cast(handleFloat32Array); + EXPECT_TRUE(handleTagValFloat32Array->IsJSFloat32Array() && handleTagValFloat32Array->IsTypedArray()); + + JSHandle handleFloat64Array = CreateNumberTypedArray(thread, JSType::JS_FLOAT64_ARRAY); + JSHandle handleTagValFloat64Array = JSHandle::Cast(handleFloat64Array); + EXPECT_TRUE(handleTagValFloat64Array->IsJSFloat64Array() && handleTagValFloat64Array->IsTypedArray()); +} + +/* + * Feature: JSTypedArray + * Function: SetViewedArrayBuffer + * SubFunction: GetViewedArrayBuffer/ObjectFactory::NewJSArrayBuffer + * FunctionPoints: Set ViewedArrayBuffer + * CaseDescription: Check whether the JSArrayBuffer returned through calling GetViewedArrayBuffer function from the + * JSTypedArray changed through calling SetViewedArrayBuffer function is within expectations. + */ +TEST_F(JSTypedArrayTest, SetViewedArrayBuffer) +{ + ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); + JSHandle handleArrayBufferFrom = factory->NewJSArrayBuffer(10); + JSHandle handleTagValArrayBufferFrom = JSHandle::Cast(handleArrayBufferFrom); + + for (size_t i = 0; i < cVecJSType.size(); i++) { + JSHandle handleTypedArray = CreateNumberTypedArray(thread, cVecJSType.at(i)); + + EXPECT_EQ(handleTypedArray->GetViewedArrayBuffer(), JSTaggedValue::Undefined()); + handleTypedArray->SetViewedArrayBuffer(thread, handleTagValArrayBufferFrom); + EXPECT_EQ(handleTypedArray->GetViewedArrayBuffer(), handleTagValArrayBufferFrom.GetTaggedValue()); + } +} + +/* + * Feature: JSTypedArray + * Function: SetTypedArrayName + * SubFunction: GetTypedArrayName + * FunctionPoints: Set TypedArrayName + * CaseDescription: Check whether the JSTaggedValue returned through calling GetTypedArrayName function from the + * JSTypedArray changed through calling SetTypedArrayName function is within expectations. + */ +TEST_F(JSTypedArrayTest, SetTypedArrayName) +{ + CString cStrName = "cStrName"; + ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); + JSHandle handleEcmaStrNameFrom = factory->NewFromString(cStrName); + JSHandle handleTagValEcmaStrNameFrom = JSHandle::Cast(handleEcmaStrNameFrom); + + for (size_t i = 0; i < cVecJSType.size(); i++) { + JSHandle handleTypedArray = CreateNumberTypedArray(thread, cVecJSType.at(i)); + + EXPECT_EQ(handleTypedArray->GetTypedArrayName(), JSTaggedValue::Undefined()); + handleTypedArray->SetTypedArrayName(thread, handleTagValEcmaStrNameFrom); + EXPECT_EQ(handleTypedArray->GetTypedArrayName(), handleTagValEcmaStrNameFrom.GetTaggedValue()); + } +} + +/* + * Feature: JSTypedArray + * Function: SetByteLength + * SubFunction: GetByteLength + * FunctionPoints: Set ByteLength + * CaseDescription: Check whether the Number returned through calling GetByteLength function from the JSTypedArray + * changed through calling SetByteLength function is within expectations. + */ +TEST_F(JSTypedArrayTest, SetByteLength) +{ + uint32_t u32ByteLength = 2; + JSHandle handleTagValByteLengthFrom(thread, JSTaggedValue(u32ByteLength)); + + for (size_t i = 0; i < cVecJSType.size(); i++) { + JSHandle handleTypedArray = CreateNumberTypedArray(thread, cVecJSType.at(i)); + + EXPECT_EQ(handleTypedArray->GetByteLength(), JSTaggedValue(0)); + handleTypedArray->SetByteLength(thread, handleTagValByteLengthFrom); + EXPECT_EQ(handleTypedArray->GetByteLength(), handleTagValByteLengthFrom.GetTaggedValue()); + } +} + +/* + * Feature: JSTypedArray + * Function: SetByteOffset + * SubFunction: GetByteOffset + * FunctionPoints: Set ByteOffset + * CaseDescription: Check whether the Number returned through calling GetByteOffset function from the JSTypedArray + * changed through calling SetByteOffset function is within expectations. + */ +TEST_F(JSTypedArrayTest, SetByteOffset) +{ + uint32_t u32ByteOffset = 2; + JSHandle handleTagValByteOffsetFrom(thread, JSTaggedValue(u32ByteOffset)); + + for (size_t i = 0; i < cVecJSType.size(); i++) { + JSHandle handleTypedArray = CreateNumberTypedArray(thread, cVecJSType.at(i)); + + EXPECT_EQ(handleTypedArray->GetByteOffset(), JSTaggedValue(0)); + handleTypedArray->SetByteOffset(thread, handleTagValByteOffsetFrom); + EXPECT_EQ(handleTypedArray->GetByteOffset(), handleTagValByteOffsetFrom.GetTaggedValue()); + } +} + +/* + * Feature: JSTypedArray + * Function: SetArrayLength + * SubFunction: GetArrayLength + * FunctionPoints: Set ArrayLength + * CaseDescription: Check whether the Number returned through calling GetArrayLength function from the JSTypedArray + * changed through calling SetArrayLength function is within expectations. + */ +TEST_F(JSTypedArrayTest, SetArrayLength) +{ + uint32_t u32ArrayLength = 2; + JSHandle handleTagValArrayLengthFrom(thread, JSTaggedValue(u32ArrayLength)); + + for (size_t i = 0; i < cVecJSType.size(); i++) { + JSHandle handleTypedArray = CreateNumberTypedArray(thread, cVecJSType.at(i)); + + EXPECT_EQ(handleTypedArray->GetArrayLength(), JSTaggedValue(0)); + handleTypedArray->SetArrayLength(thread, handleTagValArrayLengthFrom); + EXPECT_EQ(handleTypedArray->GetArrayLength(), handleTagValArrayLengthFrom.GetTaggedValue()); + } +} + +/* + * Feature: JSTypedArray + * Function: IntegerIndexedElementSet + * SubFunction: IntegerIndexedElementGet + * FunctionPoints: Set Element At Integer Index(JSTaggedValue) Of JSTypedArray + * CaseDescription: Check whether the OperationResults returned through calling IntegerIndexedElementGet function from + * the JSTypedArray changed through calling IntegerIndexedElementSet function are within expectations. + */ +TEST_F(JSTypedArrayTest, DISABLED_IntegerIndexedElementSet_Int8Array_001) +{ + uint32_t numElementsInt8Array = 256; + ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); + JSHandle handleInt8Array = CreateNumberTypedArray(thread, JSType::JS_INT8_ARRAY); + JSHandle handleTagValInt8Array = JSHandle::Cast(handleInt8Array); + + uint32_t sizeElement = + ecmascript::base::TypedArrayHelper::GetElementSize(JSHandle::Cast(handleInt8Array)); + uint32_t byteLengthViewdArrayBuffer = sizeElement * numElementsInt8Array; + JSHandle handleTagValArrayBufferFrom = + JSHandle::Cast(factory->NewJSArrayBuffer(byteLengthViewdArrayBuffer)); + handleInt8Array->SetViewedArrayBuffer(thread, handleTagValArrayBufferFrom); + handleInt8Array->SetArrayLength(thread, JSTaggedValue(numElementsInt8Array)); + + CVector cVecOpResult = {}; + for (size_t i = 0; i < numElementsInt8Array; i++) { + EXPECT_TRUE(JSTypedArray::IntegerIndexedElementSet( + thread, handleTagValInt8Array, JSTaggedValue(i), + JSHandle(thread, JSTaggedValue(std::numeric_limits::min() + i)))); + OperationResult opResult = + JSTypedArray::IntegerIndexedElementGet(thread, handleTagValInt8Array, JSTaggedValue(i)); + cVecOpResult.push_back(opResult); + } + for (size_t i = 0; i < numElementsInt8Array; i++) { + EXPECT_EQ(cVecOpResult.at(i).GetValue().GetTaggedValue().GetNumber(), std::numeric_limits::min() + i); + } + cVecOpResult.clear(); + + OperationResult opResult1 = + JSTypedArray::IntegerIndexedElementGet(thread, handleTagValInt8Array, JSTaggedValue(-1)); + OperationResult opResult2 = + JSTypedArray::IntegerIndexedElementGet(thread, handleTagValInt8Array, JSTaggedValue(-0.0)); + OperationResult opResult3 = + JSTypedArray::IntegerIndexedElementGet(thread, handleTagValInt8Array, JSTaggedValue(1.1)); + OperationResult opResult4 = + JSTypedArray::IntegerIndexedElementGet(thread, handleTagValInt8Array, JSTaggedValue(numElementsInt8Array)); + EXPECT_EQ(opResult1.GetValue().GetTaggedValue(), JSTaggedValue::Undefined()); + EXPECT_EQ(opResult2.GetValue().GetTaggedValue(), JSTaggedValue::Undefined()); + EXPECT_EQ(opResult3.GetValue().GetTaggedValue(), JSTaggedValue::Undefined()); + EXPECT_EQ(opResult4.GetValue().GetTaggedValue(), JSTaggedValue::Undefined()); + + EXPECT_FALSE(JSTypedArray::IntegerIndexedElementSet(thread, handleTagValInt8Array, JSTaggedValue(-1), + JSHandle(thread, JSTaggedValue(0)))); + EXPECT_FALSE(JSTypedArray::IntegerIndexedElementSet(thread, handleTagValInt8Array, + JSTaggedValue(numElementsInt8Array), + JSHandle(thread, JSTaggedValue(0)))); +} + +// Nonstandard input value for Int8Array +TEST_F(JSTypedArrayTest, IntegerIndexedElementSet_Int8Array_002) +{ + uint32_t numElementsInt8Array = 16; + ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); + JSHandle handleInt8Array = CreateNumberTypedArray(thread, JSType::JS_INT8_ARRAY); + JSHandle handleTagValInt8Array = JSHandle::Cast(handleInt8Array); + + uint32_t sizeElement = + ecmascript::base::TypedArrayHelper::GetElementSize(JSHandle::Cast(handleInt8Array)); + uint32_t byteLengthViewdArrayBuffer = sizeElement * numElementsInt8Array; + JSHandle handleTagValArrayBufferFrom = + JSHandle::Cast(factory->NewJSArrayBuffer(byteLengthViewdArrayBuffer)); + handleInt8Array->SetViewedArrayBuffer(thread, handleTagValArrayBufferFrom); + handleInt8Array->SetArrayLength(thread, JSTaggedValue(numElementsInt8Array)); + + int64_t value1 = -129; // to int8 : 127 + int64_t value2 = 128; // to int8 : -128 + double value3 = 13.4; // to int8 : 13 + double value4 = 13.6; // to int8 : 13 + JSHandle handleTagValValueSet1(thread, JSTaggedValue(value1)); + JSHandle handleTagValValueSet2(thread, JSTaggedValue(value2)); + JSHandle handleTagValValueSet3(thread, JSTaggedValue(value3)); + JSHandle handleTagValValueSet4(thread, JSTaggedValue(value4)); + + EXPECT_TRUE( + JSTypedArray::IntegerIndexedElementSet(thread, handleTagValInt8Array, JSTaggedValue(0), handleTagValValueSet1)); + OperationResult opResult1 = JSTypedArray::IntegerIndexedElementGet(thread, handleTagValInt8Array, JSTaggedValue(0)); + EXPECT_TRUE( + JSTypedArray::IntegerIndexedElementSet(thread, handleTagValInt8Array, JSTaggedValue(0), handleTagValValueSet2)); + OperationResult opResult2 = JSTypedArray::IntegerIndexedElementGet(thread, handleTagValInt8Array, JSTaggedValue(0)); + EXPECT_TRUE( + JSTypedArray::IntegerIndexedElementSet(thread, handleTagValInt8Array, JSTaggedValue(0), handleTagValValueSet3)); + OperationResult opResult3 = JSTypedArray::IntegerIndexedElementGet(thread, handleTagValInt8Array, JSTaggedValue(0)); + EXPECT_TRUE( + JSTypedArray::IntegerIndexedElementSet(thread, handleTagValInt8Array, JSTaggedValue(0), handleTagValValueSet4)); + OperationResult opResult4 = JSTypedArray::IntegerIndexedElementGet(thread, handleTagValInt8Array, JSTaggedValue(0)); + + EXPECT_NE(value1, opResult1.GetValue().GetTaggedValue().GetNumber()); + EXPECT_NE(value2, opResult2.GetValue().GetTaggedValue().GetNumber()); + EXPECT_NE(value3, opResult3.GetValue().GetTaggedValue().GetNumber()); + EXPECT_NE(value4, opResult4.GetValue().GetTaggedValue().GetNumber()); + EXPECT_EQ(127, opResult1.GetValue().GetTaggedValue().GetNumber()); + EXPECT_EQ(-128, opResult2.GetValue().GetTaggedValue().GetNumber()); + EXPECT_EQ(13, opResult3.GetValue().GetTaggedValue().GetNumber()); + EXPECT_EQ(13, opResult4.GetValue().GetTaggedValue().GetNumber()); +} + +TEST_F(JSTypedArrayTest, IntegerIndexedElementSet_Uint8Array_001) +{ + uint32_t numElementsUint8Array = 256; + ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); + JSHandle handleUint8Array = CreateNumberTypedArray(thread, JSType::JS_UINT8_ARRAY); + JSHandle handleTagValUint8Array = JSHandle::Cast(handleUint8Array); + + uint32_t sizeElement = + ecmascript::base::TypedArrayHelper::GetElementSize(JSHandle::Cast(handleUint8Array)); + uint32_t byteLengthViewdArrayBuffer = sizeElement * numElementsUint8Array; + JSHandle handleTagValArrayBufferFrom = + JSHandle::Cast(factory->NewJSArrayBuffer(byteLengthViewdArrayBuffer)); + handleUint8Array->SetViewedArrayBuffer(thread, handleTagValArrayBufferFrom); + handleUint8Array->SetArrayLength(thread, JSTaggedValue(numElementsUint8Array)); + + CVector cVecOpResult = {}; + for (uint32_t i = 0; i < numElementsUint8Array; i++) { + EXPECT_TRUE(JSTypedArray::IntegerIndexedElementSet( + thread, handleTagValUint8Array, JSTaggedValue(i), + JSHandle(thread, JSTaggedValue(std::numeric_limits::min() + i)))); + OperationResult opResult = + JSTypedArray::IntegerIndexedElementGet(thread, handleTagValUint8Array, JSTaggedValue(i)); + cVecOpResult.push_back(opResult); + } + for (uint32_t i = 0; i < numElementsUint8Array; i++) { + EXPECT_EQ(cVecOpResult.at(i).GetValue().GetTaggedValue().GetNumber(), std::numeric_limits::min() + i); + } + cVecOpResult.clear(); + + OperationResult opResult1 = + JSTypedArray::IntegerIndexedElementGet(thread, handleTagValUint8Array, JSTaggedValue(-1)); + OperationResult opResult2 = + JSTypedArray::IntegerIndexedElementGet(thread, handleTagValUint8Array, JSTaggedValue(-0.0)); + OperationResult opResult3 = + JSTypedArray::IntegerIndexedElementGet(thread, handleTagValUint8Array, JSTaggedValue(1.1)); + OperationResult opResult4 = + JSTypedArray::IntegerIndexedElementGet(thread, handleTagValUint8Array, JSTaggedValue(numElementsUint8Array)); + EXPECT_EQ(opResult1.GetValue().GetTaggedValue(), JSTaggedValue::Undefined()); + EXPECT_EQ(opResult2.GetValue().GetTaggedValue(), JSTaggedValue::Undefined()); + EXPECT_EQ(opResult3.GetValue().GetTaggedValue(), JSTaggedValue::Undefined()); + EXPECT_EQ(opResult4.GetValue().GetTaggedValue(), JSTaggedValue::Undefined()); + + EXPECT_FALSE(JSTypedArray::IntegerIndexedElementSet(thread, handleTagValUint8Array, JSTaggedValue(-1), + JSHandle(thread, JSTaggedValue(0)))); + EXPECT_FALSE(JSTypedArray::IntegerIndexedElementSet(thread, handleTagValUint8Array, + JSTaggedValue(numElementsUint8Array), + JSHandle(thread, JSTaggedValue(0)))); +} + +// Nonstandard input value for Uint8Array +TEST_F(JSTypedArrayTest, IntegerIndexedElementSet_Uint8Array_002) +{ + uint32_t numElementsUint8Array = 16; + ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); + JSHandle handleUint8Array = CreateNumberTypedArray(thread, JSType::JS_UINT8_ARRAY); + JSHandle handleTagValUint8Array = JSHandle::Cast(handleUint8Array); + + uint32_t sizeElement = + ecmascript::base::TypedArrayHelper::GetElementSize(JSHandle::Cast(handleUint8Array)); + uint32_t byteLengthViewdArrayBuffer = sizeElement * numElementsUint8Array; + JSHandle handleTagValArrayBufferFrom = + JSHandle::Cast(factory->NewJSArrayBuffer(byteLengthViewdArrayBuffer)); + handleUint8Array->SetViewedArrayBuffer(thread, handleTagValArrayBufferFrom); + handleUint8Array->SetArrayLength(thread, JSTaggedValue(numElementsUint8Array)); + + int64_t value1 = -1; // to uint8 : 255 + int64_t value2 = 256; // to uint8 : 0 + double value3 = 13.4; // to uint8 : 13 + double value4 = 13.6; // to uint8 : 13 + JSHandle handleTagValValueSet1(thread, JSTaggedValue(value1)); + JSHandle handleTagValValueSet2(thread, JSTaggedValue(value2)); + JSHandle handleTagValValueSet3(thread, JSTaggedValue(value3)); + JSHandle handleTagValValueSet4(thread, JSTaggedValue(value4)); + + EXPECT_TRUE(JSTypedArray::IntegerIndexedElementSet(thread, handleTagValUint8Array, JSTaggedValue(0), + handleTagValValueSet1)); + OperationResult opResult1 = + JSTypedArray::IntegerIndexedElementGet(thread, handleTagValUint8Array, JSTaggedValue(0)); + EXPECT_TRUE(JSTypedArray::IntegerIndexedElementSet(thread, handleTagValUint8Array, JSTaggedValue(0), + handleTagValValueSet2)); + OperationResult opResult2 = + JSTypedArray::IntegerIndexedElementGet(thread, handleTagValUint8Array, JSTaggedValue(0)); + EXPECT_TRUE(JSTypedArray::IntegerIndexedElementSet(thread, handleTagValUint8Array, JSTaggedValue(0), + handleTagValValueSet3)); + OperationResult opResult3 = + JSTypedArray::IntegerIndexedElementGet(thread, handleTagValUint8Array, JSTaggedValue(0)); + EXPECT_TRUE(JSTypedArray::IntegerIndexedElementSet(thread, handleTagValUint8Array, JSTaggedValue(0), + handleTagValValueSet4)); + OperationResult opResult4 = + JSTypedArray::IntegerIndexedElementGet(thread, handleTagValUint8Array, JSTaggedValue(0)); + + EXPECT_NE(value1, opResult1.GetValue().GetTaggedValue().GetNumber()); + EXPECT_NE(value2, opResult2.GetValue().GetTaggedValue().GetNumber()); + EXPECT_NE(value3, opResult3.GetValue().GetTaggedValue().GetNumber()); + EXPECT_NE(value4, opResult4.GetValue().GetTaggedValue().GetNumber()); + EXPECT_EQ(255, opResult1.GetValue().GetTaggedValue().GetNumber()); + EXPECT_EQ(0, opResult2.GetValue().GetTaggedValue().GetNumber()); + EXPECT_EQ(13, opResult3.GetValue().GetTaggedValue().GetNumber()); + EXPECT_EQ(13, opResult4.GetValue().GetTaggedValue().GetNumber()); +} + +TEST_F(JSTypedArrayTest, IntegerIndexedElementSet_Uint8ClampedArray_001) +{ + uint32_t numElementsUint8ClampedArray = 256; + ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); + JSHandle handleUint8ClampedArray = CreateNumberTypedArray(thread, JSType::JS_UINT8_CLAMPED_ARRAY); + JSHandle handleTagValUint8ClampedArray = JSHandle::Cast(handleUint8ClampedArray); + + uint32_t sizeElement = + ecmascript::base::TypedArrayHelper::GetElementSize(JSHandle::Cast(handleUint8ClampedArray)); + uint32_t byteLengthViewdArrayBuffer = sizeElement * numElementsUint8ClampedArray; + JSHandle handleTagValArrayBufferFrom = + JSHandle::Cast(factory->NewJSArrayBuffer(byteLengthViewdArrayBuffer)); + handleUint8ClampedArray->SetViewedArrayBuffer(thread, handleTagValArrayBufferFrom); + handleUint8ClampedArray->SetArrayLength(thread, JSTaggedValue(numElementsUint8ClampedArray)); + + CVector cVecOpResult = {}; + for (uint32_t i = 0; i < numElementsUint8ClampedArray; i++) { + EXPECT_TRUE(JSTypedArray::IntegerIndexedElementSet( + thread, handleTagValUint8ClampedArray, JSTaggedValue(i), + JSHandle(thread, JSTaggedValue(std::numeric_limits::min() + i)))); + OperationResult opResult = + JSTypedArray::IntegerIndexedElementGet(thread, handleTagValUint8ClampedArray, JSTaggedValue(i)); + cVecOpResult.push_back(opResult); + } + for (uint32_t i = 0; i < numElementsUint8ClampedArray; i++) { + EXPECT_EQ(cVecOpResult.at(i).GetValue().GetTaggedValue().GetNumber(), std::numeric_limits::min() + i); + } + cVecOpResult.clear(); + + OperationResult opResult1 = + JSTypedArray::IntegerIndexedElementGet(thread, handleTagValUint8ClampedArray, JSTaggedValue(-1)); + OperationResult opResult2 = + JSTypedArray::IntegerIndexedElementGet(thread, handleTagValUint8ClampedArray, JSTaggedValue(-0.0)); + OperationResult opResult3 = + JSTypedArray::IntegerIndexedElementGet(thread, handleTagValUint8ClampedArray, JSTaggedValue(1.1)); + OperationResult opResult4 = JSTypedArray::IntegerIndexedElementGet(thread, handleTagValUint8ClampedArray, + JSTaggedValue(numElementsUint8ClampedArray)); + EXPECT_EQ(opResult1.GetValue().GetTaggedValue(), JSTaggedValue::Undefined()); + EXPECT_EQ(opResult2.GetValue().GetTaggedValue(), JSTaggedValue::Undefined()); + EXPECT_EQ(opResult3.GetValue().GetTaggedValue(), JSTaggedValue::Undefined()); + EXPECT_EQ(opResult4.GetValue().GetTaggedValue(), JSTaggedValue::Undefined()); + + EXPECT_FALSE(JSTypedArray::IntegerIndexedElementSet(thread, handleTagValUint8ClampedArray, JSTaggedValue(-1), + JSHandle(thread, JSTaggedValue(0)))); + EXPECT_FALSE(JSTypedArray::IntegerIndexedElementSet(thread, handleTagValUint8ClampedArray, + JSTaggedValue(numElementsUint8ClampedArray), + JSHandle(thread, JSTaggedValue(0)))); +} + +// Nonstandard input value for Uint8ClampedArray +TEST_F(JSTypedArrayTest, IntegerIndexedElementSet_Uint8ClampedArray_002) +{ + uint32_t numElementsUint8ClampedArray = 16; + ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); + JSHandle handleUint8ClampedArray = CreateNumberTypedArray(thread, JSType::JS_UINT8_CLAMPED_ARRAY); + JSHandle handleTagValUint8ClampedArray = JSHandle::Cast(handleUint8ClampedArray); + + uint32_t sizeElement = + ecmascript::base::TypedArrayHelper::GetElementSize(JSHandle::Cast(handleUint8ClampedArray)); + uint32_t byteLengthViewdArrayBuffer = sizeElement * numElementsUint8ClampedArray; + JSHandle handleTagValArrayBufferFrom = + JSHandle::Cast(factory->NewJSArrayBuffer(byteLengthViewdArrayBuffer)); + handleUint8ClampedArray->SetViewedArrayBuffer(thread, handleTagValArrayBufferFrom); + handleUint8ClampedArray->SetArrayLength(thread, JSTaggedValue(numElementsUint8ClampedArray)); + + int64_t value1 = -1; // to uint8_clamped : 0 + int64_t value2 = 256; // to uint8_clamped : 255 + double value3 = 13.4; // to uint8_clamped : 13 + double value4 = 13.6; // to uint8_clamped : 14 + JSHandle handleTagValValueSet1(thread, JSTaggedValue(value1)); + JSHandle handleTagValValueSet2(thread, JSTaggedValue(value2)); + JSHandle handleTagValValueSet3(thread, JSTaggedValue(value3)); + JSHandle handleTagValValueSet4(thread, JSTaggedValue(value4)); + + EXPECT_TRUE(JSTypedArray::IntegerIndexedElementSet(thread, handleTagValUint8ClampedArray, JSTaggedValue(0), + handleTagValValueSet1)); + OperationResult opResult1 = + JSTypedArray::IntegerIndexedElementGet(thread, handleTagValUint8ClampedArray, JSTaggedValue(0)); + EXPECT_TRUE(JSTypedArray::IntegerIndexedElementSet(thread, handleTagValUint8ClampedArray, JSTaggedValue(0), + handleTagValValueSet2)); + OperationResult opResult2 = + JSTypedArray::IntegerIndexedElementGet(thread, handleTagValUint8ClampedArray, JSTaggedValue(0)); + EXPECT_TRUE(JSTypedArray::IntegerIndexedElementSet(thread, handleTagValUint8ClampedArray, JSTaggedValue(0), + handleTagValValueSet3)); + OperationResult opResult3 = + JSTypedArray::IntegerIndexedElementGet(thread, handleTagValUint8ClampedArray, JSTaggedValue(0)); + EXPECT_TRUE(JSTypedArray::IntegerIndexedElementSet(thread, handleTagValUint8ClampedArray, JSTaggedValue(0), + handleTagValValueSet4)); + OperationResult opResult4 = + JSTypedArray::IntegerIndexedElementGet(thread, handleTagValUint8ClampedArray, JSTaggedValue(0)); + + EXPECT_NE(value1, opResult1.GetValue().GetTaggedValue().GetNumber()); + EXPECT_NE(value2, opResult2.GetValue().GetTaggedValue().GetNumber()); + EXPECT_NE(value3, opResult3.GetValue().GetTaggedValue().GetNumber()); + EXPECT_NE(value4, opResult4.GetValue().GetTaggedValue().GetNumber()); + EXPECT_EQ(0, opResult1.GetValue().GetTaggedValue().GetNumber()); + EXPECT_EQ(255, opResult2.GetValue().GetTaggedValue().GetNumber()); + EXPECT_EQ(13, opResult3.GetValue().GetTaggedValue().GetNumber()); + EXPECT_EQ(14, opResult4.GetValue().GetTaggedValue().GetNumber()); +} + +TEST_F(JSTypedArrayTest, DISABLED_IntegerIndexedElementSet_Int16Array_001) +{ + uint32_t numElementsInt16Array = 100; + int16_t scaleForInt16ValueSet = 100; + ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); + JSHandle handleInt16Array = CreateNumberTypedArray(thread, JSType::JS_INT16_ARRAY); + JSHandle handleTagValInt16Array = JSHandle::Cast(handleInt16Array); + + uint32_t sizeElement = + ecmascript::base::TypedArrayHelper::GetElementSize(JSHandle::Cast(handleInt16Array)); + uint32_t byteLengthViewdArrayBuffer = sizeElement * numElementsInt16Array; + JSHandle handleTagValArrayBufferFrom = + JSHandle::Cast(factory->NewJSArrayBuffer(byteLengthViewdArrayBuffer)); + handleInt16Array->SetViewedArrayBuffer(thread, handleTagValArrayBufferFrom); + handleInt16Array->SetArrayLength(thread, JSTaggedValue(numElementsInt16Array)); + + CVector cVecOpResult = {}; + for (size_t i = 0; i < numElementsInt16Array; i++) { + EXPECT_TRUE(JSTypedArray::IntegerIndexedElementSet( + thread, handleTagValInt16Array, JSTaggedValue(i), + JSHandle(thread, + JSTaggedValue(std::numeric_limits::min() + i * scaleForInt16ValueSet)))); + OperationResult opResult = + JSTypedArray::IntegerIndexedElementGet(thread, handleTagValInt16Array, JSTaggedValue(i)); + cVecOpResult.push_back(opResult); + } + for (size_t i = 0; i < numElementsInt16Array; i++) { + EXPECT_EQ(cVecOpResult.at(i).GetValue().GetTaggedValue().GetNumber(), + std::numeric_limits::min() + i * scaleForInt16ValueSet); + } + cVecOpResult.clear(); + + OperationResult opResult1 = + JSTypedArray::IntegerIndexedElementGet(thread, handleTagValInt16Array, JSTaggedValue(-1)); + OperationResult opResult2 = + JSTypedArray::IntegerIndexedElementGet(thread, handleTagValInt16Array, JSTaggedValue(-0.0)); + OperationResult opResult3 = + JSTypedArray::IntegerIndexedElementGet(thread, handleTagValInt16Array, JSTaggedValue(1.1)); + OperationResult opResult4 = + JSTypedArray::IntegerIndexedElementGet(thread, handleTagValInt16Array, JSTaggedValue(numElementsInt16Array)); + EXPECT_EQ(opResult1.GetValue().GetTaggedValue(), JSTaggedValue::Undefined()); + EXPECT_EQ(opResult2.GetValue().GetTaggedValue(), JSTaggedValue::Undefined()); + EXPECT_EQ(opResult3.GetValue().GetTaggedValue(), JSTaggedValue::Undefined()); + EXPECT_EQ(opResult4.GetValue().GetTaggedValue(), JSTaggedValue::Undefined()); + + EXPECT_FALSE(JSTypedArray::IntegerIndexedElementSet(thread, handleTagValInt16Array, JSTaggedValue(-1), + JSHandle(thread, JSTaggedValue(0)))); + EXPECT_FALSE(JSTypedArray::IntegerIndexedElementSet(thread, handleTagValInt16Array, + JSTaggedValue(numElementsInt16Array), + JSHandle(thread, JSTaggedValue(0)))); +} + +// Nonstandard input value for Int16Array +TEST_F(JSTypedArrayTest, IntegerIndexedElementSet_Int16Array_002) +{ + uint32_t numElementsInt16Array = 16; + ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); + JSHandle handleInt16Array = CreateNumberTypedArray(thread, JSType::JS_INT16_ARRAY); + JSHandle handleTagValInt16Array = JSHandle::Cast(handleInt16Array); + + uint32_t sizeElement = + ecmascript::base::TypedArrayHelper::GetElementSize(JSHandle::Cast(handleInt16Array)); + uint32_t byteLengthViewdArrayBuffer = sizeElement * numElementsInt16Array; + JSHandle handleTagValArrayBufferFrom = + JSHandle::Cast(factory->NewJSArrayBuffer(byteLengthViewdArrayBuffer)); + handleInt16Array->SetViewedArrayBuffer(thread, handleTagValArrayBufferFrom); + handleInt16Array->SetArrayLength(thread, JSTaggedValue(numElementsInt16Array)); + + int64_t value1 = -32769; // to int16 : 32767 + int64_t value2 = 32768; // to int16 : -32768 + double value3 = 13.4; // to int16 : 13 + double value4 = 13.6; // to int16 : 13 + JSHandle handleTagValValueSet1(thread, JSTaggedValue(value1)); + JSHandle handleTagValValueSet2(thread, JSTaggedValue(value2)); + JSHandle handleTagValValueSet3(thread, JSTaggedValue(value3)); + JSHandle handleTagValValueSet4(thread, JSTaggedValue(value4)); + + EXPECT_TRUE(JSTypedArray::IntegerIndexedElementSet(thread, handleTagValInt16Array, JSTaggedValue(0), + handleTagValValueSet1)); + OperationResult opResult1 = + JSTypedArray::IntegerIndexedElementGet(thread, handleTagValInt16Array, JSTaggedValue(0)); + EXPECT_TRUE(JSTypedArray::IntegerIndexedElementSet(thread, handleTagValInt16Array, JSTaggedValue(0), + handleTagValValueSet2)); + OperationResult opResult2 = + JSTypedArray::IntegerIndexedElementGet(thread, handleTagValInt16Array, JSTaggedValue(0)); + EXPECT_TRUE(JSTypedArray::IntegerIndexedElementSet(thread, handleTagValInt16Array, JSTaggedValue(0), + handleTagValValueSet3)); + OperationResult opResult3 = + JSTypedArray::IntegerIndexedElementGet(thread, handleTagValInt16Array, JSTaggedValue(0)); + EXPECT_TRUE(JSTypedArray::IntegerIndexedElementSet(thread, handleTagValInt16Array, JSTaggedValue(0), + handleTagValValueSet4)); + OperationResult opResult4 = + JSTypedArray::IntegerIndexedElementGet(thread, handleTagValInt16Array, JSTaggedValue(0)); + + EXPECT_NE(value1, opResult1.GetValue().GetTaggedValue().GetNumber()); + EXPECT_NE(value2, opResult2.GetValue().GetTaggedValue().GetNumber()); + EXPECT_NE(value3, opResult3.GetValue().GetTaggedValue().GetNumber()); + EXPECT_NE(value4, opResult4.GetValue().GetTaggedValue().GetNumber()); + EXPECT_EQ(32767, opResult1.GetValue().GetTaggedValue().GetNumber()); + EXPECT_EQ(-32768, opResult2.GetValue().GetTaggedValue().GetNumber()); + EXPECT_EQ(13, opResult3.GetValue().GetTaggedValue().GetNumber()); + EXPECT_EQ(13, opResult4.GetValue().GetTaggedValue().GetNumber()); +} + +TEST_F(JSTypedArrayTest, IntegerIndexedElementSet_Uint16Array_001) +{ + uint32_t numElementsUint16Array = 100; + uint32_t scaleForUint16ValueSet = 100; + ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); + JSHandle handleUint16Array = CreateNumberTypedArray(thread, JSType::JS_UINT16_ARRAY); + JSHandle handleTagValUint16Array = JSHandle::Cast(handleUint16Array); + + uint32_t sizeElement = + ecmascript::base::TypedArrayHelper::GetElementSize(JSHandle::Cast(handleUint16Array)); + uint32_t byteLengthViewdArrayBuffer = sizeElement * numElementsUint16Array; + JSHandle handleTagValArrayBufferFrom = + JSHandle::Cast(factory->NewJSArrayBuffer(byteLengthViewdArrayBuffer)); + handleUint16Array->SetViewedArrayBuffer(thread, handleTagValArrayBufferFrom); + handleUint16Array->SetArrayLength(thread, JSTaggedValue(numElementsUint16Array)); + + CVector cVecOpResult = {}; + for (uint32_t i = 0; i < numElementsUint16Array; i++) { + EXPECT_TRUE(JSTypedArray::IntegerIndexedElementSet( + thread, handleTagValUint16Array, JSTaggedValue(i), + JSHandle(thread, + JSTaggedValue(std::numeric_limits::min() + i * scaleForUint16ValueSet)))); + OperationResult opResult = + JSTypedArray::IntegerIndexedElementGet(thread, handleTagValUint16Array, JSTaggedValue(i)); + cVecOpResult.push_back(opResult); + } + for (uint32_t i = 0; i < numElementsUint16Array; i++) { + EXPECT_EQ(cVecOpResult.at(i).GetValue().GetTaggedValue().GetNumber(), + std::numeric_limits::min() + i * scaleForUint16ValueSet); + } + cVecOpResult.clear(); + + OperationResult opResult1 = + JSTypedArray::IntegerIndexedElementGet(thread, handleTagValUint16Array, JSTaggedValue(-1)); + OperationResult opResult2 = + JSTypedArray::IntegerIndexedElementGet(thread, handleTagValUint16Array, JSTaggedValue(-0.0)); + OperationResult opResult3 = + JSTypedArray::IntegerIndexedElementGet(thread, handleTagValUint16Array, JSTaggedValue(1.1)); + OperationResult opResult4 = + JSTypedArray::IntegerIndexedElementGet(thread, handleTagValUint16Array, JSTaggedValue(numElementsUint16Array)); + EXPECT_EQ(opResult1.GetValue().GetTaggedValue(), JSTaggedValue::Undefined()); + EXPECT_EQ(opResult2.GetValue().GetTaggedValue(), JSTaggedValue::Undefined()); + EXPECT_EQ(opResult3.GetValue().GetTaggedValue(), JSTaggedValue::Undefined()); + EXPECT_EQ(opResult4.GetValue().GetTaggedValue(), JSTaggedValue::Undefined()); + + EXPECT_FALSE(JSTypedArray::IntegerIndexedElementSet(thread, handleTagValUint16Array, JSTaggedValue(-1), + JSHandle(thread, JSTaggedValue(0)))); + EXPECT_FALSE(JSTypedArray::IntegerIndexedElementSet(thread, handleTagValUint16Array, + JSTaggedValue(numElementsUint16Array), + JSHandle(thread, JSTaggedValue(0)))); +} + +// Nonstandard input value for Uint16Array +TEST_F(JSTypedArrayTest, IntegerIndexedElementSet_Uint16Array_002) +{ + uint32_t numElementsUint16Array = 16; + ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); + JSHandle handleUint16Array = CreateNumberTypedArray(thread, JSType::JS_UINT16_ARRAY); + JSHandle handleTagValUint16Array = JSHandle::Cast(handleUint16Array); + + uint32_t sizeElement = + ecmascript::base::TypedArrayHelper::GetElementSize(JSHandle::Cast(handleUint16Array)); + uint32_t byteLengthViewdArrayBuffer = sizeElement * numElementsUint16Array; + JSHandle handleTagValArrayBufferFrom = + JSHandle::Cast(factory->NewJSArrayBuffer(byteLengthViewdArrayBuffer)); + handleUint16Array->SetViewedArrayBuffer(thread, handleTagValArrayBufferFrom); + handleUint16Array->SetArrayLength(thread, JSTaggedValue(numElementsUint16Array)); + + int64_t value1 = -1; // to uint16 : 65535 + int64_t value2 = 65536; // to uint16 : 0 + double value3 = 13.4; // to uint16 : 13 + double value4 = 13.6; // to uint16 : 13 + JSHandle handleTagValValueSet1(thread, JSTaggedValue(value1)); + JSHandle handleTagValValueSet2(thread, JSTaggedValue(value2)); + JSHandle handleTagValValueSet3(thread, JSTaggedValue(value3)); + JSHandle handleTagValValueSet4(thread, JSTaggedValue(value4)); + + EXPECT_TRUE(JSTypedArray::IntegerIndexedElementSet(thread, handleTagValUint16Array, JSTaggedValue(0), + handleTagValValueSet1)); + OperationResult opResult1 = + JSTypedArray::IntegerIndexedElementGet(thread, handleTagValUint16Array, JSTaggedValue(0)); + EXPECT_TRUE(JSTypedArray::IntegerIndexedElementSet(thread, handleTagValUint16Array, JSTaggedValue(0), + handleTagValValueSet2)); + OperationResult opResult2 = + JSTypedArray::IntegerIndexedElementGet(thread, handleTagValUint16Array, JSTaggedValue(0)); + EXPECT_TRUE(JSTypedArray::IntegerIndexedElementSet(thread, handleTagValUint16Array, JSTaggedValue(0), + handleTagValValueSet3)); + OperationResult opResult3 = + JSTypedArray::IntegerIndexedElementGet(thread, handleTagValUint16Array, JSTaggedValue(0)); + EXPECT_TRUE(JSTypedArray::IntegerIndexedElementSet(thread, handleTagValUint16Array, JSTaggedValue(0), + handleTagValValueSet4)); + OperationResult opResult4 = + JSTypedArray::IntegerIndexedElementGet(thread, handleTagValUint16Array, JSTaggedValue(0)); + + EXPECT_NE(value1, opResult1.GetValue().GetTaggedValue().GetNumber()); + EXPECT_NE(value2, opResult2.GetValue().GetTaggedValue().GetNumber()); + EXPECT_NE(value3, opResult3.GetValue().GetTaggedValue().GetNumber()); + EXPECT_NE(value4, opResult4.GetValue().GetTaggedValue().GetNumber()); + EXPECT_EQ(65535, opResult1.GetValue().GetTaggedValue().GetNumber()); + EXPECT_EQ(0, opResult2.GetValue().GetTaggedValue().GetNumber()); + EXPECT_EQ(13, opResult3.GetValue().GetTaggedValue().GetNumber()); + EXPECT_EQ(13, opResult4.GetValue().GetTaggedValue().GetNumber()); +} + +TEST_F(JSTypedArrayTest, DISABLED_IntegerIndexedElementSet_Int32Array_001) +{ + uint32_t numElementsInt32Array = 100; + int32_t scaleForInt32ValueSet = 100000; + ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); + JSHandle handleInt32Array = CreateNumberTypedArray(thread, JSType::JS_INT32_ARRAY); + JSHandle handleTagValInt32Array = JSHandle::Cast(handleInt32Array); + + uint32_t sizeElement = + ecmascript::base::TypedArrayHelper::GetElementSize(JSHandle::Cast(handleInt32Array)); + uint32_t byteLengthViewdArrayBuffer = sizeElement * numElementsInt32Array; + JSHandle handleTagValArrayBufferFrom = + JSHandle::Cast(factory->NewJSArrayBuffer(byteLengthViewdArrayBuffer)); + handleInt32Array->SetViewedArrayBuffer(thread, handleTagValArrayBufferFrom); + handleInt32Array->SetArrayLength(thread, JSTaggedValue(numElementsInt32Array)); + + CVector cVecOpResult = {}; + for (size_t i = 0; i < numElementsInt32Array; i++) { + EXPECT_TRUE(JSTypedArray::IntegerIndexedElementSet( + thread, handleTagValInt32Array, JSTaggedValue(i), + JSHandle(thread, + JSTaggedValue(std::numeric_limits::min() + i * scaleForInt32ValueSet)))); + OperationResult opResult = + JSTypedArray::IntegerIndexedElementGet(thread, handleTagValInt32Array, JSTaggedValue(i)); + cVecOpResult.push_back(opResult); + } + for (size_t i = 0; i < numElementsInt32Array; i++) { + EXPECT_EQ(cVecOpResult.at(i).GetValue().GetTaggedValue().GetNumber(), + std::numeric_limits::min() + i * scaleForInt32ValueSet); + } + cVecOpResult.clear(); + + OperationResult opResult1 = + JSTypedArray::IntegerIndexedElementGet(thread, handleTagValInt32Array, JSTaggedValue(-1)); + OperationResult opResult2 = + JSTypedArray::IntegerIndexedElementGet(thread, handleTagValInt32Array, JSTaggedValue(-0.0)); + OperationResult opResult3 = + JSTypedArray::IntegerIndexedElementGet(thread, handleTagValInt32Array, JSTaggedValue(1.1)); + OperationResult opResult4 = + JSTypedArray::IntegerIndexedElementGet(thread, handleTagValInt32Array, JSTaggedValue(numElementsInt32Array)); + EXPECT_EQ(opResult1.GetValue().GetTaggedValue(), JSTaggedValue::Undefined()); + EXPECT_EQ(opResult2.GetValue().GetTaggedValue(), JSTaggedValue::Undefined()); + EXPECT_EQ(opResult3.GetValue().GetTaggedValue(), JSTaggedValue::Undefined()); + EXPECT_EQ(opResult4.GetValue().GetTaggedValue(), JSTaggedValue::Undefined()); + + EXPECT_FALSE(JSTypedArray::IntegerIndexedElementSet(thread, handleTagValInt32Array, JSTaggedValue(-1), + JSHandle(thread, JSTaggedValue(0)))); + EXPECT_FALSE(JSTypedArray::IntegerIndexedElementSet(thread, handleTagValInt32Array, + JSTaggedValue(numElementsInt32Array), + JSHandle(thread, JSTaggedValue(0)))); +} + +// Nonstandard input value for Int32Array +TEST_F(JSTypedArrayTest, IntegerIndexedElementSet_Int32Array_002) +{ + uint32_t numElementsInt32Array = 16; + ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); + JSHandle handleInt32Array = CreateNumberTypedArray(thread, JSType::JS_INT32_ARRAY); + JSHandle handleTagValInt32Array = JSHandle::Cast(handleInt32Array); + + uint32_t sizeElement = + ecmascript::base::TypedArrayHelper::GetElementSize(JSHandle::Cast(handleInt32Array)); + uint32_t byteLengthViewdArrayBuffer = sizeElement * numElementsInt32Array; + JSHandle handleTagValArrayBufferFrom = + JSHandle::Cast(factory->NewJSArrayBuffer(byteLengthViewdArrayBuffer)); + handleInt32Array->SetViewedArrayBuffer(thread, handleTagValArrayBufferFrom); + handleInt32Array->SetArrayLength(thread, JSTaggedValue(numElementsInt32Array)); + + int64_t value1 = -2147483649; // to int32 : 2147483647 + int64_t value2 = 2147483648; // to int32 : -2147483648 + double value3 = 13.4; // to int32 : 13 + double value4 = 13.6; // to int32 : 13 + JSHandle handleTagValValueSet1(thread, JSTaggedValue(value1)); + JSHandle handleTagValValueSet2(thread, JSTaggedValue(value2)); + JSHandle handleTagValValueSet3(thread, JSTaggedValue(value3)); + JSHandle handleTagValValueSet4(thread, JSTaggedValue(value4)); + + EXPECT_TRUE(JSTypedArray::IntegerIndexedElementSet(thread, handleTagValInt32Array, JSTaggedValue(0), + handleTagValValueSet1)); + OperationResult opResult1 = + JSTypedArray::IntegerIndexedElementGet(thread, handleTagValInt32Array, JSTaggedValue(0)); + EXPECT_TRUE(JSTypedArray::IntegerIndexedElementSet(thread, handleTagValInt32Array, JSTaggedValue(0), + handleTagValValueSet2)); + OperationResult opResult2 = + JSTypedArray::IntegerIndexedElementGet(thread, handleTagValInt32Array, JSTaggedValue(0)); + EXPECT_TRUE(JSTypedArray::IntegerIndexedElementSet(thread, handleTagValInt32Array, JSTaggedValue(0), + handleTagValValueSet3)); + OperationResult opResult3 = + JSTypedArray::IntegerIndexedElementGet(thread, handleTagValInt32Array, JSTaggedValue(0)); + EXPECT_TRUE(JSTypedArray::IntegerIndexedElementSet(thread, handleTagValInt32Array, JSTaggedValue(0), + handleTagValValueSet4)); + OperationResult opResult4 = + JSTypedArray::IntegerIndexedElementGet(thread, handleTagValInt32Array, JSTaggedValue(0)); + + EXPECT_NE(value1, opResult1.GetValue().GetTaggedValue().GetNumber()); + EXPECT_NE(value2, opResult2.GetValue().GetTaggedValue().GetNumber()); + EXPECT_NE(value3, opResult3.GetValue().GetTaggedValue().GetNumber()); + EXPECT_NE(value4, opResult4.GetValue().GetTaggedValue().GetNumber()); + EXPECT_EQ(2147483647, opResult1.GetValue().GetTaggedValue().GetNumber()); + EXPECT_EQ(-2147483648, opResult2.GetValue().GetTaggedValue().GetNumber()); + EXPECT_EQ(13, opResult3.GetValue().GetTaggedValue().GetNumber()); + EXPECT_EQ(13, opResult4.GetValue().GetTaggedValue().GetNumber()); +} + +TEST_F(JSTypedArrayTest, IntegerIndexedElementSet_Uint32Array_001) +{ + uint32_t numElementsUint32Array = 100; + uint32_t scaleForUint32ValueSet = 100000; + ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); + JSHandle handleUint32Array = CreateNumberTypedArray(thread, JSType::JS_UINT32_ARRAY); + JSHandle handleTagValUint32Array = JSHandle::Cast(handleUint32Array); + + uint32_t sizeElement = + ecmascript::base::TypedArrayHelper::GetElementSize(JSHandle::Cast(handleUint32Array)); + uint32_t byteLengthViewdArrayBuffer = sizeElement * numElementsUint32Array; + JSHandle handleTagValArrayBufferFrom = + JSHandle::Cast(factory->NewJSArrayBuffer(byteLengthViewdArrayBuffer)); + handleUint32Array->SetViewedArrayBuffer(thread, handleTagValArrayBufferFrom); + handleUint32Array->SetArrayLength(thread, JSTaggedValue(numElementsUint32Array)); + + CVector cVecOpResult = {}; + for (uint32_t i = 0; i < numElementsUint32Array; i++) { + EXPECT_TRUE(JSTypedArray::IntegerIndexedElementSet( + thread, handleTagValUint32Array, JSTaggedValue(i), + JSHandle(thread, + JSTaggedValue(std::numeric_limits::min() + i * scaleForUint32ValueSet)))); + OperationResult opResult = + JSTypedArray::IntegerIndexedElementGet(thread, handleTagValUint32Array, JSTaggedValue(i)); + cVecOpResult.push_back(opResult); + } + for (uint32_t i = 0; i < numElementsUint32Array; i++) { + EXPECT_EQ(cVecOpResult.at(i).GetValue().GetTaggedValue().GetNumber(), + std::numeric_limits::min() + i * scaleForUint32ValueSet); + } + cVecOpResult.clear(); + + OperationResult opResult1 = + JSTypedArray::IntegerIndexedElementGet(thread, handleTagValUint32Array, JSTaggedValue(-1)); + OperationResult opResult2 = + JSTypedArray::IntegerIndexedElementGet(thread, handleTagValUint32Array, JSTaggedValue(-0.0)); + OperationResult opResult3 = + JSTypedArray::IntegerIndexedElementGet(thread, handleTagValUint32Array, JSTaggedValue(1.1)); + OperationResult opResult4 = + JSTypedArray::IntegerIndexedElementGet(thread, handleTagValUint32Array, JSTaggedValue(numElementsUint32Array)); + EXPECT_EQ(opResult1.GetValue().GetTaggedValue(), JSTaggedValue::Undefined()); + EXPECT_EQ(opResult2.GetValue().GetTaggedValue(), JSTaggedValue::Undefined()); + EXPECT_EQ(opResult3.GetValue().GetTaggedValue(), JSTaggedValue::Undefined()); + EXPECT_EQ(opResult4.GetValue().GetTaggedValue(), JSTaggedValue::Undefined()); + + EXPECT_FALSE(JSTypedArray::IntegerIndexedElementSet(thread, handleTagValUint32Array, JSTaggedValue(-1), + JSHandle(thread, JSTaggedValue(0)))); + EXPECT_FALSE(JSTypedArray::IntegerIndexedElementSet(thread, handleTagValUint32Array, + JSTaggedValue(numElementsUint32Array), + JSHandle(thread, JSTaggedValue(0)))); +} + +// Nonstandard input value for Uint32Array +TEST_F(JSTypedArrayTest, IntegerIndexedElementSet_Uint32Array_002) +{ + int32_t numElementsUint32Array = 16; + ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); + JSHandle handleUint32Array = CreateNumberTypedArray(thread, JSType::JS_UINT32_ARRAY); + JSHandle handleTagValUint32Array = JSHandle::Cast(handleUint32Array); + + uint32_t sizeElement = + ecmascript::base::TypedArrayHelper::GetElementSize(JSHandle::Cast(handleUint32Array)); + int32_t byteLengthViewdArrayBuffer = sizeElement * numElementsUint32Array; + JSHandle handleTagValArrayBufferFrom = + JSHandle::Cast(factory->NewJSArrayBuffer(byteLengthViewdArrayBuffer)); + handleUint32Array->SetViewedArrayBuffer(thread, handleTagValArrayBufferFrom); + handleUint32Array->SetArrayLength(thread, JSTaggedValue(numElementsUint32Array)); + + int64_t value1 = -1; // to uint32 : 4294967295 + int64_t value2 = 4294967296; // to uint32 : 0 + double value3 = 13.4; // to uint32 : 13 + double value4 = 13.6; // to uint32 : 13 + JSHandle handleTagValValueSet1(thread, JSTaggedValue(value1)); + JSHandle handleTagValValueSet2(thread, JSTaggedValue(value2)); + JSHandle handleTagValValueSet3(thread, JSTaggedValue(value3)); + JSHandle handleTagValValueSet4(thread, JSTaggedValue(value4)); + + EXPECT_TRUE(JSTypedArray::IntegerIndexedElementSet(thread, handleTagValUint32Array, JSTaggedValue(0), + handleTagValValueSet1)); + OperationResult opResult1 = + JSTypedArray::IntegerIndexedElementGet(thread, handleTagValUint32Array, JSTaggedValue(0)); + EXPECT_TRUE(JSTypedArray::IntegerIndexedElementSet(thread, handleTagValUint32Array, JSTaggedValue(0), + handleTagValValueSet2)); + OperationResult opResult2 = + JSTypedArray::IntegerIndexedElementGet(thread, handleTagValUint32Array, JSTaggedValue(0)); + EXPECT_TRUE(JSTypedArray::IntegerIndexedElementSet(thread, handleTagValUint32Array, JSTaggedValue(0), + handleTagValValueSet3)); + OperationResult opResult3 = + JSTypedArray::IntegerIndexedElementGet(thread, handleTagValUint32Array, JSTaggedValue(0)); + EXPECT_TRUE(JSTypedArray::IntegerIndexedElementSet(thread, handleTagValUint32Array, JSTaggedValue(0), + handleTagValValueSet4)); + OperationResult opResult4 = + JSTypedArray::IntegerIndexedElementGet(thread, handleTagValUint32Array, JSTaggedValue(0)); + + EXPECT_NE(value1, opResult1.GetValue().GetTaggedValue().GetNumber()); + EXPECT_NE(value2, opResult2.GetValue().GetTaggedValue().GetNumber()); + EXPECT_NE(value3, opResult3.GetValue().GetTaggedValue().GetNumber()); + EXPECT_NE(value4, opResult4.GetValue().GetTaggedValue().GetNumber()); + EXPECT_EQ(4294967295, opResult1.GetValue().GetTaggedValue().GetNumber()); + EXPECT_EQ(0, opResult2.GetValue().GetTaggedValue().GetNumber()); + EXPECT_EQ(13, opResult3.GetValue().GetTaggedValue().GetNumber()); + EXPECT_EQ(13, opResult4.GetValue().GetTaggedValue().GetNumber()); +} + +TEST_F(JSTypedArrayTest, IntegerIndexedElementSet_Float32Array) +{ + uint32_t numElementsFloat32Array = 100; + ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); + JSHandle handleFloat32Array = CreateNumberTypedArray(thread, JSType::JS_FLOAT32_ARRAY); + JSHandle handleTagValFloat32Array = JSHandle::Cast(handleFloat32Array); + + uint32_t sizeElement = + ecmascript::base::TypedArrayHelper::GetElementSize(JSHandle::Cast(handleFloat32Array)); + uint32_t byteLengthViewdArrayBuffer = sizeElement * numElementsFloat32Array; + JSHandle handleTagValArrayBufferFrom = + JSHandle::Cast(factory->NewJSArrayBuffer(byteLengthViewdArrayBuffer)); + handleFloat32Array->SetViewedArrayBuffer(thread, handleTagValArrayBufferFrom); + handleFloat32Array->SetArrayLength(thread, JSTaggedValue(numElementsFloat32Array)); + + CVector cVecOpResult = {}; + float floatMaxValue = std::numeric_limits::max(); + for (uint32_t i = 0; i < numElementsFloat32Array; i++) { + EXPECT_TRUE(JSTypedArray::IntegerIndexedElementSet( + thread, handleTagValFloat32Array, JSTaggedValue(i), + JSHandle(thread, + JSTaggedValue(floatMaxValue - (i * (floatMaxValue / numElementsFloat32Array)))))); + OperationResult opResult = + JSTypedArray::IntegerIndexedElementGet(thread, handleTagValFloat32Array, JSTaggedValue(i)); + cVecOpResult.push_back(opResult); + } + for (uint32_t i = 0; i < numElementsFloat32Array; i++) { + EXPECT_EQ(cVecOpResult.at(i).GetValue().GetTaggedValue().GetNumber(), + floatMaxValue - (i * (floatMaxValue / numElementsFloat32Array))); + } + cVecOpResult.clear(); + + OperationResult opResult1 = + JSTypedArray::IntegerIndexedElementGet(thread, handleTagValFloat32Array, JSTaggedValue(-1)); + OperationResult opResult2 = + JSTypedArray::IntegerIndexedElementGet(thread, handleTagValFloat32Array, JSTaggedValue(-0.0)); + OperationResult opResult3 = + JSTypedArray::IntegerIndexedElementGet(thread, handleTagValFloat32Array, JSTaggedValue(1.1)); + OperationResult opResult4 = JSTypedArray::IntegerIndexedElementGet(thread, handleTagValFloat32Array, + JSTaggedValue(numElementsFloat32Array)); + EXPECT_EQ(opResult1.GetValue().GetTaggedValue(), JSTaggedValue::Undefined()); + EXPECT_EQ(opResult2.GetValue().GetTaggedValue(), JSTaggedValue::Undefined()); + EXPECT_EQ(opResult3.GetValue().GetTaggedValue(), JSTaggedValue::Undefined()); + EXPECT_EQ(opResult4.GetValue().GetTaggedValue(), JSTaggedValue::Undefined()); + + EXPECT_FALSE(JSTypedArray::IntegerIndexedElementSet(thread, handleTagValFloat32Array, JSTaggedValue(-1), + JSHandle(thread, JSTaggedValue(0)))); + EXPECT_FALSE(JSTypedArray::IntegerIndexedElementSet(thread, handleTagValFloat32Array, + JSTaggedValue(numElementsFloat32Array), + JSHandle(thread, JSTaggedValue(0)))); +} + +TEST_F(JSTypedArrayTest, IntegerIndexedElementSet_Float64Array) +{ + uint32_t numElementsFloat64Array = 100; + ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); + JSHandle handleFloat64Array = CreateNumberTypedArray(thread, JSType::JS_FLOAT64_ARRAY); + JSHandle handleTagValFloat64Array = JSHandle::Cast(handleFloat64Array); + + uint32_t sizeElement = + ecmascript::base::TypedArrayHelper::GetElementSize(JSHandle::Cast(handleFloat64Array)); + uint32_t byteLengthViewdArrayBuffer = sizeElement * numElementsFloat64Array; + JSHandle handleTagValArrayBufferFrom = + JSHandle::Cast(factory->NewJSArrayBuffer(byteLengthViewdArrayBuffer)); + handleFloat64Array->SetViewedArrayBuffer(thread, handleTagValArrayBufferFrom); + handleFloat64Array->SetArrayLength(thread, JSTaggedValue(numElementsFloat64Array)); + + CVector cVecOpResult = {}; + double doubleMaxValue = std::numeric_limits::max(); + for (uint32_t i = 0; i < numElementsFloat64Array; i++) { + EXPECT_TRUE(JSTypedArray::IntegerIndexedElementSet( + thread, handleTagValFloat64Array, JSTaggedValue(i), + JSHandle(thread, + JSTaggedValue(doubleMaxValue - (i * (doubleMaxValue / numElementsFloat64Array)))))); + OperationResult opResult = + JSTypedArray::IntegerIndexedElementGet(thread, handleTagValFloat64Array, JSTaggedValue(i)); + cVecOpResult.push_back(opResult); + } + for (uint32_t i = 0; i < numElementsFloat64Array; i++) { + EXPECT_EQ(cVecOpResult.at(i).GetValue().GetTaggedValue().GetNumber(), + doubleMaxValue - (i * (doubleMaxValue / numElementsFloat64Array))); + } + cVecOpResult.clear(); + + OperationResult opResult1 = + JSTypedArray::IntegerIndexedElementGet(thread, handleTagValFloat64Array, JSTaggedValue(-1)); + OperationResult opResult2 = + JSTypedArray::IntegerIndexedElementGet(thread, handleTagValFloat64Array, JSTaggedValue(-0.0)); + OperationResult opResult3 = + JSTypedArray::IntegerIndexedElementGet(thread, handleTagValFloat64Array, JSTaggedValue(1.1)); + OperationResult opResult4 = JSTypedArray::IntegerIndexedElementGet(thread, handleTagValFloat64Array, + JSTaggedValue(numElementsFloat64Array)); + EXPECT_EQ(opResult1.GetValue().GetTaggedValue(), JSTaggedValue::Undefined()); + EXPECT_EQ(opResult2.GetValue().GetTaggedValue(), JSTaggedValue::Undefined()); + EXPECT_EQ(opResult3.GetValue().GetTaggedValue(), JSTaggedValue::Undefined()); + EXPECT_EQ(opResult4.GetValue().GetTaggedValue(), JSTaggedValue::Undefined()); + + EXPECT_FALSE(JSTypedArray::IntegerIndexedElementSet(thread, handleTagValFloat64Array, JSTaggedValue(-1), + JSHandle(thread, JSTaggedValue(0)))); + EXPECT_FALSE(JSTypedArray::IntegerIndexedElementSet(thread, handleTagValFloat64Array, + JSTaggedValue(numElementsFloat64Array), + JSHandle(thread, JSTaggedValue(0)))); +} + +/* + * Feature: JSTypedArray + * Function: FastElementGet + * SubFunction: IntegerIndexedElementSet + * FunctionPoints: Get Element At Index(uint32_t) Of JSTypedArray + * CaseDescription: Check whether the OperationResults returned through calling FastElementGet function from the + * JSTypedArray changed through calling IntegerIndexedElementSet function are within expectations. + */ +TEST_F(JSTypedArrayTest, FastElementGet_TypedArray) +{ + ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); + + for (uint32_t j = 0; j < cVecJSType.size(); j++) { + uint32_t numElementsTypedArray = 10; + JSHandle handleTypedArray = CreateNumberTypedArray(thread, cVecJSType.at(j)); + JSHandle handleTagValTypedArray = JSHandle::Cast(handleTypedArray); + + uint32_t sizeElement = + ecmascript::base::TypedArrayHelper::GetElementSize(JSHandle::Cast(handleTypedArray)); + uint32_t byteLengthViewdArrayBuffer = sizeElement * numElementsTypedArray; + JSHandle handleTagValArrayBufferFrom = + JSHandle::Cast(factory->NewJSArrayBuffer(byteLengthViewdArrayBuffer)); + handleTypedArray->SetViewedArrayBuffer(thread, handleTagValArrayBufferFrom); + handleTypedArray->SetArrayLength(thread, JSTaggedValue(numElementsTypedArray)); + + JSHandle handleTagValValueSet(thread, JSTaggedValue(cVecHandleTagValValueForTypedArray.at(j))); + for (uint32_t i = 0; i < numElementsTypedArray; i++) { + EXPECT_TRUE(JSTypedArray::IntegerIndexedElementSet(thread, handleTagValTypedArray, JSTaggedValue(i), + handleTagValValueSet)); + } + for (uint32_t i = 0; i < numElementsTypedArray; i++) { + OperationResult opResult = JSTypedArray::FastElementGet(thread, handleTagValTypedArray, i); + EXPECT_EQ(opResult.GetValue().GetTaggedValue().GetNumber(), + handleTagValValueSet.GetTaggedValue().GetNumber()); + } + + OperationResult opResult1 = JSTypedArray::FastElementGet(thread, handleTagValTypedArray, -1); + OperationResult opResult2 = JSTypedArray::FastElementGet(thread, handleTagValTypedArray, numElementsTypedArray); + EXPECT_EQ(opResult1.GetValue().GetTaggedValue(), JSTaggedValue::Undefined()); + EXPECT_EQ(opResult2.GetValue().GetTaggedValue(), JSTaggedValue::Undefined()); + } +} + +/* + * Feature: JSTypedArray + * Function: DefineOwnProperty + * SubFunction: GetOwnProperty/HasProperty + * PropertyDescriptor::HasWritable/HasEnumerable/HasConfigurable/IsWritable/IsEnumerable/IsConfigurable + * FunctionPoints: Define Own Property For Element At Index(JSTaggedValue) Of JSTypedArray + * CaseDescription: Call DefineOwnProperty function with a JSTypedArray, a index(JSTaggedValue) and a source + * PropertyDescriptor, check whether the bool returned through calling HasProperty function with the + * JSTypedArray and the index(JSTaggedValue) is within expectations, check whether the target + * PropertyDescriptor changed through GetOwnProperty function is with expectations. + */ +TEST_F(JSTypedArrayTest, DefineOwnProperty_TypedArray) +{ + ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); + + for (size_t j = 0; j < cVecJSType.size(); j++) { + int32_t numElementsTypedArray = 10; + JSHandle handleTagValValueDef(thread, cVecHandleTagValValueForTypedArray.at(j)); + PropertyDescriptor descFrom1(thread, handleTagValValueDef, true, true, true); + JSHandle handleTypedArray = CreateNumberTypedArray(thread, cVecJSType.at(j)); + JSHandle handleTagValTypedArray = JSHandle::Cast(handleTypedArray); + + uint32_t sizeElement = + ecmascript::base::TypedArrayHelper::GetElementSize(JSHandle::Cast(handleTypedArray)); + int32_t byteLengthViewdArrayBuffer = sizeElement * numElementsTypedArray; + JSHandle handleTagValArrayBufferFrom = + JSHandle::Cast(factory->NewJSArrayBuffer(byteLengthViewdArrayBuffer)); + handleTypedArray->SetViewedArrayBuffer(thread, handleTagValArrayBufferFrom); + handleTypedArray->SetArrayLength(thread, JSTaggedValue(numElementsTypedArray)); + + for (int32_t i = 0; i < numElementsTypedArray; i++) { + JSHandle handleTagValKey(thread, JSTaggedValue(i)); + EXPECT_FALSE(JSTypedArray::HasProperty(thread, handleTagValTypedArray, handleTagValKey)); + EXPECT_TRUE(JSTypedArray::DefineOwnProperty(thread, handleTagValTypedArray, handleTagValKey, descFrom1)); + EXPECT_TRUE(JSTypedArray::HasProperty(thread, handleTagValTypedArray, handleTagValKey)); + EXPECT_TRUE(JSTaggedValue::StrictEqual( + thread, handleTagValValueDef, + JSTypedArray::GetProperty(thread, handleTagValTypedArray, handleTagValKey).GetValue())); + + PropertyDescriptor descTo1(thread); + EXPECT_FALSE(descTo1.HasWritable() || descTo1.HasEnumerable() || descTo1.HasConfigurable()); + EXPECT_TRUE(JSTypedArray::GetOwnProperty(thread, handleTagValTypedArray, handleTagValKey, descTo1)); + EXPECT_TRUE(descTo1.HasWritable() && descTo1.HasEnumerable() && descTo1.HasConfigurable()); + EXPECT_TRUE(descTo1.IsWritable() && descTo1.IsEnumerable() && descTo1.IsConfigurable()); + EXPECT_TRUE(JSTaggedValue::StrictEqual(thread, descTo1.GetValue(), handleTagValValueDef)); + } + } +} + +/* + * Feature: JSTypedArray + * Function: SetProperty + * SubFunction: GetProperty/HasProperty + * FunctionPoints: Set Property For Element At Index(JSTaggedValue) Of JSTypedArray + * CaseDescription: Call SetProperty function with a JSTypedArray, a index(JSTaggedValue) and a source + * value(JSTaggedValue), check whether the bool returned through calling HasProperty function with the + * JSTypedArray and the index(JSTaggedValue) is within expectations, check whether the + * value(JSTaggedValue) of the OperationResult returned through calling GetProperty function with the + * JSTypedArray and the index(JSTaggedValue) is the same with the source value(JSTaggedValue). + */ +TEST_F(JSTypedArrayTest, SetProperty_TypedArray) +{ + ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); + + for (size_t j = 0; j < cVecJSType.size(); j++) { + int32_t numElementsTypedArray = 10; + JSHandle handleTagValValueSet(thread, cVecHandleTagValValueForTypedArray.at(j)); + JSHandle handleTypedArray = CreateNumberTypedArray(thread, cVecJSType.at(j)); + JSHandle handleTagValTypedArray = JSHandle::Cast(handleTypedArray); + + uint32_t sizeElement = + ecmascript::base::TypedArrayHelper::GetElementSize(JSHandle::Cast(handleTypedArray)); + int32_t byteLengthViewdArrayBuffer = sizeElement * numElementsTypedArray; + JSHandle handleTagValArrayBufferFrom = + JSHandle::Cast(factory->NewJSArrayBuffer(byteLengthViewdArrayBuffer)); + handleTypedArray->SetViewedArrayBuffer(thread, handleTagValArrayBufferFrom); + handleTypedArray->SetArrayLength(thread, JSTaggedValue(numElementsTypedArray)); + + for (int32_t i = 0; i < numElementsTypedArray; i++) { + JSHandle handleTagValKey(thread, JSTaggedValue(i)); + EXPECT_FALSE(JSTypedArray::HasProperty(thread, handleTagValTypedArray, handleTagValKey)); + EXPECT_TRUE( + JSTypedArray::SetProperty(thread, handleTagValTypedArray, handleTagValKey, handleTagValValueSet)); + EXPECT_TRUE(JSTypedArray::HasProperty(thread, handleTagValTypedArray, handleTagValKey)); + EXPECT_TRUE(JSTaggedValue::StrictEqual( + thread, handleTagValValueSet, + JSTypedArray::GetProperty(thread, handleTagValTypedArray, handleTagValKey).GetValue())); + } + } +} + +/* + * Feature: JSTypedArray + * Function: FastCopyElementToArray + * SubFunction: IntegerIndexedElementSet/TaggedArray::Get + * FunctionPoints: Copy All Elements Of JSTypedArray To TaggedArray Fast + * CaseDescription: Create a source JSTypedArray and a target TaggedArray, init the elements of the source JSTypedArray + * with a certain value(JSTaggedValue) through calling IntegerIndexedElementSet function. Call + * FastCopyElementToArray function with the source JSTypedArray and the target TaggedArray. Check + * whether the values(JSTaggedValue) returned through Get(TaggedArray) function are the same with the + * certain value(JSTaggedValue). + */ +TEST_F(JSTypedArrayTest, FastCopyElementToArray_TypedArray) +{ + ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); + + for (size_t j = 0; j < cVecJSType.size(); j++) { + int32_t numElementsTypedArray = 10; + JSHandle handleTagValValueSet(thread, cVecHandleTagValValueForTypedArray.at(j)); + JSHandle handleTypedArrayFrom = CreateNumberTypedArray(thread, cVecJSType.at(j)); + JSHandle handleTagValTypedArrayFrom = JSHandle::Cast(handleTypedArrayFrom); + + uint32_t sizeElement = + ecmascript::base::TypedArrayHelper::GetElementSize(JSHandle::Cast(handleTypedArrayFrom)); + int32_t byteLengthViewdArrayBuffer = sizeElement * numElementsTypedArray; + JSHandle handleTagValArrayBuffer = + JSHandle::Cast(factory->NewJSArrayBuffer(byteLengthViewdArrayBuffer)); + JSHandle handleTagArrTo = factory->NewTaggedArray(byteLengthViewdArrayBuffer); + handleTypedArrayFrom->SetViewedArrayBuffer(thread, handleTagValArrayBuffer); + handleTypedArrayFrom->SetArrayLength(thread, JSTaggedValue(numElementsTypedArray)); + + for (int32_t i = 0; i < numElementsTypedArray; i++) { + EXPECT_TRUE(JSTypedArray::IntegerIndexedElementSet(thread, handleTagValTypedArrayFrom, JSTaggedValue(i), + handleTagValValueSet)); + } + EXPECT_TRUE(JSTypedArray::FastCopyElementToArray(thread, handleTagValTypedArrayFrom, handleTagArrTo)); + for (int32_t i = 0; i < numElementsTypedArray; i++) { + EXPECT_EQ(handleTagArrTo->Get(i), handleTagValValueSet.GetTaggedValue()); + } + } +} +} // namespace panda::test diff --git a/tests/runtime/common/js_verification_test.cpp b/tests/runtime/common/js_verification_test.cpp new file mode 100644 index 000000000..84b1f7063 --- /dev/null +++ b/tests/runtime/common/js_verification_test.cpp @@ -0,0 +1,107 @@ +/* + * Copyright (c) 2021-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 "gtest/gtest.h" +#include "test_helper.h" + +#include "plugins/ecmascript/runtime/ecma_string.h" +#include "plugins/ecmascript/runtime/js_thread.h" +#include "include/runtime.h" +#include "plugins/ecmascript/runtime/object_factory.h" +#include "plugins/ecmascript/runtime/ecma_vm.h" +#include "plugins/ecmascript/runtime/tagged_array-inl.h" +#include "plugins/ecmascript/runtime/mem/verification.h" +#include "plugins/ecmascript/runtime/js_object.h" +#include "plugins/ecmascript/runtime/js_hclass.h" +#include "plugins/ecmascript/runtime/mem/space.h" +#include "plugins/ecmascript/runtime/mem/heap.h" + +using namespace panda::ecmascript; +using namespace panda::ecmascript::base; +using namespace panda::coretypes; + +namespace panda::test { +class JSVerificationTest : public testing::Test { +public: + void SetUp() override + { + TestHelper::CreateEcmaVMWithScope(instance, thread, scope); + } + + void TearDown() override + { + TestHelper::DestroyEcmaVMWithScope(instance, scope); + } + + PandaVM *instance {nullptr}; + EcmaHandleScope *scope {nullptr}; + JSThread *thread; +}; + +TEST_F(JSVerificationTest, DISABLED_IsHeapAddress) // issue #5368 +{ + auto ecmaVm = thread->GetEcmaVM(); + auto heap = ecmaVm->GetHeap(); + auto objectFactory = ecmaVm->GetFactory(); + auto verifier = Verification(heap); + EXPECT_FALSE(verifier.IsHeapAddress(reinterpret_cast(1))); + EXPECT_FALSE(verifier.IsHeapAddress(reinterpret_cast(2))); + EXPECT_FALSE(verifier.IsHeapAddress(nullptr)); + EXPECT_FALSE(verifier.IsHeapAddress(&verifier)); + + auto funcVerify = [&](ObjectHeader *object, Verification &v, const Heap *h) { + EXPECT_TRUE(v.IsHeapAddress(reinterpret_cast(object))); + EXPECT_TRUE(h->ContainObject(TaggedObject::Cast(object))); + EXPECT_TRUE(h->IsLive(TaggedObject::Cast(object))); + }; + + // new space object + JSHandle string = objectFactory->NewFromString("123"); + funcVerify(*string, verifier, heap); + + // old space object + auto oldArray = objectFactory->NewTaggedArray(2, JSTaggedValue::Undefined(), MemSpaceType::OLD_SPACE); + funcVerify(*oldArray, verifier, heap); + + // no movable object + auto nonMovableArray = objectFactory->NewTaggedArray(2, JSTaggedValue::Undefined(), MemSpaceType::NON_MOVABLE); + funcVerify(*nonMovableArray, verifier, heap); +} + +TEST_F(JSVerificationTest, DISABLED_VerifyHeapObjects) // issue #5368 +{ + auto ecmaVm = thread->GetEcmaVM(); + auto heap = const_cast(ecmaVm->GetHeap()); + auto objectFactory = ecmaVm->GetFactory(); + EXPECT_TRUE(heap->VerifyHeapObjects() == 0); // failcount is 0 + + JSTaggedValue oldArray; + auto verifier = Verification(heap); + { + EcmaHandleScope handleScope(thread); + auto newArray = objectFactory->NewTaggedArray(1, JSTaggedValue::Undefined(), MemSpaceType::SEMI_SPACE); + + oldArray = + (objectFactory->NewTaggedArray(1, JSTaggedValue::Undefined(), MemSpaceType::NON_MOVABLE)).GetTaggedValue(); + newArray->Set(thread, 0, oldArray); + } + ecmaVm->GetGC()->WaitForGC(GCTask(GCTaskCause::EXPLICIT_CAUSE)); + EXPECT_TRUE(verifier.VerifyRoot() == 0); + size_t failCount = 0; + VerifyObjectVisitor objVerifier(heap, &failCount); + heap->GetNewSpace()->IterateOverObjects(objVerifier); // newspace reference the old space + EXPECT_TRUE(failCount != 0); +} +} // namespace panda::test diff --git a/tests/runtime/common/large_object_test.cpp b/tests/runtime/common/large_object_test.cpp new file mode 100644 index 000000000..6ba931dcc --- /dev/null +++ b/tests/runtime/common/large_object_test.cpp @@ -0,0 +1,119 @@ +/* + * Copyright (c) 2021-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 "gtest/gtest.h" +#include "test_helper.h" + +#include "handle_base.h" +#include "include/runtime.h" +#include "runtime/include/thread_scopes.h" +#include "plugins/ecmascript/runtime/ecma_vm.h" +#include "plugins/ecmascript/runtime/global_env.h" +#include "plugins/ecmascript/runtime/js_handle.h" +#include "plugins/ecmascript/runtime/tagged_array-inl.h" +#include "plugins/ecmascript/runtime/object_factory.h" +#include "plugins/ecmascript/runtime/mem/space.h" +#include "plugins/ecmascript/runtime/mem/verification.h" + +using namespace panda::ecmascript; +using namespace panda::coretypes; + +namespace panda::test { +class LargeObjectTest : public testing::Test { +public: + void SetUp() override + { + TestHelper::CreateEcmaVMWithScope(instance, thread, scope); + thread->GetEcmaVM()->GetFactory()->SetTriggerGc(false); + } + + void TearDown() override + { + TestHelper::DestroyEcmaVMWithScope(instance, scope); + } + + JSThread *thread; + PandaVM *instance {nullptr}; + ecmascript::EcmaHandleScope *scope {nullptr}; +}; + +#if !defined(NDEBUG) +static JSObject *JSObjectTestCreate(JSThread *thread) +{ + [[maybe_unused]] ecmascript::EcmaHandleScope scope(thread); + EcmaVM *ecma_vm = thread->GetEcmaVM(); + JSHandle global_env = ecma_vm->GetGlobalEnv(); + JSHandle js_func = global_env->GetObjectFunction(); + JSHandle new_obj = + ecma_vm->GetFactory()->NewJSObjectByConstructor(JSHandle(js_func), js_func); + return *new_obj; +} +#endif + +#if !defined(NDEBUG) +static TaggedArray *LargeArrayTestCreate(JSThread *thread) +{ + [[maybe_unused]] ecmascript::EcmaHandleScope scope(thread); + JSHandle array = thread->GetEcmaVM()->GetFactory()->NewTaggedArray(20 * 10000); + return *array; +} +#endif + +TEST_F(LargeObjectTest, LargeArrayKeep) +{ +#if !defined(NDEBUG) + TaggedArray *array = LargeArrayTestCreate(thread); + EXPECT_TRUE(array != nullptr); + JSHandle arrayHandle(thread, array); + JSHandle newObj(thread, JSObjectTestCreate(thread)); + arrayHandle->Set(thread, 0, newObj.GetTaggedValue()); + auto ecmaVm = thread->GetEcmaVM(); + EXPECT_EQ(*arrayHandle, reinterpret_cast(array)); + ecmaVm->GetGC()->WaitForGCInManaged(GCTask(GCTaskCause::EXPLICIT_CAUSE)); // Trigger GC. + ecmaVm->GetGC()->WaitForGCInManaged(GCTask(GCTaskCause::EXPLICIT_CAUSE)); // Trigger GC. + EXPECT_EQ(*newObj, array->Get(0).GetHeapObject()); + EXPECT_EQ(*arrayHandle, reinterpret_cast(array)); +#endif +} + +TEST_F(LargeObjectTest, DISABLED_MultipleArrays) // issue #5368 +{ +#if !defined(NDEBUG) + auto ecmaVm = thread->GetEcmaVM(); + auto heap = ecmaVm->GetHeap(); + const HugeObjectSpace *space = heap->GetHugeObjectSpace(); + JSHandle array1(thread, LargeArrayTestCreate(thread)); + { + DISALLOW_GARBAGE_COLLECTION; + [[maybe_unused]] TaggedArray *array2 = LargeArrayTestCreate(thread); + } + JSHandle array3(thread, LargeArrayTestCreate(thread)); + + Region *firstPage = space->GetRegionList().GetFirst(); + Region *secondPage = firstPage->GetNext(); + Region *thirdPage = secondPage->GetNext(); + + ecmaVm->GetGC()->WaitForGCInManaged(GCTask(GCTaskCause::EXPLICIT_CAUSE)); // Trigger GC. + + EXPECT_EQ(firstPage->GetNext(), thirdPage); + + size_t failCount = 0; + VerifyObjectVisitor objVerifier(heap, &failCount); + heap->GetHugeObjectSpace()->IterateOverObjects(objVerifier); // newspace reference the old space + EXPECT_TRUE(failCount == 0); +#endif +} + +} // namespace panda::test diff --git a/tests/runtime/common/lexical_env_test.cpp b/tests/runtime/common/lexical_env_test.cpp new file mode 100644 index 000000000..520599a74 --- /dev/null +++ b/tests/runtime/common/lexical_env_test.cpp @@ -0,0 +1,47 @@ +/* + * Copyright (c) 2021-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 "gtest/gtest.h" +#include "test_helper.h" +#include "include/runtime.h" +#include "plugins/ecmascript/runtime/lexical_env.h" +#include "plugins/ecmascript/runtime/object_factory.h" + +using namespace panda::ecmascript; + +namespace panda::test { +class LexicalEnvTest : public testing::Test { +public: + void SetUp() override + { + TestHelper::CreateEcmaVMWithScope(instance, thread, scope); + } + + void TearDown() override + { + TestHelper::DestroyEcmaVMWithScope(instance, scope); + } + + PandaVM *instance {nullptr}; + ecmascript::EcmaHandleScope *scope {nullptr}; + JSThread *thread; +}; + +TEST_F(LexicalEnvTest, LexicalEnv_Create) +{ + JSHandle lexicalEnv = thread->GetEcmaVM()->GetFactory()->NewLexicalEnv(0); + EXPECT_TRUE(lexicalEnv.GetTaggedValue().IsObject()); +} +} // namespace panda::test diff --git a/tests/runtime/common/lexicalenv/CMakeLists.txt b/tests/runtime/common/lexicalenv/CMakeLists.txt new file mode 100644 index 000000000..d98533f5f --- /dev/null +++ b/tests/runtime/common/lexicalenv/CMakeLists.txt @@ -0,0 +1,23 @@ +# Huawei Technologies Co.,Ltd. + +set(LEXICALENV_OUTPUT ${CMAKE_CURRENT_BINARY_DIR}/lexicalenv.txt) +set(LEXICALENV_BIN ${CMAKE_CURRENT_BINARY_DIR}/lexicalenv.abc) +set(LEXICALENV_PA ${CMAKE_CURRENT_BINARY_DIR}/lexicalenv.pa) +set(LEXICALENV_JS ${CMAKE_CURRENT_SOURCE_DIR}/lexicalenv.js) +set(LEXICALENV_VERIFY ${CMAKE_CURRENT_SOURCE_DIR}/verify.sh) + +set(RUNTIME_ARGUMENTS --boot-panda-files=${PANDA_BINARY_ROOT}/pandastdlib/arkstdlib.abc --load-runtimes=\"ecmascript\" --compiler-enable-jit=false --gc-type=stw --run-gc-in-place ${LEXICALENV_BIN} _GLOBAL::func_main_0) + +add_custom_command( + OUTPUT ${LEXICALENV_OUTPUT} + COMMENT "running javascript lexicalenv testcase" + COMMAND ${PANDA_RUN_PREFIX} $ ${LEXICALENV_JS} --dump-assembly --output ${LEXICALENV_BIN} > ${LEXICALENV_PA} + COMMAND rm -f ${LEXICALENV_OUTPUT} + COMMAND ${PANDA_RUN_PREFIX} $ ${RUNTIME_ARGUMENTS} > ${LEXICALENV_OUTPUT} + COMMAND bash ${LEXICALENV_VERIFY} ${LEXICALENV_OUTPUT} +) +add_custom_target(lexicalenv + DEPENDS ${LEXICALENV_OUTPUT} ${LEXICALENV_VERIFY} +) +add_dependencies(lexicalenv es2panda ark) +add_dependencies(ecmascript_common_tests lexicalenv) diff --git a/tests/runtime/common/lexicalenv/lexicalenv.js b/tests/runtime/common/lexicalenv/lexicalenv.js new file mode 100644 index 000000000..f95aea31f --- /dev/null +++ b/tests/runtime/common/lexicalenv/lexicalenv.js @@ -0,0 +1,10 @@ +function foo(a) { + var b = 2; + var c = 3 + function bar() { + print(a, b, c) + } + bar() +} + +foo(1) diff --git a/tests/runtime/common/lexicalenv/verify.sh b/tests/runtime/common/lexicalenv/verify.sh new file mode 100755 index 000000000..d260194fa --- /dev/null +++ b/tests/runtime/common/lexicalenv/verify.sh @@ -0,0 +1,15 @@ +#!/bin/bash +# Copyright (c) Huawei Technologies Co., Ltd. 2020-2021. All rights reserved. + +set -eo pipefail + +expected="1 2 3" +actual=$(cat "$1") +if [[ "$actual" == "$expected" ]];then + exit 0; +else + echo -e "expected:"$expected + echo -e "actual:"$actual + echo -e "\033[31mlexicalenv test failed\033[0m" + exit 1; +fi diff --git a/tests/runtime/common/linked_hash_table_test.cpp b/tests/runtime/common/linked_hash_table_test.cpp new file mode 100644 index 000000000..656505368 --- /dev/null +++ b/tests/runtime/common/linked_hash_table_test.cpp @@ -0,0 +1,284 @@ +/* + * Copyright (c) 2021-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 "plugins/ecmascript/runtime/ecma_vm.h" +#include "plugins/ecmascript/runtime/global_env.h" +#include "plugins/ecmascript/runtime/js_handle.h" +#include "plugins/ecmascript/runtime/js_object-inl.h" +#include "plugins/ecmascript/runtime/js_tagged_value.h" +#include "plugins/ecmascript/runtime/linked_hash_table-inl.h" +#include "plugins/ecmascript/runtime/linked_hash_table.h" +#include "plugins/ecmascript/runtime/object_factory.h" +#include "plugins/ecmascript/runtime/tagged_hash_table-inl.h" +#include "plugins/ecmascript/tests/runtime/common/test_helper.h" + +using namespace panda; + +using namespace panda::ecmascript; + +namespace panda::test { +class LinkedHashTableTest : public testing::Test { +public: + static void SetUpTestCase() + { + GTEST_LOG_(INFO) << "SetUpTestCase"; + } + + static void TearDownTestCase() + { + GTEST_LOG_(INFO) << "TearDownCase"; + } + + void SetUp() override + { + TestHelper::CreateEcmaVMWithScope(instance, thread, scope); + } + + void TearDown() override + { + TestHelper::DestroyEcmaVMWithScope(instance, scope); + } + + PandaVM *instance {nullptr}; + EcmaHandleScope *scope {nullptr}; + JSThread *thread {nullptr}; + + JSHandle GetGlobalEnv() + { + EcmaVM *ecma = thread->GetEcmaVM(); + return ecma->GetGlobalEnv(); + } +}; + +TEST_F(LinkedHashTableTest, MapCreate) +{ + int numOfElement = 64; + JSHandle dict = LinkedHashMap::Create(thread, numOfElement); + EXPECT_TRUE(*dict != nullptr); +} + +TEST_F(LinkedHashTableTest, SetCreate) +{ + int numOfElement = 64; + JSHandle set = LinkedHashSet::Create(thread, numOfElement); + EXPECT_TRUE(*set != nullptr); +} + +TEST_F(LinkedHashTableTest, addKeyAndValue) +{ + ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); + // mock object needed in test + int numOfElement = 64; + JSHandle dictHandle = LinkedHashMap::Create(thread, numOfElement); + EXPECT_TRUE(*dictHandle != nullptr); + JSHandle objFun = GetGlobalEnv()->GetObjectFunction(); + + char keyArray[] = "hello"; + JSHandle stringKey1 = factory->NewFromCanBeCompressString(keyArray); + JSHandle key1(stringKey1); + JSHandle value1(factory->NewJSObjectByConstructor(JSHandle(objFun), objFun)); + + char key2Array[] = "hello2"; + JSHandle stringKey2 = factory->NewFromCanBeCompressString(key2Array); + JSHandle key2(stringKey2); + JSHandle value2(factory->NewJSObjectByConstructor(JSHandle(objFun), objFun)); + + // test set() + dictHandle = LinkedHashMap::Set(thread, dictHandle, key1, value1); + EXPECT_EQ(dictHandle->NumberOfElements(), 1); + + // test find() + int entry1 = dictHandle->FindElement(key1.GetTaggedValue()); + EXPECT_EQ(key1.GetTaggedValue(), dictHandle->GetKey(entry1)); + EXPECT_EQ(value1.GetTaggedValue(), dictHandle->GetValue(entry1)); + + dictHandle = LinkedHashMap::Set(thread, dictHandle, key2, value2); + EXPECT_EQ(dictHandle->NumberOfElements(), 2); + // test remove() + dictHandle = LinkedHashMap::Delete(thread, dictHandle, key1); + EXPECT_EQ(-1, dictHandle->FindElement(key1.GetTaggedValue())); + EXPECT_EQ(dictHandle->NumberOfElements(), 1); + + JSHandle undefinedKey(thread, JSTaggedValue::Undefined()); + dictHandle = LinkedHashMap::Set(thread, dictHandle, undefinedKey, value1); + int entry2 = dictHandle->FindElement(undefinedKey.GetTaggedValue()); + EXPECT_EQ(value1.GetTaggedValue(), dictHandle->GetValue(entry2)); +} + +TEST_F(LinkedHashTableTest, SetaddKeyAndValue) +{ + ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); + // mock object needed in test + int numOfElement = 64; + JSHandle setHandle = LinkedHashSet::Create(thread, numOfElement); + EXPECT_TRUE(*setHandle != nullptr); + JSHandle objFun = GetGlobalEnv()->GetObjectFunction(); + + char keyArray[] = "hello"; + JSHandle stringKey1 = factory->NewFromCanBeCompressString(keyArray); + JSHandle key1(stringKey1); + JSHandle value1(factory->NewJSObjectByConstructor(JSHandle(objFun), objFun)); + + char key2Array[] = "hello2"; + JSHandle stringKey2 = factory->NewFromCanBeCompressString(key2Array); + JSHandle key2(stringKey2); + JSHandle value2(factory->NewJSObjectByConstructor(JSHandle(objFun), objFun)); + + // test set() + setHandle = LinkedHashSet::Add(thread, setHandle, key1); + EXPECT_EQ(setHandle->NumberOfElements(), 1); + + // test has() + EXPECT_TRUE(setHandle->Has(key1.GetTaggedValue())); + + setHandle = LinkedHashSet::Add(thread, setHandle, key2); + EXPECT_EQ(setHandle->NumberOfElements(), 2); + // test remove() + setHandle = LinkedHashSet::Delete(thread, setHandle, key1); + EXPECT_EQ(-1, setHandle->FindElement(key1.GetTaggedValue())); + EXPECT_EQ(setHandle->NumberOfElements(), 1); + + JSHandle undefinedKey(thread, JSTaggedValue::Undefined()); + setHandle = LinkedHashSet::Add(thread, setHandle, undefinedKey); + EXPECT_TRUE(setHandle->Has(undefinedKey.GetTaggedValue())); +} + +TEST_F(LinkedHashTableTest, GrowCapacity) +{ + ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); + int numOfElement = 8; + JSHandle dictHandle = LinkedHashMap::Create(thread, numOfElement); + EXPECT_TRUE(*dictHandle != nullptr); + JSHandle objFun(GetGlobalEnv()->GetObjectFunction()); + char keyArray[7] = "hello"; + for (int i = 0; i < 33; i++) { + keyArray[5] = '1' + i; + keyArray[6] = 0; + JSHandle key(factory->NewFromCanBeCompressString(keyArray)); + JSHandle value(thread, JSTaggedValue(i)); + + // test insert() + dictHandle = LinkedHashMap::Set(thread, dictHandle, key, value); + EXPECT_EQ(i, dictHandle->FindElement(key.GetTaggedValue())); + } + + // test order + for (int i = 0; i < 33; i++) { + keyArray[5] = '1' + i; + keyArray[6] = 0; + JSHandle stringKey = factory->NewFromCanBeCompressString(keyArray); + // test insert() + EXPECT_EQ(i, dictHandle->FindElement(stringKey.GetTaggedValue())); + } + EXPECT_EQ(dictHandle->NumberOfElements(), 33); + EXPECT_EQ(dictHandle->Capacity(), 64); +} + +TEST_F(LinkedHashTableTest, SetGrowCapacity) +{ + ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); + int numOfElement = 8; + JSHandle setHandle = LinkedHashSet::Create(thread, numOfElement); + EXPECT_TRUE(*setHandle != nullptr); + JSHandle objFun(GetGlobalEnv()->GetObjectFunction()); + // create key and values + char keyArray[7] = "hello"; + for (int i = 0; i < 33; i++) { + keyArray[5] = '1' + i; + keyArray[6] = 0; + JSHandle stringKey = factory->NewFromCanBeCompressString(keyArray); + JSHandle key(stringKey); + + // test insert() + setHandle = LinkedHashSet::Add(thread, setHandle, key); + EXPECT_EQ(i, setHandle->FindElement(key.GetTaggedValue())); + } + + // test order + for (int i = 0; i < 33; i++) { + keyArray[5] = '1' + i; + keyArray[6] = 0; + JSHandle stringKey = factory->NewFromCanBeCompressString(keyArray); + // test insert() + EXPECT_EQ(i, setHandle->FindElement(stringKey.GetTaggedValue())); + } + EXPECT_EQ(setHandle->NumberOfElements(), 33); + EXPECT_EQ(setHandle->Capacity(), 64); +} + +TEST_F(LinkedHashTableTest, ShrinkCapacity) +{ + ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); + int numOfElement = 64; + JSHandle dictHandle = LinkedHashMap::Create(thread, numOfElement); + EXPECT_TRUE(*dictHandle != nullptr); + JSHandle objFun(GetGlobalEnv()->GetObjectFunction()); + char keyArray[7] = "hello"; + for (int i = 0; i < 10; i++) { + keyArray[5] = '1' + i; + keyArray[6] = 0; + JSHandle key(factory->NewFromCanBeCompressString(keyArray)); + JSHandle value(thread, JSTaggedValue(i)); + + // test insert() + dictHandle = LinkedHashMap::Set(thread, dictHandle, key, value); + } + keyArray[5] = '1' + 9; + JSHandle key(factory->NewFromCanBeCompressString(keyArray)); + dictHandle = LinkedHashMap::Delete(thread, dictHandle, key); + // test order + for (int i = 0; i < 9; i++) { + keyArray[5] = '1' + i; + keyArray[6] = 0; + JSHandle stringKey = factory->NewFromCanBeCompressString(keyArray); + // test insert() + EXPECT_EQ(i, dictHandle->FindElement(stringKey.GetTaggedValue())); + } + EXPECT_EQ(dictHandle->NumberOfElements(), 9); + EXPECT_EQ(dictHandle->Capacity(), 16); +} + +TEST_F(LinkedHashTableTest, SetShrinkCapacity) +{ + ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); + int numOfElement = 64; + JSHandle setHandle = LinkedHashSet::Create(thread, numOfElement); + EXPECT_TRUE(*setHandle != nullptr); + JSHandle objFun(GetGlobalEnv()->GetObjectFunction()); + // create key and values + char keyArray[7] = "hello"; + for (int i = 0; i < 10; i++) { + keyArray[5] = '1' + i; + keyArray[6] = 0; + JSHandle key(factory->NewFromCanBeCompressString(keyArray)); + + // test insert() + setHandle = LinkedHashSet::Add(thread, setHandle, key); + } + keyArray[5] = '1' + 9; + JSHandle keyHandle(factory->NewFromCanBeCompressString(keyArray)); + setHandle = LinkedHashSet::Delete(thread, setHandle, keyHandle); + // test order + for (int i = 0; i < 9; i++) { + keyArray[5] = '1' + i; + keyArray[6] = 0; + JSHandle stringKey = factory->NewFromCanBeCompressString(keyArray); + // test insert() + EXPECT_EQ(i, setHandle->FindElement(stringKey.GetTaggedValue())); + } + EXPECT_EQ(setHandle->NumberOfElements(), 9); + EXPECT_EQ(setHandle->Capacity(), 16); +} +} // namespace panda::test diff --git a/tests/runtime/common/mem_controller_test.cpp b/tests/runtime/common/mem_controller_test.cpp new file mode 100644 index 000000000..52c768df4 --- /dev/null +++ b/tests/runtime/common/mem_controller_test.cpp @@ -0,0 +1,135 @@ +/* + * Copyright (c) 2021-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 "plugins/ecmascript/runtime/ecma_string.h" +#include "plugins/ecmascript/runtime/ecma_vm.h" +#include "plugins/ecmascript/runtime/js_hclass.h" +#include "plugins/ecmascript/runtime/js_object-inl.h" +#include "plugins/ecmascript/runtime/js_thread.h" + +#include "plugins/ecmascript/runtime/mem/heap.h" +#include "plugins/ecmascript/runtime/mem/space.h" +#include "plugins/ecmascript/runtime/object_factory.h" +#include "plugins/ecmascript/runtime/tagged_array-inl.h" +#include "plugins/ecmascript/tests/runtime/common/test_helper.h" + +using namespace panda::ecmascript; +using namespace panda::ecmascript::base; + +namespace panda::test { +class MemControllerTest : public testing::Test { +public: + static void SetUpTestCase() + { + GTEST_LOG_(INFO) << "SetUpTestCase"; + } + + static void TearDownTestCase() + { + GTEST_LOG_(INFO) << "TearDownCase"; + } + + void SetUp() override + { + TestHelper::CreateEcmaVMWithScope(instance, thread, scope); + } + + void TearDown() override + { + TestHelper::DestroyEcmaVMWithScope(instance, scope); + } + + PandaVM *instance {nullptr}; + EcmaHandleScope *scope {nullptr}; + JSThread *thread {nullptr}; +}; + +TEST_F(MemControllerTest, AllocationVerify) +{ +#ifdef NDEBUG + auto ecmaVm = thread->GetEcmaVM(); + auto heap = const_cast(ecmaVm->GetHeap()); + auto objectFactory = ecmaVm->GetFactory(); + auto memController = heap->GetMemController(); + + heap->CollectGarbage(TriggerGCType::COMPRESS_FULL_GC); + + for (int i = 0; i < 1024; i++) { + // old space object + [[maybe_unused]] auto oldArray = objectFactory->NewTaggedArray(128, JSTaggedValue::Undefined(), + MemSpaceType::OLD_SPACE); + } + sleep(5); + heap->CollectGarbage(TriggerGCType::COMPRESS_FULL_GC); + double mutatorSpeed1 = memController->GetCurrentOldSpaceAllocationThroughtputPerMS(0); + for (int i = 0; i < 1024; i++) { + // old space object + [[maybe_unused]] auto oldArray = objectFactory->NewTaggedArray(128, JSTaggedValue::Undefined(), + MemSpaceType::OLD_SPACE); + } + sleep(10); + + heap->CollectGarbage(TriggerGCType::COMPRESS_FULL_GC); + double mutatorSpeed2 = memController->GetCurrentOldSpaceAllocationThroughtputPerMS(0); + ASSERT_TRUE(mutatorSpeed2 < mutatorSpeed1); +#endif +} + +TEST_F(MemControllerTest, VerifyMutatorSpeed) +{ +#ifdef NDEBUG + auto ecmaVm = thread->GetEcmaVM(); + auto heap = const_cast(ecmaVm->GetHeap()); + auto objectFactory = ecmaVm->GetFactory(); + auto memController = heap->GetMemController(); + + heap->CollectGarbage(TriggerGCType::SEMI_GC); + size_t oldSpaceAllocatedSizeBefore = memController->GetOldSpaceAllocAccumulatorSize(); + size_t nonMovableSpaceAllocatedSizeBefore = memController->GetNonMovableSpaceAllocAccumulatorSize(); + double allocDurationBefore = memController->GetAllocTimeMs(); + sleep(1); + + // new space object + auto newArray = objectFactory->NewTaggedArray(2, JSTaggedValue::Undefined(), MemSpaceType::SEMI_SPACE); + // old space object + auto oldArray = objectFactory->NewTaggedArray(2, JSTaggedValue::Undefined(), MemSpaceType::OLD_SPACE); + // non movable object + auto nonMovableArray = objectFactory->NewTaggedArray(2, JSTaggedValue::Undefined(), MemSpaceType::NON_MOVABLE); + + // huge space object + static constexpr size_t SIZE = 1024 * 1024; + auto hugeArray = objectFactory->NewTaggedArray(SIZE); + + ASSERT_TRUE(heap->GetNewSpace()->GetAllocatedSizeSinceGC() + == newArray->ComputeSize(JSTaggedValue::TaggedTypeSize(), 2)); + + heap->CollectGarbage(TriggerGCType::SEMI_GC); + + size_t oldSpaceAllocatedSizeAfter = memController->GetOldSpaceAllocAccumulatorSize(); + size_t nonMovableSpaceAllocatedSizeAfter = memController->GetNonMovableSpaceAllocAccumulatorSize(); + double allocDurationAfter = memController->GetAllocTimeMs(); + + size_t hugeObjectAllocSizeInLastGC = memController->GetHugeObjectAllocSizeSinceGC(); + + ASSERT_TRUE(allocDurationAfter - allocDurationBefore > 1000); + ASSERT_TRUE(oldSpaceAllocatedSizeAfter - oldSpaceAllocatedSizeBefore + == oldArray->ComputeSize(JSTaggedValue::TaggedTypeSize(), 2)); + ASSERT_TRUE(nonMovableSpaceAllocatedSizeAfter - nonMovableSpaceAllocatedSizeBefore + == nonMovableArray->ComputeSize(JSTaggedValue::TaggedTypeSize(), 2)); + // The allocated size of huge object must be larger than the object size. + ASSERT_TRUE(hugeObjectAllocSizeInLastGC > hugeArray->ComputeSize(JSTaggedValue::TaggedTypeSize(), SIZE)); +#endif +} +} // namespace panda::test diff --git a/tests/runtime/common/missingargs/CMakeLists.txt b/tests/runtime/common/missingargs/CMakeLists.txt new file mode 100644 index 000000000..30e63d90e --- /dev/null +++ b/tests/runtime/common/missingargs/CMakeLists.txt @@ -0,0 +1,24 @@ +# Huawei Technologies Co.,Ltd. + +set(MISSINGARGS_OUTPUT ${CMAKE_CURRENT_BINARY_DIR}/missingargs.txt) +set(MISSINGARGS_BIN ${CMAKE_CURRENT_BINARY_DIR}/missingargs.abc) +set(MISSINGARGS_JS ${CMAKE_CURRENT_SOURCE_DIR}/missingargs.js) +set(MISSINGARGS_VERIFY ${CMAKE_CURRENT_SOURCE_DIR}/verify.sh) + +set(RUNTIME_ARGUMENTS --boot-panda-files=${PANDA_BINARY_ROOT}/pandastdlib/arkstdlib.abc --load-runtimes=\"ecmascript\" --compiler-enable-jit=false --gc-type=epsilon ${MISSINGARGS_BIN} _GLOBAL::func_main_0) + +add_custom_command( + OUTPUT ${MISSINGARGS_OUTPUT} + COMMENT "running javascript missingargs testcase" + COMMAND ${PANDA_RUN_PREFIX} $ ${MISSINGARGS_JS} --output ${MISSINGARGS_BIN} + COMMAND rm -f ${MISSINGARGS_OUTPUT} + COMMAND ${PANDA_RUN_PREFIX} $ ${RUNTIME_ARGUMENTS} 2>&1 > ${MISSINGARGS_OUTPUT} + COMMAND bash ${MISSINGARGS_VERIFY} ${MISSINGARGS_OUTPUT} +) +add_custom_target(missingargs + DEPENDS ${MISSINGARGS_OUTPUT} ${MISSINGARGS_VERIFY} +) +add_dependencies(missingargs es2panda ark) +if(PANDA_TARGET_64) + add_dependencies(tests missingargs) +endif() diff --git a/tests/runtime/common/missingargs/missingargs.js b/tests/runtime/common/missingargs/missingargs.js new file mode 100644 index 000000000..53be8d0c6 --- /dev/null +++ b/tests/runtime/common/missingargs/missingargs.js @@ -0,0 +1,8 @@ +function foo(a, b, c, d) { + print(a) + print(b) + print(c) + print(d) +} + +foo(1, 2) diff --git a/tests/runtime/common/missingargs/verify.sh b/tests/runtime/common/missingargs/verify.sh new file mode 100755 index 000000000..7bc3b345f --- /dev/null +++ b/tests/runtime/common/missingargs/verify.sh @@ -0,0 +1,18 @@ +#!/bin/bash +# Copyright (c) Huawei Technologies Co., Ltd. 2020-2021. All rights reserved. + +set -eo pipefail + +expected="1 +2 +undefined +undefined" +actual=$(cat "$1") +if [[ "$actual" == "$expected" ]];then + exit 0; +else + echo -e "expected:"$expected + echo -e "actual:"$actual + echo -e "\033[31mmissingargstest failed\033[0m" + exit 1; +fi diff --git a/tests/runtime/common/module/CMakeLists.txt b/tests/runtime/common/module/CMakeLists.txt new file mode 100644 index 000000000..1e3a4aa71 --- /dev/null +++ b/tests/runtime/common/module/CMakeLists.txt @@ -0,0 +1,28 @@ +# Huawei Technologies Co.,Ltd. + +set(MODULE_OUTPUT ${CMAKE_CURRENT_BINARY_DIR}/module.txt) +set(MODULE_JS_A ${CMAKE_CURRENT_SOURCE_DIR}/module_a.js) +set(MODULE_JS_B ${CMAKE_CURRENT_SOURCE_DIR}/module_b.js) +set(MODULE_JS_C ${CMAKE_CURRENT_SOURCE_DIR}/module_c.js) +set(MODULE_BIN_A ${CMAKE_CURRENT_BINARY_DIR}/module_a.abc) +set(MODULE_BIN_B ${CMAKE_CURRENT_BINARY_DIR}/module_b.abc) +set(MODULE_BIN_C ${CMAKE_CURRENT_BINARY_DIR}/module_c.abc) +set(MODULE_VERIFY ${CMAKE_CURRENT_SOURCE_DIR}/verify.sh) + +set(RUNTIME_ARGUMENTS --boot-panda-files=${PANDA_BINARY_ROOT}/pandastdlib/arkstdlib.abc --load-runtimes=\"ecmascript\" --compiler-enable-jit=false --gc-type=stw --run-gc-in-place ${MODULE_BIN_A} _GLOBAL::func_main_0) + +add_custom_command( + OUTPUT ${MODULE_OUTPUT} + COMMENT "running javascript module testcase" + COMMAND ${PANDA_RUN_PREFIX} $ --module ${MODULE_JS_A} --output ${MODULE_BIN_A} + COMMAND ${PANDA_RUN_PREFIX} $ --module ${MODULE_JS_B} --output ${MODULE_BIN_B} + COMMAND ${PANDA_RUN_PREFIX} $ --module ${MODULE_JS_C} --output ${MODULE_BIN_C} + COMMAND rm -f ${MODULE_OUTPUT} + COMMAND ${PANDA_RUN_PREFIX} $ ${RUNTIME_ARGUMENTS} 2>&1 > ${MODULE_OUTPUT} + COMMAND bash ${MODULE_VERIFY} ${MODULE_OUTPUT} +) +add_custom_target(module + DEPENDS ${MODULE_OUTPUT} ${MODULE_VERIFY} +) +add_dependencies(module ark_asm ark) +add_dependencies(ecmascript_common_tests module) diff --git a/tests/runtime/common/module/module_a.js b/tests/runtime/common/module/module_a.js new file mode 100644 index 000000000..4754ea566 --- /dev/null +++ b/tests/runtime/common/module/module_a.js @@ -0,0 +1,30 @@ +import { Car } from 'module_b.abc'; // Test direct Export & use after import +import { Star } from 'module_c.abc'; // Test indirect Export & use after import + +let A = Car; + +var myCar = { + name: "HWCar_Test", + type: "HW_Test", + price: "CNY:XXW_Test" +} + +var infoA = A.carInfo.apply(myCar); + +let C = Star; + +var myStar = { + name: "Polaris_Test", + type: "fixedStar_Test", + color: "Yellow_Test" +} + +var infoC = Star.starColor.apply(myStar); + +if (infoA != "HWCar_Test:HW_Test:CNY:XXW_Test" ) { + print("Direct Export Fail"); +} else if (infoC != "Polaris_Test:fixedStar_Test:Yellow_Test") { + print("Indirect Export Fail"); +} else { + print("Pass!!"); +} \ No newline at end of file diff --git a/tests/runtime/common/module/module_b.js b/tests/runtime/common/module/module_b.js new file mode 100644 index 000000000..f0c56cac0 --- /dev/null +++ b/tests/runtime/common/module/module_b.js @@ -0,0 +1,16 @@ +export { Car }; +export * from 'module_c.abc'; + +var Car = { + carInfo: function() { + return this.name + ":" + this.type + ":" + this.price; + } +} + +var myCar = { + name: "HwCar", + type: "HW", + price: "CNY:XXW" +} + +var info = Car.carInfo.apply(myCar); \ No newline at end of file diff --git a/tests/runtime/common/module/module_c.js b/tests/runtime/common/module/module_c.js new file mode 100644 index 000000000..df0c63350 --- /dev/null +++ b/tests/runtime/common/module/module_c.js @@ -0,0 +1,15 @@ +var Star = { + starColor: function() { + return this.name + ":" + this.type + ":" + this.color; + } +} + +export { Star }; + +var myStar = { + name: "Polaris", + type: "FixedStar", + color: "Yellow" +} + +var info = Star.starColor.apply(myStar); \ No newline at end of file diff --git a/tests/runtime/common/module/verify.sh b/tests/runtime/common/module/verify.sh new file mode 100755 index 000000000..83cb247aa --- /dev/null +++ b/tests/runtime/common/module/verify.sh @@ -0,0 +1,15 @@ +#!/bin/bash +# Copyright (c) Huawei Technologies Co., Ltd. 2020-2021. All rights reserved. + +set -eo pipefail + +expected="Pass!!" +actual=$(cat "$1") +if [[ "$actual" == "$expected" ]];then + exit 0; +else + echo -e "expected:"$expected + echo -e "actual:"$actual + echo -e "\033[31m module test failed\033[0m" + exit 1; +fi \ No newline at end of file diff --git a/tests/runtime/common/multiargs/CMakeLists.txt b/tests/runtime/common/multiargs/CMakeLists.txt new file mode 100644 index 000000000..6c0fcef1e --- /dev/null +++ b/tests/runtime/common/multiargs/CMakeLists.txt @@ -0,0 +1,23 @@ +# Huawei Technologies Co.,Ltd. + +set(MULTIARGS_JS ${CMAKE_CURRENT_SOURCE_DIR}/multiargs.js) +set(MULTIARGS_OUTPUT ${CMAKE_CURRENT_BINARY_DIR}/multiargs.txt) +set(MULTIARGS_BIN ${CMAKE_CURRENT_BINARY_DIR}/multiargs.abc) +set(MULTIARGS_PA ${CMAKE_CURRENT_BINARY_DIR}/multiargs.pa) +set(MULTIARGS_VERIFY ${CMAKE_CURRENT_SOURCE_DIR}/verify.sh) + +set(RUNTIME_ARGUMENTS --boot-panda-files=${PANDA_BINARY_ROOT}/pandastdlib/arkstdlib.abc --load-runtimes=\"ecmascript\" --compiler-enable-jit=false --gc-type=stw --run-gc-in-place ${MULTIARGS_BIN} _GLOBAL::func_main_0) + +add_custom_command( + OUTPUT ${MULTIARGS_OUTPUT} + COMMENT "running javascript multiargs testcase" + COMMAND ${PANDA_RUN_PREFIX} $ ${MULTIARGS_JS} --dump-assembly --output ${MULTIARGS_BIN} > ${MULTIARGS_PA} + COMMAND rm -f ${MULTIARGS_OUTPUT} + COMMAND ${PANDA_RUN_PREFIX} $ ${RUNTIME_ARGUMENTS} > ${MULTIARGS_OUTPUT} + COMMAND bash ${MULTIARGS_VERIFY} ${MULTIARGS_OUTPUT} +) +add_custom_target(multiargs + DEPENDS ${MULTIARGS_OUTPUT} ${MULTIARGS_VERIFY} +) +add_dependencies(multiargs es2panda ark) +add_dependencies(ecmascript_common_tests multiargs) diff --git a/tests/runtime/common/multiargs/multiargs.js b/tests/runtime/common/multiargs/multiargs.js new file mode 100644 index 000000000..39f35b66f --- /dev/null +++ b/tests/runtime/common/multiargs/multiargs.js @@ -0,0 +1,47 @@ +function zero() +{ + var a = ' 0' + var b = ' 000' + var c = ' 00000' + var d = '0000000' + print(a+b+c+d) +} + +function one(x) +{ + print(x) +} + +function two(x,y) +{ + print(x+y) +} + +function three(x,y,z) +{ + print(x+y+z) +} + +function four(x,y,z,t) +{ + print(x+y+z+t) +} + +function five(x,y,z,t,a) +{ + let s = x + 10*y+ 100*z + 1000*t + 10000*a + print(s.toString(10)) +} + +zero() +one(123456789) +two('hello,',' world') +three('aaa','bbb','ccc') + +let x = 111 +let y = 222 +let z = 333 +let a = 666 + +four(x.toString(10),y.toString(10),z.toString(10),a.toString(10)) +five(1,2,3,4,5) \ No newline at end of file diff --git a/tests/runtime/common/multiargs/verify.sh b/tests/runtime/common/multiargs/verify.sh new file mode 100755 index 000000000..7e1f731c3 --- /dev/null +++ b/tests/runtime/common/multiargs/verify.sh @@ -0,0 +1,20 @@ +#!/bin/bash +# Copyright (c) Huawei Technologies Co., Ltd. 2020-2021. All rights reserved. + +set -eo pipefail + +expected=" 0 000 000000000000 +123456789 +hello, world +aaabbbccc +111222333666 +54321" +actual=$(cat "$1") +if [[ "$actual" == "$expected" ]];then + exit 0; +else + echo -e "expected:"$expected + echo -e "actual:"$actual + echo -e "\033[31mmultiargs test failed\033[0m" + exit 1; +fi diff --git a/tests/runtime/common/name_dictionary_test.cpp b/tests/runtime/common/name_dictionary_test.cpp new file mode 100644 index 000000000..9a9de1470 --- /dev/null +++ b/tests/runtime/common/name_dictionary_test.cpp @@ -0,0 +1,192 @@ +/* + * Copyright (c) 2021-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 + +#include "plugins/ecmascript/runtime/ecma_vm.h" +#include "plugins/ecmascript/runtime/global_env.h" +#include "plugins/ecmascript/runtime/js_handle.h" +#include "plugins/ecmascript/runtime/js_object-inl.h" +#include "plugins/ecmascript/runtime/js_tagged_value.h" +#include "plugins/ecmascript/runtime/js_thread.h" +#include "plugins/ecmascript/runtime/object_factory.h" +#include "plugins/ecmascript/runtime/tagged_dictionary.h" +#include "plugins/ecmascript/runtime/tagged_hash_table-inl.h" +#include "plugins/ecmascript/runtime/tagged_hash_table.h" +#include "plugins/ecmascript/tests/runtime/common/test_helper.h" + +using namespace panda; + +using namespace panda::ecmascript; + +namespace panda::test { +class NameDictionaryTest : public testing::Test { +public: + static void SetUpTestCase() + { + GTEST_LOG_(INFO) << "SetUpTestCase"; + } + + static void TearDownTestCase() + { + GTEST_LOG_(INFO) << "TearDownCase"; + } + + void SetUp() override + { + TestHelper::CreateEcmaVMWithScope(instance, thread, scope); + } + + void TearDown() override + { + TestHelper::DestroyEcmaVMWithScope(instance, scope); + } + + PandaVM *instance {nullptr}; + EcmaHandleScope *scope {nullptr}; + JSThread *thread {nullptr}; +}; + +static JSHandle GetGlobalEnv(JSThread *thread) +{ + EcmaVM *ecma = thread->GetEcmaVM(); + return ecma->GetGlobalEnv(); +} + +TEST_F(NameDictionaryTest, createDictionary) +{ + int numOfElement = 64; + JSHandle dict = NameDictionary::Create(thread, numOfElement); + EXPECT_TRUE(*dict != nullptr); +} + +TEST_F(NameDictionaryTest, addKeyAndValue) +{ + // mock object needed in test + int numOfElement = 64; + JSHandle dictJShandle(NameDictionary::Create(thread, numOfElement)); + EXPECT_TRUE(*dictJShandle != nullptr); + JSMutableHandle dictHandle(dictJShandle); + JSHandle objFun = GetGlobalEnv(thread)->GetObjectFunction(); + + // create key and values + JSHandle jsObject = + thread->GetEcmaVM()->GetFactory()->NewJSObjectByConstructor(JSHandle(objFun), objFun); + EXPECT_TRUE(*jsObject != nullptr); + + char keyArray[] = "hello"; + JSHandle stringKey1 = thread->GetEcmaVM()->GetFactory()->NewFromCanBeCompressString(keyArray); + JSHandle key1(stringKey1); + JSHandle taggedkey1(stringKey1); + JSHandle value1( + thread->GetEcmaVM()->GetFactory()->NewJSObjectByConstructor(JSHandle(objFun), objFun)); + PropertyAttributes metaData1; + + char key2Array[] = "hello2"; + JSHandle stringKey2 = thread->GetEcmaVM()->GetFactory()->NewFromCanBeCompressString(key2Array); + JSHandle key2(stringKey2); + JSHandle taggedkey2(stringKey2); + JSHandle value2( + thread->GetEcmaVM()->GetFactory()->NewJSObjectByConstructor(JSHandle(objFun), objFun)); + PropertyAttributes metaData2; + + // test insert() + JSHandle dict(NameDictionary::PutIfAbsent(thread, dictHandle, key1, value1, metaData1)); + dictHandle.Update(dict.GetTaggedValue()); + EXPECT_EQ(dict->EntriesCount(), 1); + + // test find() and lookup() + int entry1 = dict->FindEntry(key1.GetTaggedValue()); + EXPECT_EQ(key1.GetTaggedValue(), JSTaggedValue(dict->GetKey(entry1).GetRawData())); + EXPECT_EQ(value1.GetTaggedValue(), JSTaggedValue(dict->GetValue(entry1).GetRawData())); + + JSHandle dict2(NameDictionary::PutIfAbsent(thread, dictHandle, key2, value2, metaData2)); + EXPECT_EQ(dict2->EntriesCount(), 2); + // test remove() + dict = NameDictionary::Remove(thread, dictHandle, entry1); + EXPECT_EQ(-1, dict->FindEntry(key1.GetTaggedValue())); + EXPECT_EQ(dict->EntriesCount(), 1); +} + +TEST_F(NameDictionaryTest, GrowCapacity) +{ + int numOfElement = 8; + JSHandle dictHandle(NameDictionary::Create(thread, numOfElement)); + EXPECT_TRUE(*dictHandle != nullptr); + JSHandle objFun = GetGlobalEnv(thread)->GetObjectFunction(); + // create key and values + JSHandle jsObject = + thread->GetEcmaVM()->GetFactory()->NewJSObjectByConstructor(JSHandle(objFun), objFun); + EXPECT_TRUE(*jsObject != nullptr); + char keyArray[7] = "hello"; + for (int i = 0; i < 9; i++) { + JSHandle tempHandle = dictHandle; + keyArray[5] = '1' + i; + keyArray[6] = 0; + + JSHandle stringKey = thread->GetEcmaVM()->GetFactory()->NewFromCanBeCompressString(keyArray); + ecmascript::JSHandle key(stringKey); + JSHandle keyHandle(key); + ecmascript::JSHandle value(thread, JSTaggedValue(i)); + JSHandle valueHandle(value); + PropertyAttributes metaData; + + // test insert() + dictHandle = NameDictionary::PutIfAbsent(thread, tempHandle, keyHandle, valueHandle, metaData); + } + EXPECT_EQ(dictHandle->EntriesCount(), 9); + EXPECT_EQ(dictHandle->Size(), 16); +} + +TEST_F(NameDictionaryTest, ShrinkCapacity) +{ + int numOfElement = 64; + JSMutableHandle dictHandle(NameDictionary::Create(thread, numOfElement)); + EXPECT_TRUE(*dictHandle != nullptr); + JSHandle objFun = GetGlobalEnv(thread)->GetObjectFunction(); + // create key and values + JSHandle jsObject = + thread->GetEcmaVM()->GetFactory()->NewJSObjectByConstructor(JSHandle(objFun), objFun); + EXPECT_TRUE(*jsObject != nullptr); + uint8_t keyArray[7] = "hello"; + + auto stringTable = thread->GetEcmaVM()->GetEcmaStringTable(); + for (int i = 0; i < 10; i++) { + keyArray[5] = '0' + i; + keyArray[6] = 0; + + JSHandle key(thread, stringTable->GetOrInternString(keyArray, utf::Mutf8Size(keyArray), true)); + JSHandle value(thread, JSTaggedValue(i)); + PropertyAttributes metaData; + + // test insert() + JSHandle newDict = NameDictionary::PutIfAbsent(thread, dictHandle, key, value, metaData); + dictHandle.Update(newDict.GetTaggedValue()); + } + + keyArray[5] = '2'; + keyArray[6] = 0; + JSHandle arrayHandle(thread, + stringTable->GetOrInternString(keyArray, utf::Mutf8Size(keyArray), true)); + + int entry = dictHandle->FindEntry(arrayHandle.GetTaggedValue()); + EXPECT_NE(entry, -1); + + JSHandle newDict1 = NameDictionary::Remove(thread, dictHandle, entry); + dictHandle.Update(newDict1.GetTaggedValue()); + EXPECT_EQ(dictHandle->EntriesCount(), 9); + EXPECT_EQ(dictHandle->Size(), 16); +} +} // namespace panda::test diff --git a/tests/runtime/common/native_methods_api_no_crash/CMakeLists.txt b/tests/runtime/common/native_methods_api_no_crash/CMakeLists.txt new file mode 100644 index 000000000..39a0eb157 --- /dev/null +++ b/tests/runtime/common/native_methods_api_no_crash/CMakeLists.txt @@ -0,0 +1,13 @@ +# Huawei Technologies Co.,Ltd. + +panda_add_gtest( + NAME native_methods_api_no_crash + SOURCES + native_methods_api_no_crash.cpp + LIBRARIES + arkruntime + DEPS_TARGETS + ecmastdlib + SANITIZERS + ${PANDA_SANITIZERS_LIST} +) diff --git a/tests/runtime/common/native_methods_api_no_crash/native_methods_api_no_crash.cpp b/tests/runtime/common/native_methods_api_no_crash/native_methods_api_no_crash.cpp new file mode 100644 index 000000000..a3d3e9e78 --- /dev/null +++ b/tests/runtime/common/native_methods_api_no_crash/native_methods_api_no_crash.cpp @@ -0,0 +1,116 @@ +/* + * Copyright (c) 2021-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 + +#include "runtime/include/runtime.h" +#include "plugins/ecmascript/runtime/ecma_vm.h" + +namespace panda::test { +class EcmaEmptyClassCheck : public testing::Test { +public: + EcmaEmptyClassCheck() + { + auto exec_path = panda::os::file::File::GetExecutablePath(); + ecma_std_lib_ = exec_path.Value() + "/../plugins/ecmascript/ecmastdlib/ecmastdlib.abc"; + } + + const std::string &GetEcmaStdLibPath() const + { + return ecma_std_lib_; + } + +private: + std::string ecma_std_lib_; +}; + +static void CheckAPINoCrash(Method *m) +{ + m->IsAbstract(); + m->IsConstructor(); + m->IsDefaultInterfaceMethod(); + m->IsFinal(); + m->IsInstanceConstructor(); + m->IsIntrinsic(); + m->IsNative(); + m->IsPrivate(); + m->IsProfiling(); + m->IsProfilingWithoutLock(); + m->IsProxy(); + m->IsProtected(); + m->IsPublic(); + m->IsStatic(); + m->IsStaticConstructor(); + m->IsSynchronized(); + m->IsSynthetic(); + m->IsVerified(); + m->GetClass(); + m->GetCompilationStatus(); + m->GetSingleImplementation(); + m->GetProfilingData(); + m->GetProto(); + m->GetProtoId(); + m->GetProfilingData(); + m->GetPandaFile(); + m->GetShorty(); + m->GetInstructions(); + m->GetCompiledEntryPoint(); + m->GetIntrinsic(); + m->GetCodeId(); + m->GetFileId(); + m->GetClassName(); + m->GetClassSourceFile(); + m->GetName(); + m->GetEffectiveReturnType(); + m->GetReturnType(); + m->GetFrameSize(); + m->GetHotnessCounter(); + m->GetVTableIndex(); + m->GetAccessFlags(); + m->GetCodeSize(); + m->GetNumArgs(); + m->GetNumVregs(); + m->GetUniqId(); + m->GetNativePointer(); + m->HasCompiledCode(); + m->HasSingleImplementation(); + m->GetCompilationStatus(0); + m->GetFullName(false); + m->GetFullName(true); + m->GetLineNumFromBytecodeOffset(0); + m->GetArgType(0); + m->GetEffectiveArgType(0); +} + +TEST_F(EcmaEmptyClassCheck, TestJSABC) +{ + RuntimeOptions options; + options.SetLoadRuntimes({"ecmascript"}); + options.SetBootPandaFiles({}); + options.SetHeapSizeLimit(50_MB); + options.SetGcType("epsilon"); + + ASSERT_TRUE(Runtime::Create(options)); + + panda::ecmascript::EcmaVM *ecma_vm = + reinterpret_cast(Runtime::GetCurrent()->GetPandaVM()); + + for (Method *m : ecma_vm->GetNativeMethods()) { + CheckAPINoCrash(m); + } + + Runtime::Destroy(); +} +} // namespace panda::test diff --git a/tests/runtime/common/native_pointer_test.cpp b/tests/runtime/common/native_pointer_test.cpp new file mode 100644 index 000000000..2debf37c9 --- /dev/null +++ b/tests/runtime/common/native_pointer_test.cpp @@ -0,0 +1,75 @@ +/* + * Copyright (c) 2021-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 "plugins/ecmascript/runtime/ecma_string.h" +#include "plugins/ecmascript/runtime/ecma_vm.h" +#include "plugins/ecmascript/runtime/global_env.h" +#include "plugins/ecmascript/runtime/js_function.h" +#include "plugins/ecmascript/runtime/js_handle.h" +#include "plugins/ecmascript/runtime/js_object-inl.h" +#include "plugins/ecmascript/runtime/object_factory.h" +#include "plugins/ecmascript/tests/runtime/common/test_helper.h" + +using namespace panda::ecmascript; + +using FunctionPtr = void (*)(panda::ecmascript::JSHandle &); + +namespace panda::test { +class NativePointerTest : public testing::Test { +public: + static void SetUpTestCase() + { + GTEST_LOG_(INFO) << "SetUpTestCase"; + } + + static void TearDownTestCase() + { + GTEST_LOG_(INFO) << "TearDownCase"; + } + + void SetUp() override + { + TestHelper::CreateEcmaVMWithScope(instance, thread, scope); + } + + void TearDown() override + { + TestHelper::DestroyEcmaVMWithScope(instance, scope); + } + + PandaVM *instance {nullptr}; + ecmascript::EcmaHandleScope *scope {nullptr}; + JSThread *thread {nullptr}; +}; + +TEST_F(NativePointerTest, Print) +{ + // mock object needed in test + char array[] = "Hello World!"; + ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); + JSHandle env = thread->GetEcmaVM()->GetGlobalEnv(); + JSHandle str = factory->NewFromCanBeCompressString(array); + EXPECT_TRUE(*str != nullptr); + + JSHandle jsFunction = factory->NewJSFunction(env); + EXPECT_TRUE(*jsFunction != nullptr); + + JSMethod *target = thread->GetEcmaVM()->GetMethodForNativeFunction(nullptr); + jsFunction->SetCallTarget(thread, target); + + // run cpp methed 'Print' + ASSERT_EQ(target, jsFunction->GetCallTarget()); +} +} // namespace panda::test diff --git a/tests/runtime/common/newobjdynrange/CMakeLists.txt b/tests/runtime/common/newobjdynrange/CMakeLists.txt new file mode 100644 index 000000000..44bd6584a --- /dev/null +++ b/tests/runtime/common/newobjdynrange/CMakeLists.txt @@ -0,0 +1,23 @@ +# Huawei Technologies Co.,Ltd. + +set(NEWOBJDYNRANGE_OUTPUT ${CMAKE_CURRENT_BINARY_DIR}/newobjdynrange.txt) +set(NEWOBJDYNRANGE_JS ${CMAKE_CURRENT_SOURCE_DIR}/newobjdynrange.js) +set(NEWOBJDYNRANGE_BIN ${CMAKE_CURRENT_BINARY_DIR}/newobjdynrange.abc) +set(NEWOBJDYNRANGE_PA ${CMAKE_CURRENT_BINARY_DIR}/newobjdynrange.pa) +set(NEWOBJDYNRANGE_VERIFY ${CMAKE_CURRENT_SOURCE_DIR}/verify.sh) + +set(RUNTIME_ARGUMENTS --boot-panda-files=${PANDA_BINARY_ROOT}/pandastdlib/arkstdlib.abc --load-runtimes=\"ecmascript\" --compiler-enable-jit=false --gc-type=stw --run-gc-in-place ${NEWOBJDYNRANGE_BIN} _GLOBAL::func_main_0) + +add_custom_command( + OUTPUT ${NEWOBJDYNRANGE_OUTPUT} + COMMENT "running javascript newobjdynrange testcase" + COMMAND ${PANDA_RUN_PREFIX} $ ${NEWOBJDYNRANGE_JS} --dump-assembly --output ${NEWOBJDYNRANGE_BIN} > ${NEWOBJDYNRANGE_PA} + COMMAND rm -f ${NEWOBJDYNRANGE_OUTPUT} + COMMAND ${PANDA_RUN_PREFIX} $ ${RUNTIME_ARGUMENTS} > ${NEWOBJDYNRANGE_OUTPUT} + COMMAND bash ${NEWOBJDYNRANGE_VERIFY} ${NEWOBJDYNRANGE_OUTPUT} +) +add_custom_target(newobjdynrange + DEPENDS ${NEWOBJDYNRANGE_OUTPUT} ${NEWOBJDYNRANGE_VERIFY} +) +add_dependencies(newobjdynrange es2panda ark) +add_dependencies(ecmascript_common_tests newobjdynrange) diff --git a/tests/runtime/common/newobjdynrange/newobjdynrange.js b/tests/runtime/common/newobjdynrange/newobjdynrange.js new file mode 100644 index 000000000..e1cc5f90d --- /dev/null +++ b/tests/runtime/common/newobjdynrange/newobjdynrange.js @@ -0,0 +1,6 @@ +function foo(arg1, arg2) { + this.arg1 = arg1 + this.arg2 = arg2 +} +var p = new foo("arg1", "arg2") +print(p.arg1 === "arg1" && p.arg2 === "arg2") \ No newline at end of file diff --git a/tests/runtime/common/newobjdynrange/verify.sh b/tests/runtime/common/newobjdynrange/verify.sh new file mode 100755 index 000000000..5586e27c1 --- /dev/null +++ b/tests/runtime/common/newobjdynrange/verify.sh @@ -0,0 +1,15 @@ +#!/bin/bash +# Copyright (c) Huawei Technologies Co., Ltd. 2020-2021. All rights reserved. + +set -eo pipefail + +expected="true" +actual=$(cat "$1") +if [[ "$actual" == "$expected" ]];then + exit 0; +else + echo -e "expected:"$expected + echo -e "actual:"$actual + echo -e "\033[31mnewobjdynrange test failed\033[0m" + exit 1; +fi diff --git a/tests/runtime/common/object_factory_test.cpp b/tests/runtime/common/object_factory_test.cpp new file mode 100644 index 000000000..febb0ddd0 --- /dev/null +++ b/tests/runtime/common/object_factory_test.cpp @@ -0,0 +1,183 @@ +/* + * Copyright (c) 2021-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 "plugins/ecmascript/runtime/ecma_string-inl.h" +#include "plugins/ecmascript/runtime/ecma_vm.h" +#include "plugins/ecmascript/runtime/global_env.h" +#include "plugins/ecmascript/runtime/js_array.h" +#include "plugins/ecmascript/runtime/js_function.h" +#include "plugins/ecmascript/runtime/js_hclass.h" +#include "plugins/ecmascript/runtime/js_object-inl.h" +#include "plugins/ecmascript/runtime/js_primitive_ref.h" +#include "plugins/ecmascript/runtime/js_tagged_value-inl.h" +#include "plugins/ecmascript/runtime/lexical_env.h" +#include "plugins/ecmascript/runtime/object_factory.h" +#include "plugins/ecmascript/runtime/tagged_array-inl.h" +#include "plugins/ecmascript/tests/runtime/common/test_helper.h" + +using namespace panda; + +using namespace panda::ecmascript; + +namespace panda::test { +class ObjectFactoryTest : public testing::Test { +public: + static void SetUpTestCase() + { + GTEST_LOG_(INFO) << "SetUpTestCase"; + } + + static void TearDownTestCase() + { + GTEST_LOG_(INFO) << "TearDownCase"; + } + + void SetUp() override + { + TestHelper::CreateEcmaVMWithScope(instance, thread, scope); + } + + void TearDown() override + { + TestHelper::DestroyEcmaVMWithScope(instance, scope); + } + + PandaVM *instance {nullptr}; + ecmascript::EcmaHandleScope *scope {nullptr}; + JSThread *thread {nullptr}; +}; + +JSHandle GetGlobal(JSThread *thread) +{ + return thread->GetEcmaVM()->GetGlobalEnv(); +} + +TEST_F(ObjectFactoryTest, DISABLED_NewJSObjectByConstructor) // TODO(vpukhov) +{ + thread->GetEcmaVM()->SetEnableForceGC(false); + ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); + JSHandle objFun = GetGlobal(thread)->GetObjectFunction(); + + // check mem alloc + JSHandle newObj = factory->NewJSObjectByConstructor(JSHandle(objFun), objFun); + JSHandle newObjCls(thread, newObj->GetJSHClass()); + EXPECT_TRUE(*newObj != nullptr); + EXPECT_TRUE(*newObjCls != nullptr); + + // check feild + EXPECT_EQ(newObj->GetProperties(), GetGlobal(thread)->GetEmptyArray().GetTaggedValue()); + EXPECT_EQ(newObj->GetElements(), GetGlobal(thread)->GetEmptyArray().GetTaggedValue()); + EXPECT_TRUE(JSTaggedValue(*newObj).IsECMAObject()); + + // check jshclass + JSHClass *cls = *newObjCls; + EXPECT_TRUE(cls->GetObjectSize() == + JSObject::SIZE + JSHClass::DEFAULT_CAPACITY_OF_IN_OBJECTS * JSTaggedValue::TaggedTypeSize()); + EXPECT_TRUE(cls->GetPrototype() == GetGlobal(thread)->GetObjectFunctionPrototype().GetTaggedValue()); + EXPECT_TRUE(cls->GetObjectType() == JSType::JS_OBJECT); + + // check gc handle update + auto *prototype = cls->GetPrototype().GetTaggedObject(); + thread->GetEcmaVM()->CollectGarbage(TriggerGCType::COMPRESS_FULL_GC); + // CompressGC not the same + EXPECT_TRUE(prototype != newObjCls->GetPrototype().GetTaggedObject()); + thread->GetEcmaVM()->SetEnableForceGC(true); +} + +TEST_F(ObjectFactoryTest, NewJSFunction) +{ + ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); + JSHandle env = thread->GetEcmaVM()->GetGlobalEnv(); + + // check mem alloc + JSHandle newFun = factory->NewJSFunction(env); + JSHandle newFunCls(thread, newFun->GetJSHClass()); + EXPECT_TRUE(*newFun != nullptr); + EXPECT_TRUE(*newFunCls != nullptr); + + // check feild + EXPECT_EQ(newFun->GetProperties(), GetGlobal(thread)->GetEmptyArray().GetTaggedValue()); + EXPECT_EQ(newFun->GetElements(), GetGlobal(thread)->GetEmptyArray().GetTaggedValue()); + EXPECT_EQ(newFun->GetProtoOrDynClass(), JSTaggedValue::Hole()); + EXPECT_EQ(newFun->GetHomeObject(), JSTaggedValue::Undefined()); + EXPECT_TRUE(JSTaggedValue(*newFun).IsJSFunction()); + + // check jshclass + JSHClass *cls = *newFunCls; + EXPECT_TRUE(cls->GetObjectSize() == + JSFunction::SIZE + JSHClass::DEFAULT_CAPACITY_OF_IN_OBJECTS * JSTaggedValue::TaggedTypeSize()); + EXPECT_TRUE(cls->GetPrototype() == GetGlobal(thread)->GetFunctionPrototype().GetTaggedValue()); + EXPECT_TRUE(cls->GetObjectType() == JSType::JS_FUNCTION); + EXPECT_TRUE(cls->IsCallable()); + EXPECT_TRUE(cls->IsExtensible()); + EXPECT_TRUE(!cls->IsConstructor()); +} + +TEST_F(ObjectFactoryTest, NewJSBoundFunction) +{ + ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); + + // test prepare + JSHandle funFun(GetGlobal(thread)->GetObjectFunction()); + JSHandle bound(thread, GetGlobal(thread)->GetObjectFunctionPrototype().GetTaggedValue()); + const JSHandle array(GetGlobal(thread)->GetEmptyArray()); + + // check mem alloc + JSHandle targetFunc(funFun); + JSHandle newBoundFun = factory->NewJSBoundFunction(targetFunc, bound, array); + JSHandle newBoundFunCls(thread, newBoundFun->GetJSHClass()); + EXPECT_TRUE(*newBoundFun != nullptr); + EXPECT_TRUE(*newBoundFunCls != nullptr); +} + +TEST_F(ObjectFactoryTest, NewJSPrimitiveRef) +{ + ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); + + // test prepare + JSHandle numberFun(GetGlobal(thread)->GetNumberFunction()); + JSHandle primitive(thread, JSTaggedValue(1)); + + // check mem alloc + JSHandle newPrimitive = factory->NewJSPrimitiveRef(numberFun, primitive); + JSHandle newPrimitiveCls(thread, newPrimitive->GetJSHClass()); + EXPECT_TRUE(*newPrimitive != nullptr); + EXPECT_TRUE(*newPrimitiveCls != nullptr); + + EXPECT_TRUE(newPrimitive->GetValue() == JSTaggedValue(1)); +} + +TEST_F(ObjectFactoryTest, NewLexicalEnv) +{ + ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); + + // check mem alloc + JSHandle newLexicalEnv = factory->NewLexicalEnv(0); + JSHandle newLexicalEnvCls(thread, newLexicalEnv->GetClass()); + EXPECT_TRUE(*newLexicalEnv != nullptr); + EXPECT_TRUE(*newLexicalEnvCls != nullptr); +} + +TEST_F(ObjectFactoryTest, NewJSArray) +{ + ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); + + // check mem alloc + JSHandle newJSAarray = factory->NewJSArray(); + JSHandle newJSArrayCls(thread, newJSAarray->GetJSHClass()); + EXPECT_TRUE(*newJSAarray != nullptr); + EXPECT_TRUE(*newJSArrayCls != nullptr); +} +} // namespace panda::test diff --git a/tests/runtime/common/promise/CMakeLists.txt b/tests/runtime/common/promise/CMakeLists.txt new file mode 100644 index 000000000..de1fc33c7 --- /dev/null +++ b/tests/runtime/common/promise/CMakeLists.txt @@ -0,0 +1,23 @@ +# Huawei Technologies Co.,Ltd. + +set(PROMISE_OUTPUT ${CMAKE_CURRENT_BINARY_DIR}/promise.txt) +set(PROMISE_JS ${CMAKE_CURRENT_SOURCE_DIR}/promise.js) +set(PROMISE_BIN ${CMAKE_CURRENT_BINARY_DIR}/promise.abc) +set(PROMISE_PA ${CMAKE_CURRENT_BINARY_DIR}/promise.pa) +set(PROMISE_VERIFY ${CMAKE_CURRENT_SOURCE_DIR}/verify.sh) + +set(RUNTIME_ARGUMENTS --boot-panda-files=${PANDA_BINARY_ROOT}/pandastdlib/arkstdlib.abc --load-runtimes=\"ecmascript\" --compiler-enable-jit=false --gc-type=stw --run-gc-in-place ${PROMISE_BIN} _GLOBAL::func_main_0) + +add_custom_command( + OUTPUT ${PROMISE_OUTPUT} + COMMENT "running javascript promise testcase" + COMMAND ${PANDA_RUN_PREFIX} $ ${PROMISE_JS} --dump-assembly --output ${PROMISE_BIN} > ${PROMISE_PA} + COMMAND rm -f ${PROMISE_OUTPUT} + COMMAND ${PANDA_RUN_PREFIX} $ ${RUNTIME_ARGUMENTS} > ${PROMISE_OUTPUT} + COMMAND bash ${PROMISE_VERIFY} ${PROMISE_OUTPUT} +) +add_custom_target(promise + DEPENDS ${PROMISE_OUTPUT} ${PROMISE_VERIFY} +) +add_dependencies(promise es2panda ark) +add_dependencies(ecmascript_common_tests promise) diff --git a/tests/runtime/common/promise/promise.js b/tests/runtime/common/promise/promise.js new file mode 100644 index 000000000..a35211bcc --- /dev/null +++ b/tests/runtime/common/promise/promise.js @@ -0,0 +1,33 @@ +var p = new Promise((resolve, reject) => { + resolve(1479); +}) +var p1 = Promise.reject(1357); +var p2 = Promise.resolve(2468); +var p3 = Promise.race([p1, p2]); +p3.then( + (value) => { + print("resolve"); + print(value); + }, + (value) => { + print("reject"); + print(value); + } +) + +p3.catch((value) => { + print("catch"); + print(value); +}) + +var p4 = Promise.all([p, p2]); +p4.then( + (value) => { + print("resolve"); + print(value); + }, + (value) => { + print("reject"); + print(value); + } +) diff --git a/tests/runtime/common/promise/verify.sh b/tests/runtime/common/promise/verify.sh new file mode 100755 index 000000000..7c3ec824a --- /dev/null +++ b/tests/runtime/common/promise/verify.sh @@ -0,0 +1,20 @@ +#!/bin/bash +# Copyright (c) Huawei Technologies Co., Ltd. 2020-2021. All rights reserved. + +set -eo pipefail + +expected="reject +1357 +catch +1357 +resolve +1479,2468" +actual=$(cat "$1") +if [[ "$actual" == "$expected" ]];then + exit 0; +else + echo -e "expected:"$expected + echo -e "actual:"$actual + echo -e "\033[31mpromise test failed\033[0m" + exit 1; +fi diff --git a/tests/runtime/common/restargs/CMakeLists.txt b/tests/runtime/common/restargs/CMakeLists.txt new file mode 100644 index 000000000..5897d82d4 --- /dev/null +++ b/tests/runtime/common/restargs/CMakeLists.txt @@ -0,0 +1,22 @@ +# Huawei Technologies Co.,Ltd. + +set(RESTARGS_OUTPUT ${CMAKE_CURRENT_BINARY_DIR}/restargs.txt) +set(RESTARGS_BIN ${CMAKE_CURRENT_BINARY_DIR}/restargs.abc) +set(RESTARGS_JS ${CMAKE_CURRENT_SOURCE_DIR}/restargs.js) +set(RESTARGS_VERIFY ${CMAKE_CURRENT_SOURCE_DIR}/verify.sh) + +set(RUNTIME_ARGUMENTS --boot-panda-files=${CMAKE_BINARY_DIR}/pandastdlib/arkstdlib.abc --boot-class-spaces=\"ecmascript\" --compiler-enable-jit=false --gc-type=stw --run-gc-in-place --runtime-type \"ecmascript\" ${RESTARGS_BIN} _GLOBAL::func_main_0) + +add_custom_command( + OUTPUT ${RESTARGS_OUTPUT} + COMMENT "running javascript restargs testcase" + COMMAND ${PANDA_RUN_PREFIX} $ ${RESTARGS_JS} --output ${RESTARGS_BIN} + COMMAND rm -f ${RESTARGS_OUTPUT} + COMMAND ${PANDA_RUN_PREFIX} $ ${RUNTIME_ARGUMENTS} 2>&1 > ${RESTARGS_OUTPUT} + COMMAND bash ${RESTARGS_VERIFY} ${RESTARGS_OUTPUT} +) +add_custom_target(restargs + DEPENDS ${RESTARGS_OUTPUT} ${RESTARGS_VERIFY} +) +add_dependencies(restargs es2panda ark) +add_dependencies(ecmascript_common_tests restargs) diff --git a/tests/runtime/common/restargs/restargs.js b/tests/runtime/common/restargs/restargs.js new file mode 100644 index 000000000..ebd9727e6 --- /dev/null +++ b/tests/runtime/common/restargs/restargs.js @@ -0,0 +1,25 @@ +function sum_va(...args) { + if (!args.length) { + return undefined; + } + let acc = args[0]; + for (let i = 1; i < args.length; ++i) { + acc = acc + args[i]; + } + return acc; +} + +print(sum_va()); +print(sum_va(1, 2, 3, 4)); + +let arr = []; +for (let i = 0; i < 4; ++i) { + arr[i] = i; +} +print(sum_va(...arr)); + +arr = []; +for (let i = 0; i < 4; ++i) { + arr[i] = i.toString(); +} +print(sum_va(...arr)); \ No newline at end of file diff --git a/tests/runtime/common/restargs/verify.sh b/tests/runtime/common/restargs/verify.sh new file mode 100755 index 000000000..72aeba1c0 --- /dev/null +++ b/tests/runtime/common/restargs/verify.sh @@ -0,0 +1,18 @@ +#!/bin/bash +# Copyright (c) Huawei Technologies Co., Ltd. 2020-2021. All rights reserved. + +set -eo pipefail + +expected="undefined +10 +6 +0123" +actual=$(cat "$1") +if [[ "$actual" == "$expected" ]];then + exit 0; +else + echo -e "expected:"$expected + echo -e "actual:"$actual + echo -e "\033[31mrestargstest failed\033[0m" + exit 1; +fi diff --git a/tests/runtime/common/returnundefined/CMakeLists.txt b/tests/runtime/common/returnundefined/CMakeLists.txt new file mode 100644 index 000000000..5913aa778 --- /dev/null +++ b/tests/runtime/common/returnundefined/CMakeLists.txt @@ -0,0 +1,22 @@ +# Huawei Technologies Co.,Ltd. + +set(RETURN_UNDEFINED_OUTPUT ${CMAKE_CURRENT_BINARY_DIR}/returnundefined.txt) +set(RETURN_UNDEFINED_BIN ${CMAKE_CURRENT_BINARY_DIR}/returnundefined.abc) +set(RETURN_UNDEFINED_JS ${CMAKE_CURRENT_SOURCE_DIR}/returnundefined.js) +set(RETURN_UNDEFINED_VERIFY ${CMAKE_CURRENT_SOURCE_DIR}/verify.sh) + +set(RUNTIME_ARGUMENTS --boot-panda-files=${PANDA_BINARY_ROOT}/pandastdlib/arkstdlib.abc --load-runtimes=\"ecmascript\" --compiler-enable-jit=false --gc-type=stw --run-gc-in-place ${RETURN_UNDEFINED_BIN} _GLOBAL::func_main_0) + +add_custom_command( + OUTPUT ${RETURN_UNDEFINED_OUTPUT} + COMMENT "running javascript returnundefined testcase" + COMMAND ${PANDA_RUN_PREFIX} $ ${RETURN_UNDEFINED_JS} --output ${RETURN_UNDEFINED_BIN} + COMMAND rm -f ${RETURN_UNDEFINED_OUTPUT} + COMMAND ${PANDA_RUN_PREFIX} $ ${RUNTIME_ARGUMENTS} > ${RETURN_UNDEFINED_OUTPUT} + COMMAND bash ${RETURN_UNDEFINED_VERIFY} ${RETURN_UNDEFINED_OUTPUT} +) +add_custom_target(returnundefined + DEPENDS ${RETURN_UNDEFINED_OUTPUT} ${RETURN_UNDEFINED_VERIFY} +) +add_dependencies(returnundefined es2panda ark) +add_dependencies(ecmascript_common_tests returnundefined) diff --git a/tests/runtime/common/returnundefined/returnundefined.js b/tests/runtime/common/returnundefined/returnundefined.js new file mode 100644 index 000000000..c8b439b13 --- /dev/null +++ b/tests/runtime/common/returnundefined/returnundefined.js @@ -0,0 +1,12 @@ +function BOOM(x) {this.x = x} + +BOOM.prototype.run = function() { + print('run'); + return 123456789; +}; + +var bodies = new BOOM(Array()); +var ret = bodies.run(); + +if (ret == 123456789) + print('Test OK') diff --git a/tests/runtime/common/returnundefined/verify.sh b/tests/runtime/common/returnundefined/verify.sh new file mode 100755 index 000000000..acbb76c2d --- /dev/null +++ b/tests/runtime/common/returnundefined/verify.sh @@ -0,0 +1,17 @@ +#!/bin/bash +# Copyright (c) Huawei Technologies Co., Ltd. 2020-2021. All rights reserved. + +set -eo pipefail + +expected="run +Test OK" +actual=$(cat "$1") +if [[ "$actual" == "$expected" ]];then + exit 0; +else + echo -e "expected:"$expected + echo -e "actual:"$actual + echo -e "\033[31mreturnundefined test failed\033[0m" + exit 1; +fi + diff --git a/tests/runtime/common/separate_jsvm_test.cpp b/tests/runtime/common/separate_jsvm_test.cpp new file mode 100644 index 000000000..ff0b66cde --- /dev/null +++ b/tests/runtime/common/separate_jsvm_test.cpp @@ -0,0 +1,95 @@ +/* + * Copyright (c) 2021-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 "gtest/gtest.h" +#include "test_helper.h" +#include "include/coretypes/tagged_value.h" +#include "include/runtime.h" +#include "include/runtime_options.h" +#include "plugins/ecmascript/runtime/js_handle.h" +#include "plugins/ecmascript/runtime/object_factory.h" +#include "plugins/ecmascript/runtime/ecma_vm.h" +#include "plugins/ecmascript/runtime/global_env.h" + +using namespace panda; + +using namespace panda::ecmascript; +using namespace panda::coretypes; + +namespace panda::test { +class SepareteJSVMTest : public testing::Test { +public: + static void SetUpTestCase() + { + RuntimeOptions options; + options.SetLoadRuntimes({"core", "ecmascript"}); + options.SetRuntimeType("core"); + options.SetBootPandaFiles({}); + options.SetShouldLoadBootPandaFiles(false); + options.SetBootIntrinsicSpaces({"ecmascript"}); + options.SetRunGcInPlace(true); + options.SetGcType("stw"); + [[maybe_unused]] bool success = Runtime::Create(options); + ASSERT_TRUE(success) << "Cannot create Runtime"; + } + + static void TearDownTestCase() + { + [[maybe_unused]] bool success = Runtime::Destroy(); + ASSERT_TRUE(success) << "Cannot destroy Runtime"; + } + + EcmaVM *CreateJSVM() + { + JSRuntimeOptions options; + options.SetBootPandaFiles({}); + options.SetLoadRuntimes({"ecmascript"}); + options.SetRunGcInPlace(true); + options.SetGcType("stw"); + return EcmaVM::Create(options); + } + + PandaVM *instance; +}; + +TEST_F(SepareteJSVMTest, CreateInstance) +{ + std::function func([this]() { + auto ecma_vm = SepareteJSVMTest::CreateJSVM(); + ASSERT_TRUE(ecma_vm != nullptr); + { + auto *thread = ecma_vm->GetAssociatedJSThread(); + [[maybe_unused]] ecmascript::EcmaHandleScope scope(thread); + + JSHandle global_env = thread->GetEcmaVM()->GetGlobalEnv(); + JSHandle dynclass = global_env->GetObjectFunction(); + JSHandle jsobject = + thread->GetEcmaVM()->GetFactory()->NewJSObjectByConstructor(JSHandle(dynclass), dynclass); + JSHandle jsobject2 = + thread->GetEcmaVM()->GetFactory()->NewJSObjectByConstructor(JSHandle(dynclass), dynclass); + EXPECT_TRUE(*jsobject != nullptr); + EXPECT_TRUE(*jsobject2 != nullptr); + EXPECT_TRUE(!jsobject->IsCallable()); + } + + EcmaVM::Destroy(ecma_vm); + }); + + std::thread vm_t1(func); + + vm_t1.join(); +} + +} // namespace panda::test diff --git a/tests/runtime/common/sieve/CMakeLists.txt b/tests/runtime/common/sieve/CMakeLists.txt new file mode 100644 index 000000000..be36ce6d6 --- /dev/null +++ b/tests/runtime/common/sieve/CMakeLists.txt @@ -0,0 +1,22 @@ +# Huawei Technologies Co.,Ltd. + +set(SIEVE_OUTPUT ${CMAKE_CURRENT_BINARY_DIR}/sieve.txt) +set(SIEVE_BIN ${CMAKE_CURRENT_BINARY_DIR}/sieve.abc) +set(SIEVE_JS ${CMAKE_CURRENT_SOURCE_DIR}/sieve.js) +set(SIEVE_VERIFY ${CMAKE_CURRENT_SOURCE_DIR}/verify.sh) + +set(RUNTIME_ARGUMENTS --boot-panda-files=${PANDA_BINARY_ROOT}/pandastdlib/arkstdlib.abc --load-runtimes=\"ecmascript\" --compiler-enable-jit=false --gc-type=stw --run-gc-in-place ${SIEVE_BIN} _GLOBAL::func_main_0) + +add_custom_command( + OUTPUT ${SIEVE_OUTPUT} + COMMENT "running javascript sieve testcase" + COMMAND ${PANDA_RUN_PREFIX} $ ${SIEVE_JS} --output ${SIEVE_BIN} + COMMAND rm -f ${SIEVE_OUTPUT} + COMMAND ${PANDA_RUN_PREFIX} $ ${RUNTIME_ARGUMENTS} > ${SIEVE_OUTPUT} + COMMAND bash ${SIEVE_VERIFY} ${SIEVE_OUTPUT} +) +add_custom_target(sieve + DEPENDS ${SIEVE_OUTPUT} ${SIEVE_VERIFY} +) +add_dependencies(sieve es2panda ark) +add_dependencies(ecmascript_common_tests sieve) diff --git a/tests/runtime/common/sieve/sieve.js b/tests/runtime/common/sieve/sieve.js new file mode 100644 index 000000000..b376c2259 --- /dev/null +++ b/tests/runtime/common/sieve/sieve.js @@ -0,0 +1,47 @@ +function Sieve() {} + +Sieve.prototype.nsieve = function(m, isPrime) { + var i, k, count; + + for (i = 2; i <= m; i++) { + isPrime[i] = true; + } + count = 0; + for (i = 2; i <= m; i++) { + if (isPrime[i]) { + for (k = i + i; k <= m; k += i) isPrime[k] = false; + count++; + } + } + return count; +}; + +Sieve.prototype.sieve = function() { + var sum = 0; + for (var i = 1; i <= 3; i++) { + var m = (1 << i) * 10000; + var flags = Array(m + 1); + sum += this.nsieve(m, flags); + } + return sum; +}; + +Sieve.prototype.run = function() { + var expected = 14302; + var result = this.sieve(); + if (result != expected) + throw 'ERROR: bad result: expected ' + expected + ' but got ' + result; +}; + + + +function Controller() {}; + +Controller.prototype.execute = function() { + benchmark.run(); +}; + +var benchmark = new Sieve(); +var controller = new Controller(); +controller.execute(); +print('OK') diff --git a/tests/runtime/common/sieve/verify.sh b/tests/runtime/common/sieve/verify.sh new file mode 100755 index 000000000..5e65b79ef --- /dev/null +++ b/tests/runtime/common/sieve/verify.sh @@ -0,0 +1,16 @@ +#!/bin/bash +# Copyright (c) Huawei Technologies Co., Ltd. 2020-2021. All rights reserved. + +set -eo pipefail + +expected="OK" +actual=$(cat "$1") +if [[ "$actual" == "$expected" ]];then + exit 0; +else + echo -e "expected:"$expected + echo -e "actual:"$actual + echo -e "\033[31msieve test failed\033[0m" + exit 1; +fi + diff --git a/tests/runtime/common/strictequal/CMakeLists.txt b/tests/runtime/common/strictequal/CMakeLists.txt new file mode 100644 index 000000000..ab2283e49 --- /dev/null +++ b/tests/runtime/common/strictequal/CMakeLists.txt @@ -0,0 +1,23 @@ +# Huawei Technologies Co.,Ltd. + +set(STRICT_EQUAL_OUTPUT ${CMAKE_CURRENT_BINARY_DIR}/strictequal.txt) +set(STRICT_EQUAL_BIN ${CMAKE_CURRENT_BINARY_DIR}/strictequal.abc) +set(STRICT_EQUAL_PA ${CMAKE_CURRENT_BINARY_DIR}/strictequal.pa) +set(STRICT_EQUAL_JS ${CMAKE_CURRENT_SOURCE_DIR}/strictequal.js) +set(STRICT_EQUAL_VERIFY ${CMAKE_CURRENT_SOURCE_DIR}/verify.sh) + +set(RUNTIME_ARGUMENTS --boot-panda-files=${PANDA_BINARY_ROOT}/pandastdlib/arkstdlib.abc --load-runtimes=\"ecmascript\" --compiler-enable-jit=false --gc-type=stw --run-gc-in-place ${STRICT_EQUAL_BIN} _GLOBAL::func_main_0) + +add_custom_command( + OUTPUT ${STRICT_EQUAL_OUTPUT} + COMMENT "running javascript strictequal testcase" + COMMAND ${PANDA_RUN_PREFIX} $ ${STRICT_EQUAL_JS} --dump-assembly --output ${STRICT_EQUAL_BIN} > ${STRICT_EQUAL_PA} + COMMAND rm -f ${STRICT_EQUAL_OUTPUT} + COMMAND ${PANDA_RUN_PREFIX} $ ${RUNTIME_ARGUMENTS} > ${STRICT_EQUAL_OUTPUT} + COMMAND bash ${STRICT_EQUAL_VERIFY} ${STRICT_EQUAL_OUTPUT} +) +add_custom_target(strictequal + DEPENDS ${STRICT_EQUAL_OUTPUT} ${STRICT_EQUAL_VERIFY} +) +add_dependencies(strictequal es2panda ark) +add_dependencies(ecmascript_common_tests strictequal) diff --git a/tests/runtime/common/strictequal/strictequal.js b/tests/runtime/common/strictequal/strictequal.js new file mode 100644 index 000000000..8b637b92d --- /dev/null +++ b/tests/runtime/common/strictequal/strictequal.js @@ -0,0 +1,19 @@ +print(1 === 1); +print('hello' === 'hello'); +print('1' === 1); +print(0 === false); + +print(1 == 1); +print('hello' == 'hello'); +print('1' == 1); +print(0 == false); + +print(1 !== 1); +print('hello' !== 'hello'); +print('1' !== 1); +print(0 !== false); + +print(1 != 1); +print('hello' != 'hello'); +print('1' != 1); +print(0 != false); \ No newline at end of file diff --git a/tests/runtime/common/strictequal/verify.sh b/tests/runtime/common/strictequal/verify.sh new file mode 100755 index 000000000..78eba7929 --- /dev/null +++ b/tests/runtime/common/strictequal/verify.sh @@ -0,0 +1,31 @@ +#!/bin/bash +# Copyright (c) Huawei Technologies Co., Ltd. 2020-2021. All rights reserved. + +set -eo pipefail + +expected="true +true +false +false +true +true +true +true +false +false +true +true +false +false +false +false" +actual=$(cat "$1") +if [[ "$actual" == "$expected" ]];then + exit 0; +else + echo -e "expected:"$expected + echo -e "actual:"$actual + echo -e "\033[31mstrictequal test failed\033[0m" + exit 1; +fi + diff --git a/tests/runtime/common/symbol_table_test.cpp b/tests/runtime/common/symbol_table_test.cpp new file mode 100644 index 000000000..5f9776302 --- /dev/null +++ b/tests/runtime/common/symbol_table_test.cpp @@ -0,0 +1,270 @@ +/* + * Copyright (c) 2021-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 "plugins/ecmascript/runtime/symbol_table-inl.h" +#include "plugins/ecmascript/runtime/ecma_string.h" +#include "plugins/ecmascript/runtime/js_symbol.h" +#include "plugins/ecmascript/runtime/object_factory.h" +#include "plugins/ecmascript/runtime/js_tagged_value.h" +#include "plugins/ecmascript/runtime/tagged_array-inl.h" +#include "plugins/ecmascript/tests/runtime/common/test_helper.h" +using namespace panda::ecmascript; + +namespace panda::test { +class SymbolTableTest : public testing::Test { +public: + static void SetUpTestCase() + { + GTEST_LOG_(INFO) << "SetUpTestCase"; + } + + static void TearDownTestCase() + { + GTEST_LOG_(INFO) << "TearDownCase"; + } + + void SetUp() override + { + TestHelper::CreateEcmaVMWithScope(instance, thread, scope); + } + + void TearDown() override + { + TestHelper::DestroyEcmaVMWithScope(instance, scope); + } + ecmascript::EcmaHandleScope *scope {nullptr}; + PandaVM *instance {nullptr}; + JSThread *thread {nullptr}; +}; + +TEST_F(SymbolTableTest, GetKeyIndex) +{ + int entry = 0; + EXPECT_EQ(SymbolTable::GetKeyIndex(entry), 3); +} + +TEST_F(SymbolTableTest, GetValueIndex) +{ + int entry = 0; + EXPECT_EQ(SymbolTable::GetValueIndex(entry), 4); +} + +TEST_F(SymbolTableTest, GetEntryIndex) +{ + int entry = 0; + EXPECT_EQ(SymbolTable::GetEntryIndex(entry), 3); +} + +TEST_F(SymbolTableTest, GetEntrySize) +{ + EXPECT_EQ(SymbolTable::GetEntrySize(), 2); +} + +/* + * Feature: SymbolTableTest + * Function: IsMatch + * SubFunction: StringsAreEqual + * FunctionPoints: Is Match + * CaseDescription: Judge whether two string variables are equal. If they are equal, + * it returns true, otherwise it returns false. + */ +TEST_F(SymbolTableTest, IsMatch) +{ + ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); + + JSHandle symbolTableString = factory->NewFromCanBeCompressString("name"); + JSTaggedValue symbolTableOther = symbolTableString.GetTaggedValue(); + + JSTaggedValue symbolTableName1 = JSTaggedValue::Hole(); + EXPECT_EQ(SymbolTable::IsMatch(symbolTableName1, symbolTableOther), false); + + JSTaggedValue symbolTableName2 = JSTaggedValue::Undefined(); + EXPECT_EQ(SymbolTable::IsMatch(symbolTableName2, symbolTableOther), false); + + JSTaggedValue symbolTableName3 = symbolTableString.GetTaggedValue(); + EXPECT_EQ(SymbolTable::IsMatch(symbolTableName3, symbolTableOther), true); +} + +/* + * Feature: SymbolTableTest + * Function: Hash_Utf8 + * SubFunction: GetHashCode + * FunctionPoints: Hash + * CaseDescription: The hash code is obtained by passing in a character string array or an uint8_t + * type array through a specific calculation formula. + */ +TEST_F(SymbolTableTest, Hash_Utf8) +{ + ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); + // test obj is not string + JSHandle jsObJect(factory->NewEmptyJSObject()); + EXPECT_EQ(SymbolTable::Hash(jsObJect.GetTaggedValue()), JSSymbol::ComputeHash()); + + // the CompressedStringsEnabled must be true + bool flag = EcmaString::GetCompressedStringsEnabled(); + EXPECT_EQ(flag, true); + + uint8_t utf8ArrayName1[4] = {1, 2, 3}; // The last element is "\0" + uint32_t utf8ArrayNameLen1 = sizeof(utf8ArrayName1) - 1; + JSHandle nameStringUtf8Obj1 =factory->NewFromUtf8(utf8ArrayName1, utf8ArrayNameLen1); + EXPECT_EQ(SymbolTable::Hash(nameStringUtf8Obj1.GetTaggedValue()), 1026); // 1026 = (1 << 5 - 1 + 2) << 5 - 2 + 3 + + uint8_t utf8ArrayName2[] = "key"; + uint32_t utf8ArrayNameLen2 = sizeof(utf8ArrayName2) - 1; + JSHandle nameStringUtf8Obj2 =factory->NewFromUtf8(utf8ArrayName2, utf8ArrayNameLen2); + EXPECT_EQ(SymbolTable::Hash(nameStringUtf8Obj2.GetTaggedValue()), 106079); +} + +/* + * Feature: SymbolTableTest + * Function: Hash_Utf16 + * SubFunction: GetHashCode + * FunctionPoints: Hash + * CaseDescription: The hash code is obtained by passing in a character string array or an uint16_t + * type array through a specific calculation formula. + */ +TEST_F(SymbolTableTest, Hash_Utf16) +{ + ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); + + uint16_t utf16ArrayName1[] = {1, 2, 3}; + uint32_t utf16ArrayNameLen1 = sizeof(utf16ArrayName1) / sizeof(utf16ArrayName1[0]); + JSHandle nameStringUtf16Obj1 =factory->NewFromUtf16(utf16ArrayName1, utf16ArrayNameLen1); + EXPECT_EQ(SymbolTable::Hash(nameStringUtf16Obj1.GetTaggedValue()), 1026); // 1026 = (1 << 5 - 1 + 2) << 5 - 2 + 3 + + uint16_t utf16ArrayName2[] = {0, 1, 2}; + uint32_t utf16ArrayNameLen2 = sizeof(utf16ArrayName2) / sizeof(utf16ArrayName2[0]); + JSHandle nameStringUtf16Obj2 =factory->NewFromUtf16(utf16ArrayName2, utf16ArrayNameLen2); + EXPECT_EQ(SymbolTable::Hash(nameStringUtf16Obj2.GetTaggedValue()), 33); // 33 = (0 << 5 - 0 + 1) << 5 - 1 + 2 +} + +/* + * Feature: SymbolTableTest + * Function: Create + * SubFunction: *Value + * FunctionPoints: Create + * CaseDescription: A pointer variable of symboltable type is obtained by changing the function, + * If it is created successfully, the pointer variable is not equal to null, + * The prerequisite for creation is that compressedstringsenabled must be true. + */ +TEST_F(SymbolTableTest, Create) +{ + int numberOfElements = SymbolTable::DEFAULT_ELEMENTS_NUMBER; + // the CompressedStringsEnabled must be true + bool flag = EcmaString::GetCompressedStringsEnabled(); + EXPECT_EQ(flag, true); + + JSHandle symbolTable = SymbolTable::Create(thread, numberOfElements); + EXPECT_TRUE(*symbolTable != nullptr); +} + +/* + * Feature: SymbolTableTest + * Function: ContainsKey + * SubFunction: Getkey + * FunctionPoints: Contains Key + * CaseDescription: Judge whether the key value can be found in the key value in your own created symbol + * table.If you do not have a key value, you will return false. + */ +TEST_F(SymbolTableTest, ContainsKey) +{ + ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); + JSHandle symbolTableStringKey1 = factory->NewFromCanBeCompressString("key"); + JSHandle symbolTableStringKey2 = factory->NewFromCanBeCompressString("key1"); + JSHandle symbolTableStringKey3 = factory->NewFromCanBeCompressString("value"); + + int numberOfElements = 2; + JSHandle symbolTable = SymbolTable::Create(thread, numberOfElements); + EXPECT_EQ(symbolTable->ContainsKey(thread, symbolTableStringKey1.GetTaggedValue()), false); + + symbolTable->SetKey(thread, 1, JSTaggedValue::Hole()); + EXPECT_EQ(symbolTable->ContainsKey(thread, symbolTableStringKey1.GetTaggedValue()), false); + + symbolTable->SetKey(thread, 1, JSTaggedValue::Undefined()); + EXPECT_EQ(symbolTable->ContainsKey(thread, symbolTableStringKey1.GetTaggedValue()), false); + + symbolTable->SetKey(thread, 1, symbolTableStringKey1.GetTaggedValue()); + EXPECT_EQ(symbolTable->ContainsKey(thread, symbolTableStringKey1.GetTaggedValue()), true); + + // the key value has numbers + symbolTable->SetKey(thread, 1, symbolTableStringKey2.GetTaggedValue()); + EXPECT_EQ(symbolTable->ContainsKey(thread, symbolTableStringKey2.GetTaggedValue()), false); + + symbolTable->SetKey(thread, 1, symbolTableStringKey3.GetTaggedValue()); + EXPECT_EQ(symbolTable->ContainsKey(thread, symbolTableStringKey3.GetTaggedValue()), true); +} + +/* + * Feature: SymbolTableTest + * Function: GetSymbol + * SubFunction: GetValue + * FunctionPoints: Get Symbol + * CaseDescription: This function obtains the value in the key of symbol table pointer variable created + * by the create function. If the pointer variable has no value set, it returns false. + */ +TEST_F(SymbolTableTest, GetSymbol) +{ + ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); + int numberOfElements = 2; + + JSHandle symbolTableStringKey = factory->NewFromCanBeCompressString("key"); + JSHandle symbolTable = SymbolTable::Create(thread, numberOfElements); + + symbolTable->SetKey(thread, 1, symbolTableStringKey.GetTaggedValue()); + EXPECT_EQ(symbolTable->GetSymbol(symbolTableStringKey.GetTaggedValue()), JSTaggedValue::Undefined()); + + symbolTable->SetValue(thread, 0, JSTaggedValue(1)); + EXPECT_EQ(symbolTable->GetSymbol(symbolTableStringKey.GetTaggedValue()), JSTaggedValue::Undefined()); + + symbolTable->SetValue(thread, 1, JSTaggedValue(1)); + EXPECT_EQ(symbolTable->GetSymbol(symbolTableStringKey.GetTaggedValue()).GetInt(), 1); +} + +/* + * Feature: SymbolTableTest + * Function: FindSymbol + * SubFunction: GetKey + * FunctionPoints: Find Symbol + * CaseDescription: This function compares the key value in the symboltable pointer variable created by the create + * function with the description value in the jssymbol type variable. If they are equal, it indicates + * that the find symbol is successful, and the return value is the key value. Before creating the + * symboltable pointer, perform the newfromcanbecompressstring operation to obtain the array length. + */ +TEST_F(SymbolTableTest, FindSymbol) +{ + ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); + JSHandle symbolTableStringKey1(factory->NewFromCanBeCompressString("key")); + JSHandle symbolTableStringKey2(factory->NewFromCanBeCompressString("key1")); + + int numberOfElements = 2; + JSHandle handleSymbol = factory->NewJSSymbol(); + JSHandle symbolTable = SymbolTable::Create(thread, numberOfElements); + + JSTaggedValue resultValue1 = symbolTable->FindSymbol(thread, handleSymbol.GetTaggedValue()); + EXPECT_EQ(JSTaggedValue::SameValue(resultValue1, JSTaggedValue::Undefined()), true); + + handleSymbol->SetDescription(thread, symbolTableStringKey1.GetTaggedValue()); + JSTaggedValue resultValue2 = symbolTable->FindSymbol(thread, handleSymbol.GetTaggedValue()); + EXPECT_EQ(JSTaggedValue::SameValue(resultValue2, JSTaggedValue::Undefined()), true); + + symbolTable->SetKey(thread, 1, symbolTableStringKey1.GetTaggedValue()); + JSTaggedValue resultValue3 = symbolTable->FindSymbol(thread, handleSymbol.GetTaggedValue()); + EXPECT_EQ(resultValue3.GetRawData() == symbolTableStringKey1.GetTaggedValue().GetRawData(), true); + + symbolTable->SetKey(thread, 1, symbolTableStringKey2.GetTaggedValue()); + JSTaggedValue resultValue4 = symbolTable->FindSymbol(thread, handleSymbol.GetTaggedValue()); + EXPECT_EQ(JSTaggedValue::SameValue(resultValue4, JSTaggedValue::Undefined()), true); +} +} // namespace panda::ecmascript diff --git a/tests/runtime/common/tagged_value_test.cpp b/tests/runtime/common/tagged_value_test.cpp new file mode 100644 index 000000000..e7ec8fc26 --- /dev/null +++ b/tests/runtime/common/tagged_value_test.cpp @@ -0,0 +1,1209 @@ +/* + * Copyright (c) 2021-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 "plugins/ecmascript/runtime/object_factory.h" +#include "plugins/ecmascript/runtime/js_handle.h" +#include "gtest/gtest.h" +#include "test_helper.h" +#include "plugins/ecmascript/runtime/js_hclass.h" +#include "plugins/ecmascript/runtime/js_tagged_value-inl.h" +#include "plugins/ecmascript/runtime/js_object.h" +#include "plugins/ecmascript/runtime/js_function.h" +#include "plugins/ecmascript/runtime/js_primitive_ref.h" +#include "plugins/ecmascript/runtime/js_symbol.h" +#include "plugins/ecmascript/runtime/js_thread.h" +#include "include/runtime.h" +#include "plugins/ecmascript/runtime/object_factory.h" +#include "plugins/ecmascript/runtime/ecma_vm.h" +#include "plugins/ecmascript/runtime/global_env.h" +#include "plugins/ecmascript/runtime/snapshot/mem/snapshot.h" + +using namespace panda::ecmascript; + +using namespace panda::coretypes; + +namespace panda::test { +class JSTaggedValueTest : public testing::Test { +public: + void SetUp() override + { + TestHelper::CreateEcmaVMWithScope(instance, thread, scope); + } + + void TearDown() override + { + TestHelper::DestroyEcmaVMWithScope(instance, scope); + } + + PandaVM *instance {nullptr}; + ecmascript::EcmaHandleScope *scope {nullptr}; + JSThread *thread; +}; + +TEST_F(JSTaggedValueTest, Double) +{ + double d = 1.1; + JSTaggedValue td(d); + EXPECT_EQ(true, td.IsDouble()); + EXPECT_EQ(false, td.IsInt()); + EXPECT_EQ(false, td.IsObject()); + ASSERT_DOUBLE_EQ(td.GetDouble(), d); + + double nan = std::nan(""); + JSTaggedValue tNan(nan); + EXPECT_EQ(true, tNan.IsDouble()); + EXPECT_EQ(false, tNan.IsInt()); + EXPECT_EQ(false, tNan.IsObject()); + EXPECT_EQ(ReinterpretDoubleToTaggedType(tNan.GetDouble()), ReinterpretDoubleToTaggedType(nan)); + + double pureNaN = ReinterpretTaggedTypeToDouble(JSTaggedValue::TAG_INT - JSTaggedValue::DOUBLE_ENCODE_OFFSET); + EXPECT_EQ(true, JSTaggedValue::IsImpureNaN(pureNaN)); +} + +TEST_F(JSTaggedValueTest, Int) +{ + int i = 0x5c; + JSTaggedValue t(0x5c); + EXPECT_EQ(true, t.IsInt()); + EXPECT_EQ(false, t.IsObject()); + EXPECT_EQ(false, t.IsDouble()); + EXPECT_EQ(t.GetInt(), i); +} + +TEST_F(JSTaggedValueTest, IsObject) +{ + panda::ObjectHeader *p = reinterpret_cast(0xffff0000UL); + JSTaggedValue t(p); + EXPECT_EQ(true, t.IsObject()); + EXPECT_EQ(false, t.IsInt()); + EXPECT_EQ(false, t.IsDouble()); + EXPECT_EQ(t.GetHeapObject(), p); +} + +TEST_F(JSTaggedValueTest, False) +{ + JSTaggedValue t = JSTaggedValue::False(); + EXPECT_EQ(t.IsFalse(), true); +} + +TEST_F(JSTaggedValueTest, True) +{ + JSTaggedValue t = JSTaggedValue::True(); + EXPECT_EQ(t.IsTrue(), true); +} + +TEST_F(JSTaggedValueTest, Undefined) +{ + JSTaggedValue t = JSTaggedValue::Undefined(); + EXPECT_EQ(t.IsUndefined(), true); +} +TEST_F(JSTaggedValueTest, Null) +{ + JSTaggedValue t = JSTaggedValue::Null(); + EXPECT_EQ(t.IsNull(), true); +} + +TEST_F(JSTaggedValueTest, Hole) +{ + JSTaggedValue t = JSTaggedValue::Hole(); + EXPECT_EQ(t.IsHole(), true); +} + +TEST_F(JSTaggedValueTest, ToPrimitive) +{ + JSTaggedValue result; + + JSTaggedValue int_v(100); + result = JSTaggedValue::ToPrimitive(thread, JSHandle(thread, int_v)); + EXPECT_EQ(result.GetInt(), 100); + + JSTaggedValue double_v((double)100.0); + result = JSTaggedValue::ToPrimitive(thread, JSHandle(thread, double_v)); + EXPECT_EQ(result.GetDouble(), (double)100.0); + + JSTaggedValue undefined_v = JSTaggedValue::Undefined(); + result = JSTaggedValue::ToPrimitive(thread, JSHandle(thread, undefined_v)); + EXPECT_TRUE(result.IsUndefined()); + + JSTaggedValue hole_v = JSTaggedValue::Hole(); + result = JSTaggedValue::ToPrimitive(thread, JSHandle(thread, hole_v)); + EXPECT_TRUE(result.IsHole()); + + JSTaggedValue null_v = JSTaggedValue::Null(); + result = JSTaggedValue::ToPrimitive(thread, JSHandle(thread, null_v)); + EXPECT_TRUE(result.IsNull()); + + JSTaggedValue false_v = JSTaggedValue::False(); + result = JSTaggedValue::ToPrimitive(thread, JSHandle(thread, false_v)); + EXPECT_TRUE(result.IsFalse()); + + JSTaggedValue true_v = JSTaggedValue::True(); + result = JSTaggedValue::ToPrimitive(thread, JSHandle(thread, true_v)); + EXPECT_TRUE(result.IsTrue()); +} + +TEST_F(JSTaggedValueTest, ToBoolean) +{ + EXPECT_TRUE(JSTaggedValue(100).ToBoolean()); + EXPECT_FALSE(JSTaggedValue(0).ToBoolean()); + + EXPECT_TRUE(JSTaggedValue((double)100.0).ToBoolean()); + EXPECT_FALSE(JSTaggedValue(std::nan("")).ToBoolean()); + + EXPECT_FALSE(JSTaggedValue::Undefined().ToBoolean()); + + EXPECT_FALSE(JSTaggedValue::Hole().ToBoolean()); + + EXPECT_FALSE(JSTaggedValue::Null().ToBoolean()); + + EXPECT_FALSE(JSTaggedValue::False().ToBoolean()); + EXPECT_TRUE(JSTaggedValue::True().ToBoolean()); + + EXPECT_FALSE(thread->GetEcmaVM()->GetFactory()->GetEmptyString().GetTaggedValue().ToBoolean()); + EXPECT_TRUE(thread->GetEcmaVM()->GetFactory()->NewFromString("test").GetTaggedValue().ToBoolean()); +} + +TEST_F(JSTaggedValueTest, ToNumber) +{ + JSTaggedNumber result; + + JSTaggedValue int_v(100); + result = JSTaggedValue::ToNumber(thread, JSHandle(thread, int_v)); + EXPECT_EQ(result.GetNumber(), 100); + + JSTaggedValue double_v((double)100.0); + result = JSTaggedValue::ToNumber(thread, JSHandle(thread, double_v)); + EXPECT_EQ(result.GetNumber(), (double)100.0); + + JSTaggedValue undefined_v = JSTaggedValue::Undefined(); + result = JSTaggedValue::ToNumber(thread, JSHandle(thread, undefined_v)); + EXPECT_TRUE(std::isnan(result.GetNumber())); + + JSTaggedValue hole_v = JSTaggedValue::Hole(); + result = JSTaggedValue::ToNumber(thread, JSHandle(thread, hole_v)); + EXPECT_TRUE(std::isnan(result.GetNumber())); + + JSTaggedValue null_v = JSTaggedValue::Null(); + result = JSTaggedValue::ToNumber(thread, JSHandle(thread, null_v)); + EXPECT_EQ(result.GetNumber(), 0); + + JSTaggedValue false_v = JSTaggedValue::False(); + result = JSTaggedValue::ToNumber(thread, JSHandle(thread, false_v)); + EXPECT_EQ(result.GetNumber(), 0); + + JSTaggedValue true_v = JSTaggedValue::True(); + result = JSTaggedValue::ToNumber(thread, JSHandle(thread, true_v)); + EXPECT_EQ(result.GetNumber(), 1); + + JSHandle string_v0(thread->GetEcmaVM()->GetFactory()->NewFromString(" 1234 ")); + result = JSTaggedValue::ToNumber(thread, string_v0); + EXPECT_EQ(result.GetNumber(), 1234); + + JSHandle string_v1(thread->GetEcmaVM()->GetFactory()->NewFromString(" 0b1010 ")); + result = JSTaggedValue::ToNumber(thread, string_v1); + EXPECT_EQ(result.GetNumber(), 10); + + JSHandle string_v2(thread->GetEcmaVM()->GetFactory()->NewFromString(" 0O11 ")); + result = JSTaggedValue::ToNumber(thread, string_v2); + EXPECT_EQ(result.GetNumber(), 9); + + JSHandle string_v3(thread->GetEcmaVM()->GetFactory()->NewFromString(" 0x2d ")); + result = JSTaggedValue::ToNumber(thread, string_v3); + EXPECT_EQ(result.GetNumber(), 45); + + JSHandle string_v4(thread->GetEcmaVM()->GetFactory()->NewFromString(" 0.000001 ")); + result = JSTaggedValue::ToNumber(thread, string_v4); + EXPECT_EQ(result.GetNumber(), 0.000001); + + JSHandle string_v5(thread->GetEcmaVM()->GetFactory()->NewFromString(" 1.23 ")); + result = JSTaggedValue::ToNumber(thread, string_v5); + EXPECT_EQ(result.GetNumber(), 1.23); + + JSHandle string_v6(thread->GetEcmaVM()->GetFactory()->NewFromString(" -1.23e2 ")); + result = JSTaggedValue::ToNumber(thread, string_v6); + EXPECT_EQ(result.GetNumber(), -123); + + JSHandle string_v7(thread->GetEcmaVM()->GetFactory()->NewFromString(" -123e-2")); + result = JSTaggedValue::ToNumber(thread, string_v7); + EXPECT_EQ(result.GetNumber(), -1.23); + + JSHandle string_v8(thread->GetEcmaVM()->GetFactory()->NewFromString(" Infinity ")); + result = JSTaggedValue::ToNumber(thread, string_v8); + EXPECT_TRUE(std::isinf(result.GetNumber())); + + JSHandle string_v9(thread->GetEcmaVM()->GetFactory()->NewFromString("100e307")); + result = JSTaggedValue::ToNumber(thread, string_v9); + EXPECT_TRUE(std::isinf(result.GetNumber())); + + JSHandle string_v10(thread->GetEcmaVM()->GetFactory()->NewFromString(" .")); + result = JSTaggedValue::ToNumber(thread, string_v10); + EXPECT_TRUE(std::isnan(result.GetNumber())); + + JSHandle string_v11(thread->GetEcmaVM()->GetFactory()->NewFromString("12e+")); + result = JSTaggedValue::ToNumber(thread, string_v11); + EXPECT_TRUE(std::isnan(result.GetNumber())); + + JSHandle string_v12(thread->GetEcmaVM()->GetFactory()->NewFromString(".e3")); + result = JSTaggedValue::ToNumber(thread, string_v12); + EXPECT_TRUE(std::isnan(result.GetNumber())); + + JSHandle string_v13(thread->GetEcmaVM()->GetFactory()->NewFromString("23eE")); + result = JSTaggedValue::ToNumber(thread, string_v13); + EXPECT_TRUE(std::isnan(result.GetNumber())); + + JSHandle string_v14(thread->GetEcmaVM()->GetFactory()->NewFromString("a")); + result = JSTaggedValue::ToNumber(thread, string_v14); + EXPECT_TRUE(std::isnan(result.GetNumber())); + + JSHandle string_v15(thread->GetEcmaVM()->GetFactory()->NewFromString("0o12e3")); + result = JSTaggedValue::ToNumber(thread, string_v15); + EXPECT_TRUE(std::isnan(result.GetNumber())); + + JSHandle string_v16(thread->GetEcmaVM()->GetFactory()->NewFromString("0x12.3")); + result = JSTaggedValue::ToNumber(thread, string_v16); + EXPECT_TRUE(std::isnan(result.GetNumber())); + + JSHandle string_v17(thread->GetEcmaVM()->GetFactory()->NewFromString(" 12.4.")); + result = JSTaggedValue::ToNumber(thread, string_v17); + EXPECT_TRUE(std::isnan(result.GetNumber())); + + JSHandle string_v18(thread->GetEcmaVM()->GetFactory()->NewFromString("123test")); + result = JSTaggedValue::ToNumber(thread, string_v18); + EXPECT_TRUE(std::isnan(result.GetNumber())); + + JSHandle string_v19(thread->GetEcmaVM()->GetFactory()->NewFromString("123test")); + result = JSTaggedValue::ToNumber(thread, string_v19); + EXPECT_TRUE(std::isnan(result.GetNumber())); + + JSHandle string_v20(thread->GetEcmaVM()->GetFactory()->NewFromString(" 0b ")); + result = JSTaggedValue::ToNumber(thread, string_v20); + EXPECT_TRUE(std::isnan(result.GetNumber())); + + JSHandle string_v21(thread->GetEcmaVM()->GetFactory()->NewFromString(" 0b0000 ")); + result = JSTaggedValue::ToNumber(thread, string_v21); + EXPECT_EQ(result.GetNumber(), 0); + + JSHandle string_v22(thread->GetEcmaVM()->GetFactory()->NewFromString(" 0o0000 ")); + result = JSTaggedValue::ToNumber(thread, string_v22); + EXPECT_EQ(result.GetNumber(), 0); + + JSHandle string_v23(thread->GetEcmaVM()->GetFactory()->NewFromString(" 0X0000 ")); + result = JSTaggedValue::ToNumber(thread, string_v23); + EXPECT_EQ(result.GetNumber(), 0); + + JSHandle string_v24(thread->GetEcmaVM()->GetFactory()->NewFromString(" 000.00000 ")); + result = JSTaggedValue::ToNumber(thread, string_v24); + EXPECT_EQ(result.GetNumber(), 0); + + JSHandle string_v25(thread->GetEcmaVM()->GetFactory()->NewFromString("")); + result = JSTaggedValue::ToNumber(thread, string_v25); + EXPECT_EQ(result.GetNumber(), 0); + + JSHandle string_v26(thread->GetEcmaVM()->GetFactory()->NewFromString(" ")); + result = JSTaggedValue::ToNumber(thread, string_v26); + EXPECT_EQ(result.GetNumber(), 0); + + JSHandle string_v27(thread->GetEcmaVM()->GetFactory()->NewFromString("0")); + result = JSTaggedValue::ToNumber(thread, string_v27); + EXPECT_EQ(result.GetNumber(), 0); + + JSHandle string_v28(thread->GetEcmaVM()->GetFactory()->NewFromString(" 0 ")); + result = JSTaggedValue::ToNumber(thread, string_v28); + EXPECT_EQ(result.GetNumber(), 0); + + JSHandle string_v29(thread->GetEcmaVM()->GetFactory()->NewFromString("00000000")); + result = JSTaggedValue::ToNumber(thread, string_v29); + EXPECT_EQ(result.GetNumber(), 0); + + JSHandle string_v30(thread->GetEcmaVM()->GetFactory()->NewFromString(" 00000000 ")); + result = JSTaggedValue::ToNumber(thread, string_v30); + EXPECT_EQ(result.GetNumber(), 0); + + thread->ClearException(); + JSHandle symbol_v1(thread->GetEcmaVM()->GetFactory()->NewJSSymbol()); + JSTaggedValue::ToNumber(thread, symbol_v1); + EXPECT_TRUE(thread->HasPendingException()); + EXPECT_TRUE(thread->GetException().IsJSError()); +} + +TEST_F(JSTaggedValueTest, ToInteger) +{ + JSTaggedNumber result; + + JSTaggedValue int_v(100); + result = JSTaggedValue::ToInteger(thread, JSHandle(thread, int_v)); + EXPECT_EQ(result.GetNumber(), 100); + + JSTaggedValue double_v1((double)100.0); + result = JSTaggedValue::ToInteger(thread, JSHandle(thread, double_v1)); + EXPECT_EQ(result.GetNumber(), (double)100.0); + + JSTaggedValue double_v2((double)100.123); + result = JSTaggedValue::ToInteger(thread, JSHandle(thread, double_v2)); + EXPECT_EQ(result.GetNumber(), (double)100); + + JSTaggedValue undefined_v = JSTaggedValue::Undefined(); + result = JSTaggedValue::ToInteger(thread, JSHandle(thread, undefined_v)); + EXPECT_EQ(result.GetNumber(), 0); + + JSTaggedValue hole_v = JSTaggedValue::Hole(); + result = JSTaggedValue::ToInteger(thread, JSHandle(thread, hole_v)); + EXPECT_EQ(result.GetNumber(), 0); + + JSTaggedValue null_v = JSTaggedValue::Null(); + result = JSTaggedValue::ToInteger(thread, JSHandle(thread, null_v)); + EXPECT_EQ(result.GetNumber(), 0); + + JSTaggedValue false_v = JSTaggedValue::False(); + result = JSTaggedValue::ToInteger(thread, JSHandle(thread, false_v)); + EXPECT_EQ(result.GetNumber(), 0); + + JSTaggedValue true_v = JSTaggedValue::True(); + result = JSTaggedValue::ToInteger(thread, JSHandle(thread, true_v)); + EXPECT_EQ(result.GetNumber(), 1); +} + +TEST_F(JSTaggedValueTest, ToInt32) +{ + int32_t result; + + JSTaggedValue int_v(100); + result = JSTaggedValue::ToInt32(thread, JSHandle(thread, int_v)); + EXPECT_EQ(result, 100); + + JSTaggedValue double_v1((double)100.0); + result = JSTaggedValue::ToInt32(thread, JSHandle(thread, double_v1)); + EXPECT_EQ(result, 100); + + JSTaggedValue double_v2((double)100.123); + result = JSTaggedValue::ToInt32(thread, JSHandle(thread, double_v2)); + EXPECT_EQ(result, 100); + + double input_1 = (static_cast(UINT32_MAX) + 1) + 12345; + JSTaggedValue double_v3(input_1); + result = JSTaggedValue::ToInt32(thread, JSHandle(thread, double_v3)); + EXPECT_EQ(result, 12345); + + double input_2 = 100 * (static_cast(UINT32_MAX) + 1) + 23456; + JSTaggedValue double_v4(input_2); + result = JSTaggedValue::ToInt32(thread, JSHandle(thread, double_v4)); + EXPECT_EQ(result, 23456); + + double input_3 = 100 * (static_cast(UINT32_MAX) + 1) + INT32_MAX + 1 + 23456; + JSTaggedValue double_v5(input_3); + result = JSTaggedValue::ToInt32(thread, JSHandle(thread, double_v5)); + EXPECT_EQ(result, 23456 - static_cast(INT32_MAX) - 1); + + JSTaggedValue undefined_v = JSTaggedValue::Undefined(); + result = JSTaggedValue::ToInt32(thread, JSHandle(thread, undefined_v)); + EXPECT_EQ(result, 0); + + JSTaggedValue hole_v = JSTaggedValue::Hole(); + result = JSTaggedValue::ToInt32(thread, JSHandle(thread, hole_v)); + EXPECT_EQ(result, 0); + + JSTaggedValue null_v = JSTaggedValue::Null(); + result = JSTaggedValue::ToInt32(thread, JSHandle(thread, null_v)); + EXPECT_EQ(result, 0); + + JSTaggedValue false_v = JSTaggedValue::False(); + result = JSTaggedValue::ToInt32(thread, JSHandle(thread, false_v)); + EXPECT_EQ(result, 0); + + JSTaggedValue true_v = JSTaggedValue::True(); + result = JSTaggedValue::ToInt32(thread, JSHandle(thread, true_v)); + EXPECT_EQ(result, 1); +} + +TEST_F(JSTaggedValueTest, ToUint32) +{ + uint32_t result; + + JSTaggedValue int_v(100); + result = JSTaggedValue::ToUint32(thread, JSHandle(thread, int_v)); + EXPECT_EQ(result, 100); + + JSTaggedValue double_v1((double)100.0); + result = JSTaggedValue::ToUint32(thread, JSHandle(thread, double_v1)); + EXPECT_EQ(result, 100); + + JSTaggedValue double_v2((double)100.123); + result = JSTaggedValue::ToUint32(thread, JSHandle(thread, double_v2)); + EXPECT_EQ(result, 100); + + double input_1 = (static_cast(UINT32_MAX) + 1) + 12345; + JSTaggedValue double_v3(input_1); + result = JSTaggedValue::ToUint32(thread, JSHandle(thread, double_v3)); + EXPECT_EQ(result, 12345); + + double input_2 = 100 * (static_cast(UINT32_MAX) + 1) + 23456; + JSTaggedValue double_v4(input_2); + result = JSTaggedValue::ToUint32(thread, JSHandle(thread, double_v4)); + EXPECT_EQ(result, 23456); + + double input_3 = 100 * (static_cast(UINT32_MAX) + 1) + INT32_MAX + 1 + 23456; + JSTaggedValue double_v5(input_3); + result = JSTaggedValue::ToUint32(thread, JSHandle(thread, double_v5)); + EXPECT_EQ(result, static_cast(INT32_MAX) + 1 + 23456); + + JSTaggedValue undefined_v = JSTaggedValue::Undefined(); + result = JSTaggedValue::ToUint32(thread, JSHandle(thread, undefined_v)); + EXPECT_EQ(result, 0); + + JSTaggedValue hole_v = JSTaggedValue::Hole(); + result = JSTaggedValue::ToUint32(thread, JSHandle(thread, hole_v)); + EXPECT_EQ(result, 0); + + JSTaggedValue null_v = JSTaggedValue::Null(); + result = JSTaggedValue::ToUint32(thread, JSHandle(thread, null_v)); + EXPECT_EQ(result, 0); + + JSTaggedValue false_v = JSTaggedValue::False(); + result = JSTaggedValue::ToUint32(thread, JSHandle(thread, false_v)); + EXPECT_EQ(result, 0); + + JSTaggedValue true_v = JSTaggedValue::True(); + result = JSTaggedValue::ToUint32(thread, JSHandle(thread, true_v)); + EXPECT_EQ(result, 1); +} + +TEST_F(JSTaggedValueTest, ToInt16) +{ + int32_t result; + + JSTaggedValue int_v(100); + result = JSTaggedValue::ToInt16(thread, JSHandle(thread, int_v)); + EXPECT_EQ(result, 100); + + JSTaggedValue double_v1((double)100.0); + result = JSTaggedValue::ToInt16(thread, JSHandle(thread, double_v1)); + EXPECT_EQ(result, 100); + + JSTaggedValue double_v2((double)100.123); + result = JSTaggedValue::ToInt16(thread, JSHandle(thread, double_v2)); + EXPECT_EQ(result, 100); + + double input_1 = (static_cast(UINT16_MAX) + 1) + 12345; + JSTaggedValue double_v3(input_1); + result = JSTaggedValue::ToInt16(thread, JSHandle(thread, double_v3)); + EXPECT_EQ(result, 12345); + + double input_2 = 100 * (static_cast(UINT16_MAX) + 1) + 23456; + JSTaggedValue double_v4(input_2); + result = JSTaggedValue::ToInt16(thread, JSHandle(thread, double_v4)); + EXPECT_EQ(result, 23456); + + double input_3 = 100 * (static_cast(UINT16_MAX) + 1) + INT16_MAX + 1 + 23456; + JSTaggedValue double_v5(input_3); + result = JSTaggedValue::ToInt16(thread, JSHandle(thread, double_v5)); + EXPECT_EQ(result, 23456 - static_cast(INT16_MAX) - 1); + + JSTaggedValue undefined_v = JSTaggedValue::Undefined(); + result = JSTaggedValue::ToInt16(thread, JSHandle(thread, undefined_v)); + EXPECT_EQ(result, 0); + + JSTaggedValue hole_v = JSTaggedValue::Hole(); + result = JSTaggedValue::ToInt16(thread, JSHandle(thread, hole_v)); + EXPECT_EQ(result, 0); + + JSTaggedValue null_v = JSTaggedValue::Null(); + result = JSTaggedValue::ToInt16(thread, JSHandle(thread, null_v)); + EXPECT_EQ(result, 0); + + JSTaggedValue false_v = JSTaggedValue::False(); + result = JSTaggedValue::ToInt16(thread, JSHandle(thread, false_v)); + EXPECT_EQ(result, 0); + + JSTaggedValue true_v = JSTaggedValue::True(); + result = JSTaggedValue::ToInt16(thread, JSHandle(thread, true_v)); + EXPECT_EQ(result, 1); +} + +TEST_F(JSTaggedValueTest, ToUint16) +{ + uint32_t result; + + JSTaggedValue int_v(100); + result = JSTaggedValue::ToUint16(thread, JSHandle(thread, int_v)); + EXPECT_EQ(result, 100); + + JSTaggedValue double_v1((double)100.0); + result = JSTaggedValue::ToUint16(thread, JSHandle(thread, double_v1)); + EXPECT_EQ(result, 100); + + JSTaggedValue double_v2((double)100.123); + result = JSTaggedValue::ToUint16(thread, JSHandle(thread, double_v2)); + EXPECT_EQ(result, 100); + + double input_1 = (static_cast(UINT16_MAX) + 1) + 12345; + JSTaggedValue double_v3(input_1); + result = JSTaggedValue::ToUint16(thread, JSHandle(thread, double_v3)); + EXPECT_EQ(result, 12345); + + double input_2 = 100 * (static_cast(UINT16_MAX) + 1) + 23456; + JSTaggedValue double_v4(input_2); + result = JSTaggedValue::ToUint16(thread, JSHandle(thread, double_v4)); + EXPECT_EQ(result, 23456); + + double input_3 = 100 * (static_cast(UINT16_MAX) + 1) + INT16_MAX + 1 + 23456; + JSTaggedValue double_v5(input_3); + result = JSTaggedValue::ToUint16(thread, JSHandle(thread, double_v5)); + EXPECT_EQ(result, static_cast(INT16_MAX) + 1 + 23456); + + JSTaggedValue undefined_v = JSTaggedValue::Undefined(); + result = JSTaggedValue::ToUint16(thread, JSHandle(thread, undefined_v)); + EXPECT_EQ(result, 0); + + JSTaggedValue hole_v = JSTaggedValue::Hole(); + result = JSTaggedValue::ToUint16(thread, JSHandle(thread, hole_v)); + EXPECT_EQ(result, 0); + + JSTaggedValue null_v = JSTaggedValue::Null(); + result = JSTaggedValue::ToUint16(thread, JSHandle(thread, null_v)); + EXPECT_EQ(result, 0); + + JSTaggedValue false_v = JSTaggedValue::False(); + result = JSTaggedValue::ToUint16(thread, JSHandle(thread, false_v)); + EXPECT_EQ(result, 0); + + JSTaggedValue true_v = JSTaggedValue::True(); + result = JSTaggedValue::ToUint16(thread, JSHandle(thread, true_v)); + EXPECT_EQ(result, 1); +} + +TEST_F(JSTaggedValueTest, ToInt8) +{ + int32_t result; + + JSTaggedValue int_v(100); + result = JSTaggedValue::ToInt8(thread, JSHandle(thread, int_v)); + EXPECT_EQ(result, 100); + + JSTaggedValue double_v1((double)100.0); + result = JSTaggedValue::ToInt8(thread, JSHandle(thread, double_v1)); + EXPECT_EQ(result, 100); + + JSTaggedValue double_v2((double)100.123); + result = JSTaggedValue::ToInt8(thread, JSHandle(thread, double_v2)); + EXPECT_EQ(result, 100); + + double input_1 = (static_cast(UINT8_MAX) + 1) + 45; + JSTaggedValue double_v3(input_1); + result = JSTaggedValue::ToInt8(thread, JSHandle(thread, double_v3)); + EXPECT_EQ(result, 45); + + double input_2 = 100 * (static_cast(UINT8_MAX) + 1) + 56; + JSTaggedValue double_v4(input_2); + result = JSTaggedValue::ToInt8(thread, JSHandle(thread, double_v4)); + EXPECT_EQ(result, 56); + + double input_3 = 100 * (static_cast(UINT8_MAX) + 1) + INT8_MAX + 1 + 23; + JSTaggedValue double_v5(input_3); + result = JSTaggedValue::ToInt8(thread, JSHandle(thread, double_v5)); + EXPECT_EQ(result, 23 - static_cast(INT8_MAX) - 1); + + JSTaggedValue undefined_v = JSTaggedValue::Undefined(); + result = JSTaggedValue::ToInt8(thread, JSHandle(thread, undefined_v)); + EXPECT_EQ(result, 0); + + JSTaggedValue hole_v = JSTaggedValue::Hole(); + result = JSTaggedValue::ToInt8(thread, JSHandle(thread, hole_v)); + EXPECT_EQ(result, 0); + + JSTaggedValue null_v = JSTaggedValue::Null(); + result = JSTaggedValue::ToInt8(thread, JSHandle(thread, null_v)); + EXPECT_EQ(result, 0); + + JSTaggedValue false_v = JSTaggedValue::False(); + result = JSTaggedValue::ToInt8(thread, JSHandle(thread, false_v)); + EXPECT_EQ(result, 0); + + JSTaggedValue true_v = JSTaggedValue::True(); + result = JSTaggedValue::ToInt8(thread, JSHandle(thread, true_v)); + EXPECT_EQ(result, 1); +} + +TEST_F(JSTaggedValueTest, ToUint8) +{ + uint32_t result; + + JSTaggedValue int_v(100); + result = JSTaggedValue::ToUint8(thread, JSHandle(thread, int_v)); + EXPECT_EQ(result, 100); + + JSTaggedValue double_v1((double)100.0); + result = JSTaggedValue::ToUint8(thread, JSHandle(thread, double_v1)); + EXPECT_EQ(result, 100); + + JSTaggedValue double_v2((double)100.123); + result = JSTaggedValue::ToUint8(thread, JSHandle(thread, double_v2)); + EXPECT_EQ(result, 100); + + double input_1 = (static_cast(UINT8_MAX) + 1) + 34; + JSTaggedValue double_v3(input_1); + result = JSTaggedValue::ToUint8(thread, JSHandle(thread, double_v3)); + EXPECT_EQ(result, 34); + + double input_2 = 100 * (static_cast(UINT8_MAX) + 1) + 45; + JSTaggedValue double_v4(input_2); + result = JSTaggedValue::ToUint8(thread, JSHandle(thread, double_v4)); + EXPECT_EQ(result, 45); + + double input_3 = 100 * (static_cast(UINT8_MAX) + 1) + INT8_MAX + 1 + 56; + JSTaggedValue double_v5(input_3); + result = JSTaggedValue::ToUint8(thread, JSHandle(thread, double_v5)); + EXPECT_EQ(result, static_cast(INT8_MAX) + 1 + 56); + + JSTaggedValue undefined_v = JSTaggedValue::Undefined(); + result = JSTaggedValue::ToUint8(thread, JSHandle(thread, undefined_v)); + EXPECT_EQ(result, 0); + + JSTaggedValue hole_v = JSTaggedValue::Hole(); + result = JSTaggedValue::ToUint8(thread, JSHandle(thread, hole_v)); + EXPECT_EQ(result, 0); + + JSTaggedValue null_v = JSTaggedValue::Null(); + result = JSTaggedValue::ToUint8(thread, JSHandle(thread, null_v)); + EXPECT_EQ(result, 0); + + JSTaggedValue false_v = JSTaggedValue::False(); + result = JSTaggedValue::ToUint8(thread, JSHandle(thread, false_v)); + EXPECT_EQ(result, 0); + + JSTaggedValue true_v = JSTaggedValue::True(); + result = JSTaggedValue::ToUint8(thread, JSHandle(thread, true_v)); + EXPECT_EQ(result, 1); +} + +TEST_F(JSTaggedValueTest, ToUint8Clamp) +{ + uint32_t result; + + JSTaggedValue int_v1(-100); + result = JSTaggedValue::ToUint8Clamp(thread, JSHandle(thread, int_v1)); + EXPECT_EQ(result, 0); + + JSTaggedValue int_v2(100); + result = JSTaggedValue::ToUint8Clamp(thread, JSHandle(thread, int_v2)); + EXPECT_EQ(result, 100); + + JSTaggedValue int_v3(300); + result = JSTaggedValue::ToUint8Clamp(thread, JSHandle(thread, int_v3)); + EXPECT_EQ(result, 255); + + JSTaggedValue double_v1((double)-100.123); + result = JSTaggedValue::ToUint8Clamp(thread, JSHandle(thread, double_v1)); + EXPECT_EQ(result, 0); + + JSTaggedValue double_v2((double)100.123); + result = JSTaggedValue::ToUint8Clamp(thread, JSHandle(thread, double_v2)); + EXPECT_EQ(result, 100); + + JSTaggedValue double_v3((double)100.55); + result = JSTaggedValue::ToUint8Clamp(thread, JSHandle(thread, double_v3)); + EXPECT_EQ(result, 101); + + JSTaggedValue double_v4((double)99.9); + result = JSTaggedValue::ToUint8Clamp(thread, JSHandle(thread, double_v4)); + EXPECT_EQ(result, 100); + + JSTaggedValue double_v5((double)300.5); + result = JSTaggedValue::ToUint8Clamp(thread, JSHandle(thread, double_v5)); + EXPECT_EQ(result, 255); + + JSTaggedValue undefined_v = JSTaggedValue::Undefined(); + result = JSTaggedValue::ToUint8Clamp(thread, JSHandle(thread, undefined_v)); + EXPECT_EQ(result, 0); + + JSTaggedValue hole_v = JSTaggedValue::Hole(); + result = JSTaggedValue::ToUint8Clamp(thread, JSHandle(thread, hole_v)); + EXPECT_EQ(result, 0); + + JSTaggedValue null_v = JSTaggedValue::Null(); + result = JSTaggedValue::ToUint8Clamp(thread, JSHandle(thread, null_v)); + EXPECT_EQ(result, 0); + + JSTaggedValue false_v = JSTaggedValue::False(); + result = JSTaggedValue::ToUint8Clamp(thread, JSHandle(thread, false_v)); + EXPECT_EQ(result, 0); + + JSTaggedValue true_v = JSTaggedValue::True(); + result = JSTaggedValue::ToUint8Clamp(thread, JSHandle(thread, true_v)); + EXPECT_EQ(result, 1); +} + +TEST_F(JSTaggedValueTest, ToPropertyKey) +{ + JSTaggedValue result; + JSHandle str = thread->GetEcmaVM()->GetFactory()->NewFromString("null"); + JSTaggedValue key = str.GetTaggedValue(); + result = JSTaggedValue::ToPropertyKey(thread, JSHandle(thread, key)).GetTaggedValue(); + EXPECT_TRUE(key == result); +} + +void check_ok_string(JSThread *thread, const JSHandle &tagged, CString &right_c_str) +{ + JSHandle result = JSTaggedValue::ToString(thread, tagged); + JSHandle right_string = thread->GetEcmaVM()->GetFactory()->NewFromString(right_c_str); + EXPECT_TRUE(EcmaString::StringsAreEqual(EcmaString::Cast(result.GetObject()), + EcmaString::Cast(right_string.GetObject()))); +} + +void CheckOkString(JSThread *thread, const JSHandle &tagged, CString &rightCStr) +{ + JSHandle result = JSTaggedValue::ToString(thread, tagged); + JSHandle rightString = thread->GetEcmaVM()->GetFactory()->NewFromCanBeCompressString(rightCStr); + EXPECT_TRUE(EcmaString::StringsAreEqual(EcmaString::Cast(result.GetObject()), + EcmaString::Cast(rightString.GetObject()))); +} + +TEST_F(JSTaggedValueTest, ToString) +{ + CString rightCStr = ""; + CheckOkString(thread, JSHandle(thread, JSTaggedValue()), rightCStr); + + rightCStr = "undefined"; + CheckOkString(thread, JSHandle(thread, JSTaggedValue::Undefined()), rightCStr); + + rightCStr = "null"; + CheckOkString(thread, JSHandle(thread, JSTaggedValue::Null()), rightCStr); + + rightCStr = "true"; + CheckOkString(thread, JSHandle(thread, JSTaggedValue::True()), rightCStr); + + rightCStr = "false"; + CheckOkString(thread, JSHandle(thread, JSTaggedValue::False()), rightCStr); + + rightCStr = "hello world"; + CheckOkString(thread, + JSHandle(thread->GetEcmaVM()->GetFactory()->NewFromCanBeCompressString(rightCStr)), + rightCStr); + + double num = 1; + JSTaggedNumber numberNum = JSTaggedNumber(num); + rightCStr = "1"; + CheckOkString(thread, JSHandle(thread, numberNum), rightCStr); + + num = 1.23; + numberNum = JSTaggedNumber(num); + rightCStr = "1.23"; + CheckOkString(thread, JSHandle(thread, numberNum), rightCStr); + + int numInt = 2; + JSHandle value1(thread, JSTaggedValue(numInt)); + rightCStr = "2"; + CheckOkString(thread, JSHandle::Cast(JSTaggedValue::ToObject(thread, value1)), rightCStr); + + num = 1.23; + JSHandle value2(thread, JSTaggedValue(num)); + rightCStr = "1.23"; + CheckOkString(thread, JSHandle::Cast(JSTaggedValue::ToObject(thread, value2)), rightCStr); + + bool valueBool = true; + JSHandle value3(thread, JSTaggedValue(valueBool)); + rightCStr = "true"; + CheckOkString(thread, JSHandle::Cast(JSTaggedValue::ToObject(thread, value3)), rightCStr); +} + +TEST_F(JSTaggedValueTest, CanonicalNumericIndexString) +{ + JSTaggedValue result; + + JSHandle str = thread->GetEcmaVM()->GetFactory()->NewFromString("-0"); + JSTaggedValue tmp_str = str.GetTaggedValue(); + result = JSTaggedValue::CanonicalNumericIndexString(thread, JSHandle(thread, tmp_str)); + EXPECT_EQ(result.GetDouble(), -0.0); + + JSTaggedValue tmp_int(1); + result = JSTaggedValue::CanonicalNumericIndexString(thread, JSHandle(thread, tmp_int)); + EXPECT_TRUE(result.IsUndefined()); + + JSTaggedValue tmp_double((double)100.0); + result = JSTaggedValue::CanonicalNumericIndexString(thread, JSHandle(thread, tmp_double)); + EXPECT_TRUE(result.IsUndefined()); +} + +TEST_F(JSTaggedValueTest, ToObject) +{ + ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); + + // int -> JSObject + JSHandle value1(thread, JSTaggedValue(2)); + JSTaggedValue tagged1 = + JSTaggedValue(JSHandle::Cast(JSTaggedValue::ToObject(thread, value1))->GetValue()); + EXPECT_EQ(tagged1.GetRawData(), JSTaggedValue(2).GetRawData()); + + // double -> JSObject + JSHandle value2(thread, JSTaggedValue(2.2)); + JSTaggedValue tagged2 = + JSTaggedValue(JSHandle::Cast(JSTaggedValue::ToObject(thread, value2))->GetValue()); + EXPECT_EQ(tagged2.GetRawData(), JSTaggedValue(static_cast(2.2)).GetRawData()); + + // bool -> JSObject + JSHandle value3(thread, JSTaggedValue::True()); + JSTaggedValue tagged3 = + JSTaggedValue(JSHandle::Cast(JSTaggedValue::ToObject(thread, value3))->GetValue()); + EXPECT_EQ(tagged3.GetRawData(), JSTaggedValue::True().GetRawData()); + + // String -> JSObject + JSHandle value4(factory->NewFromString("aaa")); + JSTaggedValue tagged4 = + JSTaggedValue(JSHandle::Cast(JSTaggedValue::ToObject(thread, value4))->GetValue()); + EXPECT_TRUE(tagged4.IsString()); + EXPECT_EQ(reinterpret_cast(tagged4.GetRawData())->Compare(value4.GetObject()), 0); + + // JSSymbol -> JSObject + JSHandle symbol = factory->NewPublicSymbolWithChar("bbb"); + JSHandle str = factory->NewFromString("bbb"); + JSHandle value5(symbol); + JSTaggedValue tagged5 = + JSTaggedValue(JSHandle::Cast(JSTaggedValue::ToObject(thread, value5))->GetValue()); + EXPECT_EQ(EcmaString::Cast(reinterpret_cast(tagged5.GetRawData())->GetDescription().GetHeapObject()) + ->Compare(*str), + 0); + EXPECT_TRUE(tagged5.IsSymbol()); + + // JSObject(include all types of objects inherited from JSObject) -> JSObject + EcmaVM *ecma = thread->GetEcmaVM(); + JSHandle object_fun = ecma->GetGlobalEnv()->GetObjectFunction(); + JSHandle js_obj = factory->NewJSObjectByConstructor(JSHandle(object_fun), object_fun); + JSHandle value(js_obj); + EXPECT_EQ(*JSTaggedValue::ToObject(thread, value), *js_obj); +} + +TEST_F(JSTaggedValueTest, ToLength) +{ + JSTaggedNumber result; + + JSTaggedValue int_v(100); + result = JSTaggedValue::ToLength(thread, JSHandle(thread, int_v)); + EXPECT_EQ(result.GetNumber(), 100); + + JSTaggedValue int_v2(-1); + result = JSTaggedValue::ToLength(thread, JSHandle(thread, int_v2)); + EXPECT_EQ(result.GetNumber(), 0); + + JSTaggedValue double_v1((double)100.0); + result = JSTaggedValue::ToLength(thread, JSHandle(thread, double_v1)); + EXPECT_EQ(result.GetNumber(), (double)100.0); + + JSTaggedValue double_v2((double)100.123); + result = JSTaggedValue::ToLength(thread, JSHandle(thread, double_v2)); + EXPECT_EQ(result.GetNumber(), (double)100); + + JSTaggedValue double_v3((double)-1.0); + result = JSTaggedValue::ToLength(thread, JSHandle(thread, double_v3)); + EXPECT_EQ(result.GetNumber(), (double)0); + + JSTaggedValue double_v4((double)9007199254740992); + result = JSTaggedValue::ToLength(thread, JSHandle(thread, double_v4)); + EXPECT_EQ(result.GetNumber(), (double)9007199254740991); + + JSTaggedValue undefined_v = JSTaggedValue::Undefined(); + result = JSTaggedValue::ToLength(thread, JSHandle(thread, undefined_v)); + EXPECT_EQ(result.GetNumber(), 0); + + JSTaggedValue hole_v = JSTaggedValue::Hole(); + result = JSTaggedValue::ToLength(thread, JSHandle(thread, hole_v)); + EXPECT_EQ(result.GetNumber(), 0); + + JSTaggedValue null_v = JSTaggedValue::Null(); + result = JSTaggedValue::ToLength(thread, JSHandle(thread, null_v)); + EXPECT_EQ(result.GetNumber(), 0); + + JSTaggedValue false_v = JSTaggedValue::False(); + result = JSTaggedValue::ToLength(thread, JSHandle(thread, false_v)); + EXPECT_EQ(result.GetNumber(), 0); + + JSTaggedValue true_v = JSTaggedValue::True(); + result = JSTaggedValue::ToLength(thread, JSHandle(thread, true_v)); + EXPECT_EQ(result.GetNumber(), 1); +} + +TEST_F(JSTaggedValueTest, IsArray) +{ + EcmaVM *ecma = thread->GetEcmaVM(); + JSHandle object_fun = ecma->GetGlobalEnv()->GetArrayFunction(); + + JSHandle js_obj = + thread->GetEcmaVM()->GetFactory()->NewJSObjectByConstructor(JSHandle(object_fun), object_fun); + + ASSERT_TRUE(js_obj->IsJSArray()); + ASSERT_FALSE(JSTaggedValue(1).IsArray(thread)); + + ASSERT_FALSE(thread->GetEcmaVM()->GetFactory()->NewFromString("test").GetTaggedValue().IsArray(thread)); +} + +TEST_F(JSTaggedValueTest, IsCallable_IsConstructor_IsExtensible) +{ + JSHandle env = thread->GetEcmaVM()->GetGlobalEnv(); + JSHandle js_function = thread->GetEcmaVM()->GetFactory()->NewJSFunction(env); + JSHClass *js_hclass = js_function->GetJSHClass(); + ASSERT_TRUE(js_function->IsCallable()); + js_hclass->SetConstructor(true); + ASSERT_TRUE(js_function->IsConstructor()); + js_hclass->SetConstructor(false); + ASSERT_FALSE(js_function->IsConstructor()); + js_hclass->SetExtensible(true); + ASSERT_TRUE(js_function->IsExtensible()); + js_hclass->SetExtensible(false); + ASSERT_FALSE(js_function->IsExtensible()); + ASSERT_FALSE(JSTaggedValue(1).IsExtensible(thread)); + ASSERT_FALSE(JSTaggedValue(1).IsConstructor()); + ASSERT_FALSE(JSTaggedValue(1).IsCallable()); +} + +TEST_F(JSTaggedValueTest, IsInteger) +{ + ASSERT_TRUE(JSTaggedValue(1).IsInteger()); + ASSERT_TRUE(JSTaggedValue(1.0).IsInteger()); + ASSERT_TRUE(JSTaggedValue(-1.0).IsInteger()); + ASSERT_FALSE(JSTaggedValue(-1.1).IsInteger()); + ASSERT_FALSE(JSTaggedValue(1.1).IsInteger()); + ASSERT_FALSE(JSTaggedValue(std::numeric_limits::infinity()).IsInteger()); + ASSERT_FALSE(JSTaggedValue((-1) * std::numeric_limits::infinity()).IsInteger()); + ASSERT_TRUE(JSTaggedValue(0).IsInteger()); + ASSERT_TRUE(JSTaggedValue(0.0).IsInteger()); + ASSERT_FALSE(JSTaggedValue::True().IsInteger()); + ASSERT_FALSE(JSTaggedValue::Undefined().IsInteger()); + ASSERT_FALSE(JSTaggedValue::Null().IsInteger()); + ASSERT_FALSE(JSTaggedValue::False().IsInteger()); + ASSERT_FALSE(JSTaggedValue::Hole().IsInteger()); + ASSERT_FALSE(thread->GetEcmaVM()->GetFactory()->NewFromString("test").GetTaggedValue().IsInteger()); +} + +TEST_F(JSTaggedValueTest, IsPropertyKey) +{ + ASSERT_TRUE(JSTaggedValue::IsPropertyKey( + JSHandle(thread->GetEcmaVM()->GetFactory()->NewFromString("test")))); +} + +TEST_F(JSTaggedValueTest, IsRegExp) +{ + JSHandle string = thread->GetEcmaVM()->GetFactory()->NewFromString("test"); + JSHandle obj = JSHandle::Cast(string); + ASSERT_FALSE(JSObject::IsRegExp(thread, obj)); +} + +TEST_F(JSTaggedValueTest, SameValue) +{ + EcmaVM *ecma = thread->GetEcmaVM(); + JSHandle object_fun = ecma->GetGlobalEnv()->GetObjectFunction(); + + JSHandle js_obj = + ecma->GetFactory()->NewJSObjectByConstructor(JSHandle(object_fun), object_fun); + + // not same type + ASSERT_FALSE(JSTaggedValue::SameValue(JSTaggedValue(1), JSTaggedValue::False())); + ASSERT_FALSE(JSTaggedValue::SameValue(JSTaggedValue(1.0), JSTaggedValue::True())); + ASSERT_FALSE( + JSTaggedValue::SameValue(JSTaggedValue(1), ecma->GetFactory()->NewFromString("test").GetTaggedValue())); + ASSERT_FALSE(JSTaggedValue::SameValue(JSTaggedValue(1), JSTaggedValue(*js_obj))); + JSHandle test(ecma->GetFactory()->NewFromString("test")); + ASSERT_FALSE(JSTaggedValue::SameValue(test.GetTaggedValue(), JSTaggedValue(*js_obj))); + + // number compare + ASSERT_TRUE(JSTaggedValue::SameValue(JSTaggedValue(1), JSTaggedValue(1))); + ASSERT_TRUE(JSTaggedValue::SameValue(JSTaggedValue(1), JSTaggedValue(1.0))); + ASSERT_FALSE(JSTaggedValue::SameValue(JSTaggedValue(1.0), JSTaggedValue(2.0))); + ASSERT_FALSE(JSTaggedValue::SameValue(JSTaggedValue(std::numeric_limits::quiet_NaN()), JSTaggedValue(2.0))); + ASSERT_TRUE(JSTaggedValue::SameValue(JSTaggedValue(std::numeric_limits::quiet_NaN()), + JSTaggedValue(std::numeric_limits::quiet_NaN()))); + ASSERT_TRUE(JSTaggedValue::SameValue(JSTaggedValue(std::numeric_limits::quiet_NaN()), + JSTaggedValue(std::numeric_limits::quiet_NaN()))); + ASSERT_FALSE(JSTaggedValue::SameValue(JSTaggedValue(0.0), JSTaggedValue(-0.0))); + ASSERT_TRUE(JSTaggedValue::SameValue(JSTaggedValue(0), JSTaggedValue(-0))); + ASSERT_FALSE(JSTaggedValue::SameValue(JSTaggedValue(1.0), JSTaggedValue(-1.0))); + + // string compare + JSHandle test1(ecma->GetFactory()->NewFromString("test1")); + ASSERT_FALSE(JSTaggedValue::SameValue(test.GetTaggedValue(), test1.GetTaggedValue())); + ASSERT_TRUE(JSTaggedValue::SameValue(test.GetTaggedValue(), test.GetTaggedValue())); + + // bool compare + ASSERT_TRUE(JSTaggedValue::SameValue(JSTaggedValue::True(), JSTaggedValue::True())); + ASSERT_TRUE(JSTaggedValue::SameValue(JSTaggedValue::False(), JSTaggedValue::False())); + ASSERT_FALSE(JSTaggedValue::SameValue(JSTaggedValue::False(), JSTaggedValue::True())); + ASSERT_FALSE(JSTaggedValue::SameValue(JSTaggedValue::True(), JSTaggedValue::False())); + + // js object compare + ASSERT_TRUE(JSTaggedValue::SameValue(js_obj.GetTaggedValue(), js_obj.GetTaggedValue())); + + // undefined or null compare + ASSERT_TRUE(JSTaggedValue::SameValue(JSTaggedValue::Undefined(), JSTaggedValue::Undefined())); + ASSERT_TRUE(JSTaggedValue::SameValue(JSTaggedValue::Null(), JSTaggedValue::Null())); + ASSERT_FALSE(JSTaggedValue::SameValue(JSTaggedValue::Undefined(), JSTaggedValue::Null())); + ASSERT_FALSE(JSTaggedValue::SameValue(JSTaggedValue::Null(), JSTaggedValue::Undefined())); +} + +TEST_F(JSTaggedValueTest, SameValueZero) +{ + // SameValueZero differs from SameValue only in its treatment of +0 and -0. + ASSERT_TRUE(JSTaggedValue::SameValueZero(JSTaggedValue(0.0), JSTaggedValue(-0.0))); +} + +TEST_F(JSTaggedValueTest, Less) +{ + JSHandle test(thread->GetEcmaVM()->GetFactory()->NewFromString("test")); + JSHandle test1(thread->GetEcmaVM()->GetFactory()->NewFromString("test1")); + JSHandle test2(thread->GetEcmaVM()->GetFactory()->NewFromString("test2")); + + ASSERT_TRUE(JSTaggedValue::Less(thread, JSHandle(thread, JSTaggedValue(1.0)), + JSHandle(thread, JSTaggedValue(2.0)))); + ASSERT_FALSE(JSTaggedValue::Less(thread, JSHandle(thread, JSTaggedValue(0.0)), + JSHandle(thread, JSTaggedValue(-0.0)))); + ASSERT_TRUE(JSTaggedValue::Less(thread, JSHandle(thread, JSTaggedValue(1)), + JSHandle(thread, JSTaggedValue(2)))); + + ASSERT_TRUE(JSTaggedValue::Less(thread, test, test1)); + ASSERT_FALSE(JSTaggedValue::Less(thread, test2, test1)); + ASSERT_FALSE(JSTaggedValue::Less(thread, JSHandle(thread, JSTaggedValue(1)), test1)); + ASSERT_FALSE(JSTaggedValue::Less(thread, test2, JSHandle(thread, JSTaggedValue(2)))); + + ASSERT_TRUE(JSTaggedValue::Less(thread, + JSHandle(thread->GetEcmaVM()->GetFactory()->NewFromString("1")), + JSHandle(thread, JSTaggedValue(2)))); + ASSERT_TRUE(JSTaggedValue::Less(thread, JSHandle(thread, JSTaggedValue(1)), + JSHandle(thread->GetEcmaVM()->GetFactory()->NewFromString("2")))); + + ASSERT_TRUE(JSTaggedValue::Less(thread, JSHandle(thread, JSTaggedValue::False()), + JSHandle(thread, JSTaggedValue::True()))); + ASSERT_FALSE(JSTaggedValue::Less(thread, JSHandle(thread, JSTaggedValue::True()), + JSHandle(thread, JSTaggedValue::False()))); + ASSERT_FALSE(JSTaggedValue::Less(thread, JSHandle(thread, JSTaggedValue::Undefined()), + JSHandle(thread, JSTaggedValue::Null()))); + ASSERT_FALSE(JSTaggedValue::Less(thread, JSHandle(thread, JSTaggedValue::Null()), + JSHandle(thread, JSTaggedValue::Undefined()))); +} + +TEST_F(JSTaggedValueTest, Equal) +{ + ASSERT_TRUE(JSTaggedValue::Equal(thread, JSHandle(thread, JSTaggedValue::Undefined()), + JSHandle(thread, JSTaggedValue::Undefined()))); + ASSERT_TRUE(JSTaggedValue::Equal(thread, JSHandle(thread, JSTaggedValue::Null()), + JSHandle(thread, JSTaggedValue::Undefined()))); + ASSERT_TRUE(JSTaggedValue::Equal(thread, JSHandle(thread, JSTaggedValue::Null()), + JSHandle(thread, JSTaggedValue::Null()))); + ASSERT_TRUE(JSTaggedValue::Equal(thread, JSHandle(thread, JSTaggedValue::Null()), + JSHandle(thread, JSTaggedValue::Undefined()))); + ASSERT_FALSE(JSTaggedValue::Equal(thread, JSHandle(thread, JSTaggedValue::Null()), + JSHandle(thread, JSTaggedValue(1)))); + ASSERT_FALSE(JSTaggedValue::Equal(thread, JSHandle(thread, JSTaggedValue::Null()), + JSHandle(thread, JSTaggedValue::True()))); + ASSERT_FALSE(JSTaggedValue::Equal(thread, JSHandle(thread, JSTaggedValue::Null()), + JSHandle(thread, JSTaggedValue::False()))); + + // number compare + ASSERT_TRUE(JSTaggedValue::Equal(thread, JSHandle(thread, JSTaggedValue(1)), + JSHandle(thread, JSTaggedValue(1)))); + ASSERT_FALSE(JSTaggedValue::Equal(thread, JSHandle(thread, JSTaggedValue(1)), + JSHandle(thread, JSTaggedValue(2)))); + ASSERT_TRUE(JSTaggedValue::Equal(thread, JSHandle(thread, JSTaggedValue(1.0)), + JSHandle(thread, JSTaggedValue(1.0)))); + ASSERT_TRUE(JSTaggedValue::Equal(thread, JSHandle(thread, JSTaggedValue(0.0)), + JSHandle(thread, JSTaggedValue(0.0)))); + ASSERT_TRUE(JSTaggedValue::Equal(thread, JSHandle(thread, JSTaggedValue(0.0)), + JSHandle(thread, JSTaggedValue(-0.0)))); + ASSERT_TRUE(JSTaggedValue::Equal(thread, JSHandle(thread, JSTaggedValue(0)), + JSHandle(thread, JSTaggedValue(-0)))); + ASSERT_TRUE(JSTaggedValue::Equal(thread, JSHandle(thread, JSTaggedValue(1)), + JSHandle(thread, JSTaggedValue::True()))); + ASSERT_TRUE(JSTaggedValue::Equal(thread, JSHandle(thread, JSTaggedValue(0)), + JSHandle(thread, JSTaggedValue::False()))); + ASSERT_FALSE(JSTaggedValue::Equal(thread, JSHandle(thread, JSTaggedValue(0)), + JSHandle(thread, JSTaggedValue::Undefined()))); + ASSERT_FALSE(JSTaggedValue::Equal(thread, JSHandle(thread, JSTaggedValue(0)), + JSHandle(thread, JSTaggedValue::Null()))); + + JSHandle test(thread->GetEcmaVM()->GetFactory()->NewFromString("test")); + JSHandle test1(thread->GetEcmaVM()->GetFactory()->NewFromString("test1")); + JSHandle empty(thread->GetEcmaVM()->GetFactory()->NewFromString("")); + JSHandle char0(thread->GetEcmaVM()->GetFactory()->NewFromString("0")); + JSHandle char0_point_0(thread->GetEcmaVM()->GetFactory()->NewFromString("0.0")); + JSHandle char1(thread->GetEcmaVM()->GetFactory()->NewFromString("1")); + JSHandle char1_point_0(thread->GetEcmaVM()->GetFactory()->NewFromString("1.0")); + JSHandle char_1(thread->GetEcmaVM()->GetFactory()->NewFromString("-1")); + JSHandle char_0_point_0(thread->GetEcmaVM()->GetFactory()->NewFromString("-0.0")); + JSHandle char_0_point_1(thread->GetEcmaVM()->GetFactory()->NewFromString("-0.1")); + + ASSERT_TRUE(JSTaggedValue::Equal(thread, JSHandle(thread, JSTaggedValue(0)), char0)); + ASSERT_TRUE(JSTaggedValue::Equal(thread, JSHandle(thread, JSTaggedValue(0.0)), char0)); + ASSERT_TRUE(JSTaggedValue::Equal(thread, JSHandle(thread, JSTaggedValue(1.0)), char1)); + ASSERT_TRUE(JSTaggedValue::Equal(thread, JSHandle(thread, JSTaggedValue(-1.0)), char_1)); + ASSERT_TRUE(JSTaggedValue::Equal(thread, JSHandle(thread, JSTaggedValue(0.0)), char_0_point_0)); + ASSERT_FALSE(JSTaggedValue::Equal(thread, JSHandle(thread, JSTaggedValue(0.0)), char_0_point_1)); + + // string compare + ASSERT_TRUE(JSTaggedValue::Equal(thread, test, test)); + ASSERT_FALSE(JSTaggedValue::Equal(thread, test, test1)); + ASSERT_FALSE(JSTaggedValue::Equal(thread, test, empty)); + ASSERT_TRUE(JSTaggedValue::Equal(thread, empty, empty)); + + // ASSERT_FALSE(JSTaggedValue::Equal(JSTaggedValue(thread->GetEcmaVM()->GetFactory()->NewFromString(""))), + // JSTaggedValue(1))); + ASSERT_TRUE(JSTaggedValue::Equal(thread, char1, JSHandle(thread, JSTaggedValue(1)))); + // ASSERT_FALSE(JSTaggedValue::Equal( + // JSTaggedValue(thread->GetEcmaVM()->GetFactory()->NewFromString("aaa"))), + // JSTaggedValue(0))); + // ASSERT_FALSE(JSTaggedValue::Equal( + // JSTaggedValue(thread->GetEcmaVM()->GetFactory()->NewFromString("true"))), + // JSTaggedValue::True())); + ASSERT_TRUE(JSTaggedValue::Equal(thread, char1, JSHandle(thread, JSTaggedValue::True()))); + ASSERT_TRUE(JSTaggedValue::Equal(thread, char0, JSHandle(thread, JSTaggedValue::False()))); + ASSERT_FALSE(JSTaggedValue::Equal(thread, char0, JSHandle(thread, JSTaggedValue::Undefined()))); + ASSERT_FALSE(JSTaggedValue::Equal(thread, char0, JSHandle(thread, JSTaggedValue::Null()))); + // boolean compare + ASSERT_TRUE(JSTaggedValue::Equal(thread, JSHandle(thread, JSTaggedValue::True()), + JSHandle(thread, JSTaggedValue::True()))); + ASSERT_TRUE(JSTaggedValue::Equal(thread, JSHandle(thread, JSTaggedValue::False()), + JSHandle(thread, JSTaggedValue::False()))); + ASSERT_FALSE(JSTaggedValue::Equal(thread, JSHandle(thread, JSTaggedValue::True()), + JSHandle(thread, JSTaggedValue::False()))); + ASSERT_FALSE(JSTaggedValue::Equal(thread, JSHandle(thread, JSTaggedValue::False()), + JSHandle(thread, JSTaggedValue::True()))); + ASSERT_TRUE(JSTaggedValue::Equal(thread, JSHandle(thread, JSTaggedValue::True()), + JSHandle(thread, JSTaggedValue(1)))); + ASSERT_TRUE(JSTaggedValue::Equal(thread, JSHandle(thread, JSTaggedValue::True()), + JSHandle(thread, JSTaggedValue(1.0)))); + ASSERT_FALSE(JSTaggedValue::Equal(thread, JSHandle(thread, JSTaggedValue::True()), + JSHandle(thread, JSTaggedValue(0)))); + ASSERT_FALSE(JSTaggedValue::Equal(thread, JSHandle(thread, JSTaggedValue::True()), + JSHandle(thread, JSTaggedValue(0.0)))); + ASSERT_TRUE(JSTaggedValue::Equal(thread, JSHandle(thread, JSTaggedValue::False()), + JSHandle(thread, JSTaggedValue(0)))); + ASSERT_TRUE(JSTaggedValue::Equal(thread, JSHandle(thread, JSTaggedValue::False()), + JSHandle(thread, JSTaggedValue(0.0)))); + ASSERT_FALSE(JSTaggedValue::Equal(thread, JSHandle(thread, JSTaggedValue::False()), + JSHandle(thread, JSTaggedValue(1)))); + ASSERT_FALSE(JSTaggedValue::Equal(thread, JSHandle(thread, JSTaggedValue::False()), + JSHandle(thread, JSTaggedValue(1.0)))); + ASSERT_TRUE(JSTaggedValue::Equal(thread, JSHandle(thread, JSTaggedValue::False()), char0)); + ASSERT_TRUE(JSTaggedValue::Equal(thread, JSHandle(thread, JSTaggedValue::False()), char0_point_0)); + ASSERT_TRUE(JSTaggedValue::Equal(thread, JSHandle(thread, JSTaggedValue::True()), char1)); + ASSERT_TRUE(JSTaggedValue::Equal(thread, JSHandle(thread, JSTaggedValue::True()), char1_point_0)); + ASSERT_FALSE(JSTaggedValue::Equal(thread, JSHandle(thread, JSTaggedValue::True()), char0)); + ASSERT_FALSE(JSTaggedValue::Equal(thread, JSHandle(thread, JSTaggedValue::False()), char1)); +} + +TEST_F(JSTaggedValueTest, StrictEqual) +{ + // This algorithm differs from the SameValue Algorithm in its treatment of signed zeroes and NaNs. + ASSERT_TRUE(JSTaggedValue::StrictEqual(thread, JSHandle(thread, JSTaggedValue(0.0)), + JSHandle(thread, JSTaggedValue(-0.0)))); + ASSERT_FALSE(JSTaggedValue::StrictEqual( + thread, JSHandle(thread, JSTaggedValue(std::numeric_limits::quiet_NaN())), + JSHandle(thread, JSTaggedValue(std::numeric_limits::quiet_NaN())))); +} +} // namespace panda::test diff --git a/tests/runtime/common/test_helper.cpp b/tests/runtime/common/test_helper.cpp new file mode 100644 index 000000000..9aa89f29b --- /dev/null +++ b/tests/runtime/common/test_helper.cpp @@ -0,0 +1,72 @@ +/* + * Copyright (c) 2021-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 "test_helper.h" +#include "plugins/ecmascript/runtime/interpreter/interpreter-inl.h" + +namespace panda::test { +using panda::ecmascript::EcmaRuntimeCallInfo; +using panda::ecmascript::JSTaggedValue; +using panda::ecmascript::JSThread; + +thread_local bool TestHelper::isLeaf = true; +ecmascript::JSHandle TestHelper::methodFunction_ {}; + +std::unique_ptr TestHelper::CreateEcmaRuntimeCallInfo(JSThread *thread, JSTaggedValue newTgt, + array_size_t argvLength) +{ + auto method = thread->GetEcmaVM()->GetMethodForNativeFunction(nullptr); + if (isLeaf && thread->GetCurrentFrame() != nullptr) { + method->ExitNativeMethodFrame(thread); + } + isLeaf = true; + + const uint8_t testDecodedSize = 2; + // argvLength includes number of int64_t to store value and tag of function, 'this' and call args + // It doesn't include new.target argument + uint32_t numActualArgs = argvLength / testDecodedSize + 1; + + std::vector args; + for (size_t i = 0; i < numActualArgs; ++i) { + args.emplace_back(JSTaggedValue::VALUE_UNDEFINED); + } + + Frame *frame = + method->EnterNativeMethodFrame(thread, 0, numActualArgs, args.data()); + + auto callInfo = std::make_unique(thread, numActualArgs, &frame->GetVReg(0)); + + callInfo->SetFunction(methodFunction_.GetTaggedValue()); + callInfo->SetNewTarget(newTgt); + return callInfo; +} + +Frame *TestHelper::SetupFrame(JSThread *thread, [[maybe_unused]] EcmaRuntimeCallInfo *info) +{ + isLeaf = false; + ASSERT((void *)info->GetArgAddress(0) == &thread->GetCurrentFrame()->GetVReg(0)); + return thread->GetCurrentFrame()->GetPrevFrame(); +} + +void TestHelper::TearDownFrame([[maybe_unused]] JSThread *thread, [[maybe_unused]] Frame *prev) +{ + isLeaf = true; + auto frame = thread->GetCurrentFrame(); + while (frame != nullptr && frame->GetPrevFrame() != prev) { + Method::ExitNativeMethodFrame(thread); + frame = thread->GetCurrentFrame(); + } +} +} // namespace panda::test diff --git a/tests/runtime/common/test_helper.h b/tests/runtime/common/test_helper.h new file mode 100644 index 000000000..294c0f66e --- /dev/null +++ b/tests/runtime/common/test_helper.h @@ -0,0 +1,90 @@ +/* + * Copyright (c) 2021-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 PLUGINS_ECMASCRIPT_TESTS_RUNTIME_COMMON_TEST_HELPER_H +#define PLUGINS_ECMASCRIPT_TESTS_RUNTIME_COMMON_TEST_HELPER_H + +#include "gtest/gtest.h" + +#include "runtime/include/method-inl.h" +#include "runtime/include/thread_scopes.h" +#include "plugins/ecmascript/runtime/interpreter/ecma-interpreter.h" +#include "plugins/ecmascript/runtime/interpreter/interpreter.h" +#include "plugins/ecmascript/runtime/ecma_language_context.h" +#include "plugins/ecmascript/runtime/ecma_runtime_call_info.h" +#include "plugins/ecmascript/runtime/ecma_vm.h" +#include "plugins/ecmascript/runtime/object_factory.h" + +namespace panda::test { +using panda::ecmascript::EcmaHandleScope; +using panda::ecmascript::EcmaRuntimeCallInfo; +using panda::ecmascript::EcmaVM; +using panda::ecmascript::JSTaggedValue; +using panda::ecmascript::JSThread; + +class TestHelper { +public: + static std::unique_ptr CreateEcmaRuntimeCallInfo(JSThread *thread, JSTaggedValue newTgt, + array_size_t argvLength); + static Frame *SetupFrame(JSThread *thread, [[maybe_unused]] EcmaRuntimeCallInfo *info); + static void TearDownFrame([[maybe_unused]] JSThread *thread, [[maybe_unused]] Frame *prev); + + static inline void CreateEcmaVMWithScope(PandaVM *&instance, JSThread *&thread, EcmaHandleScope *&scope, + bool enter_managed_code = true) + { + RuntimeOptions options; + options.SetShouldLoadBootPandaFiles(false); + options.SetShouldInitializeIntrinsics(false); + options.SetLoadRuntimes({"ecmascript"}); + options.SetRunGcInPlace(true); + static EcmaLanguageContext lcEcma; + [[maybe_unused]] bool success = Runtime::Create(options, {&lcEcma}); + ASSERT_TRUE(success) << "Cannot create Runtime"; + instance = Runtime::GetCurrent()->GetPandaVM(); + ASSERT_TRUE(instance != nullptr) << "Cannot create EcmaVM"; + thread = EcmaVM::Cast(instance)->GetAssociatedJSThread(); + scope = new EcmaHandleScope(thread); + EcmaVM *ecmaVm = thread->GetEcmaVM(); + auto globalEnv = ecmaVm->GetGlobalEnv(); + { + ScopedManagedCodeThread s(thread); + methodFunction_ = ecmaVm->GetFactory()->NewJSFunction(globalEnv); + } + if (enter_managed_code) { + thread->ManagedCodeBegin(); + } + } + + static inline void DestroyEcmaVMWithScope(PandaVM *instance, EcmaHandleScope *scope, + bool exit_managed_code = true) + { + auto thread = EcmaVM::Cast(instance)->GetAssociatedJSThread(); + if (exit_managed_code) { + thread->ManagedCodeEnd(); + } + delete scope; + EcmaVM::Cast(instance)->GetFactory()->SetTriggerGc(false); + thread->ClearException(); + [[maybe_unused]] bool success = Runtime::Destroy(); + ASSERT_TRUE(success) << "Cannot destroy Runtime"; + } + +private: + static thread_local bool isLeaf; + static ecmascript::JSHandle methodFunction_; +}; +} // namespace panda::test + +#endif // PLUGINS_ECMASCRIPT_TESTS_RUNTIME_COMMON_TEST_HELPER_H diff --git a/tests/runtime/common/throwdyn/CMakeLists.txt b/tests/runtime/common/throwdyn/CMakeLists.txt new file mode 100644 index 000000000..44f3cea8b --- /dev/null +++ b/tests/runtime/common/throwdyn/CMakeLists.txt @@ -0,0 +1,22 @@ +# Huawei Technologies Co.,Ltd. + +set(THROWDYN_OUTPUT ${CMAKE_CURRENT_BINARY_DIR}/throwdyn.txt) +set(THROWDYN_BIN ${CMAKE_CURRENT_BINARY_DIR}/throwdyn.abc) +set(THROWDYN_JS ${CMAKE_CURRENT_SOURCE_DIR}/throwdyn.js) +set(THROWDYN_VERIFY ${CMAKE_CURRENT_SOURCE_DIR}/verify.sh) + +set(RUNTIME_ARGUMENTS --boot-panda-files=${PANDA_BINARY_ROOT}/pandastdlib/arkstdlib.abc --load-runtimes=\"ecmascript\" --compiler-enable-jit=false --gc-type=stw --run-gc-in-place ${THROWDYN_BIN} _GLOBAL::func_main_0) + +add_custom_command( + OUTPUT ${THROWDYN_OUTPUT} + COMMENT "running javascript throwdyn testcase" + COMMAND ${PANDA_RUN_PREFIX} $ ${THROWDYN_JS} --output ${THROWDYN_BIN} + COMMAND rm -f ${THROWDYN_OUTPUT} + COMMAND ${PANDA_RUN_PREFIX} $ ${RUNTIME_ARGUMENTS} 2>&1 > ${THROWDYN_OUTPUT} + COMMAND bash ${THROWDYN_VERIFY} ${THROWDYN_OUTPUT} +) +add_custom_target(throwdyn + DEPENDS ${THROWDYN_OUTPUT} ${THROWDYN_VERIFY} +) +add_dependencies(throwdyn es2panda ark) +add_dependencies(ecmascript_common_tests throwdyn) diff --git a/tests/runtime/common/throwdyn/throwdyn.js b/tests/runtime/common/throwdyn/throwdyn.js new file mode 100644 index 000000000..aa41fd1de --- /dev/null +++ b/tests/runtime/common/throwdyn/throwdyn.js @@ -0,0 +1,13 @@ +function throwError() { + throw "sub_error" +} + +function catchSubError(){ + try { + throwError() + }catch (e) { + print(e) + } +} + +catchSubError() \ No newline at end of file diff --git a/tests/runtime/common/throwdyn/verify.sh b/tests/runtime/common/throwdyn/verify.sh new file mode 100755 index 000000000..ae22227fe --- /dev/null +++ b/tests/runtime/common/throwdyn/verify.sh @@ -0,0 +1,15 @@ +#!/bin/bash +# Copyright (c) Huawei Technologies Co., Ltd. 2020-2021. All rights reserved. + +set -eo pipefail + +expected="sub_error" +actual=$(cat "$1") +if [[ "$actual" == "$expected" ]];then + exit 0; +else + echo -e "expected:"$expected + echo -e "actual:"$actual + echo -e "\033[31mthrowdyntest failed\033[0m" + exit 1; +fi diff --git a/tests/runtime/common/visitor_compatibility_tests.cpp b/tests/runtime/common/visitor_compatibility_tests.cpp new file mode 100644 index 000000000..956a0e226 --- /dev/null +++ b/tests/runtime/common/visitor_compatibility_tests.cpp @@ -0,0 +1,291 @@ +/* + * Copyright (c) 2021-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 +#include +#include "gtest/gtest.h" + +#include "class_linker/program_object.h" +#include "ecma_module.h" +#include "jobs/micro_job_queue.h" +#include "js_array.h" +#include "js_array_iterator.h" +#include "js_arraybuffer.h" +#include "js_async_function.h" +#include "js_date.h" +#include "js_for_in_iterator.h" +#include "js_hclass.h" +#include "js_locale.h" +#include "js_map.h" +#include "js_map_iterator.h" +#include "js_promise.h" +#include "js_regexp.h" +#include "js_set.h" +#include "js_set_iterator.h" +#include "js_string_iterator.h" +#include "js_tagged_value.h" +#include "mem/heap_roots.h" +#include "object_factory.h" +#include "test_helper.h" +#include "js_int8_array.h" +#include "js_uint8_array.h" +#include "js_uint8_clamped_array.h" +#include "js_int16_array.h" +#include "js_uint16_array.h" +#include "js_int32_array.h" +#include "js_uint32_array.h" +#include "js_float32_array.h" +#include "js_float64_array.h" +#include "js_primitive_ref.h" +#include "js_weak_container.h" +#include "js_dataview.h" +#include "js_typed_array.h" +#include "ic/ic_handler.h" +#include "ic/proto_change_details.h" +#include "js_relative_time_format.h" +#include "js_number_format.h" +#include "js_date_time_format.h" +#include "js_handle.h" +#include "js_object.h" +#include "base/builtins_base.h" +#include "js_arguments.h" +#include "js_generator_object.h" +#include "mem/heap_roots-inl.h" +#include "builtins.h" +#include "ecma_vm.h" +#include "global_env.h" +#include "js_thread.h" + +#include "runtime/include/class.h" +#include "runtime/include/coretypes/array.h" +#include "runtime/include/coretypes/dyn_objects.h" +#include "runtime/include/hclass.h" +#include "runtime/include/object_header.h" +#include "runtime/include/runtime.h" +#include "runtime/include/thread.h" + +using namespace panda::coretypes; +using namespace panda::ecmascript; +using namespace panda::ecmascript::base; + +namespace panda::test { + +template +void VisitAllClassTaggedValues(coretypes::DynClass *dyn_class, const Visitor &v) +{ + HClass *klass = dyn_class->GetHClass(); + + size_t klass_size = dyn_class->ClassAddr()->GetObjectSize(); + + uintptr_t start_addr = reinterpret_cast(klass) + sizeof(HClass); + size_t body_size = klass_size - sizeof(DynClass) - sizeof(HClass); + size_t num_of_fields = body_size / TaggedValue::TaggedTypeSize(); + for (size_t i = 0; i < num_of_fields; i++) { + auto field_addr = start_addr + i * TaggedValue::TaggedTypeSize(); + v(field_addr); + } +} + +template +void VisitAllObjectTaggedValues(ObjectHeader *object, BaseClass *cls, const Visitor &v) +{ + auto obj_body_size = cls->GetObjectSize() - ObjectHeader::ObjectHeaderSize(); + ASSERT(obj_body_size % TaggedValue::TaggedTypeSize() == 0); + int num_of_fields = static_cast(obj_body_size / TaggedValue::TaggedTypeSize()); + size_t start_addr = reinterpret_cast(object) + ObjectHeader::ObjectHeaderSize(); + uintptr_t method_addr = + static_cast(cls)->HasMethod() ? reinterpret_cast(object) + HClass::METHOD_OFFSET : 0; + for (int i = 0; i < num_of_fields; i++) { + auto addr = start_addr + i * TaggedValue::TaggedTypeSize(); + if (addr != method_addr) { + v(addr); + } + } +} + +template +void VisitAllArrayTaggedValues(coretypes::Array *array_object, const Visitor &v) +{ + auto array_length = array_object->GetLength(); + uintptr_t array_start_addr = reinterpret_cast(array_object) + coretypes::Array::GetDataOffset(); + for (coretypes::array_size_t i = 0; i < array_length; i++) { + auto element_addr = array_start_addr + i * TaggedValue::TaggedTypeSize(); + v(element_addr); + } +} + +template +void AllTaggedValueVisitor(ObjectHeader *object, BaseClass *cls, const Visitor &v) +{ + auto hclass = static_cast(cls); + if (hclass->IsHClass()) { + VisitAllClassTaggedValues(static_cast(object), v); + } else if (hclass->IsArray()) { + VisitAllArrayTaggedValues(static_cast(object), v); + } else { + VisitAllObjectTaggedValues(object, cls, v); + } +} + +std::vector SwitchCaseVisitor(ObjectHeader *object, JSHClass *klass) +{ + std::vector res; + EcmaObjectRangeVisitor visitor = [&res](ObjectHeader *, ObjectSlot start, ObjectSlot end) { + for (ObjectSlot slot = start; slot < end; slot++) { + res.push_back(slot.SlotAddress()); + } + }; + + HeapRootManager hrm(static_cast(ManagedThread::GetCurrent()->GetVM())); + hrm.MarkObjectBody(object, klass, visitor); + return res; +} + +std::vector UnifiedVisitor(ObjectHeader *object, JSHClass *klass) +{ + std::vector res; + AllTaggedValueVisitor(object, klass, [&res](uintptr_t val) { res.push_back(val); }); + return res; +} + +class VisitorCompatibilityTest : public testing::Test { +public: + void SetUp() override + { + TestHelper::CreateEcmaVMWithScope(instance, thread, scope); + if (thread == nullptr) { + std::cerr << "Error in initialization" << std::endl; + std::abort(); + } + ecma_vm = thread->GetEcmaVM(); + factory = ecma_vm->GetFactory(); + } + + void TearDown() override + { + TestHelper::DestroyEcmaVMWithScope(instance, scope); + } + + bool Cmp(std::vector &&vec1, std::vector &&vec2) + { + if (vec1.size() != vec2.size()) { + return false; + } + while (!vec1.empty()) { + auto it = std::find(vec2.rbegin(), vec2.rend(), vec1.back()); + if (it == vec2.rend()) { + return false; + } + vec2.erase(std::next(it).base()); + vec1.pop_back(); + } + return true; + } + + bool WrappedCmp(ObjectHeader *object, JSHClass *klass) + { + return Cmp(SwitchCaseVisitor(object, klass), UnifiedVisitor(object, klass)); + } + + PandaVM *instance{nullptr}; + EcmaVM *ecma_vm{nullptr}; + EcmaHandleScope *scope{nullptr}; + ObjectFactory *factory{nullptr}; + JSThread *thread; +}; + +TEST_F(VisitorCompatibilityTest, JSObject) +{ + JSHandle env = ecma_vm->GetGlobalEnv(); + + JSHandle object_function(env->GetObjectFunction()); + JSHandle obj = + factory->NewJSObjectByConstructor(object_function, JSHandle(object_function)); + auto klass = obj->GetJSHClass(); + ASSERT_EQ(klass->GetObjectType(), JSType::JS_OBJECT); + ASSERT_TRUE(WrappedCmp(*obj, klass)); +} + +TEST_F(VisitorCompatibilityTest, GlobalEnv) +{ + JSHandle env = ecma_vm->GetGlobalEnv(); + auto klass = JSHClass::Cast(env->ClassAddr()); + ASSERT_EQ(klass->GetObjectType(), JSType::GLOBAL_ENV); + ASSERT_TRUE(WrappedCmp(*env, klass)); +} + +TEST_F(VisitorCompatibilityTest, JSGlobalObject) +{ + JSHandle env = ecma_vm->GetGlobalEnv(); + + auto global_object = env->GetJSGlobalObject(); + auto klass = JSObject::Cast(global_object.GetTaggedValue())->GetJSHClass(); + ASSERT_EQ(klass->GetObjectType(), JSType::JS_GLOBAL_OBJECT); + ASSERT_TRUE(WrappedCmp(JSObject::Cast(global_object.GetTaggedValue()), + klass)); +} + +TEST_F(VisitorCompatibilityTest, JSTaggedArray) +{ + auto array = factory->NewTaggedArray(4); + auto klass = array->ClassAddr(); + ASSERT_EQ(klass->GetObjectType(), JSType::TAGGED_ARRAY); + ASSERT_TRUE(WrappedCmp(*array, klass)); +} + +TEST_F(VisitorCompatibilityTest, HClass) +{ + JSHandle env = ecma_vm->GetGlobalEnv(); + JSHClass *kls = env->ClassAddr(); + auto klass_klass = kls->GetManagedObject()->ClassAddr(); + + ASSERT_EQ(klass_klass->GetObjectType(), JSType::HCLASS); + ASSERT_TRUE(WrappedCmp(kls->GetManagedObject(), klass_klass)); +} + +TEST_F(VisitorCompatibilityTest, Program) +{ + auto hclass = DynClass::Cast(thread->GlobalConstants()->GetProgramClass().GetHeapObject())->GetHClass(); + auto program = factory->NewProgram(); + auto klass = JSHClass::Cast(hclass); + ASSERT_TRUE(WrappedCmp(*program, klass)); +} + +TEST_F(VisitorCompatibilityTest, JSProxy) +{ + auto hclass = DynClass::Cast(thread->GlobalConstants()->GetJSProxyOrdinaryClass().GetHeapObject())->GetHClass(); + JSTaggedValue object(hclass->GetManagedObject()); + JSHandle target(thread, object); + JSHandle handler(thread, object); + JSHandle proxy = factory->NewJSProxy(target, handler); + auto klass = JSHClass::Cast(hclass); + ASSERT_EQ(klass->GetObjectType(), JSType::JS_PROXY); + ASSERT_TRUE(WrappedCmp(*proxy, klass)); +} + +TEST_F(VisitorCompatibilityTest, JSFunction) +{ + JSHandle env = ecma_vm->GetGlobalEnv(); + JSHandle functionClass = JSHandle::Cast(env->GetFunctionClassWithoutName()); + JSHandle jsFunc = + factory->NewJSFunctionByDynClass(env, nullptr, functionClass, FunctionKind::CLASS_CONSTRUCTOR); + auto klass = JSHClass::Cast(functionClass->GetHClass()); + ASSERT_EQ(klass->GetObjectType(), JSType::JS_FUNCTION); + ASSERT_TRUE(WrappedCmp(*jsFunc, klass)); +} + +// TODO(maksenov): add tests for all remaining object types + +} // namespace panda::test diff --git a/tests/runtime/common/weak_ref_gen_gc_test.cpp b/tests/runtime/common/weak_ref_gen_gc_test.cpp new file mode 100644 index 000000000..f53afe044 --- /dev/null +++ b/tests/runtime/common/weak_ref_gen_gc_test.cpp @@ -0,0 +1,163 @@ +/* + * Copyright (c) 2021-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 "gtest/gtest.h" +#include "test_helper.h" + +#include "include/runtime.h" +#include "plugins/ecmascript/runtime/ecma_vm.h" +#include "plugins/ecmascript/runtime/global_env.h" +#include "plugins/ecmascript/runtime/js_handle.h" +#include "plugins/ecmascript/runtime/tagged_array-inl.h" +#include "plugins/ecmascript/runtime/object_factory.h" + +using namespace panda::ecmascript; +using namespace panda::coretypes; + +namespace panda::test { +class WeakRefGenGCTest : public testing::Test { +public: + void SetUp() override + { + TestHelper::CreateEcmaVMWithScope(instance, thread, scope, "stw"); // issue #5368 + } + + void TearDown() override + { + TestHelper::DestroyEcmaVMWithScope(instance, scope); + } + + PandaVM *instance {nullptr}; + EcmaHandleScope *scope {nullptr}; + JSThread *thread; +}; + +#if !defined(NDEBUG) +static JSObject *JSObjectTestCreate(JSThread *thread) +{ + [[maybe_unused]] ecmascript::EcmaHandleScope scope(thread); + EcmaVM *ecma_vm = thread->GetEcmaVM(); + JSHandle global_env = ecma_vm->GetGlobalEnv(); + JSFunction *js_func = global_env->GetObjectFunction().GetObject(); + JSHandle js_func1(thread, js_func); + JSHandle new_obj = + ecma_vm->GetFactory()->NewJSObjectByConstructor(JSHandle(js_func1), js_func1); + return *new_obj; +} +#endif + +#if !defined(NDEBUG) +static TaggedArray *ArrayTestCreate(JSThread *thread) +{ + [[maybe_unused]] ecmascript::EcmaHandleScope scope(thread); + JSHandle array = thread->GetEcmaVM()->GetFactory()->NewTaggedArray(2); + return *array; +} +#endif + +TEST_F(WeakRefGenGCTest, DISABLED_ArrayNonMovable) // issue #5368 +{ +#if !defined(NDEBUG) + auto vm = thread->GetEcmaVM(); + auto array = vm->GetFactory()->NewTaggedArray(2, JSTaggedValue::Undefined(), true); + JSHandle new_obj1(thread, JSObjectTestCreate(thread)); + array->Set(thread, 0, new_obj1.GetTaggedValue()); + + JSObject *new_obj2 = JSObjectTestCreate(thread); + JSTaggedValue value(new_obj2); + value.CreateWeakRef(); + array->Set(thread, 1, value); + EXPECT_EQ(new_obj1.GetTaggedValue(), array->Get(0)); + EXPECT_EQ(value, array->Get(1)); + vm->GetGC()->WaitForGC(GCTask(GCTaskCause::EXPLICIT_CAUSE)); + EXPECT_EQ(new_obj1.GetTaggedValue(), array->Get(0)); + EXPECT_EQ(JSTaggedValue::Undefined(), array->Get(1)); +#endif +} + +TEST_F(WeakRefGenGCTest, DISABLED_ArrayUndefined) +{ +#if !defined(NDEBUG) + EcmaVM *ecma_vm = thread->GetEcmaVM(); + JSHandle array = ecma_vm->GetFactory()->NewTaggedArray(2); + EXPECT_TRUE(*array != nullptr); + JSHandle new_obj1(thread, JSObjectTestCreate(thread)); + array->Set(thread, 0, new_obj1.GetTaggedValue()); + + JSObject *new_obj2 = JSObjectTestCreate(thread); + JSTaggedValue value(new_obj2); + value.CreateWeakRef(); + array->Set(thread, 1, value); + EXPECT_EQ(new_obj1.GetTaggedValue(), array->Get(0)); + EXPECT_EQ(value, array->Get(1)); + ecma_vm->GetGC()->WaitForGC(GCTask(GCTaskCause::EXPLICIT_CAUSE)); + EXPECT_EQ(new_obj1.GetTaggedValue(), array->Get(0)); + EXPECT_EQ(JSTaggedValue::Undefined(), array->Get(1)); +#endif +} + +TEST_F(WeakRefGenGCTest, DISABLED_ArrayKeep) +{ +#if !defined(NDEBUG) + EcmaVM *ecma_vm = thread->GetEcmaVM(); + JSHandle array = ecma_vm->GetFactory()->NewTaggedArray(2); + EXPECT_TRUE(*array != nullptr); + JSHandle new_obj1(thread, JSObjectTestCreate(thread)); + array->Set(thread, 0, new_obj1.GetTaggedValue()); + + JSHandle new_obj2(thread, JSObjectTestCreate(thread)); + JSTaggedValue value(new_obj2.GetTaggedValue()); + value.CreateWeakRef(); + array->Set(thread, 1, value); + EXPECT_EQ(new_obj1.GetTaggedValue(), array->Get(0)); + EXPECT_EQ(value, array->Get(1)); + ecma_vm->GetGC()->WaitForGC(GCTask(GCTaskCause::EXPLICIT_CAUSE)); + EXPECT_EQ(new_obj1.GetTaggedValue(), array->Get(0)); + EXPECT_EQ(true, array->Get(1).IsWeak()); + value = new_obj2.GetTaggedValue(); + value.CreateWeakRef(); + EXPECT_EQ(value, array->Get(1)); +#endif +} + +TEST_F(WeakRefGenGCTest, DISABLED_DynObjectUndefined) +{ +#if !defined(NDEBUG) + JSHandle new_obj1(thread, JSObjectTestCreate(thread)); + JSTaggedValue array(ArrayTestCreate(thread)); + array.CreateWeakRef(); + new_obj1->SetElements(thread, array); + EXPECT_EQ(new_obj1->GetElements(), array); + thread->GetEcmaVM()->GetGC()->WaitForGC(GCTask(GCTaskCause::EXPLICIT_CAUSE)); + EXPECT_EQ(new_obj1->GetElements(), JSTaggedValue::Undefined()); +#endif +} + +TEST_F(WeakRefGenGCTest, DISABLED_DynObjectKeep) // issue #5368 +{ +#if !defined(NDEBUG) + JSHandle new_obj1(thread, JSObjectTestCreate(thread)); + JSHandle array(thread, ArrayTestCreate(thread)); + JSTaggedValue value = array.GetTaggedValue(); + value.CreateWeakRef(); + new_obj1->SetElements(thread, value); + EXPECT_EQ(new_obj1->GetElements(), value); + thread->GetEcmaVM()->GetGC()->WaitForGC(GCTask(GCTaskCause::EXPLICIT_CAUSE)); + value = array.GetTaggedValue(); + value.CreateWeakRef(); + EXPECT_EQ(new_obj1->GetElements(), value); +#endif +} +} // namespace panda::test diff --git a/tests/runtime/common/weak_ref_stw_gc_test.cpp b/tests/runtime/common/weak_ref_stw_gc_test.cpp new file mode 100644 index 000000000..07ee3757e --- /dev/null +++ b/tests/runtime/common/weak_ref_stw_gc_test.cpp @@ -0,0 +1,144 @@ +/* + * Copyright (c) 2021-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 "gtest/gtest.h" +#include "test_helper.h" + +#include "handle_base.h" +#include "include/runtime.h" +#include "plugins/ecmascript/runtime/ecma_vm.h" +#include "plugins/ecmascript/runtime/global_env.h" +#include "plugins/ecmascript/runtime/js_handle.h" +#include "plugins/ecmascript/runtime/tagged_array-inl.h" +#include "plugins/ecmascript/runtime/object_factory.h" + +using namespace panda::ecmascript; +using namespace panda::coretypes; + +namespace panda::test { +class WeakRefStwGCTest : public testing::Test { +public: + void SetUp() override + { + TestHelper::CreateEcmaVMWithScope(instance, thread, scope, "stw"); + } + + void TearDown() override + { + TestHelper::DestroyEcmaVMWithScope(instance, scope); + } + + PandaVM *instance {nullptr}; + EcmaHandleScope *scope {nullptr}; + JSThread *thread; +}; + +#if !defined(NDEBUG) +static JSObject *JSObjectTestCreate(JSThread *thread) +{ + [[maybe_unused]] ecmascript::EcmaHandleScope scope(thread); + EcmaVM *ecma_vm = thread->GetEcmaVM(); + JSHandle global_env = ecma_vm->GetGlobalEnv(); + JSHandle js_func = global_env->GetObjectFunction(); + JSHandle new_obj = + ecma_vm->GetFactory()->NewJSObjectByConstructor(JSHandle(js_func), js_func); + return *new_obj; +} +#endif + +#if !defined(NDEBUG) +static TaggedArray *ArrayTestCreate(JSThread *thread) +{ + [[maybe_unused]] ecmascript::EcmaHandleScope scope(thread); + JSHandle array = thread->GetEcmaVM()->GetFactory()->NewTaggedArray(2); + return *array; +} +#endif + +TEST_F(WeakRefStwGCTest, ArrayUndefined) +{ +#if !defined(NDEBUG) + EcmaVM *ecma_vm = thread->GetEcmaVM(); + JSHandle array = ecma_vm->GetFactory()->NewTaggedArray(2); + EXPECT_TRUE(*array != nullptr); + JSHandle new_obj1(thread, JSObjectTestCreate(thread)); + array->Set(thread, 0, new_obj1.GetTaggedValue()); + + JSObject *new_obj2 = JSObjectTestCreate(thread); + JSTaggedValue value(new_obj2); + value.CreateWeakRef(); + array->Set(thread, 1, value); + EXPECT_EQ(new_obj1.GetTaggedValue(), array->Get(0)); + EXPECT_EQ(value, array->Get(1)); + ecma_vm->GetGC()->WaitForGC(GCTask(GCTaskCause::EXPLICIT_CAUSE)); + EXPECT_EQ(new_obj1.GetTaggedValue(), array->Get(0)); + EXPECT_EQ(JSTaggedValue::Undefined(), array->Get(1)); +#endif +} + +TEST_F(WeakRefStwGCTest, ArrayKeep) +{ +#if !defined(NDEBUG) + EcmaVM *ecma_vm = thread->GetEcmaVM(); + JSHandle array = ecma_vm->GetFactory()->NewTaggedArray(2); + EXPECT_TRUE(*array != nullptr); + JSHandle new_obj1(thread, JSObjectTestCreate(thread)); + array->Set(thread, 0, new_obj1.GetTaggedValue()); + + JSHandle new_obj2(thread, JSObjectTestCreate(thread)); + JSTaggedValue value(new_obj2.GetTaggedValue()); + value.CreateWeakRef(); + array->Set(thread, 1, value); + EXPECT_EQ(new_obj1.GetTaggedValue(), array->Get(0)); + EXPECT_EQ(value, array->Get(1)); + ecma_vm->GetGC()->WaitForGC(GCTask(GCTaskCause::EXPLICIT_CAUSE)); + EXPECT_EQ(new_obj1.GetTaggedValue(), array->Get(0)); + EXPECT_EQ(true, array->Get(1).IsWeak()); + value = new_obj2.GetTaggedValue(); + value.CreateWeakRef(); + EXPECT_EQ(value, array->Get(1)); +#endif +} + +TEST_F(WeakRefStwGCTest, DynObjectUndefined) +{ +#if !defined(NDEBUG) + JSHandle new_obj1(thread, JSObjectTestCreate(thread)); + JSTaggedValue array(ArrayTestCreate(thread)); + array.CreateWeakRef(); + new_obj1->SetElements(thread, array); + EXPECT_EQ(new_obj1->GetElements(), array); + thread->GetEcmaVM()->GetGC()->WaitForGC(GCTask(GCTaskCause::EXPLICIT_CAUSE)); + EXPECT_EQ(new_obj1->GetElements(), JSTaggedValue::Undefined()); +#endif +} + +TEST_F(WeakRefStwGCTest, DynObjectKeep) +{ +#if !defined(NDEBUG) + JSHandle new_obj1(thread, JSObjectTestCreate(thread)); + JSHandle array(thread, ArrayTestCreate(thread)); + JSTaggedValue value = array.GetTaggedValue(); + value.CreateWeakRef(); + new_obj1->SetElements(thread, value); + EXPECT_EQ(new_obj1->GetElements(), value); + thread->GetEcmaVM()->GetGC()->WaitForGC(GCTask(GCTaskCause::EXPLICIT_CAUSE)); + value = array.GetTaggedValue(); + value.CreateWeakRef(); + EXPECT_EQ(new_obj1->GetElements(), value); +#endif +} + +} // namespace panda::test diff --git a/tests/runtime/common/yieldstar/CMakeLists.txt b/tests/runtime/common/yieldstar/CMakeLists.txt new file mode 100644 index 000000000..74f5fd791 --- /dev/null +++ b/tests/runtime/common/yieldstar/CMakeLists.txt @@ -0,0 +1,22 @@ +# Huawei Technologies Co.,Ltd. + +set(YIELDSTAR_OUTPUT ${CMAKE_CURRENT_BINARY_DIR}/yieldstar.txt) +set(YIELDSTAR_BIN ${CMAKE_CURRENT_BINARY_DIR}/yieldstar.abc) +set(YIELDSTAR_JS ${CMAKE_CURRENT_SOURCE_DIR}/yieldstar.js) +set(YIELDSTAR_VERIFY ${CMAKE_CURRENT_SOURCE_DIR}/verify.sh) + +set(RUNTIME_ARGUMENTS --boot-panda-files=${PANDA_BINARY_ROOT}/pandastdlib/arkstdlib.abc --load-runtimes=\"ecmascript\" --compiler-enable-jit=false --gc-type=stw --run-gc-in-place ${YIELDSTAR_BIN} _GLOBAL::func_main_0) + +add_custom_command( + OUTPUT ${YIELDSTAR_OUTPUT} + COMMENT "running javascript yieldstar testcase" + COMMAND ${PANDA_RUN_PREFIX} $ ${YIELDSTAR_JS} --output ${YIELDSTAR_BIN} + COMMAND rm -f ${YIELDSTAR_OUTPUT} + COMMAND ${PANDA_RUN_PREFIX} $ ${RUNTIME_ARGUMENTS} 2>&1 > ${YIELDSTAR_OUTPUT} + COMMAND bash ${YIELDSTAR_VERIFY} ${YIELDSTAR_OUTPUT} +) +add_custom_target(yieldstar + DEPENDS ${YIELDSTAR_OUTPUT} ${YIELDSTAR_VERIFY} + ) +add_dependencies(yieldstar ark_asm ark) +add_dependencies(ecmascript_common_tests yieldstar) diff --git a/tests/runtime/common/yieldstar/verify.sh b/tests/runtime/common/yieldstar/verify.sh new file mode 100755 index 000000000..82ae38173 --- /dev/null +++ b/tests/runtime/common/yieldstar/verify.sh @@ -0,0 +1,17 @@ +#!/bin/bash +# Copyright (c) Huawei Technologies Co., Ltd. 2020-2021. All rights reserved. + +set -eo pipefail + +expected="1 false +2 false +undefined true" +actual=$(cat "$1") +if [[ "$actual" == "$expected" ]];then + exit 0; +else + echo -e "expected:"$expected + echo -e "actual:"$actual + echo -e "\033[31myieldstar test failed\033[0m" + exit 1; +fi diff --git a/tests/runtime/common/yieldstar/yieldstar.js b/tests/runtime/common/yieldstar/yieldstar.js new file mode 100644 index 000000000..846178c99 --- /dev/null +++ b/tests/runtime/common/yieldstar/yieldstar.js @@ -0,0 +1,16 @@ +function *foo1() { + yield 1 + yield 2 +} + +function *foo2() { + yield *foo1() +} + +var p = foo2() +var a = p.next() +print(a.value, a.done) +var b = p.next() +print(b.value, b.done) +var c = p.next() +print(c.value, c.done) \ No newline at end of file diff --git a/tests/runtime/hprof/heap_tracker_test.cpp b/tests/runtime/hprof/heap_tracker_test.cpp new file mode 100644 index 000000000..46bb7319c --- /dev/null +++ b/tests/runtime/hprof/heap_tracker_test.cpp @@ -0,0 +1,109 @@ +/* + * Copyright (c) 2021-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 +#include + +#include "plugins/ecmascript/runtime/ecma_string.h" +#include "plugins/ecmascript/runtime/global_env.h" +#include "plugins/ecmascript/runtime/hprof/heap_profiler_interface.h" +#include "plugins/ecmascript/runtime/hprof/heap_snapshot_json_serializer.h" + +#include "plugins/ecmascript/runtime/js_tagged_value.h" +#include "plugins/ecmascript/runtime/js_thread.h" +#include "plugins/ecmascript/tests/runtime/common/test_helper.h" + +using namespace panda::ecmascript; + +namespace panda::test { +class HeapTrackerTest : public testing::Test { +public: + static void SetUpTestCase() + { + GTEST_LOG_(INFO) << "SetUpTestCase"; + } + + static void TearDownTestCase() + { + GTEST_LOG_(INFO) << "TearDownCase"; + } + + void SetUp() override + { + TestHelper::CreateEcmaVMWithScope(instance, thread, scope); + EcmaVM::Cast(instance)->SetEnableForceGC(false); + } + + void TearDown() override + { + TestHelper::DestroyEcmaVMWithScope(instance, scope); + } + + PandaVM *instance {nullptr}; + EcmaHandleScope *scope {nullptr}; + JSThread *thread {nullptr}; +}; + +TEST_F(HeapTrackerTest, DISABLED_HeapTracker) // issue #5368 +{ + [[maybe_unused]] EcmaHandleScope handleScope(thread); + HeapProfilerInterface *heapProfile = HeapProfilerInterface::CreateHeapProfiler(thread); + heapProfile->StartHeapTracking(thread, 50); + sleep(1); + int count = 100; + while (count-- > 0) { + thread->GetEcmaVM()->GetFactory()->NewJSAsyncFuncObject(); + } + sleep(1); + count = 100; + while (count-- > 0) { + thread->GetEcmaVM()->GetFactory()->NewJSSymbol(); + } + sleep(1); + count = 100; + while (count-- > 0) { + JSHandle string = thread->GetEcmaVM()->GetFactory()->NewFromCanBeCompressString("Hello World"); + thread->GetEcmaVM()->GetFactory()->NewJSString(JSHandle(string)); + } + + // Create file test.heaptimeline + std::string fileName = "test.heaptimeline"; + fstream outputString(fileName, std::ios::out); + outputString.close(); + outputString.clear(); + + heapProfile->StopHeapTracking(thread, DumpFormat::JSON, fileName.c_str()); + HeapProfilerInterface::Destroy(thread, heapProfile); + + // Check + fstream inputStream(fileName, std::ios::in); + std::string line; + std::string emptySample = "\"samples\":"; + std::string firstSample = "\"samples\":[0, "; + int emptySize = emptySample.size(); + bool isFind = false; + while (getline(inputStream, line)) { + if (line.substr(0, emptySize) == emptySample) { + ASSERT_TRUE(line.substr(0, firstSample.size()) == firstSample); + isFind = true; + } + } + ASSERT_TRUE(isFind); + + inputStream.close(); + inputStream.clear(); + std::remove(fileName.c_str()); +} +} // namespace panda::test diff --git a/tests/runtime/hprof/hprof_test.cpp b/tests/runtime/hprof/hprof_test.cpp new file mode 100644 index 000000000..8c9048bd6 --- /dev/null +++ b/tests/runtime/hprof/hprof_test.cpp @@ -0,0 +1,341 @@ +/** + * Copyright (c) Huawei Technologies Co., Ltd. 2019-2021. All rights reserved. + */ + +#include "gtest/gtest.h" +#include "test_helper.h" + +#include "plugins/ecmascript/runtime/object_factory.h" +#include "plugins/ecmascript/runtime/ecma_vm.h" +#include "plugins/ecmascript/runtime/global_env.h" +#include "include/runtime.h" +#include "plugins/ecmascript/runtime/js_thread.h" +#include "plugins/ecmascript/runtime/hprof/heap_profiler.h" +#include "plugins/ecmascript/runtime/hprof/heap_profiler_interface.h" +#include "plugins/ecmascript/runtime/hprof/heap_snapshot.h" +#include "plugins/ecmascript/runtime/hprof/heap_snapshot_json_serializer.h" +#include "plugins/ecmascript/runtime/hprof/string_hashmap.h" + +#include +#include + +using namespace panda::coretypes; +using namespace panda::ecmascript; + +namespace panda::test { +// All HProf tests was disabled, see issue #5368 +class HProfTest : public testing::Test { +public: + void SetUp() override + { + TestHelper::CreateEcmaVMWithScope(instance, thread, scope); + } + + void TearDown() override + { + TestHelper::DestroyEcmaVMWithScope(instance, scope); + } + +protected: + PandaVM *instance; + JSThread *thread; + EcmaHandleScope *scope {nullptr}; +}; + +class HProfTestHelper { +public: + explicit HProfTestHelper(const CString &aFilePath) : filePath(aFilePath) {} + ~HProfTestHelper() = default; + + bool IsFileExist() + { + bool exist = false; + exist = SafeOpenFile(); + SafeCloseFile(); + return exist; + } + + bool RemoveExistingFile() + { + int code = 0; + if (IsFileExist()) { + code = std::remove(filePath.c_str()); + } + return code == 0 ? true : false; + } + + bool GenerateHeapDump(JSThread *thread) + { + int retry = 2; + RemoveExistingFile(); + HeapProfilerInterface::DumpHeapSnapShot(thread, DumpFormat::JSON, filePath.c_str()); + while (!IsFileExist() && retry > 0) { + retry--; + HeapProfilerInterface::DumpHeapSnapShot(thread, DumpFormat::JSON, filePath.c_str()); + } + return IsFileExist(); + } + + bool ContrastJSONLineHeader(int lineId, CString lineHeader) + { + bool all_same = false; + if (!SafeOpenFile()) { // No JSON File + all_same = false; + SafeCloseFile(); + } else { + CString line; + int i = 1; + while (getline(inputFile, line)) { + if (i == lineId && line.find(lineHeader) != line.npos) { + all_same = true; + break; + } + i++; + } + SafeCloseFile(); + } + + return all_same; + } + + bool ContrastJSONSectionPayload(CString dataLable, int fieldNum) + { + if (!SafeOpenFile()) { // No JSON File + SafeCloseFile(); + return false; + } + CString line; + int i = 1; + while (getline(inputFile, line)) { + if (i > 10 && line.find(dataLable) != line.npos) { // Hit the line + CString::size_type pos = 0; + int loop = 0; + while ((pos = line.find(",", pos)) != line.npos) { + pos++; + loop++; // "," count + } + SafeCloseFile(); + return loop == fieldNum - 1; + break; + } + i++; // Search Next Line + } + SafeCloseFile(); + return false; // Lost the Line + } + + bool ContrastJSONClousure() + { + if (!SafeOpenFile()) { // No JSON File + SafeCloseFile(); + return false; + } + CString line_bk; // The Last Line + CString line; + while (getline(inputFile, line)) { + line_bk = line; + } + SafeCloseFile(); + return line_bk.compare("}") == 0; + } + + int ExtractCountFromMeta(CString typeLable) + { + if (!SafeOpenFile()) { // No JSON File + SafeCloseFile(); + return -1; + } + CString line; + int i = 1; + while (getline(inputFile, line)) { + int length = line.length() - typeLable.length() - 1; + if (line.find(typeLable) != line.npos) { // Get + if (line.find(",") == line.npos) { // "trace_function_count" end without "," + length = line.length() - typeLable.length(); + } + line = line.substr(typeLable.length(), length); + SafeCloseFile(); + return std::stoi(line.c_str()); + } + i++; + } + SafeCloseFile(); + return -1; + } + + int ExtractCountFromPayload(CString dataLabel) + { + if (!SafeOpenFile()) { // No JSON File + SafeCloseFile(); + return -1; + } + CString line; + bool hit = false; + int loop = 0; + while (getline(inputFile, line)) { + if (!hit && line.find(dataLabel) != line.npos) { // Get + loop += 1; // First Line + hit = true; + if (line.find("[]") != line.npos) { // Empty + loop = 0; + SafeCloseFile(); + return loop; + } + continue; + } + if (hit) { + if (line.find("],") != line.npos) { // Reach End + loop += 1; // End Line + SafeCloseFile(); + return loop; + } + loop++; + continue; + } + } + SafeCloseFile(); + + return -1; + } + +private: + bool SafeOpenFile() + { + inputFile.clear(); + inputFile.open(filePath.c_str(), std::ios::in); + int retry = 2; + while (!inputFile.good() && retry > 0) { + inputFile.open(filePath.c_str(), std::ios::in); + retry--; + } + return inputFile.good(); + } + + void SafeCloseFile() + { + inputFile.close(); + inputFile.clear(); // Reset File status + } + CString filePath; + std::fstream inputFile; +}; + +TEST_F(HProfTest, DISABLED_GenerateFileForManualCheck) +{ + HProfTestHelper tester("hprof_json_test.heapsnapshot"); + ASSERT_TRUE(tester.GenerateHeapDump(thread)); +} + +TEST_F(HProfTest, DISABLED_GenerateFile) +{ + HProfTestHelper tester("GenerateFile.heapsnapshot"); + ASSERT_TRUE(tester.GenerateHeapDump(thread)); + ASSERT_TRUE(tester.RemoveExistingFile()); +} + +TEST_F(HProfTest, DISABLED_ParseJSONHeader) +{ + HProfTestHelper tester("ParseJSONHeader.heapsnapshot"); + ASSERT_TRUE(tester.GenerateHeapDump(thread)); + ASSERT_TRUE(tester.ContrastJSONLineHeader(1, "{\"snapshot\":")); + ASSERT_TRUE(tester.ContrastJSONLineHeader(2, "{\"meta\":")); + ASSERT_TRUE(tester.ContrastJSONLineHeader(3, "{\"node_fields\":")); + ASSERT_TRUE(tester.ContrastJSONLineHeader(4, "\"node_types\":")); + ASSERT_TRUE(tester.ContrastJSONLineHeader(5, "\"edge_fields\":")); + ASSERT_TRUE(tester.ContrastJSONLineHeader(6, "\"edge_types\":")); + ASSERT_TRUE(tester.ContrastJSONLineHeader(7, "\"trace_function_info_fields\":")); + ASSERT_TRUE(tester.ContrastJSONLineHeader(8, "\"trace_node_fields\":")); + ASSERT_TRUE(tester.ContrastJSONLineHeader(9, "\"sample_fields\":")); + ASSERT_TRUE(tester.ContrastJSONLineHeader(10, "\"location_fields\":")); + ASSERT_TRUE(tester.RemoveExistingFile()); +} + +TEST_F(HProfTest, DISABLED_ContrastNode) +{ + HProfTestHelper tester("ContrastNode.heapsnapshot"); + ASSERT_TRUE(tester.GenerateHeapDump(thread)); + ASSERT_TRUE(tester.ContrastJSONSectionPayload("\"nodes\":", 7)); + ASSERT_TRUE(tester.RemoveExistingFile()); +} + +TEST_F(HProfTest, DISABLED_ContrastEdge) +{ + HProfTestHelper tester("ContrastEdge.heapsnapshot"); + ASSERT_TRUE(tester.GenerateHeapDump(thread)); + ASSERT_TRUE(tester.ContrastJSONSectionPayload("\"edges\":", 3)); + ASSERT_TRUE(tester.RemoveExistingFile()); +} + +TEST_F(HProfTest, DISABLED_ContrastTraceFunctionInfo) +{ + HProfTestHelper tester("ContrastTraceFunctionInfo.heapsnapshot"); + ASSERT_TRUE(tester.GenerateHeapDump(thread)); + ASSERT_TRUE(tester.ContrastJSONSectionPayload("\"trace_function_infos\":", 2)); // Empty + ASSERT_TRUE(tester.RemoveExistingFile()); +} + +TEST_F(HProfTest, DISABLED_ContrastTraceTree) +{ + HProfTestHelper tester("ContrastTraceTree.heapsnapshot"); + ASSERT_TRUE(tester.GenerateHeapDump(thread)); + ASSERT_TRUE(tester.ContrastJSONSectionPayload("\"trace_tree\":", 2)); // Empty + ASSERT_TRUE(tester.RemoveExistingFile()); +} + +TEST_F(HProfTest, DISABLED_ContrastSamples) +{ + HProfTestHelper tester("ContrastSamples.heapsnapshot"); + ASSERT_TRUE(tester.GenerateHeapDump(thread)); + ASSERT_TRUE(tester.ContrastJSONSectionPayload("\"samples\":", 2)); // Empty + ASSERT_TRUE(tester.RemoveExistingFile()); +} + +TEST_F(HProfTest, DISABLED_ContrastLocations) +{ + HProfTestHelper tester("ContrastLocations.heapsnapshot"); + ASSERT_TRUE(tester.GenerateHeapDump(thread)); + ASSERT_TRUE(tester.ContrastJSONSectionPayload("\"locations\":", 2)); // Empty + ASSERT_TRUE(tester.RemoveExistingFile()); +} + +TEST_F(HProfTest, DISABLED_ContrastString) +{ + HProfTestHelper tester("ContrastString.heapsnapshot"); + ASSERT_TRUE(tester.GenerateHeapDump(thread)); + ASSERT_TRUE(tester.ContrastJSONSectionPayload("\"strings\":[", 1 + 1)); + ASSERT_TRUE(tester.RemoveExistingFile()); +} + +TEST_F(HProfTest, DISABLED_ContrastClosure) +{ + HProfTestHelper tester("ContrastClosure.heapsnapshot"); + ASSERT_TRUE(tester.GenerateHeapDump(thread)); + ASSERT_TRUE(tester.ContrastJSONClousure()); + ASSERT_TRUE(tester.RemoveExistingFile()); +} + +TEST_F(HProfTest, DISABLED_ContrastNodeCount) +{ + HProfTestHelper tester("ContrastNodeCount.heapsnapshot"); + ASSERT_TRUE(tester.GenerateHeapDump(thread)); + ASSERT_TRUE(tester.ExtractCountFromMeta("\"node_count\":") == tester.ExtractCountFromPayload("\"nodes\":[")); + ASSERT_TRUE(tester.RemoveExistingFile()); +} + +TEST_F(HProfTest, DISABLED_ContrastEdgeCount) +{ + HProfTestHelper tester("ContrastEdgeCount.heapsnapshot"); + ASSERT_TRUE(tester.GenerateHeapDump(thread)); + ASSERT_TRUE(tester.ExtractCountFromMeta("\"edge_count\":") == tester.ExtractCountFromPayload("\"edges\":[")); + ASSERT_TRUE(tester.RemoveExistingFile()); +} + +TEST_F(HProfTest, DISABLED_ContrastTraceFunctionInfoCount) +{ + HProfTestHelper tester("ContrastTraceFunctionInfoCount.heapsnapshot"); + ASSERT_TRUE(tester.GenerateHeapDump(thread)); + ASSERT_TRUE(tester.ExtractCountFromMeta("\"trace_function_count\":") == + tester.ExtractCountFromPayload("\"trace_function_infos\":")); + ASSERT_TRUE(tester.RemoveExistingFile()); +} +} // namespace panda::test diff --git a/tests/runtime/ic/ic_binaryop_test.cpp b/tests/runtime/ic/ic_binaryop_test.cpp new file mode 100644 index 000000000..59346a839 --- /dev/null +++ b/tests/runtime/ic/ic_binaryop_test.cpp @@ -0,0 +1,384 @@ +/* + * Copyright (c) 2021-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 + +#include "plugins/ecmascript/runtime/ecma_vm.h" +#include "plugins/ecmascript/runtime/global_env_constants.h" +#include "plugins/ecmascript/runtime/global_env_constants-inl.h" +#include "plugins/ecmascript/runtime/ic/ic_binary_op-inl.h" +#include "plugins/ecmascript/runtime/interpreter/interpreter-inl.h" +#include "plugins/ecmascript/runtime/interpreter/slow_runtime_stub.h" +#include "plugins/ecmascript/runtime/object_factory.h" +#include "plugins/ecmascript/tests/runtime/common/test_helper.h" + +using namespace panda::ecmascript; +namespace panda::test { +class ICBinaryOPTest : public testing::Test { +public: + static void SetUpTestCase() + { + GTEST_LOG_(INFO) << "SetUpTestCase"; + } + + static void TearDownTestCase() + { + GTEST_LOG_(INFO) << "TearDownCase"; + } + + void SetUp() override + { + TestHelper::CreateEcmaVMWithScope(instance, thread, scope); + ecmaVm = EcmaVM::Cast(instance); + } + + void TearDown() override + { + TestHelper::DestroyEcmaVMWithScope(instance, scope); + } + + PandaVM *instance {nullptr}; + EcmaHandleScope *scope {nullptr}; + JSThread *thread {nullptr}; + EcmaVM *ecmaVm = nullptr; +}; + +TEST_F(ICBinaryOPTest, AddWithTSType) +{ + ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); + + JSHandle Str1 = factory->NewFromCanBeCompressString("AddTest"); + JSHandle Str2 = factory->NewFromCanBeCompressString("IC"); + JSHandle arg4 = factory->NewEmptyJSObject(); + JSTaggedValue arg1Value(static_cast(2)); + JSTaggedValue arg2Value(static_cast(3)); + JSTaggedValue arg3Value(static_cast(9.5561)); + JSHandle arg1(thread, arg1Value); + JSHandle arg2(thread, arg2Value); + JSHandle arg3(thread, arg3Value); + + JSTaggedValue resInSlowPath1 = SlowRuntimeStub::Add2Dyn(thread, ecmaVm, arg1.GetTaggedValue(), + arg2.GetTaggedValue()); + JSHandle slowHandle1(thread, resInSlowPath1); + JSTaggedValue resInICPath1 = ICBinaryOP::AddWithTSType(thread, ecmaVm, arg1.GetTaggedValue(), arg2.GetTaggedValue(), + JSTaggedValue(static_cast(BinaryType::NUMBER))); + EXPECT_EQ(slowHandle1.GetTaggedValue(), resInICPath1); + + JSTaggedValue resInSlowPath2 = SlowRuntimeStub::Add2Dyn(thread, ecmaVm, arg1.GetTaggedValue(), + arg3.GetTaggedValue()); + JSHandle slowHandle2(thread, resInSlowPath2); + JSTaggedValue resInICPath2 = ICBinaryOP::AddWithTSType(thread, ecmaVm, arg1.GetTaggedValue(), arg3.GetTaggedValue(), + JSTaggedValue(static_cast(BinaryType::NUMBER))); + EXPECT_EQ(slowHandle2.GetTaggedValue(), resInICPath2); + + JSTaggedValue resInSlowPath3 = SlowRuntimeStub::Add2Dyn(thread, ecmaVm, Str1.GetTaggedValue(), + Str2.GetTaggedValue()); + JSHandle slowHandle3(thread, reinterpret_cast(resInSlowPath3.GetRawData())); + JSTaggedValue resInICPath3 = ICBinaryOP::AddWithTSType(thread, ecmaVm, Str1.GetTaggedValue(), Str2.GetTaggedValue(), + JSTaggedValue(static_cast(BinaryType::STRING))); + ASSERT_TRUE(resInICPath3.IsString()); + EXPECT_EQ(slowHandle3->Compare(reinterpret_cast(resInICPath3.GetRawData())), 0); + + JSTaggedValue resInSlowPath4 = SlowRuntimeStub::Add2Dyn(thread, ecmaVm, JSTaggedValue::Undefined(), + arg2.GetTaggedValue()); + JSHandle slowHandle4(thread, resInSlowPath4); + JSTaggedValue resInICPath4 = ICBinaryOP::AddWithTSType(thread, ecmaVm, JSTaggedValue::Undefined(), + arg2.GetTaggedValue(), + JSTaggedValue(static_cast(BinaryType::NUMBER_GEN))); + EXPECT_EQ(slowHandle4.GetTaggedValue(), resInICPath4); + + JSTaggedValue resInSlowPath5 = SlowRuntimeStub::Add2Dyn(thread, ecmaVm, arg3.GetTaggedValue(), + Str1.GetTaggedValue()); + JSHandle slowHandle5(thread, reinterpret_cast(resInSlowPath5.GetRawData())); + JSTaggedValue resInICPath5 = ICBinaryOP::AddWithTSType(thread, ecmaVm, arg3.GetTaggedValue(), + Str1.GetTaggedValue(), + JSTaggedValue(static_cast(BinaryType::STRING_GEN))); + ASSERT_TRUE(resInICPath5.IsString()); + EXPECT_EQ(slowHandle5->Compare(reinterpret_cast(resInICPath5.GetRawData())), 0); + + JSTaggedValue resInSlowPath6 = SlowRuntimeStub::Add2Dyn(thread, ecmaVm, Str1.GetTaggedValue(), + JSTaggedValue::Null()); + JSHandle slowHandle6(thread, reinterpret_cast(resInSlowPath6.GetRawData())); + JSTaggedValue resInICPath6 = ICBinaryOP::AddWithTSType(thread, ecmaVm, Str1.GetTaggedValue(), JSTaggedValue::Null(), + JSTaggedValue(static_cast(BinaryType::STRING_GEN))); + ASSERT_TRUE(resInICPath6.IsString()); + EXPECT_EQ(slowHandle6->Compare(reinterpret_cast(resInICPath6.GetRawData())), 0); + + JSTaggedValue resInSlowPath7 = SlowRuntimeStub::Add2Dyn(thread, ecmaVm, arg1.GetTaggedValue(), + JSTaggedValue::True()); + JSHandle slowHandle7(thread, resInSlowPath7); + JSTaggedValue resInICPath7 = ICBinaryOP::AddWithTSType(thread, ecmaVm, arg1.GetTaggedValue(), JSTaggedValue::True(), + JSTaggedValue(static_cast(BinaryType::NUMBER_GEN))); + EXPECT_EQ(slowHandle7.GetTaggedValue(), resInICPath7); + + JSTaggedValue resInSlowPath8 = SlowRuntimeStub::Add2Dyn(thread, ecmaVm, arg4.GetTaggedValue(), + JSTaggedValue::Null()); + JSHandle slowHandle8(thread, reinterpret_cast(resInSlowPath8.GetRawData())); + JSTaggedValue resInICPath8 = ICBinaryOP::AddWithTSType(thread, ecmaVm, arg4.GetTaggedValue(), JSTaggedValue::Null(), + JSTaggedValue(static_cast(BinaryType::GENERIC))); + ASSERT_TRUE(resInICPath8.IsString()); + EXPECT_EQ(slowHandle8->Compare(reinterpret_cast(resInICPath8.GetRawData())), 0); +}; + +TEST_F(ICBinaryOPTest, SubWithTSType) +{ + JSTaggedValue arg1Value(static_cast(-2)); + JSTaggedValue arg2Value(static_cast(INT32_MAX-1)); + JSTaggedValue arg3Value(static_cast(9.5561)); + JSHandle arg1(thread, arg1Value); + JSHandle arg2(thread, arg2Value); + JSHandle arg3(thread, arg3Value); + + JSTaggedValue resInSlowPath1 = SlowRuntimeStub::Sub2Dyn(thread, arg1.GetTaggedValue(), arg2.GetTaggedValue()); + JSHandle slowHandle1(thread, resInSlowPath1); + JSTaggedValue resInICPath1 = ICBinaryOP::SubWithTSType(thread, ecmaVm, arg1.GetTaggedValue(), arg2.GetTaggedValue(), + JSTaggedValue(static_cast(BinaryType::NUMBER))); + EXPECT_EQ(slowHandle1.GetTaggedValue(), resInICPath1); + + JSTaggedValue resInSlowPath2 = SlowRuntimeStub::Sub2Dyn(thread, arg2.GetTaggedValue(), arg3.GetTaggedValue()); + JSHandle slowHandle2(thread, resInSlowPath2); + JSTaggedValue resInICPath2 = ICBinaryOP::SubWithTSType(thread, ecmaVm, arg2.GetTaggedValue(), arg3.GetTaggedValue(), + JSTaggedValue(static_cast(BinaryType::NUMBER))); + EXPECT_EQ(slowHandle2.GetTaggedValue(), resInICPath2); + + JSTaggedValue resInSlowPath3 = SlowRuntimeStub::Sub2Dyn(thread, arg1.GetTaggedValue(), JSTaggedValue::True()); + JSHandle slowHandle3(thread, resInSlowPath3); + JSTaggedValue resInICPath3 = ICBinaryOP::SubWithTSType(thread, ecmaVm, arg1.GetTaggedValue(), JSTaggedValue::True(), + JSTaggedValue(static_cast(BinaryType::GENERIC))); + EXPECT_EQ(slowHandle3.GetTaggedValue(), resInICPath3); +}; + +TEST_F(ICBinaryOPTest, MulWithTSType) +{ + JSTaggedValue arg1Value(static_cast(28.5)); + JSTaggedValue arg2Value(static_cast(354)); + JSTaggedValue arg3Value(static_cast(9.5561)); + JSHandle arg1(thread, arg1Value); + JSHandle arg2(thread, arg2Value); + JSHandle arg3(thread, arg3Value); + + JSTaggedValue resInSlowPath1 = SlowRuntimeStub::Mul2Dyn(thread, arg1.GetTaggedValue(), arg2.GetTaggedValue()); + JSHandle slowHandle1(thread, resInSlowPath1); + JSTaggedValue resInICPath1 = ICBinaryOP::MulWithTSType(thread, ecmaVm, arg1.GetTaggedValue(), arg2.GetTaggedValue(), + JSTaggedValue(static_cast(BinaryType::NUMBER))); + EXPECT_EQ(slowHandle1.GetTaggedValue(), resInICPath1); + + JSTaggedValue resInSlowPath2 = SlowRuntimeStub::Mul2Dyn(thread, arg2.GetTaggedValue(), arg3.GetTaggedValue()); + JSHandle slowHandle2(thread, resInSlowPath2); + JSTaggedValue resInICPath2 = ICBinaryOP::MulWithTSType(thread, ecmaVm, arg2.GetTaggedValue(), arg3.GetTaggedValue(), + JSTaggedValue(static_cast(BinaryType::NUMBER))); + EXPECT_EQ(slowHandle2.GetTaggedValue(), resInICPath2); + + JSTaggedValue resInSlowPath3 = SlowRuntimeStub::Mul2Dyn(thread, arg1.GetTaggedValue(), JSTaggedValue::True()); + JSHandle slowHandle3(thread, resInSlowPath3); + JSTaggedValue resInICPath3 = ICBinaryOP::MulWithTSType(thread, ecmaVm, arg1.GetTaggedValue(), JSTaggedValue::True(), + JSTaggedValue(static_cast(BinaryType::GENERIC))); + EXPECT_EQ(slowHandle3.GetTaggedValue(), resInICPath3); + +}; + +TEST_F(ICBinaryOPTest, DivWithTSType) +{ + JSTaggedValue arg1Value(static_cast(2)); + JSTaggedValue arg2Value(static_cast(39884)); + JSTaggedValue arg3Value(static_cast(0)); + JSTaggedValue arg4Value(static_cast(934.5561)); + JSHandle arg1(thread, arg1Value); + JSHandle arg2(thread, arg2Value); + JSHandle arg3(thread, arg3Value); + JSHandle arg4(thread, arg4Value); + + JSTaggedValue resInSlowPath1 = SlowRuntimeStub::Div2Dyn(thread, arg3.GetTaggedValue(), arg2.GetTaggedValue()); + JSHandle slowHandle1(thread, resInSlowPath1); + JSTaggedValue resInICPath1 = ICBinaryOP::DivWithTSType(thread, ecmaVm, arg3.GetTaggedValue(), arg2.GetTaggedValue(), + JSTaggedValue(static_cast(BinaryType::NUMBER))); + EXPECT_EQ(slowHandle1.GetTaggedValue(), resInICPath1); + + JSTaggedValue resInSlowPath2 = SlowRuntimeStub::Div2Dyn(thread, arg2.GetTaggedValue(), arg3.GetTaggedValue()); + JSHandle slowHandle2(thread, resInSlowPath2); + JSTaggedValue resInICPath2 = ICBinaryOP::DivWithTSType(thread, ecmaVm, arg2.GetTaggedValue(), arg3.GetTaggedValue(), + JSTaggedValue(static_cast(BinaryType::NUMBER))); + EXPECT_EQ(slowHandle2.GetTaggedValue(), resInICPath2); + + JSTaggedValue resInSlowPath3 = SlowRuntimeStub::Div2Dyn(thread, arg1.GetTaggedValue(), arg2.GetTaggedValue()); + JSHandle slowHandle3(thread, resInSlowPath3); + JSTaggedValue resInICPath3 = ICBinaryOP::DivWithTSType(thread, ecmaVm, arg1.GetTaggedValue(), arg2.GetTaggedValue(), + JSTaggedValue(static_cast(BinaryType::NUMBER))); + EXPECT_EQ(slowHandle3.GetTaggedValue(), resInICPath3); + + JSTaggedValue resInSlowPath4 = SlowRuntimeStub::Div2Dyn(thread, arg2.GetTaggedValue(), JSTaggedValue::True()); + JSHandle slowHandle4(thread, resInSlowPath4); + JSTaggedValue resInICPath4 = ICBinaryOP::DivWithTSType(thread, ecmaVm, arg2.GetTaggedValue(), JSTaggedValue::True(), + JSTaggedValue(static_cast(BinaryType::GENERIC))); + EXPECT_EQ(slowHandle4.GetTaggedValue(), resInICPath4); + + JSTaggedValue resInSlowPath5 = SlowRuntimeStub::Div2Dyn(thread, arg4.GetTaggedValue(), JSTaggedValue::False()); + JSHandle slowHandle5(thread, resInSlowPath5); + JSTaggedValue resInICPath5 = ICBinaryOP::DivWithTSType(thread, ecmaVm, arg4.GetTaggedValue(), + JSTaggedValue::False(), + JSTaggedValue(static_cast(BinaryType::GENERIC))); + EXPECT_EQ(slowHandle5.GetTaggedValue(), resInICPath5); +}; + +TEST_F(ICBinaryOPTest, ModWithTSType) +{ + JSTaggedValue arg1Value(static_cast(2)); + JSTaggedValue arg2Value(static_cast(39884)); + JSTaggedValue arg3Value(static_cast(0)); + JSTaggedValue arg4Value(static_cast(934.5561)); + JSHandle arg1(thread, arg1Value); + JSHandle arg2(thread, arg2Value); + JSHandle arg3(thread, arg3Value); + JSHandle arg4(thread, arg4Value); + + JSTaggedValue resInSlowPath1 = SlowRuntimeStub::Mod2Dyn(thread, arg3.GetTaggedValue(), arg2.GetTaggedValue()); + JSHandle slowHandle1(thread, resInSlowPath1); + JSTaggedValue resInICPath1 = ICBinaryOP::ModWithTSType(thread, ecmaVm, arg3.GetTaggedValue(), arg2.GetTaggedValue(), + JSTaggedValue(static_cast(BinaryType::NUMBER))); + EXPECT_EQ(slowHandle1.GetTaggedValue(), resInICPath1); + + JSTaggedValue resInSlowPath2 = SlowRuntimeStub::Mod2Dyn(thread, arg2.GetTaggedValue(), arg3.GetTaggedValue()); + JSHandle slowHandle2(thread, resInSlowPath2); + JSTaggedValue resInICPath2 = ICBinaryOP::ModWithTSType(thread, ecmaVm, arg2.GetTaggedValue(), arg3.GetTaggedValue(), + JSTaggedValue(static_cast(BinaryType::NUMBER))); + EXPECT_EQ(slowHandle2.GetTaggedValue(), resInICPath2); + + JSTaggedValue resInSlowPath3 = SlowRuntimeStub::Mod2Dyn(thread, arg1.GetTaggedValue(), arg2.GetTaggedValue()); + JSHandle slowHandle3(thread, resInSlowPath3); + JSTaggedValue resInICPath3 = ICBinaryOP::ModWithTSType(thread, ecmaVm, arg1.GetTaggedValue(), arg2.GetTaggedValue(), + JSTaggedValue(static_cast(BinaryType::NUMBER))); + EXPECT_EQ(slowHandle3.GetTaggedValue(), resInICPath3); + + + JSTaggedValue resInSlowPath4 = SlowRuntimeStub::Mod2Dyn(thread, arg2.GetTaggedValue(), JSTaggedValue::True()); + JSHandle slowHandle4(thread, resInSlowPath4); + JSTaggedValue resInICPath4 = ICBinaryOP::ModWithTSType(thread, ecmaVm, arg2.GetTaggedValue(), JSTaggedValue::True(), + JSTaggedValue(static_cast(BinaryType::GENERIC))); + EXPECT_EQ(slowHandle4.GetTaggedValue(), resInICPath4); + + + JSTaggedValue resInSlowPath5 = SlowRuntimeStub::Mod2Dyn(thread, arg4.GetTaggedValue(), JSTaggedValue::False()); + JSHandle slowHandle5(thread, resInSlowPath5); + JSTaggedValue resInICPath5 = ICBinaryOP::ModWithTSType(thread, ecmaVm, arg4.GetTaggedValue(), + JSTaggedValue::False(), + JSTaggedValue(static_cast(BinaryType::GENERIC))); + EXPECT_EQ(slowHandle5.GetTaggedValue(), resInICPath5); +}; + +TEST_F(ICBinaryOPTest, ShlWithTSType) +{ + ObjectFactory *factory = ecmaVm->GetFactory(); + + JSHandle Str1 = factory->NewFromCanBeCompressString("225"); + JSTaggedValue arg1(static_cast(286)); + JSTaggedValue arg3(static_cast(5)); + + JSTaggedValue resInICPath1 = ICBinaryOP::ShlWithTSType(thread, ecmaVm, arg1, arg3, + JSTaggedValue(static_cast(BinaryType::NUMBER))); + EXPECT_EQ(JSTaggedValue(9152), resInICPath1); + + JSTaggedValue resInICPath2 = ICBinaryOP::ShlWithTSType(thread, ecmaVm, Str1.GetTaggedValue(), arg3, + JSTaggedValue(static_cast(BinaryType::GENERIC))); + EXPECT_EQ(JSTaggedValue(7200), resInICPath2); +}; + +TEST_F(ICBinaryOPTest, ShrWithTSType) +{ + ObjectFactory *factory = ecmaVm->GetFactory(); + + JSHandle Str1 = factory->NewFromCanBeCompressString("225"); + JSTaggedValue arg1(static_cast(286)); + JSTaggedValue arg3(static_cast(5)); + + JSTaggedValue resInICPath1 = ICBinaryOP::ShrWithTSType(thread, ecmaVm, arg1, arg3, + JSTaggedValue(static_cast(BinaryType::NUMBER))); + EXPECT_EQ(JSTaggedValue(8), resInICPath1); + + JSTaggedValue resInICPath2 = ICBinaryOP::ShrWithTSType(thread, ecmaVm, Str1.GetTaggedValue(), arg3, + JSTaggedValue(static_cast(BinaryType::GENERIC))); + EXPECT_EQ(JSTaggedValue(7), resInICPath2); +}; + +TEST_F(ICBinaryOPTest, AshrWithTSType) +{ + ObjectFactory *factory = ecmaVm->GetFactory(); + + JSHandle Str1 = factory->NewFromCanBeCompressString("225"); + JSTaggedValue arg1(static_cast(286)); + JSTaggedValue arg2(static_cast(-286)); + JSTaggedValue arg3(static_cast(5)); + + JSTaggedValue resInICPath1 = ICBinaryOP::AshrWithTSType(thread, ecmaVm, arg1, arg3, + JSTaggedValue(static_cast(BinaryType::NUMBER))); + EXPECT_EQ(JSTaggedValue(8), resInICPath1); + + JSTaggedValue resInICPath3 = ICBinaryOP::AshrWithTSType(thread, ecmaVm, arg2, arg3, + JSTaggedValue(static_cast(BinaryType::NUMBER))); + EXPECT_EQ(JSTaggedValue(134217719), resInICPath3); + + JSTaggedValue resInICPath2 = ICBinaryOP::AshrWithTSType(thread, ecmaVm, Str1.GetTaggedValue(), arg3, + JSTaggedValue(static_cast(BinaryType::GENERIC))); + EXPECT_EQ(JSTaggedValue(7), resInICPath2); + +}; +TEST_F(ICBinaryOPTest, AndWithTSType) +{ + ObjectFactory *factory = ecmaVm->GetFactory(); + + JSHandle Str1 = factory->NewFromCanBeCompressString("225"); + JSTaggedValue arg1(static_cast(286)); + JSTaggedValue arg3(static_cast(541)); + + JSTaggedValue resInICPath1 = ICBinaryOP::AndWithTSType(thread, ecmaVm, arg1, arg3, + JSTaggedValue(static_cast(BinaryType::NUMBER))); + EXPECT_EQ(JSTaggedValue(28), resInICPath1); + + JSTaggedValue resInICPath2 = ICBinaryOP::AndWithTSType(thread, ecmaVm, Str1.GetTaggedValue(), arg3, + JSTaggedValue(static_cast(BinaryType::GENERIC))); + EXPECT_EQ(JSTaggedValue(1), resInICPath2); +}; +TEST_F(ICBinaryOPTest, OrWithTSType) +{ + ObjectFactory *factory = ecmaVm->GetFactory(); + + JSHandle Str1 = factory->NewFromCanBeCompressString("225"); + JSTaggedValue arg1(static_cast(286)); + JSTaggedValue arg3(static_cast(523)); + + JSTaggedValue resInICPath1 = ICBinaryOP::OrWithTSType(thread, ecmaVm, arg1, arg3, + JSTaggedValue(static_cast(BinaryType::NUMBER))); + EXPECT_EQ(JSTaggedValue(799), resInICPath1); + + JSTaggedValue resInICPath2 = ICBinaryOP::OrWithTSType(thread, ecmaVm, Str1.GetTaggedValue(), arg3, + JSTaggedValue(static_cast(BinaryType::GENERIC))); + EXPECT_EQ(JSTaggedValue(747), resInICPath2); +}; +TEST_F(ICBinaryOPTest, XorWithTSType) +{ + ObjectFactory *factory = ecmaVm->GetFactory(); + + JSHandle Str1 = factory->NewFromCanBeCompressString("1225"); + JSTaggedValue arg1(static_cast(286)); + JSTaggedValue arg3(static_cast(523)); + + JSTaggedValue resInICPath1 = ICBinaryOP::XorWithTSType(thread, ecmaVm, arg1, arg3, + JSTaggedValue(static_cast(BinaryType::NUMBER))); + EXPECT_EQ(JSTaggedValue(789), resInICPath1); + + JSTaggedValue resInICPath2 = ICBinaryOP::XorWithTSType(thread, ecmaVm, Str1.GetTaggedValue(), arg3, + JSTaggedValue(static_cast(BinaryType::GENERIC))); + EXPECT_EQ(JSTaggedValue(1730), resInICPath2); +}; +} // namespace panda::test diff --git a/tests/runtime/ic/ic_compareop_test.cpp b/tests/runtime/ic/ic_compareop_test.cpp new file mode 100644 index 000000000..318bc07ae --- /dev/null +++ b/tests/runtime/ic/ic_compareop_test.cpp @@ -0,0 +1,463 @@ +/* + * Copyright (c) 2021-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 +#include "plugins/ecmascript/runtime/builtins/builtins_boolean.h" +#include "plugins/ecmascript/runtime/ecma_runtime_call_info.h" +#include "plugins/ecmascript/runtime/ecma_string.h" +#include "plugins/ecmascript/runtime/ecma_vm.h" +#include "plugins/ecmascript/runtime/global_env.h" +#include "plugins/ecmascript/runtime/global_env_constants.h" +#include "plugins/ecmascript/runtime/global_env_constants-inl.h" +#include "plugins/ecmascript/runtime/ic/ic_compare_op.cpp" +#include "plugins/ecmascript/runtime/ic/ic_compare_op.h" +#include "plugins/ecmascript/runtime/interpreter/slow_runtime_stub.h" +#include "plugins/ecmascript/runtime/js_primitive_ref.h" +#include "plugins/ecmascript/runtime/js_tagged_value-inl.h" +#include "plugins/ecmascript/runtime/object_factory.h" +#include "plugins/ecmascript/tests/runtime/common/test_helper.h" + +using namespace panda::ecmascript; +namespace panda::test { +class IcCompareOPTest : public testing::Test { +public: + static void SetUpTestCase() + { + GTEST_LOG_(INFO) << "SetUpTestCase"; + } + + static void TearDownTestCase() + { + GTEST_LOG_(INFO) << "TearDownCase"; + } + + void SetUp() override + { + TestHelper::CreateEcmaVMWithScope(instance, thread, scope); + ecmaVm = EcmaVM::Cast(instance); + } + + void TearDown() override + { + TestHelper::DestroyEcmaVMWithScope(instance, scope); + } + + PandaVM *instance {nullptr}; + EcmaVM *ecmaVm = nullptr; + EcmaHandleScope *scope {nullptr}; + JSThread *thread {nullptr}; +}; + +TEST_F(IcCompareOPTest, EqualWithIC) +{ + ObjectFactory *factory = ecmaVm->GetFactory(); + + JSHandle Str1 = JSHandle(factory->NewFromCanBeCompressString("1")); + JSTaggedValue arg1(static_cast(1)); + JSTaggedValue arg2(static_cast(1.0)); + JSTaggedValue arg3(false); + JSTaggedValue arg4(true); + JSHandle arg1Handle(thread, arg1); + JSHandle arg2Handle(thread, arg2); + JSHandle arg3Handle(thread, arg3); + JSHandle arg4Handle(thread, arg4); + + JSHandle env = thread->GetEcmaVM()->GetGlobalEnv(); + JSHandle globalObject(thread, env->GetGlobalObject()); + + JSHandle boolean(env->GetBooleanFunction()); + auto ecmaRuntimeCallInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread, JSTaggedValue(*boolean), 6); + ecmaRuntimeCallInfo->SetFunction(boolean.GetTaggedValue()); + ecmaRuntimeCallInfo->SetThis(globalObject.GetTaggedValue()); + ecmaRuntimeCallInfo->SetCallArg(0, JSTaggedValue(static_cast(1))); + + JSTaggedValue booleanObj = builtins::BuiltinsBoolean::BooleanConstructor(ecmaRuntimeCallInfo.get()); + JSHandle booleanObjHandle(thread, booleanObj); + + JSTaggedValue resInSlowPath1 = SlowRuntimeStub::EqDyn(thread, arg1Handle.GetTaggedValue(), + arg2Handle.GetTaggedValue()); + JSTaggedValue resInSlowPath2 = SlowRuntimeStub::EqDyn(thread, Str1.GetTaggedValue(), arg1Handle.GetTaggedValue()); + JSTaggedValue resInSlowPath3 = SlowRuntimeStub::EqDyn(thread, Str1.GetTaggedValue(), arg3Handle.GetTaggedValue()); + JSTaggedValue resInSlowPath4 = SlowRuntimeStub::EqDyn(thread, Str1.GetTaggedValue(), arg4Handle.GetTaggedValue()); + JSTaggedValue resInSlowPath5 = SlowRuntimeStub::EqDyn(thread, booleanObjHandle.GetTaggedValue(), + arg4Handle.GetTaggedValue()); + JSTaggedValue resInSlowPath9 = SlowRuntimeStub::EqDyn(thread, JSTaggedValue::Undefined(), JSTaggedValue::Null()); + JSTaggedValue resInSlowPath10 = SlowRuntimeStub::EqDyn(thread, JSTaggedValue::Undefined(), JSTaggedValue::True()); + + JSTaggedValue resInICPath1 = CompareOp::EqualWithIC(thread, arg1Handle.GetTaggedValue(), + arg2Handle.GetTaggedValue(), CompareOpType::NUMBER_NUMBER); + JSTaggedValue resInICPath2 = CompareOp::EqualWithIC(thread, Str1.GetTaggedValue(), + arg1Handle.GetTaggedValue(), CompareOpType::STRING_NUMBER); + JSTaggedValue resInICPath3 = CompareOp::EqualWithIC(thread, Str1.GetTaggedValue(), + arg3Handle.GetTaggedValue(), CompareOpType::STRING_BOOLEAN); + JSTaggedValue resInICPath4 = CompareOp::EqualWithIC(thread, Str1.GetTaggedValue(), + arg4Handle.GetTaggedValue(), CompareOpType::STRING_BOOLEAN); + JSTaggedValue resInICPath5 = CompareOp::EqualWithIC(thread, booleanObjHandle.GetTaggedValue(), + arg4Handle.GetTaggedValue(), CompareOpType::OBJ_BOOLEAN); + JSTaggedValue resInICPath9 = CompareOp::EqualWithIC(thread, JSTaggedValue::Undefined(), + JSTaggedValue::Null(), CompareOpType::UNDEFINED_NULL); + JSTaggedValue resInICPath10 = CompareOp::EqualWithIC(thread, JSTaggedValue::Undefined(), + JSTaggedValue::True(), CompareOpType::OTHER); + + EXPECT_EQ(resInSlowPath1, resInICPath1); + EXPECT_EQ(resInSlowPath2, resInICPath2); + EXPECT_EQ(resInSlowPath3, resInICPath3); + EXPECT_EQ(resInSlowPath4, resInICPath4); + EXPECT_EQ(resInSlowPath5, resInICPath5); + EXPECT_EQ(resInSlowPath9, resInICPath9); + EXPECT_EQ(resInSlowPath10, resInICPath10); +}; + +TEST_F(IcCompareOPTest, NotEqualWithIC) +{ + ObjectFactory *factory = ecmaVm->GetFactory(); + + JSHandle Str1 = JSHandle(factory->NewFromCanBeCompressString("1")); + JSTaggedValue arg1(static_cast(1)); + JSTaggedValue arg2(static_cast(2.0)); + JSTaggedValue arg3(false); + JSTaggedValue arg4(true); + JSHandle arg1Handle(thread, arg1); + JSHandle arg2Handle(thread, arg2); + JSHandle arg3Handle(thread, arg3); + JSHandle arg4Handle(thread, arg4); + + JSHandle env = thread->GetEcmaVM()->GetGlobalEnv(); + JSHandle globalObject(thread, env->GetGlobalObject()); + + JSHandle boolean(env->GetBooleanFunction()); + auto ecmaRuntimeCallInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread, JSTaggedValue(*boolean), 6); + ecmaRuntimeCallInfo->SetFunction(boolean.GetTaggedValue()); + ecmaRuntimeCallInfo->SetThis(globalObject.GetTaggedValue()); + ecmaRuntimeCallInfo->SetCallArg(0, JSTaggedValue(static_cast(123))); + + JSTaggedValue booleanObj = builtins::BuiltinsBoolean::BooleanConstructor(ecmaRuntimeCallInfo.get()); + JSHandle booleanObjHandle(thread, booleanObj); + JSTaggedValue resInSlowPath1 = SlowRuntimeStub::NotEqDyn(thread, arg1Handle.GetTaggedValue(), + arg2Handle.GetTaggedValue()); + JSTaggedValue resInSlowPath2 = SlowRuntimeStub::NotEqDyn(thread, Str1.GetTaggedValue(), + arg1Handle.GetTaggedValue()); + JSTaggedValue resInSlowPath3 = SlowRuntimeStub::NotEqDyn(thread, Str1.GetTaggedValue(), + arg3Handle.GetTaggedValue()); + JSTaggedValue resInSlowPath4 = SlowRuntimeStub::NotEqDyn(thread, Str1.GetTaggedValue(), + arg4Handle.GetTaggedValue()); + JSTaggedValue resInSlowPath5 = SlowRuntimeStub::NotEqDyn(thread, arg1Handle.GetTaggedValue(), + booleanObjHandle.GetTaggedValue()); + JSTaggedValue resInSlowPath9 = SlowRuntimeStub::NotEqDyn(thread, JSTaggedValue::Undefined(), + JSTaggedValue::Null()); + JSTaggedValue resInSlowPath10 = SlowRuntimeStub::NotEqDyn(thread, JSTaggedValue::Undefined(), + JSTaggedValue::True()); + + JSTaggedValue resInICPath1 = CompareOp::NotEqualWithIC(thread, arg1Handle.GetTaggedValue(), + arg2Handle.GetTaggedValue(), + CompareOpType::NUMBER_NUMBER); + JSTaggedValue resInICPath2 = CompareOp::NotEqualWithIC(thread, Str1.GetTaggedValue(), + arg1Handle.GetTaggedValue(), CompareOpType::STRING_NUMBER); + JSTaggedValue resInICPath3 = CompareOp::NotEqualWithIC(thread, Str1.GetTaggedValue(), + arg3Handle.GetTaggedValue(), CompareOpType::STRING_BOOLEAN); + JSTaggedValue resInICPath4 = CompareOp::NotEqualWithIC(thread, Str1.GetTaggedValue(), + arg4Handle.GetTaggedValue(), CompareOpType::STRING_BOOLEAN); + JSTaggedValue resInICPath5 = CompareOp::NotEqualWithIC(thread, arg1Handle.GetTaggedValue(), + booleanObjHandle.GetTaggedValue(), + CompareOpType::NUMBER_OBJ); + JSTaggedValue resInICPath9 = CompareOp::NotEqualWithIC(thread, JSTaggedValue::Undefined(), + JSTaggedValue::Null(), CompareOpType::UNDEFINED_NULL); + JSTaggedValue resInICPath10 = CompareOp::NotEqualWithIC(thread, JSTaggedValue::Undefined(), + JSTaggedValue::True(), CompareOpType::OTHER); + + EXPECT_EQ(resInSlowPath1, resInICPath1); + EXPECT_EQ(resInSlowPath2, resInICPath2); + EXPECT_EQ(resInSlowPath3, resInICPath3); + EXPECT_EQ(resInSlowPath4, resInICPath4); + EXPECT_EQ(resInSlowPath5, resInICPath5); + EXPECT_EQ(resInSlowPath9, resInICPath9); + EXPECT_EQ(resInSlowPath10, resInICPath10); +}; + + +TEST_F(IcCompareOPTest, LessDynWithIC) +{ + ObjectFactory *factory = ecmaVm->GetFactory(); + + JSHandle Str1 = JSHandle(factory->NewFromCanBeCompressString("0")); + JSTaggedValue arg1(static_cast(1)); + JSTaggedValue arg2(static_cast(0.5)); + JSTaggedValue arg3(false); + JSTaggedValue arg4(true); + JSHandle arg1Handle(thread, arg1); + JSHandle arg2Handle(thread, arg2); + JSHandle arg3Handle(thread, arg3); + JSHandle arg4Handle(thread, arg4); + + JSHandle env = thread->GetEcmaVM()->GetGlobalEnv(); + JSHandle globalObject(thread, env->GetGlobalObject()); + + JSHandle boolean(env->GetBooleanFunction()); + auto ecmaRuntimeCallInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread, JSTaggedValue(*boolean), 6); + ecmaRuntimeCallInfo->SetFunction(boolean.GetTaggedValue()); + ecmaRuntimeCallInfo->SetThis(globalObject.GetTaggedValue()); + ecmaRuntimeCallInfo->SetCallArg(0, JSTaggedValue(static_cast(123))); + + JSTaggedValue booleanObj = builtins::BuiltinsBoolean::BooleanConstructor(ecmaRuntimeCallInfo.get()); + JSHandle booleanObjHandle(thread, booleanObj); + + JSTaggedValue resInSlowPath1 = SlowRuntimeStub::LessDyn(thread, arg1Handle.GetTaggedValue(), + arg2Handle.GetTaggedValue()); + JSTaggedValue resInSlowPath2 = SlowRuntimeStub::LessDyn(thread, Str1.GetTaggedValue(), + arg1Handle.GetTaggedValue()); + JSTaggedValue resInSlowPath3 = SlowRuntimeStub::LessDyn(thread, Str1.GetTaggedValue(), + arg3Handle.GetTaggedValue()); + JSTaggedValue resInSlowPath4 = SlowRuntimeStub::LessDyn(thread, Str1.GetTaggedValue(), + arg4Handle.GetTaggedValue()); + JSTaggedValue resInSlowPath5 = SlowRuntimeStub::LessDyn(thread, arg1Handle.GetTaggedValue(), + booleanObjHandle.GetTaggedValue()); + JSTaggedValue resInSlowPath9 = SlowRuntimeStub::LessDyn(thread, + JSTaggedValue::Undefined(), JSTaggedValue::Null()); + JSTaggedValue resInSlowPath10 = SlowRuntimeStub::LessDyn(thread, + JSTaggedValue::Undefined(), JSTaggedValue::True()); + + JSTaggedValue resInICPath1 = CompareOp::LessDynWithIC(thread, arg1Handle.GetTaggedValue(), + arg2Handle.GetTaggedValue(), CompareOpType::NUMBER_NUMBER); + JSTaggedValue resInICPath2 = CompareOp::LessDynWithIC(thread, Str1.GetTaggedValue(), + arg1Handle.GetTaggedValue(), CompareOpType::STRING_NUMBER); + JSTaggedValue resInICPath3 = CompareOp::LessDynWithIC(thread, Str1.GetTaggedValue(), + arg3Handle.GetTaggedValue(), CompareOpType::STRING_BOOLEAN); + JSTaggedValue resInICPath4 = CompareOp::LessDynWithIC(thread, Str1.GetTaggedValue(), + arg4Handle.GetTaggedValue(), CompareOpType::STRING_BOOLEAN); + JSTaggedValue resInICPath5 = CompareOp::LessDynWithIC(thread, arg1Handle.GetTaggedValue(), + booleanObjHandle.GetTaggedValue(), + CompareOpType::NUMBER_OBJ); + JSTaggedValue resInICPath9 = CompareOp::LessDynWithIC(thread, JSTaggedValue::Undefined(), + JSTaggedValue::Null(), CompareOpType::UNDEFINED_NULL); + JSTaggedValue resInICPath10 = CompareOp::LessDynWithIC(thread, JSTaggedValue::Undefined(), + JSTaggedValue::True(), CompareOpType::OTHER); + + EXPECT_EQ(resInSlowPath1, resInICPath1); + EXPECT_EQ(resInSlowPath2, resInICPath2); + EXPECT_EQ(resInSlowPath3, resInICPath3); + EXPECT_EQ(resInSlowPath4, resInICPath4); + EXPECT_EQ(resInSlowPath5, resInICPath5); + EXPECT_EQ(resInSlowPath9, resInICPath9); + EXPECT_EQ(resInSlowPath10, resInICPath10); +}; + + +TEST_F(IcCompareOPTest, LessEqDynWithIC) +{ + ObjectFactory *factory = ecmaVm->GetFactory(); + + JSHandle Str1 = JSHandle(factory->NewFromCanBeCompressString("1")); + JSTaggedValue arg1(static_cast(1)); + JSTaggedValue arg2(static_cast(0.5)); + JSTaggedValue arg3(false); + JSTaggedValue arg4(true); + JSHandle arg1Handle(thread, arg1); + JSHandle arg2Handle(thread, arg2); + JSHandle arg3Handle(thread, arg3); + JSHandle arg4Handle(thread, arg4); + JSHandle env = thread->GetEcmaVM()->GetGlobalEnv(); + JSHandle globalObject(thread, env->GetGlobalObject()); + + JSHandle boolean(env->GetBooleanFunction()); + auto ecmaRuntimeCallInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread, JSTaggedValue(*boolean), 6); + ecmaRuntimeCallInfo->SetFunction(boolean.GetTaggedValue()); + ecmaRuntimeCallInfo->SetThis(globalObject.GetTaggedValue()); + ecmaRuntimeCallInfo->SetCallArg(0, JSTaggedValue(static_cast(123))); + + JSTaggedValue booleanObj = builtins::BuiltinsBoolean::BooleanConstructor(ecmaRuntimeCallInfo.get()); + JSHandle booleanObjHandle(thread, booleanObj); + JSTaggedValue resInSlowPath1 = SlowRuntimeStub::LessEqDyn(thread, arg1Handle.GetTaggedValue(), + arg2Handle.GetTaggedValue()); + JSTaggedValue resInSlowPath2 = SlowRuntimeStub::LessEqDyn(thread, Str1.GetTaggedValue(), + arg1Handle.GetTaggedValue()); + JSTaggedValue resInSlowPath3 = SlowRuntimeStub::LessEqDyn(thread, Str1.GetTaggedValue(), + arg3Handle.GetTaggedValue()); + JSTaggedValue resInSlowPath4 = SlowRuntimeStub::LessEqDyn(thread, Str1.GetTaggedValue(), + arg4Handle.GetTaggedValue()); + JSTaggedValue resInSlowPath5 = SlowRuntimeStub::LessEqDyn(thread, arg1Handle.GetTaggedValue(), + booleanObjHandle.GetTaggedValue()); + JSTaggedValue resInSlowPath9 = SlowRuntimeStub::LessEqDyn(thread, JSTaggedValue::Undefined(), + JSTaggedValue::Null()); + JSTaggedValue resInSlowPath10 = SlowRuntimeStub::LessEqDyn(thread, JSTaggedValue::Undefined(), + JSTaggedValue::True()); + JSTaggedValue resInICPath1 = CompareOp::LessEqDynWithIC(thread, arg1Handle.GetTaggedValue(), + arg2Handle.GetTaggedValue(), + CompareOpType::NUMBER_NUMBER); + JSTaggedValue resInICPath2 = CompareOp::LessEqDynWithIC(thread, Str1.GetTaggedValue(), + arg1Handle.GetTaggedValue(), + CompareOpType::STRING_NUMBER); + JSTaggedValue resInICPath3 = CompareOp::LessEqDynWithIC(thread, Str1.GetTaggedValue(), + arg3Handle.GetTaggedValue(), + CompareOpType::STRING_BOOLEAN); + JSTaggedValue resInICPath4 = CompareOp::LessEqDynWithIC(thread, Str1.GetTaggedValue(), + arg4Handle.GetTaggedValue(), + CompareOpType::STRING_BOOLEAN); + JSTaggedValue resInICPath5 = CompareOp::LessEqDynWithIC(thread, + arg1Handle.GetTaggedValue(), + booleanObjHandle.GetTaggedValue(), + CompareOpType::NUMBER_OBJ); + JSTaggedValue resInICPath9 = CompareOp::LessEqDynWithIC(thread, JSTaggedValue::Undefined(), + JSTaggedValue::Null(), CompareOpType::UNDEFINED_NULL); + JSTaggedValue resInICPath10 = CompareOp::LessEqDynWithIC(thread, JSTaggedValue::Undefined(), + JSTaggedValue::True(), CompareOpType::OTHER); + + EXPECT_EQ(resInSlowPath1, resInICPath1); + EXPECT_EQ(resInSlowPath2, resInICPath2); + EXPECT_EQ(resInSlowPath3, resInICPath3); + EXPECT_EQ(resInSlowPath4, resInICPath4); + EXPECT_EQ(resInSlowPath5, resInICPath5); + EXPECT_EQ(resInSlowPath9, resInICPath9); + EXPECT_EQ(resInSlowPath10, resInICPath10); +}; + + +TEST_F(IcCompareOPTest, GreaterDynWithIC) +{ + ObjectFactory *factory = ecmaVm->GetFactory(); + + JSHandle Str1 = JSHandle(factory->NewFromCanBeCompressString("1")); + JSTaggedValue arg1(static_cast(1)); + JSTaggedValue arg2(static_cast(1.0)); + JSTaggedValue arg3(false); + JSTaggedValue arg4(true); + JSHandle arg1Handle(thread, arg1); + JSHandle arg2Handle(thread, arg2); + JSHandle arg3Handle(thread, arg3); + JSHandle arg4Handle(thread, arg4); + JSHandle env = thread->GetEcmaVM()->GetGlobalEnv(); + JSHandle globalObject(thread, env->GetGlobalObject()); + + JSHandle boolean(env->GetBooleanFunction()); + auto ecmaRuntimeCallInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread, JSTaggedValue(*boolean), 6); + ecmaRuntimeCallInfo->SetFunction(boolean.GetTaggedValue()); + ecmaRuntimeCallInfo->SetThis(globalObject.GetTaggedValue()); + ecmaRuntimeCallInfo->SetCallArg(0, JSTaggedValue(static_cast(1))); + + JSTaggedValue booleanObj = builtins::BuiltinsBoolean::BooleanConstructor(ecmaRuntimeCallInfo.get()); + JSHandle booleanObjHandle(thread, booleanObj); + JSTaggedValue resInSlowPath1 = SlowRuntimeStub::GreaterDyn(thread, arg1Handle.GetTaggedValue(), + arg2Handle.GetTaggedValue()); + JSTaggedValue resInSlowPath2 = SlowRuntimeStub::GreaterDyn(thread, Str1.GetTaggedValue(), + arg1Handle.GetTaggedValue()); + JSTaggedValue resInSlowPath3 = SlowRuntimeStub::GreaterDyn(thread, Str1.GetTaggedValue(), + arg3Handle.GetTaggedValue()); + JSTaggedValue resInSlowPath4 = SlowRuntimeStub::GreaterDyn(thread, Str1.GetTaggedValue(), + arg4Handle.GetTaggedValue()); + JSTaggedValue resInSlowPath5 = SlowRuntimeStub::GreaterDyn(thread, arg1Handle.GetTaggedValue(), + booleanObjHandle.GetTaggedValue()); + JSTaggedValue resInSlowPath9 = SlowRuntimeStub::GreaterDyn(thread, JSTaggedValue::Undefined(), + JSTaggedValue::Null()); + JSTaggedValue resInSlowPath10 = SlowRuntimeStub::GreaterDyn(thread, JSTaggedValue::Undefined(), + JSTaggedValue::True()); + + JSTaggedValue resInICPath1 = CompareOp::GreaterDynWithIC(thread, arg1Handle.GetTaggedValue(), + arg2Handle.GetTaggedValue(), + CompareOpType::NUMBER_NUMBER); + JSTaggedValue resInICPath2 = CompareOp::GreaterDynWithIC(thread, Str1.GetTaggedValue(), + arg1Handle.GetTaggedValue(), + CompareOpType::STRING_NUMBER); + JSTaggedValue resInICPath3 = CompareOp::GreaterDynWithIC(thread, Str1.GetTaggedValue(), + arg3Handle.GetTaggedValue(), + CompareOpType::STRING_BOOLEAN); + JSTaggedValue resInICPath4 = CompareOp::GreaterDynWithIC(thread, Str1.GetTaggedValue(), + arg4Handle.GetTaggedValue(), + CompareOpType::STRING_BOOLEAN); + JSTaggedValue resInICPath5 = CompareOp::GreaterDynWithIC(thread, arg1Handle.GetTaggedValue(), + booleanObjHandle.GetTaggedValue(), + CompareOpType::NUMBER_OBJ); + JSTaggedValue resInICPath9 = CompareOp::GreaterDynWithIC(thread, JSTaggedValue::Undefined(), + JSTaggedValue::Null(), CompareOpType::UNDEFINED_NULL); + JSTaggedValue resInICPath10 = CompareOp::GreaterDynWithIC(thread, JSTaggedValue::Undefined(), + JSTaggedValue::True(), CompareOpType::OTHER); + + EXPECT_EQ(resInSlowPath1, resInICPath1); + EXPECT_EQ(resInSlowPath2, resInICPath2); + EXPECT_EQ(resInSlowPath3, resInICPath3); + EXPECT_EQ(resInSlowPath4, resInICPath4); + EXPECT_EQ(resInSlowPath5, resInICPath5); + EXPECT_EQ(resInSlowPath9, resInICPath9); + EXPECT_EQ(resInSlowPath10, resInICPath10); +}; + + +TEST_F(IcCompareOPTest, GreaterEqDynWithIC) +{ + ObjectFactory *factory = ecmaVm->GetFactory(); + + JSHandle Str1 = JSHandle(factory->NewFromCanBeCompressString("1")); + JSTaggedValue arg1(static_cast(1)); + JSTaggedValue arg2(static_cast(1.0)); + JSTaggedValue arg3(false); + JSTaggedValue arg4(true); + JSHandle arg1Handle(thread, arg1); + JSHandle arg2Handle(thread, arg2); + JSHandle arg3Handle(thread, arg3); + JSHandle arg4Handle(thread, arg4); + + JSHandle env = thread->GetEcmaVM()->GetGlobalEnv(); + JSHandle globalObject(thread, env->GetGlobalObject()); + + JSHandle boolean(env->GetBooleanFunction()); + auto ecmaRuntimeCallInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread, JSTaggedValue(*boolean), 6); + ecmaRuntimeCallInfo->SetFunction(boolean.GetTaggedValue()); + ecmaRuntimeCallInfo->SetThis(globalObject.GetTaggedValue()); + ecmaRuntimeCallInfo->SetCallArg(0, JSTaggedValue(static_cast(0))); + + JSTaggedValue booleanObj = builtins::BuiltinsBoolean::BooleanConstructor(ecmaRuntimeCallInfo.get()); + JSHandle booleanObjHandle(thread, booleanObj); + JSTaggedValue resInSlowPath1 = SlowRuntimeStub::GreaterEqDyn(thread, arg1Handle.GetTaggedValue(), + arg2Handle.GetTaggedValue()); + JSTaggedValue resInSlowPath2 = SlowRuntimeStub::GreaterEqDyn(thread, Str1.GetTaggedValue(), + arg1Handle.GetTaggedValue()); + JSTaggedValue resInSlowPath3 = SlowRuntimeStub::GreaterEqDyn(thread, Str1.GetTaggedValue(), + arg3Handle.GetTaggedValue()); + JSTaggedValue resInSlowPath4 = SlowRuntimeStub::GreaterEqDyn(thread, Str1.GetTaggedValue(), + arg4Handle.GetTaggedValue()); + JSTaggedValue resInSlowPath5 = SlowRuntimeStub::GreaterEqDyn(thread, arg1Handle.GetTaggedValue(), + booleanObjHandle.GetTaggedValue()); + JSTaggedValue resInSlowPath9 = SlowRuntimeStub::GreaterEqDyn(thread, JSTaggedValue::Undefined(), + JSTaggedValue::Null()); + JSTaggedValue resInSlowPath10 = SlowRuntimeStub::GreaterEqDyn(thread, JSTaggedValue::Undefined(), + JSTaggedValue::True()); + + JSTaggedValue resInICPath1 = CompareOp::GreaterEqDynWithIC(thread, arg1Handle.GetTaggedValue(), + arg2Handle.GetTaggedValue(), + CompareOpType::NUMBER_NUMBER); + JSTaggedValue resInICPath2 = CompareOp::GreaterEqDynWithIC(thread, Str1.GetTaggedValue(), + arg1Handle.GetTaggedValue(), + CompareOpType::STRING_NUMBER); + JSTaggedValue resInICPath3 = CompareOp::GreaterEqDynWithIC(thread, Str1.GetTaggedValue(), + arg3Handle.GetTaggedValue(), + CompareOpType::STRING_BOOLEAN); + JSTaggedValue resInICPath4 = CompareOp::GreaterEqDynWithIC(thread, Str1.GetTaggedValue(), + arg4Handle.GetTaggedValue(), + CompareOpType::STRING_BOOLEAN); + JSTaggedValue resInICPath5 = CompareOp::GreaterEqDynWithIC(thread, arg1Handle.GetTaggedValue(), + booleanObjHandle.GetTaggedValue(), + CompareOpType::NUMBER_OBJ); + JSTaggedValue resInICPath9 = CompareOp::GreaterEqDynWithIC(thread, JSTaggedValue::Undefined(), + JSTaggedValue::Null(), CompareOpType::UNDEFINED_NULL); + JSTaggedValue resInICPath10 = CompareOp::GreaterEqDynWithIC(thread, JSTaggedValue::Undefined(), + JSTaggedValue::True(), CompareOpType::OTHER); + + EXPECT_EQ(resInSlowPath1, resInICPath1); + EXPECT_EQ(resInSlowPath2, resInICPath2); + EXPECT_EQ(resInSlowPath3, resInICPath3); + EXPECT_EQ(resInSlowPath4, resInICPath4); + EXPECT_EQ(resInSlowPath5, resInICPath5); + EXPECT_EQ(resInSlowPath9, resInICPath9); + EXPECT_EQ(resInSlowPath10, resInICPath10); +}; +} // namespace panda::test diff --git a/tests/runtime/ic/ic_invoke_test.cpp b/tests/runtime/ic/ic_invoke_test.cpp new file mode 100644 index 000000000..d41138a7c --- /dev/null +++ b/tests/runtime/ic/ic_invoke_test.cpp @@ -0,0 +1,134 @@ +/* + * Copyright (c) 2021-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 + +#include "plugins/ecmascript/runtime/ecma_vm.h" +#include "plugins/ecmascript/runtime/ic/invoke_cache.h" +#include "plugins/ecmascript/runtime/interpreter/interpreter-inl.h" +#include "plugins/ecmascript/runtime/object_factory.h" +#include "plugins/ecmascript/tests/runtime/common/test_helper.h" + +using namespace panda::ecmascript; +namespace panda::test { +class ICInvokeTest : public testing::Test { +public: + static void SetUpTestCase() + { + GTEST_LOG_(INFO) << "SetUpTestCase"; + } + + static void TearDownTestCase() + { + GTEST_LOG_(INFO) << "TearDownCase"; + } + + void SetUp() override + { + TestHelper::CreateEcmaVMWithScope(instance, thread, scope); + ecmaVm = EcmaVM::Cast(instance); + } + + void TearDown() override + { + TestHelper::DestroyEcmaVMWithScope(instance, scope); + } + + PandaVM *instance {nullptr}; + EcmaVM *ecmaVm = nullptr; + EcmaHandleScope *scope {nullptr}; + JSThread *thread {nullptr}; +}; + +TEST_F(ICInvokeTest, SetMonoConstuctCacheSlot) +{ + auto globalEnv = ecmaVm->GetGlobalEnv(); + auto factory = ecmaVm->GetFactory(); + JSHandle func = factory->NewJSFunction(globalEnv); + func.GetTaggedValue().GetTaggedObject()->GetClass()->SetClassConstructor(true); + + JSHandle array = factory->NewTaggedArray(10); + uint32_t slotId = 5; + bool setResult = InvokeCache::SetMonoConstuctCacheSlot( + thread, static_cast(*array), slotId, func.GetTaggedValue(), JSTaggedValue(123)); + ASSERT_TRUE(setResult); + ASSERT_EQ(array->Get(thread, slotId), func.GetTaggedValue()); + ASSERT_EQ(array->Get(thread, slotId + 1), JSTaggedValue(123)); +} + +TEST_F(ICInvokeTest, SetPolyConstuctCacheSlot) +{ + auto globalEnv = ecmaVm->GetGlobalEnv(); + auto factory = ecmaVm->GetFactory(); + JSHandle array1 = factory->NewTaggedArray(3); + JSHandle array2 = factory->NewTaggedArray(3); + + JSHandle func0 = factory->NewJSFunction(globalEnv); + func0.GetTaggedValue().GetTaggedObject()->GetClass()->SetClassConstructor(true); + array1->Set(thread, 0, func0.GetTaggedValue()); + array2->Set(thread, 0, JSTaggedValue(123)); + JSHandle func1 = factory->NewJSFunction(globalEnv); + func1.GetTaggedValue().GetTaggedObject()->GetClass()->SetClassConstructor(true); + array1->Set(thread, 1, func1.GetTaggedValue()); + array2->Set(thread, 1, JSTaggedValue(456)); + JSHandle func2 = factory->NewJSFunction(globalEnv); + func2.GetTaggedValue().GetTaggedObject()->GetClass()->SetClassConstructor(true); + array1->Set(thread, 2, func2.GetTaggedValue()); + array2->Set(thread, 2, JSTaggedValue(789)); + + JSHandle array = factory->NewTaggedArray(10); + uint32_t slotId = 5; + bool setResult = InvokeCache::SetPolyConstuctCacheSlot( + thread, static_cast(*array), slotId, 3, array1.GetTaggedValue(), array2.GetTaggedValue()); + ASSERT_TRUE(setResult); + JSTaggedValue slot = array->Get(thread, slotId); + ASSERT_TRUE(slot.IsTaggedArray()); + JSHandle slotArray(thread, slot); + ASSERT_EQ(slotArray->Get(thread, 0), func0.GetTaggedValue()); + ASSERT_EQ(slotArray->Get(thread, 1), JSTaggedValue(123)); + ASSERT_EQ(slotArray->Get(thread, 2), func1.GetTaggedValue()); + ASSERT_EQ(slotArray->Get(thread, 3), JSTaggedValue(456)); + ASSERT_EQ(slotArray->Get(thread, 4), func2.GetTaggedValue()); + ASSERT_EQ(slotArray->Get(thread, 5), JSTaggedValue(789)); + ASSERT_EQ(array->Get(thread, slotId + 1), JSTaggedValue::Hole()); +} + +TEST_F(ICInvokeTest, CheckPolyInvokeCache) +{ + auto globalEnv = ecmaVm->GetGlobalEnv(); + auto factory = ecmaVm->GetFactory(); + JSHandle array = factory->NewTaggedArray(6); + + JSHandle func0 = factory->NewJSFunction(globalEnv); + JSHandle func1 = factory->NewJSFunction(globalEnv); + JSHandle func2 = factory->NewJSFunction(globalEnv); + JSHandle func3 = factory->NewJSFunction(globalEnv); + array->Set(thread, 0, func0.GetTaggedValue()); + array->Set(thread, 1, JSTaggedValue(123)); + array->Set(thread, 2, func1.GetTaggedValue()); + array->Set(thread, 3, JSTaggedValue(456)); + array->Set(thread, 4, func2.GetTaggedValue()); + array->Set(thread, 5, JSTaggedValue(789)); + + JSTaggedValue testValue0 = InvokeCache::CheckPolyInvokeCache(array.GetTaggedValue(), func0.GetTaggedValue()); + ASSERT_EQ(testValue0, JSTaggedValue(123)); + JSTaggedValue testValue1 = InvokeCache::CheckPolyInvokeCache(array.GetTaggedValue(), func1.GetTaggedValue()); + ASSERT_EQ(testValue1, JSTaggedValue(456)); + JSTaggedValue testValue2 = InvokeCache::CheckPolyInvokeCache(array.GetTaggedValue(), func2.GetTaggedValue()); + ASSERT_EQ(testValue2, JSTaggedValue(789)); + JSTaggedValue testValue3 = InvokeCache::CheckPolyInvokeCache(array.GetTaggedValue(), func3.GetTaggedValue()); + ASSERT_EQ(testValue3, JSTaggedValue::Hole()); +} +} diff --git a/tests/runtime/irtoc/CMakeLists.txt b/tests/runtime/irtoc/CMakeLists.txt new file mode 100644 index 000000000..2fd563d70 --- /dev/null +++ b/tests/runtime/irtoc/CMakeLists.txt @@ -0,0 +1,34 @@ +# We have issues for arm32 +if (PANDA_TARGET_ARM32) + return() +endif() + +function(ecmascript_irtoc_interpreter_tests) + set(prefix ARG) + set(noValues) + set(singleValues TARGET TEST_NAME) + cmake_parse_arguments(${prefix} + "${noValues}" + "${singleValues}" + "${multiValues}" + ${ARGN}) + set(TEST_OUTPUT ${CMAKE_CURRENT_BINARY_DIR}/${ARG_TEST_NAME}.txt) + set(TEST_BIN ${CMAKE_CURRENT_BINARY_DIR}/${ARG_TEST_NAME}.abc) + set(TEST_PA ${CMAKE_CURRENT_BINARY_DIR}/${ARG_TEST_NAME}.pa) + set(TEST_JS ${CMAKE_CURRENT_SOURCE_DIR}/${ARG_TEST_NAME}.js) + set(RUNTIME_ARGUMENTS --interpreter-type=irtoc --load-runtimes=\"ecmascript\" --gc-type=g1-gc --run-gc-in-place ${TEST_BIN} _GLOBAL::func_main_0) + add_custom_command( + OUTPUT ${TEST_OUTPUT} + DEPENDS ${TEST_JS} + COMMENT "running irtoc ${TEST_NAME} testcase" + COMMAND ${PANDA_RUN_PREFIX} $ ${TEST_JS} --dump-assembly --output ${TEST_BIN} > ${TEST_PA} + COMMAND rm -f ${TEST_OUTPUT} + COMMAND ${PANDA_RUN_PREFIX} $ ${RUNTIME_ARGUMENTS} > ${TEST_OUTPUT} 2>&1 || (cat ${TEST_OUTPUT} && false) + ) + add_custom_target(${ARG_TARGET} DEPENDS ${TEST_OUTPUT}) + add_dependencies(${ARG_TARGET} es2panda ark) + add_dependencies(tests ${ARG_TARGET}) +endfunction() + +add_subdirectory(basic) +add_subdirectory(advanced) diff --git a/tests/runtime/irtoc/advanced/CMakeLists.txt b/tests/runtime/irtoc/advanced/CMakeLists.txt new file mode 100644 index 000000000..954808f39 --- /dev/null +++ b/tests/runtime/irtoc/advanced/CMakeLists.txt @@ -0,0 +1,8 @@ +# Huawei Technologies Co.,Ltd. + +set(TARGET ecmascript_tests_irtoc_advanced_arith) + +ecmascript_irtoc_interpreter_tests( + TARGET ${TARGET} + TEST_NAME advanced + ) \ No newline at end of file diff --git a/tests/runtime/irtoc/advanced/advanced.js b/tests/runtime/irtoc/advanced/advanced.js new file mode 100644 index 000000000..27ec082dc --- /dev/null +++ b/tests/runtime/irtoc/advanced/advanced.js @@ -0,0 +1,189 @@ +let res = 99; +let expected = 0; + +for (let i = 0; i < 12; i++) { + res -= 1.5 * i; +} + +if (res != expected) + throw "res = " + res + ", expected = " + expected; + +res = 0; +expected = 4995; + +for (let i = 10; i > -101; i--) { + res -= i; +} + +if (res != expected) + throw "res = " + res + ", expected = " + expected; + +res = 1; +expected = 131.6818944; + +for (let i = 1; i < 10; i++) { + res *= 0.5 * i; + res *= 0.2 * i; +} + +if (res - expected > 0.00001 || expected - res > 0.00001) + throw "res = " + res + ", expected = " + expected; + +res = 131681894400; +expected = 1; + +for (let i = 1; i < 10; i++) { + res /= i / 0.2; + res /= i / 0.5; + res *= 10; +} + +if (res - expected > 0.00001 || expected - res > 0.00001) + throw "res = " + res + ", expected = " + expected; + + +res = 99; +expected = 0; + +for (let i = 0; i < 12; i++) { + res += - 1.5 * i; + } + +if (res != expected) + throw "res = " + res + ", expected = " + expected; + + +res = -2147483640; +expected = -2147483740; + +res = res - 100; + +if (res != expected) + throw "res = " + res + ", expected = " + expected; + +res = 111111111 * 111111111; +expected = 12345678987654321; + +if (res != expected) + throw "res = " + res + ", expected = " + expected + + +res = 0; +res = -res; +expected = 0; + +if (res != expected) + throw "res = " + res + ", expected = " + expected + +res = -2147483648; +res = -res; +expected = 2147483648; + +if (res != expected) + throw "res = " + res + ", expected = " + expected + +function verify(res, expected, msg) { + if (res == expected) return; + let err = "FAIL: " + msg + ":\n\texpected: " + expected + "\n\tgot : " + res; + throw err; +} + +// shl2dyn, shr2dyn +function test_shifts() { + verify(3 << 2, 12, "shl i32"); + verify((-3) << 2, -12, "shl i32 neg"); + verify(1.6 << 2, 4, "shl f64"); + verify((-1.6) << 2, -4, "shl f64 neg"); + verify(NaN << 2, 0, "shl nan 1"); + verify(6 << NaN, 6, "shl nan 2"); + verify(((1 / 0) / (1 / 0)) << 1, 0, "shl qnan"); + verify(Infinity << 2, 0, "shl inf 1"); + verify(7 << Infinity, 7, "shl inf 2"); + verify(1000000000 << 3, -589934592, "shl i32 ovf"); + verify(10000000000 << 3, -1604378624, "shl f64 ovf"); + verify(12 >> 2, 3, "shr i32"); + verify((-12) >> 2, -3, "shr i32 neg"); + verify(16.6 >> 2, 4, "shr f64"); + verify((-16.6) >> 2, -4, "shr f64 neg"); + verify(NaN >> 2, 0, "shr nan 1"); + verify(6 >> NaN, 6, "shr nan 2"); + verify(((1 / 0) / (1 / 0)) >> 1, 0, "shr qnan"); + verify(Infinity >> 2, 0, "shr inf 1"); + verify(7 >> Infinity, 7, "shr inf 2"); + verify(2147483648 >> 1, -1073741824, "shr left i32 ovf"); + verify(10000000000 >> 3, 176258176, "shr left f64 ovf"); +} +test_shifts(); + +// negate +function test_negate() { + verify(!0, true, "negate i32 0"); + verify(!1, false, "negate i32 1"); + verify(!false, true, "negate i32 false"); + verify(!true, false, "negate i32 true"); + verify(!10, false, "negate i32 10"); + verify(!0.1, false, "negate f64 0.1"); + verify(!0.0000000000000001, false, "negate f64 0.1"); + verify(!NaN, true, "negate NaN"); + verify(!undefined, true, "negate undefined"); + verify(!(2147483648), false, "negate i32 ovf"); + verify(!"", true, "negate String with length=0"); + verify(!"f", false, "negate String with length!=0"); + verify(!Infinity, false, "negate Infinity"); +} +test_negate(); + +// ldlexenvdyn, ldlexvardyn +function capt(x1) { + return function(x2) { + return function() { return x1 + x2; }; + } +} +res = capt(1)(2)(); +expected = 3; + +if (res != expected) { + throw "res = " + res + ", expected = " + expected; +} + +// add2dyn +function test_add() { + verify(1 + 2, 3, "add i32"); + verify(-12345678 + 23456789, 11111111, "add i32 sgne"); + verify(2147483647 + 1, 2147483648, "add i32 ovf"); + verify(3.14 + 3.14, 6.28, "add fp"); + verify("aaa" + "bbb", "aaabbb", "add str"); + verify(Infinity + 1, Infinity, "add Infinity left"); + verify(1 + (-Infinity), -Infinity, "add -Infinity right"); +} +test_add(); + +// sub2dyn +function test_sub() { + verify(1 - 2, -1, "sub i32"); + verify(12345678 - 23456789, -11111111, "sub i32 sgne"); + verify((-(2147483647)) - 2, -2147483649, "sub i32 ovf"); + verify(Infinity - 1, Infinity, "sub Infinity left"); + verify(1 - Infinity, -Infinity, "sub Infinity right"); +} +test_sub(); + +function test_1() { + verify(0xffffffff & ~1, -2, "And: double, smi"); + verify(~1 & 0xffffffff, -2, "And: smi, double"); + verify(0xffffffff & 0xaaaaaaaa, -1431655766, "And: double, double"); + verify(0xffffffff0 & ~1, -16, ""); + + verify(-1 | 2, -1, "Or: negative smi, smi"); + verify(-1 | -2, -1, "Or: negative smi, negative smi"); + verify(0xffffffff | ~1, -1, "Or: double, smi"); + verify(~1 | 0xffffffff, -1, "Or: smi, double"); + verify(0xffffffff | 0xaaaaaaaa, -1, "Or: double, double"); + + verify(-1 ^ 2, -3, "Xor: negative smi, smi"); + verify(-1 ^ -2, 1, "Xor: negative smi, negative smi"); + verify(0xffffffff ^ ~1, 1, "Xor: double, smi"); + verify(~1 ^ 0xffffffff, 1, "Xor: smi, double"); + verify(0xffffffff ^ 0xaaaaaaaa, 1431655765, "Xor: double, double"); +} +test_1(); \ No newline at end of file diff --git a/tests/runtime/irtoc/basic/CMakeLists.txt b/tests/runtime/irtoc/basic/CMakeLists.txt new file mode 100644 index 000000000..b6171af19 --- /dev/null +++ b/tests/runtime/irtoc/basic/CMakeLists.txt @@ -0,0 +1,8 @@ +# Huawei Technologies Co.,Ltd. + +set(TARGET ecmascript_tests_irtoc_basic_arith) + +ecmascript_irtoc_interpreter_tests( + TARGET ${TARGET} + TEST_NAME basic + ) \ No newline at end of file diff --git a/tests/runtime/irtoc/basic/basic.js b/tests/runtime/irtoc/basic/basic.js new file mode 100644 index 000000000..d920e3be7 --- /dev/null +++ b/tests/runtime/irtoc/basic/basic.js @@ -0,0 +1,189 @@ +let res = 0; +let expected = 5050; + +// lessdyn, add2dyn, incdyn +for (let i = 0; i < 101; i++) { + res += i; +} + +if (res != expected) + throw "res = " + res + ", expected = " + expected; + +res = 0; +expected = 5005; + +// greatereqdyn, add2dyn, decdyn +for (let i = 100; i >= 10; i--) { + res += i; +} + +if (res != expected) + throw "res = " + res + ", expected = " + expected; + +res = 5050; +expected = 0; + +// sub2dyn, lesseqdyn +for (let i = 0; i <= 100; i++) { + res -= i; +} + +if (res != expected) + throw "res = " + res + ", expected = " + expected; + +res = 0; +expected = 9; + +// mod2dyn +for (let i = 100; i > 10; i--) { + if (i % 11 == 0) { + res++; + } +} + +if (res != expected) + throw "res = " + res + ", expected = " + expected; + +res = 1; +expected = 479001600; + +// mul2dyn +for (let i = 1; i < 13; i++) { + res = res * i; +} + +if (res != expected) + throw "res = " + res + ", expected = " + expected; + + +let left = 0; +let right = false; + +if (left === right) { + throw "left = " + left + ", right = " + right; +} + +if (left != right) { + throw "left = " + left + ", right = " + right; +} + +left = '0' + +if (left != right) { + throw "left = " + left + ", right = " + right; +} + +// ldundefined, ldnull +left = undefined +right = null + +if (left != right) { + throw "left = " + left + ", right = " + right; +} + +left = undefined +right = undefined + +// strictnoteq +if (left !== right) { + throw "left = " + left + ", right = " + right; +} + +left = 100 +right = 10 + +// div2dyn +res = left / right +expected = 10 + +if (res != expected) { + throw "res = " + res + ", expected = " + expected; +} + +left = 1 +right = 2 + +// div2dyn +res = left / right +expected = 0.5 + +if (res != expected) { + throw "res = " + res + ", expected = " + expected; +} + +left = true +right = true + +if (left !== right) { + throw "left = " + left + ", right = " + right; +} + +left = 3 +right = 4 +expected = 81 + +// expdyn +res = left ** right + +if (res != expected) { + throw "res = " + res + ", expected = " + expected; +} + +// shl2dyn, shr2dyn +res = left << right +expected = 48 + +if (res != expected) { + throw "res = " + res + ", expected = " + expected; +} + +res = left >> right +expected = 0 + +if (res != expected) { + throw "res = " + res + ", expected = " + expected; +} + +res = 100 >> 3 +expected = 12 + +if (res != expected) { + throw "res = " + res + ", expected = " + expected; +} + +// ldlexenvdyn, ldlexvardyn +function capt(x) { + return function(a) { return a + x;}; +} + +res = capt(1)(2); +expected = 3; + +if (res != expected) { + throw "res = " + res + ", expected = " + expected; +} + +// ecma.negate +let x = 0; +res = !x; +expected = true; + +if (res != expected) { + throw "res = " + res + ", expected = " + expected; +} + +x = 10; +res = !x; +expected = false; + +if (res != expected) { + throw "res = " + res + ", expected = " + expected; +} + +x = 0.1; +res = !x; +expected = false; + +if (res != expected) { + throw "res = " + res + ", expected = " + expected; +} \ No newline at end of file diff --git a/tests/runtime/mem/CMakeLists.txt b/tests/runtime/mem/CMakeLists.txt new file mode 100644 index 000000000..e564d75d0 --- /dev/null +++ b/tests/runtime/mem/CMakeLists.txt @@ -0,0 +1,42 @@ +# 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. + +cmake_minimum_required(VERSION 3.10) + +set(MEMTESTS_JS_SOURCES + weakContainers.js +) + +panda_add_gtest( + NO_CORES + NAME arkruntime4ecmascript_mem_weak_tests + SOURCES + weak_containers_test.cpp + LIBRARIES + arkruntime arkassembler + SANITIZERS + ${PANDA_SANITIZERS_LIST} +) + +foreach(JS_SOURCE ${MEMTESTS_JS_SOURCES}) + string(REGEX REPLACE "[.]js$" ".abc" ABC_FILE ${JS_SOURCE}) + add_custom_command( + OUTPUT ${ABC_FILE} + COMMAND ${PANDA_RUN_PREFIX} $ ${CMAKE_CURRENT_SOURCE_DIR}/${JS_SOURCE} --output ${ABC_FILE} + ) + add_custom_target(es2panda_convert_${JS_SOURCE} + DEPENDS ${ABC_FILE}) + # TODO(maksenov): target with _gtests postfix for test run is hidden now. + # Need to add interface to specify test run dependencies + add_dependencies(arkruntime4ecmascript_mem_weak_tests_gtests es2panda_convert_${JS_SOURCE}) +endforeach() diff --git a/tests/runtime/mem/g1gc_barrier_test.cpp b/tests/runtime/mem/g1gc_barrier_test.cpp new file mode 100644 index 000000000..3112911e4 --- /dev/null +++ b/tests/runtime/mem/g1gc_barrier_test.cpp @@ -0,0 +1,119 @@ +/** + * Copyright (c) Huawei Technologies Co., Ltd. 2019-2021. All rights reserved. + */ + +#include + +#include "runtime/mem/gc/gc.h" +#include "runtime/include/runtime.h" +#include "runtime/mem/heap_manager.h" +#include "runtime/include/thread_scopes.h" +#include "plugins/ecmascript/runtime/js_thread.h" +#include "plugins/ecmascript/runtime/ecma_string.h" +#include "plugins/ecmascript/runtime/tagged_array.h" +#include "plugins/ecmascript/runtime/js_hclass-inl.h" +#include "plugins/ecmascript/runtime/global_env_constants-inl.h" + +namespace panda::ecmascript { +class G1GCBarrierTest : public testing::Test { +public: + explicit G1GCBarrierTest(size_t promotion_region_alive_rate = 100) + { + RuntimeOptions options; + options.SetBootClassSpaces({"ecmascript"}); + options.SetRuntimeType("ecmascript"); + options.SetGcType("g1-gc"); + options.SetRunGcInPlace(true); + options.SetCompilerEnableJit(false); + options.SetGcWorkersCount(0); + options.SetG1PromotionRegionAliveRate(promotion_region_alive_rate); + options.SetGcTriggerType("debug-never"); + options.SetShouldLoadBootPandaFiles(false); + options.SetShouldInitializeIntrinsics(false); + + Runtime::Create(options); + + thread_ = JSThread::GetCurrent(); + const GlobalEnvConstants *globalConst = thread_->GlobalConstants(); + string_class_ = JSHClass::Cast(globalConst->GetStringClass().GetTaggedObject()); + array_class_ = JSHClass::Cast(globalConst->GetArrayClass().GetTaggedObject()); + } + + ~G1GCBarrierTest() + { + Runtime::Destroy(); + } + + ObjectHeader *AllocObject() + { + EcmaVM *vm = EcmaVM::Cast(thread_->GetVM()); + return EcmaString::CreateEmptyString(vm); + } + + TaggedArray *AllocArray(size_t length) + { + size_t size = TaggedArray::ComputeSize(JSTaggedValue::TaggedTypeSize(), length); + mem::HeapManager *heap_manager = thread_->GetVM()->GetHeapManager(); + auto *array = reinterpret_cast(heap_manager->AllocateObject( + array_class_->GetHClass(), size, DEFAULT_ALIGNMENT, thread_)); + array->SetLength(length); + return array; + } + + JSThread *thread_; + JSHClass *array_class_; + JSHClass *string_class_; +}; + +class ConcurrentMarkListener : public mem::GCListener { +public: + ConcurrentMarkListener(G1GCBarrierTest *test, JSHandle array, + JSHandle object, JSHandle replacement) + : test_(test), array_(array), object_(object), replacement_(replacement) + { + } + + void GCPhaseStarted(mem::GCPhase phase) override + { + if (phase != mem::GCPhase::GC_PHASE_MARK) { + return; + } + has_concurrent_mark = true; + JSThread *thread = test_->thread_; + array_->Set(thread, 0, JSTaggedValue::Undefined()); + array_->Set(thread, 1, replacement_.GetTaggedValue()); + + PandaVector *pre_buff = thread->GetPreBuff(); + EXPECT_TRUE(pre_buff != nullptr); + if (pre_buff == nullptr) { + return; + } + EXPECT_EQ(1U, pre_buff->size()); + EXPECT_EQ(object_.GetObject(), pre_buff->front()); + } + + G1GCBarrierTest *test_; + JSHandle array_; + JSHandle object_; + JSHandle replacement_; + bool has_concurrent_mark = false; +}; + +TEST_F(G1GCBarrierTest, TestPreBarrier) +{ + [[maybe_unused]] EcmaHandleScope scope(thread_); + mem::GC *gc = thread_->GetVM()->GetGC(); + JSHandle array(thread_, AllocArray(2)); + JSHandle obj(thread_, AllocObject()); + JSHandle replacement(thread_, AllocObject()); + array->Set(thread_, 0, obj.GetTaggedValue()); // test object -> undefined + array->Set(thread_, 1, JSTaggedValue::Undefined()); // test undefined -> object + + ConcurrentMarkListener listener(this, array, obj, replacement); + gc->AddListener(&listener); + + GCTask task(GCTaskCause::HEAP_USAGE_THRESHOLD_CAUSE); // trigger concurrent marking + task.Run(*gc); + ASSERT_TRUE(listener.has_concurrent_mark); +} +} // namespace panda::mem diff --git a/tests/runtime/mem/object_helpers_test.cpp b/tests/runtime/mem/object_helpers_test.cpp new file mode 100644 index 000000000..c9245f6aa --- /dev/null +++ b/tests/runtime/mem/object_helpers_test.cpp @@ -0,0 +1,174 @@ +/** + * Copyright (c) Huawei Technologies Co., Ltd. 2019-2021. All rights reserved. + */ + +#include + +#include "plugins/ecmascript/runtime/ecma_vm.h" +#include "plugins/ecmascript/runtime/js_array.h" +#include "plugins/ecmascript/runtime/js_handle.h" +#include "plugins/ecmascript/runtime/object_factory.h" +#include "runtime/include/runtime.h" +#include "runtime/include/panda_vm.h" +#include "runtime/mem/object_helpers-inl.h" + +namespace panda::ecmascript { + +inline std::string separator() +{ +#ifdef _WIN32 + return "\\"; +#else + return "/"; +#endif +} + +class DynamicObjectHelpersTest : public testing::Test { +public: + DynamicObjectHelpersTest() + { + RuntimeOptions options; + options.SetBootClassSpaces({"ecmascript"}); + options.SetRuntimeType("ecmascript"); + options.SetGcType("epsilon"); + options.SetGcTriggerType("debug-never"); + auto exec_path = panda::os::file::File::GetExecutablePath(); + std::string panda_std_lib = + exec_path.Value() + separator() + ".." + separator() + "pandastdlib" + separator() + "pandastdlib.bin"; + options.SetBootPandaFiles({panda_std_lib}); + + Runtime::Create(options); + + vm_ = EcmaVM::Cast(Runtime::GetCurrent()->GetPandaVM()); + thread_ = vm_->GetAssociatedJSThread(); + factory_ = vm_->GetFactory(); + handle_scope_ = new EcmaHandleScope(thread_); + initial_dyn_class_ = factory_->NewEcmaDynClassClass(nullptr, JSHClass::SIZE, JSType::HCLASS); + JSHClass *dynclass = reinterpret_cast(initial_dyn_class_.GetTaggedValue().GetTaggedObject()); + dynclass->SetClass(dynclass); + } + + ~DynamicObjectHelpersTest() + { + delete handle_scope_; + Runtime::Destroy(); + } + + JSHandle NewDynClass() + { + return factory_->NewEcmaDynClass(*initial_dyn_class_, JSHClass::SIZE, JSType::HCLASS, HClass::HCLASS); + } + +protected: + EcmaHandleScope *handle_scope_; + EcmaVM *vm_; + JSThread *thread_; + ObjectFactory *factory_; + JSHandle initial_dyn_class_; +}; + +TEST_F(DynamicObjectHelpersTest, TestDynClass) +{ + JSHandle dyn_class = NewDynClass(); + JSHClass *hclass = *dyn_class; + + JSHandle proto = factory_->NewEmptyJSObject(); + hclass->SetProto(thread_, proto); + JSHandle attributes = factory_->NewEmptyJSObject(); + hclass->SetLayout(thread_, attributes); + JSHandle transitions = factory_->NewEmptyJSObject(); + hclass->SetTransitions(thread_, transitions); + JSHandle parent = factory_->NewEmptyJSObject(); + hclass->SetParent(thread_, parent); + JSHandle proto_changed_cell = factory_->NewEmptyJSObject(); + hclass->SetProtoChangeMarker(thread_, proto_changed_cell); + JSHandle proto_change_details = factory_->NewEmptyJSObject(); + hclass->SetProtoChangeDetails(thread_, proto_change_details); + JSHandle enum_cache = factory_->NewEmptyJSObject(); + hclass->SetEnumCache(thread_, enum_cache); + + PandaVector> objects_seen = { + {proto.GetObject(), JSHClass::PROTOTYPE_OFFSET}, + {attributes.GetObject(), JSHClass::LAYOUT_OFFSET}, + {transitions.GetObject(), JSHClass::TRANSTIONS_OFFSET}, + {parent.GetObject(), JSHClass::PARENT_OFFSET}, + {proto_changed_cell.GetObject(), JSHClass::VALIDITY_CELL_OFFSET}, + {proto_change_details.GetObject(), JSHClass::PROTOTYPE_INFO_OFFSET}, + {enum_cache.GetObject(), JSHClass::ENUM_CACHE_OFFSET}}; + auto handler = [&objects_seen]([[maybe_unused]] ObjectHeader *obj, ObjectHeader *field, uint32_t offset, + [[maybe_unused]] bool is_volatile) { + auto it = + std::find_if(objects_seen.begin(), objects_seen.end(), + [field](const std::pair &entry) { return entry.first == field; }); + EXPECT_NE(objects_seen.end(), it); + if (it != objects_seen.end()) { + EXPECT_NE(nullptr, it->first); + EXPECT_EQ(it->second, offset); + it->first = nullptr; + } + return true; + }; + mem::GCDynamicObjectHelpers::TraverseAllObjectsWithInfo(dyn_class.GetObject(), handler); + size_t count = 0; + for (auto entry : objects_seen) { + ASSERT_EQ(nullptr, entry.first) << "Object " << count << " was not seen"; + ++count; + } +} + +TEST_F(DynamicObjectHelpersTest, TestDynObject) +{ + JSHandle object = factory_->NewEmptyJSObject(); + JSHandle key = factory_->NewFromStdString("key"); + JSHandle value = factory_->NewEmptyJSObject(); + JSObject::SetProperty(thread_, object, JSHandle(thread_, key.GetTaggedValue()), + JSHandle(thread_, value.GetTaggedValue())); + + PandaQueue queue; + bool value_seen = false; + auto handler = [&value, &value_seen, &queue]([[maybe_unused]] ObjectHeader *obj, ObjectHeader *field, + [[maybe_unused]] uint32_t offset, [[maybe_unused]] bool is_volatile) { + if (field == value.GetObject()) { + value_seen = true; + } else { + queue.push(field); + } + return true; + }; + queue.push(object.GetObject()); + while (!queue.empty()) { + ObjectHeader *front = queue.front(); + queue.pop(); + mem::GCDynamicObjectHelpers::TraverseAllObjectsWithInfo(front, handler); + } + ASSERT_TRUE(value_seen); +} + +TEST_F(DynamicObjectHelpersTest, TestDynArray) +{ + JSHandle object = factory_->NewJSArray(); + JSHandle value = factory_->NewEmptyJSObject(); + JSArray::FastSetPropertyByValue(thread_, JSHandle(thread_, object.GetTaggedValue()), 0, + JSHandle(thread_, value.GetTaggedValue())); + + PandaQueue queue; + bool value_seen = false; + auto handler = [&value, &value_seen, &queue]([[maybe_unused]] ObjectHeader *obj, ObjectHeader *field, + [[maybe_unused]] uint32_t offset, [[maybe_unused]] bool is_volatile) { + if (field == value.GetObject()) { + value_seen = true; + } else { + queue.push(field); + } + return true; + }; + queue.push(object.GetObject()); + while (!queue.empty()) { + ObjectHeader *front = queue.front(); + queue.pop(); + mem::GCDynamicObjectHelpers::TraverseAllObjectsWithInfo(front, handler); + } + ASSERT_TRUE(value_seen); +} + +} // namespace panda::ecmascript diff --git a/tests/runtime/mem/weakContainers.js b/tests/runtime/mem/weakContainers.js new file mode 100644 index 000000000..d13e1b6b1 --- /dev/null +++ b/tests/runtime/mem/weakContainers.js @@ -0,0 +1,85 @@ +/* + * 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. + */ + + +var counter = 0 +function assert(result) { + if (result != true) { + throw new Error("Failed assertion №" + counter) + } + counter += 1 +} + +function fillMapWithRefs(weakMap, strongMap, count) { + for (let i = 0; i < count; ++i) { + let obj = { field: i } + weakMap.set(obj, i) + strongMap.set(obj, i) + } +} + +function validateMap(weakMap) { + let obj = {testObject : 42} + weakMap.set(obj, "value") + if (!weakMap.has(obj)) { + return false + } + if (weakMap.get(obj) != "value") { + return false + } + weakMap.delete(obj) + return !weakMap.has(obj) +} + +function fillSetWithRefs(weakSet, strongSet, count) { + for (let i = 0; i < count; ++i) { + let obj = { field: i } + weakSet.add(obj) + strongSet.add(obj) + } +} + +function validateSet(weakSet) { + let obj = {testObject : 42} + weakSet.add(obj, "value") + if (!weakSet.has(obj)) { + return false + } + weakSet.delete(obj) + return !weakSet.has(obj) +} + +let weakMap = new WeakMap(); +let strongMap = new Map() +fillMapWithRefs(weakMap, strongMap, 20) +assert(validateMap(weakMap)) +assert(getContainerSize(weakMap) == 20) + +collectGarbage() +assert(getContainerSize(weakMap) == 20) + +strongMap.clear() +collectGarbage() +assert(getContainerSize(weakMap) == 0) +assert(validateMap(weakMap)) + +let weakSet = new WeakSet() +let strongSet = new Set() +fillSetWithRefs(weakSet, strongSet, 1) +assert(getContainerSize(weakSet) == 1) +strongSet.clear() +collectGarbage() +assert(getContainerSize(weakSet) == 0) +assert(validateSet(weakSet)) diff --git a/tests/runtime/mem/weak_containers_test.cpp b/tests/runtime/mem/weak_containers_test.cpp new file mode 100644 index 000000000..d24f766a5 --- /dev/null +++ b/tests/runtime/mem/weak_containers_test.cpp @@ -0,0 +1,118 @@ +/* + * 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 "plugins/ecmascript/tests/runtime/common/test_helper.h" + +#include + +#include "plugins/ecmascript/runtime/ecma_vm.h" +#include "plugins/ecmascript/runtime/js_thread.h" +#include "plugins/ecmascript/runtime/napi/include/jsnapi.h" +#include "plugins/ecmascript/runtime/napi/jsnapi_helper-inl.h" +#include "plugins/ecmascript/runtime/object_factory.h" +#include "plugins/ecmascript/runtime/js_weak_container.h" +#include "plugins/ecmascript/runtime/js_tagged_value-inl.h" + +#include "libpandabase/os/thread.h" + +using namespace panda; +using namespace panda::ecmascript; + +namespace panda::test { +class WeakContainersTests : public testing::Test { +public: + void SetUp() override + { + RuntimeOption option; + option.SetGcType(RuntimeOption::GC_TYPE::G1_GC); + option.SetLogLevel(RuntimeOption::LOG_LEVEL::ERROR); + vm_ = JSNApi::CreateJSVM(option); + ASSERT_TRUE(vm_ != nullptr) << "Cannot create Runtime"; + thread_ = vm_->GetAssociatedJSThread(); + vm_->GetFactory()->SetTriggerGc(true); + } + + void TearDown() override + { + vm_->GetFactory()->SetTriggerGc(false); + JSNApi::DestroyJSVM(vm_); + } + +protected: + JSThread *thread_ = nullptr; + EcmaVM *vm_ = nullptr; +}; + +Local RunGC(EcmaVM *vm, Local, const Local *, int32_t, void *) +{ + vm->GetGC()->WaitForGC(GCTask(GCTaskCause::EXPLICIT_CAUSE)); + return JSValueRef::Undefined(vm); +} + +Local GetContainerSize(EcmaVM *vm, Local, const Local *args, int32_t argc, void *) +{ + if (argc != 1) { + LOG_ECMA(FATAL) << "Unexpected number of arguments: " << argc; + } + JSHandle value = JSNApiHelper::ToJSHandle(args[0]); + if (!value->IsHeapObject()) { + LOG_ECMA(FATAL) << "Argument must be heap object"; + } + auto hclass = value->GetTaggedObject()->GetClass(); + if (!hclass->IsJSWeakMap() && ! hclass->IsJSWeakSet()) { + LOG_ECMA(FATAL) << "Argument is not weak container"; + } + if (hclass->IsJSWeakMap()) { + auto *weakMap = JSWeakMap::Cast(value->GetHeapObject()); + return IntegerRef::New(vm, weakMap->GetSize()); + } else if (hclass->IsJSWeakSet()) { + auto *weakSet = JSWeakSet::Cast(value->GetHeapObject()); + return IntegerRef::New(vm, weakSet->GetSize()); + } + UNREACHABLE(); +} + +bool RegisterFunction(EcmaVM *vm, Local &globalObject, panda::FunctionCallback callback, const char *name) +{ + Local functionRef = FunctionRef::New(vm, callback, nullptr); + if (functionRef.IsEmpty()) { + return false; + } + Local key = StringRef::NewFromUtf8(vm, name); + bool result = globalObject->Set(vm, key, functionRef); + return result; +} + +TEST_F(WeakContainersTests, WeakContainers) { + const char mainFunc[] = "_GLOBAL::func_main_0"; + JSExecutionScope executionScope(vm_); + LocalScope scope(vm_); + + Local globalObject = JSNApi::GetGlobalObject(vm_); + + ASSERT_TRUE(RegisterFunction(vm_, globalObject, RunGC, "collectGarbage")); + ASSERT_TRUE(RegisterFunction(vm_, globalObject, GetContainerSize, "getContainerSize")); + + const char fileName[] = "weakContainers.abc"; + bool ret2 = JSNApi::Execute(vm_, fileName, mainFunc); + ASSERT_EQ(ret2, true); + Local exception = JSNApi::GetUncaughtException(vm_); + if (!exception.IsEmpty() && !exception->IsHole()) { + Local msgKey = StringRef::NewFromUtf8(vm_, "message"); + auto msg = exception->Get(vm_, msgKey)->ToString(vm_); + FAIL() << msg->ToString(); + } +} +} diff --git a/tests/runtime/napi/CMakeLists.txt b/tests/runtime/napi/CMakeLists.txt new file mode 100644 index 000000000..51ce49c05 --- /dev/null +++ b/tests/runtime/napi/CMakeLists.txt @@ -0,0 +1,28 @@ +# Huawei Technologies Co.,Ltd. + +cmake_minimum_required(VERSION 3.10) + +project(jsnapi_tests) + +set(JSNAPI_TESTS_SOURCES + jsnapi_tests.cpp +) + +panda_add_gtest( + NO_CORES + NAME jsnapi_tests + SOURCES + ${JSNAPI_TESTS_SOURCES} + LIBRARIES + arkruntime + INCLUDE_DIRS + ${PANDA_ECMASCRIPT_PLUGIN_SOURCE}/tests/runtime/common + SANITIZERS + ${PANDA_SANITIZERS_LIST} +) +if(TARGET jsnapi_tests) + target_include_directories(jsnapi_tests + PUBLIC ${CMAKE_CURRENT_SOURCE_DIR} + ) + target_compile_options(jsnapi_tests PUBLIC "-Wno-ignored-attributes") +endif() diff --git a/tests/runtime/napi/jsnapi_tests.cpp b/tests/runtime/napi/jsnapi_tests.cpp new file mode 100644 index 000000000..f434863e5 --- /dev/null +++ b/tests/runtime/napi/jsnapi_tests.cpp @@ -0,0 +1,869 @@ +/* + * Copyright (c) 2021-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 "plugins/ecmascript/tests/runtime/common/test_helper.h" + +#include + +#include "plugins/ecmascript/runtime/builtins/builtins_function.h" +#include "plugins/ecmascript/runtime/global_env.h" +#include "plugins/ecmascript/runtime/ecma_vm.h" +#include "plugins/ecmascript/runtime/js_thread.h" +#include "plugins/ecmascript/runtime/napi/include/jsnapi.h" +#include "plugins/ecmascript/runtime/napi/jsnapi_helper-inl.h" +#include "plugins/ecmascript/runtime/object_factory.h" + +#include "libpandabase/os/thread.h" + +using namespace panda; +using namespace panda::ecmascript; + +namespace panda::test { +class JSNApiTests : public testing::Test { +public: + static void SetUpTestCase() + { + GTEST_LOG_(INFO) << "SetUpTestCase"; + } + + static void TearDownTestCase() + { + GTEST_LOG_(INFO) << "TearDownCase"; + } + + void SetUp() override + { + RuntimeOption option; + option.SetGcType(RuntimeOption::GC_TYPE::STW); + option.SetLogLevel(RuntimeOption::LOG_LEVEL::ERROR); + vm_ = JSNApi::CreateJSVM(option); + ASSERT_TRUE(vm_ != nullptr) << "Cannot create Runtime"; + thread_ = vm_->GetAssociatedJSThread(); + vm_->GetFactory()->SetTriggerGc(true); + } + + void TearDown() override + { + vm_->GetFactory()->SetTriggerGc(false); + JSNApi::DestroyJSVM(vm_); + } + + Local NewObjectByConstructor(Local ctor) + { + ScopedManagedCodeThread scope(thread_); + auto factory = vm_->GetFactory(); + JSHandle ctorHandle = JSNApiHelper::ToJSHandle(ctor); + JSHandle obj = factory->NewJSObjectByConstructor(JSHandle::Cast(ctorHandle), ctorHandle); + return JSNApiHelper::ToLocal(JSHandle(obj)); + } + + Local NewFunctionByHClass(JSHandle hclass, JSMethod *method) + { + ScopedManagedCodeThread scope(thread_); + auto factory = vm_->GetFactory(); + JSHandle protoFunc = factory->NewJSFunctionByDynClass(method, hclass); + return JSNApiHelper::ToLocal(JSHandle(protoFunc)); + } + + Local GetMethod(Local obj, Local key) + { + JSHandle val = JSObject::GetMethod(thread_, JSNApiHelper::ToJSHandle(obj), + JSNApiHelper::ToJSHandle(key)); + return JSNApiHelper::ToLocal(val); + } + +protected: + JSThread *thread_ = nullptr; + EcmaVM *vm_ = nullptr; +}; + +Local FunctionCallback(EcmaVM *vm, Local, const Local *, int32_t length, void *) +{ + EscapeLocalScope scope(vm); + return scope.Escape(ArrayRef::New(vm, length)); +} + +void ThreadCheck(const EcmaVM *vm) +{ + EXPECT_TRUE(vm->GetAssociatedJSThread()->GetId() != os::thread::GetCurrentThreadId()); +} + +TEST_F(JSNApiTests, GetGlobalObject) +{ + LocalScope scope(vm_); + Local globalObject = JSNApi::GetGlobalObject(vm_); + ASSERT_FALSE(globalObject.IsEmpty()); + ASSERT_TRUE(globalObject->IsObject()); +} + +TEST_F(JSNApiTests, ThreadIdCheck) +{ + EXPECT_TRUE(vm_->GetAssociatedJSThread()->GetId() == os::thread::GetCurrentThreadId()); + std::thread testThread(ThreadCheck, vm_); + testThread.join(); +} + +TEST_F(JSNApiTests, RegisterFunction) +{ + LocalScope scope(vm_); + Local callback = FunctionRef::New(vm_, FunctionCallback, nullptr); + ASSERT_TRUE(!callback.IsEmpty()); + std::vector> arguments; + arguments.emplace_back(JSValueRef::Undefined(vm_)); + Local result = callback->Call(vm_, JSValueRef::Undefined(vm_), arguments.data(), arguments.size()); + ASSERT_TRUE(result->IsArray(vm_)); + Local array(result); + ASSERT_EQ(array->Length(vm_), arguments.size()); +} + +TEST_F(JSNApiTests, GetProperty) +{ + LocalScope scope(vm_); + Local globalObject = JSNApi::GetGlobalObject(vm_); + ASSERT_FALSE(globalObject.IsEmpty()); + ASSERT_TRUE(globalObject->IsObject()); + + Local key = StringRef::NewFromUtf8(vm_, "Number"); + Local property = globalObject->Get(vm_, key); + ASSERT_TRUE(property->IsFunction()); +} + +TEST_F(JSNApiTests, SetProperty) +{ + LocalScope scope(vm_); + Local globalObject = JSNApi::GetGlobalObject(vm_); + ASSERT_FALSE(globalObject.IsEmpty()); + ASSERT_TRUE(globalObject->IsObject()); + + Local property = ArrayRef::New(vm_, 3); // 3 : length + ASSERT_TRUE(property->IsArray(vm_)); + ASSERT_EQ(property->Length(vm_), 3); // 3 : test case of input + + Local key = StringRef::NewFromUtf8(vm_, "Test"); + bool result = globalObject->Set(vm_, key, property); + ASSERT_TRUE(result); + + Local propertyGet = globalObject->Get(vm_, key); + ASSERT_TRUE(propertyGet->IsArray(vm_)); + ASSERT_EQ(Local(propertyGet)->Length(vm_), 3); // 3 : test case of input +} + +TEST_F(JSNApiTests, JsonParser) +{ + LocalScope scope(vm_); + Local globalObject = JSNApi::GetGlobalObject(vm_); + ASSERT_FALSE(globalObject.IsEmpty()); + ASSERT_TRUE(globalObject->IsObject()); + + const char *const test {R"({"orientation": "portrait"})"}; + Local jsonString = StringRef::NewFromUtf8(vm_, test); + + Local result = JSON::Parse(vm_, jsonString); + ASSERT_TRUE(result->IsObject()); + + Local keyString = StringRef::NewFromUtf8(vm_, "orientation"); + Local property = Local(result)->Get(vm_, keyString); + ASSERT_TRUE(property->IsString()); +} + +TEST_F(JSNApiTests, StrictEqual) +{ + LocalScope scope(vm_); + Local origin = StringRef::NewFromUtf8(vm_, "1"); + Local target1 = StringRef::NewFromUtf8(vm_, "1"); + Local target = NumberRef::New(vm_, 1); + + ASSERT_FALSE(origin->IsStrictEquals(vm_, target)); + ASSERT_TRUE(origin->IsStrictEquals(vm_, target1)); +} + +TEST_F(JSNApiTests, InstanceOf) +{ + LocalScope scope(vm_); + Local target = FunctionRef::New(vm_, nullptr, nullptr); + Local origin = ArrayRef::New(vm_, 1); + + ASSERT_FALSE(origin->InstanceOf(vm_, target)); +} + +TEST_F(JSNApiTests, TypeOf) +{ + LocalScope scope(vm_); + Local origin = StringRef::NewFromUtf8(vm_, "1"); + Local typeString = origin->Typeof(vm_); + ASSERT_EQ(typeString->ToString(), "string"); + + Local target = NumberRef::New(vm_, 1); + typeString = target->Typeof(vm_); + ASSERT_EQ(typeString->ToString(), "number"); +} + +TEST_F(JSNApiTests, Symbol) +{ + LocalScope scope(vm_); + Local description = StringRef::NewFromUtf8(vm_, "test"); + Local symbol = SymbolRef::New(vm_, description); + + ASSERT_FALSE(description->IsSymbol()); + ASSERT_TRUE(symbol->IsSymbol()); +} + +TEST_F(JSNApiTests, StringUtf8_001) +{ + LocalScope scope(vm_); + std::string test = "Hello world"; + Local testString = StringRef::NewFromUtf8(vm_, test.c_str()); + + EXPECT_TRUE(testString->Utf8Length() == 12); // 12 : length of testString("Hello World") + char buffer[12]; // 12 : length of testString + EXPECT_TRUE(testString->WriteUtf8(buffer, 12) == 12); // 12 : length of testString("Hello World") + std::string res(buffer); + ASSERT_EQ(res, test); +} + +TEST_F(JSNApiTests, StringUtf8_002) +{ + LocalScope scope(vm_); + std::string test = "年"; + Local testString = StringRef::NewFromUtf8(vm_, test.c_str()); + + EXPECT_TRUE(testString->Utf8Length() == 4); // 4 : length of testString("年") + char buffer[4]; // 4 : length of testString + EXPECT_TRUE(testString->WriteUtf8(buffer, 4) == 4); // 4 : length of testString("年") + std::string res(buffer); + ASSERT_EQ(res, test); +} + +TEST_F(JSNApiTests, ToType) +{ + LocalScope scope(vm_); + Local toString = StringRef::NewFromUtf8(vm_, "-123.3"); + Local toValue(toString); + + ASSERT_EQ(toString->ToNumber(vm_)->Value(), -123.3); // -123 : test case of input + ASSERT_EQ(toString->ToBoolean(vm_)->Value(), true); + ASSERT_EQ(toValue->ToString(vm_)->ToString(), "-123.3"); + ASSERT_TRUE(toValue->ToObject(vm_)->IsObject()); +} + +TEST_F(JSNApiTests, TypeValue) +{ + LocalScope scope(vm_); + Local toString = StringRef::NewFromUtf8(vm_, "-123"); + Local toValue(toString); + + ASSERT_EQ(toString->Int32Value(vm_), -123); // -123 : test case of input + ASSERT_EQ(toString->BooleaValue(), true); + ASSERT_EQ(toString->Uint32Value(vm_), 4294967173); // 4294967173 : test case of input + ASSERT_EQ(toString->IntegerValue(vm_), -123); // -123 : test case of input +} + +TEST_F(JSNApiTests, DefineProperty) +{ + LocalScope scope(vm_); + Local object = ObjectRef::New(vm_); + Local key = StringRef::NewFromUtf8(vm_, "TestKey"); + Local value = ObjectRef::New(vm_); + PropertyAttribute attribute(value, true, true, true); + + ASSERT_TRUE(object->DefineProperty(vm_, key, attribute)); + Local value1 = object->Get(vm_, key); + ASSERT_TRUE(value->IsStrictEquals(vm_, value1)); +} + +TEST_F(JSNApiTests, HasProperty) +{ + LocalScope scope(vm_); + Local object = ObjectRef::New(vm_); + Local key = StringRef::NewFromUtf8(vm_, "TestKey"); + Local value = ObjectRef::New(vm_); + PropertyAttribute attribute(value, true, true, true); + + ASSERT_TRUE(object->DefineProperty(vm_, key, attribute)); + ASSERT_TRUE(object->Has(vm_, key)); +} + +TEST_F(JSNApiTests, DeleteProperty) +{ + LocalScope scope(vm_); + Local object = ObjectRef::New(vm_); + Local key = StringRef::NewFromUtf8(vm_, "TestKey"); + Local value = ObjectRef::New(vm_); + PropertyAttribute attribute(value, true, true, true); + + ASSERT_TRUE(object->DefineProperty(vm_, key, attribute)); + ASSERT_TRUE(object->Delete(vm_, key)); + ASSERT_FALSE(object->Has(vm_, key)); +} + +TEST_F(JSNApiTests, GetProtoType) +{ + LocalScope scope(vm_); + Local function = FunctionRef::New(vm_, nullptr, nullptr); + Local protoType = function->GetPrototype(vm_); + ASSERT_TRUE(protoType->IsObject()); + + Local object = ObjectRef::New(vm_); + protoType = object->GetPrototype(vm_); + ASSERT_TRUE(protoType->IsObject()); +} + +void CheckReject(EcmaVM *, Local, const Local argv[], int32_t length, void *) +{ + ASSERT_EQ(length, 1); + Local reason = argv[0]; + ASSERT_TRUE(reason->IsString()); + ASSERT_EQ(Local(reason)->ToString(), "Reject"); +} + +Local RejectCallback(EcmaVM *vm, Local thisArg, const Local argv[], int32_t length, + void *data) +{ + LocalScope scope(vm); + CheckReject(vm, thisArg, argv, length, data); + return JSValueRef::Undefined(vm); +} + +TEST_F(JSNApiTests, PromiseCatch) +{ + LocalScope scope(vm_); + Local capability = PromiseCapabilityRef::New(vm_); + + Local promise = capability->GetPromise(vm_); + Local reject = FunctionRef::New(vm_, RejectCallback, nullptr); + Local catchPromise = promise->Catch(vm_, reject); + ASSERT_TRUE(promise->IsPromise()); + ASSERT_TRUE(catchPromise->IsPromise()); + + Local reason = StringRef::NewFromUtf8(vm_, "Reject"); + ASSERT_TRUE(capability->Reject(vm_, reason)); + + JSNApi::ExecutePendingJob(vm_); +} + +void CheckResolve(EcmaVM *, Local, const Local argv[], int32_t length, void *) +{ + ASSERT_EQ(length, 1); + Local value = argv[0]; + ASSERT_TRUE(value->IsNumber()); + ASSERT_EQ(Local(value)->Value(), 300.3); // 300.3 : test case of input +} + +Local ResolvedCallback(EcmaVM *vm, Local thisArg, const Local argv[], + int32_t length, void *data) +{ + LocalScope scope(vm); + CheckResolve(vm, thisArg, argv, length, data); + return JSValueRef::Undefined(vm); +} + +TEST_F(JSNApiTests, PromiseThen) +{ + LocalScope scope(vm_); + Local capability = PromiseCapabilityRef::New(vm_); + + Local promise = capability->GetPromise(vm_); + Local resolve = FunctionRef::New(vm_, ResolvedCallback, nullptr); + Local reject = FunctionRef::New(vm_, RejectCallback, nullptr); + Local thenPromise = promise->Then(vm_, resolve, reject); + ASSERT_TRUE(promise->IsPromise()); + ASSERT_TRUE(thenPromise->IsPromise()); + + Local value = NumberRef::New(vm_, 300.3); // 300.3 : test case of input + ASSERT_TRUE(capability->Resolve(vm_, value)); + JSNApi::ExecutePendingJob(vm_); +} + +TEST_F(JSNApiTests, Constructor) +{ + LocalScope scope(vm_); + Local object = JSNApi::GetGlobalObject(vm_); + Local key = StringRef::NewFromUtf8(vm_, "Number"); + Local numberConstructor = object->Get(vm_, key); + Local argv[1]; + argv[0] = NumberRef::New(vm_, 1.3); // 1.3 : test case of input + Local result = numberConstructor->Constructor(vm_, argv, 1); + ASSERT_TRUE(result->IsObject()); + ASSERT_EQ(result->ToNumber(vm_)->Value(), 1.3); // 1.3 : size of arguments +} + +TEST_F(JSNApiTests, ArrayBuffer) +{ + LocalScope scope(vm_); + const int32_t length = 15; + Local arrayBuffer = ArrayBufferRef::New(vm_, length); + ASSERT_TRUE(arrayBuffer->IsArrayBuffer()); + ASSERT_EQ(arrayBuffer->ByteLength(vm_), length); + ASSERT_NE(arrayBuffer->GetBuffer(), nullptr); + JSNApi::TriggerGC(vm_); +} + +TEST_F(JSNApiTests, ArrayBufferWithBuffer) +{ + static bool isFree = false; + struct Data { + int32_t length; + }; + const int32_t length = 15; + Data *data = new Data(); + data->length = length; + Deleter deleter = [](void *buffer, void *data_ptr) -> void { + delete[] reinterpret_cast(buffer); + Data *currentData = reinterpret_cast(data_ptr); + ASSERT_EQ(currentData->length, 15); // 5 : size of arguments + delete currentData; + isFree = true; + }; + { + LocalScope scope(vm_); + uint8_t *buffer = new uint8_t[length](); + Local arrayBuffer = ArrayBufferRef::New(vm_, buffer, length, deleter, data); + ASSERT_TRUE(arrayBuffer->IsArrayBuffer()); + ASSERT_EQ(arrayBuffer->ByteLength(vm_), length); + ASSERT_EQ(arrayBuffer->GetBuffer(), buffer); + } + JSNApi::TriggerGC(vm_); + ASSERT_TRUE(isFree); +} + +TEST_F(JSNApiTests, DataView) +{ + LocalScope scope(vm_); + const int32_t length = 15; + Local arrayBuffer = ArrayBufferRef::New(vm_, length); + JSNApi::TriggerGC(vm_); + ASSERT_TRUE(arrayBuffer->IsArrayBuffer()); + + // 5 : offset of byte, 7 : length + Local dataView = DataViewRef::New(vm_, arrayBuffer, 5, 7); + ASSERT_TRUE(dataView->IsDataView()); + ASSERT_EQ(dataView->GetArrayBuffer(vm_)->GetBuffer(), arrayBuffer->GetBuffer()); + ASSERT_EQ(dataView->ByteLength(), 7); // 7 : size of arguments + ASSERT_EQ(dataView->ByteOffset(), 5); // 5 : size of arguments + + // 5 : offset of byte, 11 : length + dataView = DataViewRef::New(vm_, arrayBuffer, 5, 11); + ASSERT_TRUE(dataView->IsException()); +} + +TEST_F(JSNApiTests, Int8Array) +{ + LocalScope scope(vm_); + const int32_t length = 15; + Local arrayBuffer = ArrayBufferRef::New(vm_, length); + ASSERT_TRUE(arrayBuffer->IsArrayBuffer()); + + // 5 : offset of byte, 6 : length + Local typedArray = Int8ArrayRef::New(vm_, arrayBuffer, 5, 6); + ASSERT_TRUE(typedArray->IsInt8Array()); + ASSERT_EQ(typedArray->ByteLength(vm_), 6); // 6 : length of bytes + ASSERT_EQ(typedArray->ByteOffset(vm_), 5); // 5 : offset of byte + ASSERT_EQ(typedArray->ArrayLength(vm_), 6); // 6 : length of array + ASSERT_EQ(typedArray->GetArrayBuffer(vm_)->GetBuffer(), arrayBuffer->GetBuffer()); +} + +TEST_F(JSNApiTests, Uint8Array) +{ + LocalScope scope(vm_); + const int32_t length = 15; + Local arrayBuffer = ArrayBufferRef::New(vm_, length); + ASSERT_TRUE(arrayBuffer->IsArrayBuffer()); + + // 5 : offset of byte, 6 : length + Local typedArray = Uint8ArrayRef::New(vm_, arrayBuffer, 5, 6); + ASSERT_TRUE(typedArray->IsUint8Array()); + ASSERT_EQ(typedArray->ByteLength(vm_), 6); // 6 : length of bytes + ASSERT_EQ(typedArray->ByteOffset(vm_), 5); // 5 : offset of byte + ASSERT_EQ(typedArray->ArrayLength(vm_), 6); // 6 : length of array + ASSERT_EQ(typedArray->GetArrayBuffer(vm_)->GetBuffer(), arrayBuffer->GetBuffer()); +} + +TEST_F(JSNApiTests, Uint8ClampedArray) +{ + LocalScope scope(vm_); + const int32_t length = 15; + Local arrayBuffer = ArrayBufferRef::New(vm_, length); + ASSERT_TRUE(arrayBuffer->IsArrayBuffer()); + + // 5 : offset of byte, 6 : length + Local typedArray = Uint8ClampedArrayRef::New(vm_, arrayBuffer, 5, 6); + ASSERT_TRUE(typedArray->IsUint8ClampedArray()); + ASSERT_EQ(typedArray->ByteLength(vm_), 6); // 6 : length of bytes + ASSERT_EQ(typedArray->ByteOffset(vm_), 5); // 5 : offset of byte + ASSERT_EQ(typedArray->ArrayLength(vm_), 6); // 6 : length of array + ASSERT_EQ(typedArray->GetArrayBuffer(vm_)->GetBuffer(), arrayBuffer->GetBuffer()); +} + +TEST_F(JSNApiTests, Int16Array) +{ + LocalScope scope(vm_); + const int32_t length = 30; + Local arrayBuffer = ArrayBufferRef::New(vm_, length); + ASSERT_TRUE(arrayBuffer->IsArrayBuffer()); + + // 4 : offset of byte, 6 : length + Local typedArray = Int16ArrayRef::New(vm_, arrayBuffer, 4, 6); + ASSERT_TRUE(typedArray->IsInt16Array()); + ASSERT_EQ(typedArray->ByteLength(vm_), 12); // 12 : length of bytes + ASSERT_EQ(typedArray->ByteOffset(vm_), 4); // 4 : offset of byte + ASSERT_EQ(typedArray->ArrayLength(vm_), 6); // 6 : length of array + ASSERT_EQ(typedArray->GetArrayBuffer(vm_)->GetBuffer(), arrayBuffer->GetBuffer()); +} + +TEST_F(JSNApiTests, Uint16Array) +{ + LocalScope scope(vm_); + const int32_t length = 30; + Local arrayBuffer = ArrayBufferRef::New(vm_, length); + ASSERT_TRUE(arrayBuffer->IsArrayBuffer()); + + // 4 : offset of byte, 6 : length + Local typedArray = Uint16ArrayRef::New(vm_, arrayBuffer, 4, 6); + ASSERT_TRUE(typedArray->IsUint16Array()); + ASSERT_EQ(typedArray->ByteLength(vm_), 12); // 12 : length of bytes + ASSERT_EQ(typedArray->ByteOffset(vm_), 4); // 4 : offset of byte + ASSERT_EQ(typedArray->ArrayLength(vm_), 6); // 6 : length of array + ASSERT_EQ(typedArray->GetArrayBuffer(vm_)->GetBuffer(), arrayBuffer->GetBuffer()); +} + +TEST_F(JSNApiTests, Uint32Array) +{ + LocalScope scope(vm_); + const int32_t length = 30; + Local arrayBuffer = ArrayBufferRef::New(vm_, length); + ASSERT_TRUE(arrayBuffer->IsArrayBuffer()); + + // 4 : offset of byte, 6 : length + Local typedArray = Uint32ArrayRef::New(vm_, arrayBuffer, 4, 6); + ASSERT_TRUE(typedArray->IsUint32Array()); + ASSERT_EQ(typedArray->ByteLength(vm_), 24); // 24 : length of bytes + ASSERT_EQ(typedArray->ByteOffset(vm_), 4); // 4 : offset of byte + ASSERT_EQ(typedArray->ArrayLength(vm_), 6); // 6 : length of array + ASSERT_EQ(typedArray->GetArrayBuffer(vm_)->GetBuffer(), arrayBuffer->GetBuffer()); +} + +TEST_F(JSNApiTests, Int32Array) +{ + LocalScope scope(vm_); + const int32_t length = 30; + Local arrayBuffer = ArrayBufferRef::New(vm_, length); + ASSERT_TRUE(arrayBuffer->IsArrayBuffer()); + + // 4 : offset of byte, 6 : length + Local typedArray = Int32ArrayRef::New(vm_, arrayBuffer, 4, 6); + ASSERT_TRUE(typedArray->IsInt32Array()); + ASSERT_EQ(typedArray->ByteLength(vm_), 24); // 24 : length of bytes + ASSERT_EQ(typedArray->ByteOffset(vm_), 4); // 4 : offset of byte + ASSERT_EQ(typedArray->ArrayLength(vm_), 6); // 6 : length of array + ASSERT_EQ(typedArray->GetArrayBuffer(vm_)->GetBuffer(), arrayBuffer->GetBuffer()); +} + +TEST_F(JSNApiTests, Float32Array) +{ + LocalScope scope(vm_); + const int32_t length = 30; + Local arrayBuffer = ArrayBufferRef::New(vm_, length); + ASSERT_TRUE(arrayBuffer->IsArrayBuffer()); + + // 4 : offset of byte, 6 : length + Local typedArray = Float32ArrayRef::New(vm_, arrayBuffer, 4, 6); + ASSERT_TRUE(typedArray->IsFloat32Array()); + ASSERT_EQ(typedArray->ByteLength(vm_), 24); // 24 : length of bytes + ASSERT_EQ(typedArray->ByteOffset(vm_), 4); // 4 : offset of byte + ASSERT_EQ(typedArray->ArrayLength(vm_), 6); // 6 : length of array + ASSERT_EQ(typedArray->GetArrayBuffer(vm_)->GetBuffer(), arrayBuffer->GetBuffer()); +} + +TEST_F(JSNApiTests, Float64Array) +{ + LocalScope scope(vm_); + const int32_t length = 57; + Local arrayBuffer = ArrayBufferRef::New(vm_, length); + ASSERT_TRUE(arrayBuffer->IsArrayBuffer()); + + // 8 : offset of byte, 6 : length + Local typedArray = Float64ArrayRef::New(vm_, arrayBuffer, 8, 6); + ASSERT_TRUE(typedArray->IsFloat64Array()); + ASSERT_EQ(typedArray->ByteLength(vm_), 48); // 48 : length of bytes + ASSERT_EQ(typedArray->ByteOffset(vm_), 8); // 8 : offset of byte + ASSERT_EQ(typedArray->ArrayLength(vm_), 6); // 6 : length of array + ASSERT_EQ(typedArray->GetArrayBuffer(vm_)->GetBuffer(), arrayBuffer->GetBuffer()); +} + +TEST_F(JSNApiTests, Error) +{ + LocalScope scope(vm_); + Local message = StringRef::NewFromUtf8(vm_, "ErrorTest"); + Local error = Exception::Error(vm_, message); + ASSERT_TRUE(error->IsError()); + + JSNApi::ThrowException(vm_, error); + ASSERT_TRUE(thread_->HasPendingException()); +} + +TEST_F(JSNApiTests, RangeError) +{ + LocalScope scope(vm_); + Local message = StringRef::NewFromUtf8(vm_, "ErrorTest"); + Local error = Exception::RangeError(vm_, message); + ASSERT_TRUE(error->IsError()); + + JSNApi::ThrowException(vm_, error); + ASSERT_TRUE(thread_->HasPendingException()); +} + +TEST_F(JSNApiTests, TypeError) +{ + LocalScope scope(vm_); + Local message = StringRef::NewFromUtf8(vm_, "ErrorTest"); + Local error = Exception::TypeError(vm_, message); + ASSERT_TRUE(error->IsError()); + + JSNApi::ThrowException(vm_, error); + ASSERT_TRUE(thread_->HasPendingException()); +} + +TEST_F(JSNApiTests, ReferenceError) +{ + LocalScope scope(vm_); + Local message = StringRef::NewFromUtf8(vm_, "ErrorTest"); + Local error = Exception::ReferenceError(vm_, message); + ASSERT_TRUE(error->IsError()); + + JSNApi::ThrowException(vm_, error); + ASSERT_TRUE(thread_->HasPendingException()); +} + +TEST_F(JSNApiTests, SyntaxError) +{ + LocalScope scope(vm_); + Local message = StringRef::NewFromUtf8(vm_, "ErrorTest"); + Local error = Exception::SyntaxError(vm_, message); + ASSERT_TRUE(error->IsError()); + + JSNApi::ThrowException(vm_, error); + ASSERT_TRUE(thread_->HasPendingException()); +} + +TEST_F(JSNApiTests, InheritPrototype_001) +{ + LocalScope scope(vm_); + JSHandle env = vm_->GetGlobalEnv(); + // new with Builtins::Set Prototype + JSHandle set = env->GetBuiltinsSetFunction(); + Local setLocal = JSNApiHelper::ToLocal(set); + // new with Builtins::Map Prototype + JSHandle map = env->GetBuiltinsMapFunction(); + Local mapLocal = JSNApiHelper::ToLocal(map); + JSHandle setPrototype(thread_, JSHandle::Cast(set)->GetFunctionPrototype()); + JSHandle mapPrototype(thread_, JSHandle::Cast(map)->GetFunctionPrototype()); + JSHandle mapPrototypeProto(thread_, JSHandle::Cast(mapPrototype)->GetPrototype(thread_)); + bool same = JSTaggedValue::SameValue(setPrototype, mapPrototypeProto); + // before inherit, map.Prototype.__proto__ should be different from set.Prototype + ASSERT_FALSE(same); + // before inherit, map.__proto__ should be different from set + JSHandle mapProto(thread_, JSHandle::Cast(map)->GetPrototype(thread_)); + bool same1 = JSTaggedValue::SameValue(set, mapProto); + ASSERT_FALSE(same1); + + // Set property to Set Function + Local defaultString = JSNApiHelper::ToLocal( + thread_->GlobalConstants()->GetHandledDefaultString()); + { + PropertyAttribute attr(defaultString, true, true, true); + ASSERT_TRUE(setLocal->DefineProperty(vm_, defaultString, attr)); + } + Local func = JSNApiHelper::ToLocal(env->GetTypedArrayFunction()); + Local property1String = StringRef::NewFromUtf8(vm_, "property1"); + { + PropertyAttribute attr(func, true, true, true); + ASSERT_TRUE(setLocal->DefineProperty(vm_, property1String, attr)); + } + + mapLocal->Inherit(vm_, setLocal); + JSHandle sonHandle = JSNApiHelper::ToJSHandle(mapLocal); + JSHandle sonPrototype(thread_, JSHandle::Cast(sonHandle)->GetFunctionPrototype()); + JSHandle sonPrototypeProto(thread_, JSHandle::Cast(sonPrototype)->GetPrototype(thread_)); + bool same2 = JSTaggedValue::SameValue(setPrototype, sonPrototypeProto); + ASSERT_TRUE(same2); + JSHandle sonProto(thread_, JSHandle::Cast(map)->GetPrototype(thread_)); + bool same3 = JSTaggedValue::SameValue(set, sonProto); + ASSERT_TRUE(same3); + + // son = new Son(), Son() inherit from Parent(), Test whether son.InstanceOf(Parent) is true + Local sonObj = NewObjectByConstructor(mapLocal); + bool isInstance = sonObj->InstanceOf(vm_, setLocal); + ASSERT_TRUE(isInstance); + + // Test whether son Function can access to property of parent Function + Local res = mapLocal->Get(vm_, defaultString); + ASSERT_TRUE(defaultString->IsStrictEquals(vm_, res)); + Local res1 = mapLocal->Get(vm_, property1String); + ASSERT_TRUE(func->IsStrictEquals(vm_, res1)); + + // new with empty Function Constructor + Local son1 = FunctionRef::New(vm_, FunctionCallback, nullptr); + son1->Inherit(vm_, mapLocal); + JSHandle son1Handle = JSHandle::Cast(JSNApiHelper::ToJSHandle(son1)); + ASSERT_TRUE(son1Handle->HasFunctionPrototype()); +} + +TEST_F(JSNApiTests, InheritPrototype_002) +{ + LocalScope scope(vm_); + JSHandle env = vm_->GetGlobalEnv(); + // new with Builtins::weakSet Prototype + JSHandle weakSet = env->GetBuiltinsWeakSetFunction(); + Local weakSetLocal = JSNApiHelper::ToLocal(weakSet); + // new with Builtins::weakMap Prototype + JSHandle weakMap = env->GetBuiltinsWeakMapFunction(); + Local weakMapLocal = JSNApiHelper::ToLocal(weakMap); + + weakMapLocal->Inherit(vm_, weakSetLocal); + + Local property1String = StringRef::NewFromUtf8(vm_, "property1"); + Local func = JSNApiHelper::ToLocal(env->GetArrayFunction()); + { + PropertyAttribute attr(func, true, true, true); + ASSERT_TRUE(weakMapLocal->DefineProperty(vm_, property1String, attr)); + } + + Local sonObj = NewObjectByConstructor(weakMapLocal); + + Local fatherObj = NewObjectByConstructor(weakSetLocal); + + Local sonMethod = GetMethod(sonObj, property1String); + Local fatherMethod = GetMethod(fatherObj, property1String); + ASSERT_TRUE(sonMethod->IsStrictEquals(vm_, fatherMethod)); +} + +TEST_F(JSNApiTests, InheritPrototype_003) +{ + LocalScope scope(vm_); + JSHandle env = vm_->GetGlobalEnv(); + + JSMethod *invokeSelf = vm_->GetMethodForNativeFunction( + reinterpret_cast(builtins::BuiltinsFunction::FunctionPrototypeInvokeSelf)); + // father type + Local protoLocal = + NewFunctionByHClass(JSHandle::Cast(env->GetFunctionClassWithProto()), invokeSelf); + // son type + Local noProtoLocal = + NewFunctionByHClass(JSHandle::Cast(env->GetFunctionClassWithoutProto()), invokeSelf); + + JSHandle sonHandle = JSHandle::Cast(JSNApiHelper::ToJSHandle(noProtoLocal)); + EXPECT_FALSE(sonHandle->HasFunctionPrototype()); + + Local defaultString = JSNApiHelper::ToLocal( + thread_->GlobalConstants()->GetHandledDefaultString()); + { + PropertyAttribute attr(defaultString, true, true, true); + protoLocal->DefineProperty(vm_, defaultString, attr); + } + + noProtoLocal->Inherit(vm_, protoLocal); + JSHandle son1Handle = JSHandle::Cast(JSNApiHelper::ToJSHandle(noProtoLocal)); + EXPECT_TRUE(son1Handle->HasFunctionPrototype()); + + Local res = noProtoLocal->Get(vm_, defaultString); + EXPECT_TRUE(defaultString->IsStrictEquals(vm_, res)); + + Local propertyString = StringRef::NewFromUtf8(vm_, "property"); + Local func = JSNApiHelper::ToLocal(env->GetArrayFunction()); + { + PropertyAttribute attr(func, true, true, true); + protoLocal->DefineProperty(vm_, propertyString, attr); + } + Local res1 = noProtoLocal->Get(vm_, propertyString); + EXPECT_TRUE(func->IsStrictEquals(vm_, res1)); +} + +TEST_F(JSNApiTests, DISABLED_InheritPrototype_004) // TODO(vpukhov) +{ + LocalScope scope(vm_); + JSHandle env = vm_->GetGlobalEnv(); + auto factory = vm_->GetFactory(); + + JSHandle weakSet = env->GetBuiltinsWeakSetFunction(); + JSHandle deleteString(factory->NewFromCanBeCompressString("delete")); + JSHandle addString(factory->NewFromCanBeCompressString("add")); + JSHandle defaultString = thread_->GlobalConstants()->GetHandledDefaultString(); + JSHandle deleteMethod = JSObject::GetMethod(thread_, weakSet, deleteString); + JSHandle addMethod = JSObject::GetMethod(thread_, weakSet, addString); + + JSMethod *invokeSelf = vm_->GetMethodForNativeFunction( + reinterpret_cast(builtins::BuiltinsFunction::FunctionPrototypeInvokeSelf)); + JSMethod *ctor = + vm_->GetMethodForNativeFunction(reinterpret_cast(builtins::BuiltinsFunction::FunctionConstructor)); + + JSHandle protoDynclass = JSHandle::Cast(env->GetFunctionClassWithProto()); + JSHandle funcFuncPrototype = factory->NewJSFunctionByDynClass(invokeSelf, protoDynclass); + // add method in funcPrototype + PropertyDescriptor desc = PropertyDescriptor(thread_, deleteMethod); + JSObject::DefineOwnProperty(thread_, JSHandle::Cast(funcFuncPrototype), deleteString, desc); + JSHandle funcFuncPrototypeValue(funcFuncPrototype); + + JSHandle funcFuncProtoIntanceDynclass = + factory->NewEcmaDynClass(JSFunction::SIZE, JSType::JS_FUNCTION, funcFuncPrototypeValue); + // new with NewJSFunctionByDynClass::function DynClass + JSHandle protoFunc = + factory->NewJSFunctionByDynClass(ctor, funcFuncProtoIntanceDynclass, FunctionKind::BUILTIN_CONSTRUCTOR); + EXPECT_TRUE(*protoFunc != nullptr); + // add method in funcnction + PropertyDescriptor desc1 = PropertyDescriptor(thread_, addMethod); + JSObject::DefineOwnProperty(thread_, JSHandle::Cast(protoFunc), addString, desc1); + JSObject::DefineOwnProperty(thread_, JSHandle::Cast(protoFunc), deleteString, desc); + // father type + Local protoLocal = JSNApiHelper::ToLocal(JSHandle(protoFunc)); + + JSHandle noProtoDynclass = JSHandle::Cast(env->GetFunctionClassWithoutProto()); + JSHandle funcFuncNoProtoPrototype = factory->NewJSFunctionByDynClass(invokeSelf, noProtoDynclass); + JSHandle funcFuncNoProtoPrototypeValue(funcFuncNoProtoPrototype); + + JSHandle funcFuncNoProtoProtoIntanceDynclass = + factory->NewEcmaDynClass(JSFunction::SIZE, JSType::JS_FUNCTION, funcFuncNoProtoPrototypeValue); + // new with NewJSFunctionByDynClass::function DynClass + JSHandle noProtoFunc = + factory->NewJSFunctionByDynClass(ctor, funcFuncNoProtoProtoIntanceDynclass, FunctionKind::BUILTIN_CONSTRUCTOR); + EXPECT_TRUE(*noProtoFunc != nullptr); + // set property that has same key with fater type + PropertyDescriptor desc2 = PropertyDescriptor(thread_, defaultString); + JSObject::DefineOwnProperty(thread_, JSHandle::Cast(noProtoFunc), addString, desc2); + // son type + Local noProtoLocal = JSNApiHelper::ToLocal(JSHandle(noProtoFunc)); + + noProtoLocal->Inherit(vm_, protoLocal); + + JSHandle sonHandle = JSHandle::Cast(JSNApiHelper::ToJSHandle(noProtoLocal)); + OperationResult res = JSObject::GetProperty(thread_, JSHandle::Cast(sonHandle), deleteString); + EXPECT_EQ(JSTaggedValue::SameValue(deleteMethod, res.GetValue()), true); + // test if the property value changed after inherit + OperationResult res1 = JSObject::GetProperty(thread_, JSHandle::Cast(sonHandle), addString); + EXPECT_EQ(JSTaggedValue::SameValue(defaultString, res1.GetValue()), true); +} + +TEST_F(JSNApiTests, ClassFunction) +{ + LocalScope scope(vm_); + Local cls = FunctionRef::NewClassFunction(vm_, nullptr, nullptr, nullptr); + + JSHandle clsObj = JSNApiHelper::ToJSHandle(Local(cls)); + ASSERT_TRUE(clsObj->IsClassConstructor()); + + JSTaggedValue accessor = + JSHandle(clsObj)->GetPropertyInlinedProps(JSFunction::CLASS_PROTOTYPE_INLINE_PROPERTY_INDEX); + ASSERT_TRUE(accessor.IsInternalAccessor()); +} +} // namespace panda::test diff --git a/tests/runtime/regexp/dyn_buffer_test.cpp b/tests/runtime/regexp/dyn_buffer_test.cpp new file mode 100644 index 000000000..c163e9a80 --- /dev/null +++ b/tests/runtime/regexp/dyn_buffer_test.cpp @@ -0,0 +1,96 @@ +/* + * Copyright (c) 2021-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 "gtest/gtest.h" +#include "test_helper.h" + +#include "plugins/ecmascript/runtime/ecma_vm.h" +#include "include/runtime.h" +#include "include/runtime_options.h" +#include "plugins/ecmascript/runtime/regexp/dyn_chunk.h" +#include "plugins/ecmascript/runtime/object_factory.h" + +namespace panda::test { +using namespace panda::coretypes; +using namespace panda::ecmascript; + +class DynBufferTest : public testing::Test { +public: + void SetUp() override + { + TestHelper::CreateEcmaVMWithScope(instance, thread, scope); + chunk_ = thread->GetEcmaVM()->GetChunk(); + } + + void TearDown() override + { + TestHelper::DestroyEcmaVMWithScope(instance, scope); + } + + PandaVM *instance; + JSThread *thread; + EcmaHandleScope *scope {nullptr}; + Chunk *chunk_; +}; + +TEST_F(DynBufferTest, EmitAndGet) +{ + DynChunk dynChunk = DynChunk(chunk_); + dynChunk.EmitChar(65); + dynChunk.EmitU16(66); + dynChunk.EmitU32(67); + ASSERT_EQ(dynChunk.GetSize(), 7); + ASSERT_EQ(dynChunk.GetAllocatedSize(), DynChunk::ALLOCATE_MIN_SIZE); + ASSERT_EQ(dynChunk.GetError(), false); + dynChunk.Insert(1, 1); + uint32_t val1 = dynChunk.GetU8(0); + uint32_t val2 = dynChunk.GetU16(2); + uint32_t val3 = dynChunk.GetU32(4); + ASSERT_EQ(val1, 65); + ASSERT_EQ(val2, 66); + ASSERT_EQ(val3, 67); +} + +TEST_F(DynBufferTest, EmitSelfAndGet) +{ + DynChunk dynChunk = DynChunk(chunk_); + dynChunk.EmitChar(65); + dynChunk.EmitSelf(0, 1); + ASSERT_EQ(dynChunk.GetSize(), 2); + ASSERT_EQ(dynChunk.GetAllocatedSize(), DynChunk::ALLOCATE_MIN_SIZE); + ASSERT_EQ(dynChunk.GetError(), false); + uint32_t val1 = dynChunk.GetU8(0); + uint32_t val2 = dynChunk.GetU8(1); + ASSERT_EQ(val1, 65); + ASSERT_EQ(val2, 65); +} + +TEST_F(DynBufferTest, EmitStrAndGet) +{ + DynChunk dynChunk = DynChunk(chunk_); + dynChunk.EmitStr("abc"); + ASSERT_EQ(dynChunk.GetSize(), 4); + ASSERT_EQ(dynChunk.GetAllocatedSize(), DynChunk::ALLOCATE_MIN_SIZE); + ASSERT_EQ(dynChunk.GetError(), false); + uint32_t val1 = dynChunk.GetU8(0); + uint32_t val2 = dynChunk.GetU8(1); + uint32_t val3 = dynChunk.GetU8(2); + uint32_t val4 = dynChunk.GetU8(3); + ASSERT_EQ(val1, 97); + ASSERT_EQ(val2, 98); + ASSERT_EQ(val3, 99); + ASSERT_EQ(val4, 0); +} +} // namespace panda::test diff --git a/tests/runtime/regexp/regexp_test.cpp b/tests/runtime/regexp/regexp_test.cpp new file mode 100644 index 000000000..faa9d6ef1 --- /dev/null +++ b/tests/runtime/regexp/regexp_test.cpp @@ -0,0 +1,2028 @@ +/* + * Copyright (c) 2021-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 "gtest/gtest.h" +#include "test_helper.h" + +#include "plugins/ecmascript/runtime/ecma_vm.h" +#include "include/runtime.h" +#include "include/runtime_options.h" +#include "plugins/ecmascript/runtime/regexp/regexp_parser.h" +#include "plugins/ecmascript/runtime/regexp/regexp_executor.h" +#include "plugins/ecmascript/runtime/object_factory.h" +#include "plugins/ecmascript/runtime/ecma_string-inl.h" + +namespace panda::test { +using namespace panda::ecmascript; +using MatchResult = RegExpExecutor::MatchResult; + +class RegExpTest : public testing::Test { +public: + static void SetUpTestCase() + { + GTEST_LOG_(INFO) << "SetUpTestCase"; + } + + static void TearDownTestCase() + { + GTEST_LOG_(INFO) << "TearDownCase"; + } + + void SetUp() override + { + TestHelper::CreateEcmaVMWithScope(instance, thread, scope); + chunk_ = thread->GetEcmaVM()->GetChunk(); + } + + void TearDown() override + { + TestHelper::DestroyEcmaVMWithScope(instance, scope); + } + + bool IsValidAlphaEscapeInAtom(char s) const + { + switch (s) { + // Assertion [U] :: \b + case 'b': + // Assertion [U] :: \B + case 'B': + // ControlEscape :: one of f n r t v + case 'f': + case 'n': + case 'r': + case 't': + case 'v': + // CharacterClassEscape :: one of d D s S w W + case 'd': + case 'D': + case 's': + case 'S': + case 'w': + case 'W': + return true; + default: + return false; + } + } + + bool IsValidAlphaEscapeInClass(char s) const + { + switch (s) { + // ClassEscape[U] :: b + case 'b': + // ControlEscape :: one of f n r t v + case 'f': + case 'n': + case 'r': + case 't': + case 'v': + // CharacterClassEscape :: one of d D s S w W + case 'd': + case 'D': + case 's': + case 'S': + case 'w': + case 'W': + return true; + default: + return false; + } + } + +protected: + PandaVM *instance {nullptr}; + EcmaHandleScope *scope {nullptr}; + JSThread *thread {nullptr}; + Chunk *chunk_ {nullptr}; +}; + +TEST_F(RegExpTest, ParseError1) +{ + RegExpParser parser = RegExpParser(chunk_); + CString source("0{2,1}"); + parser.Init(const_cast(reinterpret_cast(source.c_str())), source.size(), 0); + parser.Parse(); + bool parseResult = parser.IsError(); + ASSERT_TRUE(parseResult); +} + +TEST_F(RegExpTest, ParseError2) +{ + RegExpParser parser = RegExpParser(chunk_); + CString source("^[z-a]$"); + parser.Init(const_cast(reinterpret_cast(source.c_str())), source.size(), 0); + parser.Parse(); + bool parseResult = parser.IsError(); + ASSERT_TRUE(parseResult); +} + +TEST_F(RegExpTest, ParseError3) +{ + RegExpParser parser = RegExpParser(chunk_); + CString source("\\"); + parser.Init(const_cast(reinterpret_cast(source.c_str())), source.size(), 0); + parser.Parse(); + bool parseResult = parser.IsError(); + ASSERT_TRUE(parseResult); +} + +TEST_F(RegExpTest, ParseError4) +{ + RegExpParser parser = RegExpParser(chunk_); + CString source("a**"); + parser.Init(const_cast(reinterpret_cast(source.c_str())), source.size(), 0); + parser.Parse(); + bool parseResult = parser.IsError(); + ASSERT_TRUE(parseResult); +} + +TEST_F(RegExpTest, ParseError5) +{ + RegExpParser parser = RegExpParser(chunk_); + CString source("a***"); + parser.Init(const_cast(reinterpret_cast(source.c_str())), source.size(), 0); + parser.Parse(); + bool parseResult = parser.IsError(); + ASSERT_TRUE(parseResult); +} + +TEST_F(RegExpTest, ParseError6) +{ + RegExpParser parser = RegExpParser(chunk_); + CString source("a**"); + parser.Init(const_cast(reinterpret_cast(source.c_str())), source.size(), 0); + parser.Parse(); + bool parseResult = parser.IsError(); + ASSERT_TRUE(parseResult); +} + +TEST_F(RegExpTest, ParseError7) +{ + RegExpParser parser = RegExpParser(chunk_); + CString source("a++"); + parser.Init(const_cast(reinterpret_cast(source.c_str())), source.size(), 0); + parser.Parse(); + bool parseResult = parser.IsError(); + ASSERT_TRUE(parseResult); +} + +TEST_F(RegExpTest, ParseError8) +{ + RegExpParser parser = RegExpParser(chunk_); + CString source("a+++"); + parser.Init(const_cast(reinterpret_cast(source.c_str())), source.size(), 0); + parser.Parse(); + bool parseResult = parser.IsError(); + ASSERT_TRUE(parseResult); +} + +TEST_F(RegExpTest, ParseError9) +{ + RegExpParser parser = RegExpParser(chunk_); + CString source("a???"); + parser.Init(const_cast(reinterpret_cast(source.c_str())), source.size(), 0); + parser.Parse(); + bool parseResult = parser.IsError(); + ASSERT_TRUE(parseResult); +} + +TEST_F(RegExpTest, ParseError10) +{ + RegExpParser parser = RegExpParser(chunk_); + CString source("a????"); + parser.Init(const_cast(reinterpret_cast(source.c_str())), source.size(), 0); + parser.Parse(); + bool parseResult = parser.IsError(); + ASSERT_TRUE(parseResult); +} + +TEST_F(RegExpTest, ParseError11) +{ + RegExpParser parser = RegExpParser(chunk_); + CString source("*a"); + parser.Init(const_cast(reinterpret_cast(source.c_str())), source.size(), 0); + parser.Parse(); + bool parseResult = parser.IsError(); + ASSERT_TRUE(parseResult); +} + +TEST_F(RegExpTest, ParseError12) +{ + RegExpParser parser = RegExpParser(chunk_); + CString source("**a"); + parser.Init(const_cast(reinterpret_cast(source.c_str())), source.size(), 0); + parser.Parse(); + bool parseResult = parser.IsError(); + ASSERT_TRUE(parseResult); +} + +TEST_F(RegExpTest, ParseError13) +{ + RegExpParser parser = RegExpParser(chunk_); + CString source("+a"); + parser.Init(const_cast(reinterpret_cast(source.c_str())), source.size(), 0); + parser.Parse(); + bool parseResult = parser.IsError(); + ASSERT_TRUE(parseResult); +} + +TEST_F(RegExpTest, ParseError14) +{ + RegExpParser parser = RegExpParser(chunk_); + CString source("++a"); + parser.Init(const_cast(reinterpret_cast(source.c_str())), source.size(), 0); + parser.Parse(); + bool parseResult = parser.IsError(); + ASSERT_TRUE(parseResult); +} + +TEST_F(RegExpTest, ParseError15) +{ + RegExpParser parser = RegExpParser(chunk_); + CString source("?a"); + parser.Init(const_cast(reinterpret_cast(source.c_str())), source.size(), 0); + parser.Parse(); + bool parseResult = parser.IsError(); + ASSERT_TRUE(parseResult); +} + +TEST_F(RegExpTest, ParseError16) +{ + RegExpParser parser = RegExpParser(chunk_); + CString source("??a"); + parser.Init(const_cast(reinterpret_cast(source.c_str())), source.size(), 0); + parser.Parse(); + bool parseResult = parser.IsError(); + ASSERT_TRUE(parseResult); +} + +TEST_F(RegExpTest, ParseError17) +{ + RegExpParser parser = RegExpParser(chunk_); + CString source("x{1}{1,}"); + parser.Init(const_cast(reinterpret_cast(source.c_str())), source.size(), 0); + parser.Parse(); + bool parseResult = parser.IsError(); + ASSERT_TRUE(parseResult); +} + +TEST_F(RegExpTest, ParseError18) +{ + RegExpParser parser = RegExpParser(chunk_); + CString source("x{1,2}{1}"); + parser.Init(const_cast(reinterpret_cast(source.c_str())), source.size(), 0); + parser.Parse(); + bool parseResult = parser.IsError(); + ASSERT_TRUE(parseResult); +} + +TEST_F(RegExpTest, ParseError19) +{ + RegExpParser parser = RegExpParser(chunk_); + CString source("x{1,}{1}"); + parser.Init(const_cast(reinterpret_cast(source.c_str())), source.size(), 0); + parser.Parse(); + bool parseResult = parser.IsError(); + ASSERT_TRUE(parseResult); +} + +TEST_F(RegExpTest, ParseError20) +{ + RegExpParser parser = RegExpParser(chunk_); + CString source("x{0,1}{1,}"); + parser.Init(const_cast(reinterpret_cast(source.c_str())), source.size(), 0); + parser.Parse(); + bool parseResult = parser.IsError(); + ASSERT_TRUE(parseResult); +} + +TEST_F(RegExpTest, ParseError21) +{ + RegExpParser parser = RegExpParser(chunk_); + CString source("[b-ac-e]"); + parser.Init(const_cast(reinterpret_cast(source.c_str())), source.size(), 0); + parser.Parse(); + bool parseResult = parser.IsError(); + ASSERT_TRUE(parseResult); +} + +TEST_F(RegExpTest, ParseError22) +{ + RegExpParser parser = RegExpParser(chunk_); + CString source("[\\10b-G]"); + parser.Init(const_cast(reinterpret_cast(source.c_str())), source.size(), 0); + parser.Parse(); + bool parseResult = parser.IsError(); + ASSERT_TRUE(parseResult); +} + +TEST_F(RegExpTest, ParseError23) +{ + RegExpParser parser = RegExpParser(chunk_); + CString source("[\\0b-G]"); + parser.Init(const_cast(reinterpret_cast(source.c_str())), source.size(), 0); + parser.Parse(); + bool parseResult = parser.IsError(); + ASSERT_TRUE(parseResult); +} + +TEST_F(RegExpTest, ParseError24) +{ + RegExpParser parser = RegExpParser(chunk_); + CString source("("); + parser.Init(const_cast(reinterpret_cast(source.c_str())), source.size(), 16); + parser.Parse(); + bool parseResult = parser.IsError(); + ASSERT_TRUE(parseResult); +} + +TEST_F(RegExpTest, ParseError25) +{ + RegExpParser parser = RegExpParser(chunk_); + CString source(")"); + parser.Init(const_cast(reinterpret_cast(source.c_str())), source.size(), 16); + parser.Parse(); + bool parseResult = parser.IsError(); + ASSERT_TRUE(parseResult); +} + +TEST_F(RegExpTest, ParseError26) +{ + RegExpParser parser = RegExpParser(chunk_); + CString source("{"); + parser.Init(const_cast(reinterpret_cast(source.c_str())), source.size(), 16); + parser.Parse(); + bool parseResult = parser.IsError(); + ASSERT_TRUE(parseResult); +} + +TEST_F(RegExpTest, ParseError27) +{ + RegExpParser parser = RegExpParser(chunk_); + CString source("}"); + parser.Init(const_cast(reinterpret_cast(source.c_str())), source.size(), 16); + parser.Parse(); + bool parseResult = parser.IsError(); + ASSERT_TRUE(parseResult); +} + +TEST_F(RegExpTest, ParseError28) +{ + RegExpParser parser = RegExpParser(chunk_); + CString source("["); + parser.Init(const_cast(reinterpret_cast(source.c_str())), source.size(), 16); + parser.Parse(); + bool parseResult = parser.IsError(); + ASSERT_TRUE(parseResult); +} + +TEST_F(RegExpTest, ParseError29) +{ + RegExpParser parser = RegExpParser(chunk_); + CString source("]"); + parser.Init(const_cast(reinterpret_cast(source.c_str())), source.size(), 16); + parser.Parse(); + bool parseResult = parser.IsError(); + ASSERT_TRUE(parseResult); +} + +TEST_F(RegExpTest, ParseError30) +{ + RegExpParser parser = RegExpParser(chunk_); + CString source("\\c"); + parser.Init(const_cast(reinterpret_cast(source.c_str())), source.size(), 16); + parser.Parse(); + bool parseResult = parser.IsError(); + ASSERT_TRUE(parseResult); +} + +TEST_F(RegExpTest, ParseError31) +{ + RegExpParser parser = RegExpParser(chunk_); + CString source("\\c\024"); + parser.Init(const_cast(reinterpret_cast(source.c_str())), source.size(), 16); + parser.Parse(); + bool parseResult = parser.IsError(); + ASSERT_TRUE(parseResult); +} + +TEST_F(RegExpTest, ParseError32) +{ + RegExpParser parser = RegExpParser(chunk_); + CString source("[\\c]"); + parser.Init(const_cast(reinterpret_cast(source.c_str())), source.size(), 16); + parser.Parse(); + bool parseResult = parser.IsError(); + ASSERT_TRUE(parseResult); +} + +TEST_F(RegExpTest, ParseError33) +{ + RegExpParser parser = RegExpParser(chunk_); + CString source("[\\c\024]"); + parser.Init(const_cast(reinterpret_cast(source.c_str())), source.size(), 16); + parser.Parse(); + bool parseResult = parser.IsError(); + ASSERT_TRUE(parseResult); +} + +TEST_F(RegExpTest, ParseError34) +{ + RegExpParser parser = RegExpParser(chunk_); + CString source("[\\d-a]"); + parser.Init(const_cast(reinterpret_cast(source.c_str())), source.size(), 16); + parser.Parse(); + bool parseResult = parser.IsError(); + ASSERT_TRUE(parseResult); +} + +TEST_F(RegExpTest, ParseError35) +{ + RegExpParser parser = RegExpParser(chunk_); + CString source("[\\s-a]"); + parser.Init(const_cast(reinterpret_cast(source.c_str())), source.size(), 16); + parser.Parse(); + bool parseResult = parser.IsError(); + ASSERT_TRUE(parseResult); +} + +TEST_F(RegExpTest, ParseError36) +{ + RegExpParser parser = RegExpParser(chunk_); + CString source("[\\s-\\w]"); + parser.Init(const_cast(reinterpret_cast(source.c_str())), source.size(), 16); + parser.Parse(); + bool parseResult = parser.IsError(); + ASSERT_TRUE(parseResult); +} + +TEST_F(RegExpTest, ParseError37) +{ + RegExpParser parser = RegExpParser(chunk_); + CString source("[a-\\w]"); + parser.Init(const_cast(reinterpret_cast(source.c_str())), source.size(), 16); + parser.Parse(); + bool parseResult = parser.IsError(); + ASSERT_TRUE(parseResult); +} + +TEST_F(RegExpTest, ParseError38) +{ + RegExpParser parser = RegExpParser(chunk_); + CString source("\\{"); + parser.Init(const_cast(reinterpret_cast(source.c_str())), source.size(), 16); + parser.Parse(); + bool parseResult = parser.IsError(); + ASSERT_FALSE(parseResult); +} + +TEST_F(RegExpTest, ParseError39) +{ + RegExpParser parser = RegExpParser(chunk_); + CString source("\\/"); + parser.Init(const_cast(reinterpret_cast(source.c_str())), source.size(), 16); + parser.Parse(); + bool parseResult = parser.IsError(); + ASSERT_FALSE(parseResult); +} + +TEST_F(RegExpTest, ParseError40) +{ + for (char cu = 0x41; cu <= 0x5a; ++cu) { + if (!IsValidAlphaEscapeInAtom(cu)) { + CString source("\\"); + source += CString(&cu, 1); + RegExpParser parser = RegExpParser(chunk_); + parser.Init(const_cast(reinterpret_cast(source.c_str())), source.size(), 16); + parser.Parse(); + bool parseResult = parser.IsError(); + ASSERT_TRUE(parseResult); + } + } + for (char cu = 0x61; cu <= 0x7a; ++cu) { + if (!IsValidAlphaEscapeInAtom(cu)) { + CString source("\\"); + source += CString(&cu, 1); + RegExpParser parser = RegExpParser(chunk_); + parser.Init(const_cast(reinterpret_cast(source.c_str())), source.size(), 16); + parser.Parse(); + bool parseResult = parser.IsError(); + ASSERT_TRUE(parseResult); + } + } + for (char cu = 0x41; cu <= 0x5a; ++cu) { + CString source("[\\"); + if (!IsValidAlphaEscapeInAtom(cu)) { + source += CString(&cu, 1); + source += CString("]"); + RegExpParser parser = RegExpParser(chunk_); + parser.Init(const_cast(reinterpret_cast(source.c_str())), source.size(), 16); + parser.Parse(); + bool parseResult = parser.IsError(); + ASSERT_TRUE(parseResult); + } + } + for (char cu = 0x61; cu <= 0x7a; ++cu) { + CString source("[\\"); + if (!IsValidAlphaEscapeInAtom(cu)) { + source += CString(&cu, 1); + source += CString("]"); + RegExpParser parser = RegExpParser(chunk_); + parser.Init(const_cast(reinterpret_cast(source.c_str())), source.size(), 16); + parser.Parse(); + bool parseResult = parser.IsError(); + ASSERT_TRUE(parseResult); + } + } +} + +TEST_F(RegExpTest, ParseError44) +{ + RegExpParser parser = RegExpParser(chunk_); + CString source("\\1"); + parser.Init(const_cast(reinterpret_cast(source.c_str())), source.size(), 16); + parser.Parse(); + bool parseResult = parser.IsError(); + ASSERT_TRUE(parseResult); +} + +TEST_F(RegExpTest, ParseError45) +{ + RegExpParser parser = RegExpParser(chunk_); + CString source("[\\1]"); + parser.Init(const_cast(reinterpret_cast(source.c_str())), source.size(), 16); + parser.Parse(); + bool parseResult = parser.IsError(); + ASSERT_TRUE(parseResult); +} + +TEST_F(RegExpTest, ParseError46) +{ + RegExpParser parser = RegExpParser(chunk_); + CString source("\\00"); + parser.Init(const_cast(reinterpret_cast(source.c_str())), source.size(), 16); + parser.Parse(); + bool parseResult = parser.IsError(); + ASSERT_TRUE(parseResult); +} + +TEST_F(RegExpTest, ParseError47) +{ + RegExpParser parser = RegExpParser(chunk_); + CString source("[\\00]"); + parser.Init(const_cast(reinterpret_cast(source.c_str())), source.size(), 16); + parser.Parse(); + bool parseResult = parser.IsError(); + ASSERT_TRUE(parseResult); +} + +TEST_F(RegExpTest, ParseNoError1) +{ + RegExpParser parser = RegExpParser(chunk_); + CString source("a{10,2147483648}"); // 2^31 + parser.Init(const_cast(reinterpret_cast(source.c_str())), source.size(), 16); + parser.Parse(); + bool parseResult = parser.IsError(); + ASSERT_FALSE(parseResult); +} + +TEST_F(RegExpTest, ParseNoError2) +{ + RegExpParser parser = RegExpParser(chunk_); + CString source("a{10,4294967306}"); // 2^32+10 + parser.Init(const_cast(reinterpret_cast(source.c_str())), source.size(), 16); + parser.Parse(); + bool parseResult = parser.IsError(); + ASSERT_FALSE(parseResult); +} + +TEST_F(RegExpTest, ParseAndExec1) +{ + ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); + RegExpParser parser = RegExpParser(chunk_); + CString source("ab"); + parser.Init(const_cast(reinterpret_cast(source.c_str())), source.size(), 0); + parser.Parse(); + bool parseResult = parser.IsError(); + ASSERT_FALSE(parseResult); + + RegExpExecutor executor(chunk_); + CString input("abc"); + bool ret = + executor.Execute(reinterpret_cast(input.c_str()), 0, input.size(), parser.GetOriginBuffer()); + ASSERT_TRUE(ret); + + MatchResult result = executor.GetResult(thread, ret); + ASSERT_EQ(result.captures_.size(), 1); + JSHandle str = factory->NewFromCanBeCompressString("ab"); + ASSERT_TRUE(result.captures_[0].second->Compare(*str) == 0); +} + +TEST_F(RegExpTest, ParseAndExec2) +{ + ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); + RegExpParser parser = RegExpParser(chunk_); + CString source("(((ab)|(cd)|(de))|((ef)|(gh)|(jk)))"); + parser.Init(const_cast(reinterpret_cast(source.c_str())), source.size(), 0); + parser.Parse(); + bool parseResult = parser.IsError(); + ASSERT_FALSE(parseResult); + + RegExpExecutor executor(chunk_); + CString input("cabd"); + bool ret = + executor.Execute(reinterpret_cast(input.c_str()), 0, input.size(), parser.GetOriginBuffer()); + ASSERT_TRUE(ret); + + MatchResult result = executor.GetResult(thread, ret); + ASSERT_EQ(result.captures_.size(), 10); + JSHandle str = factory->NewFromCanBeCompressString("ab"); + ASSERT_TRUE(result.captures_[0].second->Compare(*str) == 0); + ASSERT_TRUE(result.captures_[1].second->Compare(*str) == 0); + ASSERT_TRUE(result.captures_[2].second->Compare(*str) == 0); + ASSERT_TRUE(result.captures_[3].second->Compare(*str) == 0); + ASSERT_TRUE(result.captures_[4].first); + ASSERT_TRUE(result.captures_[5].first); + ASSERT_TRUE(result.captures_[6].first); + ASSERT_TRUE(result.captures_[7].first); + ASSERT_TRUE(result.captures_[8].first); + ASSERT_TRUE(result.captures_[9].first); +} + +TEST_F(RegExpTest, ParseAndExec3) +{ + ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); + RegExpParser parser = RegExpParser(chunk_); + CString source("(aa|aabaac|ba|b|c)*"); + parser.Init(const_cast(reinterpret_cast(source.c_str())), source.size(), 0); + parser.Parse(); + bool parseResult = parser.IsError(); + ASSERT_FALSE(parseResult); + + RegExpExecutor executor(chunk_); + CString input("aabaac"); + bool ret = + executor.Execute(reinterpret_cast(input.c_str()), 0, input.size(), parser.GetOriginBuffer()); + ASSERT_TRUE(ret); + + MatchResult result = executor.GetResult(thread, ret); + ASSERT_EQ(result.captures_.size(), 2); + JSHandle str1 = factory->NewFromCanBeCompressString("aaba"); + JSHandle str2 = factory->NewFromCanBeCompressString("ba"); + ASSERT_TRUE(result.captures_[0].second->Compare(*str1) == 0); + ASSERT_TRUE(result.captures_[1].second->Compare(*str2) == 0); +} + +TEST_F(RegExpTest, ParseAndExec4) +{ + ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); + RegExpParser parser = RegExpParser(chunk_); + CString source("a*"); + parser.Init(const_cast(reinterpret_cast(source.c_str())), source.size(), 0); + parser.Parse(); + bool parseResult = parser.IsError(); + ASSERT_FALSE(parseResult); + + RegExpExecutor executor(chunk_); + CString input("aabaac"); + bool ret = + executor.Execute(reinterpret_cast(input.c_str()), 0, input.size(), parser.GetOriginBuffer()); + ASSERT_TRUE(ret); + + MatchResult result = executor.GetResult(thread, ret); + ASSERT_EQ(result.captures_.size(), 1); + JSHandle str = factory->NewFromCanBeCompressString("aa"); + ASSERT_TRUE(result.captures_[0].second->Compare(*str) == 0); +} + +TEST_F(RegExpTest, ParseAndExec5) +{ + ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); + RegExpParser parser = RegExpParser(chunk_); + CString source("a?"); + parser.Init(const_cast(reinterpret_cast(source.c_str())), source.size(), 0); + parser.Parse(); + bool parseResult = parser.IsError(); + ASSERT_FALSE(parseResult); + + RegExpExecutor executor(chunk_); + CString input("b"); + bool ret = + executor.Execute(reinterpret_cast(input.c_str()), 0, input.size(), parser.GetOriginBuffer()); + ASSERT_TRUE(ret); + + MatchResult result = executor.GetResult(thread, ret); + ASSERT_EQ(result.captures_.size(), 1); + JSHandle str = factory->NewFromCanBeCompressString(""); + ASSERT_TRUE(result.captures_[0].second->Compare(*str) == 0); +} + +TEST_F(RegExpTest, ParseAndExec6) +{ + ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); + RegExpParser parser = RegExpParser(chunk_); + CString source("(z)((a+)?(b+)?(c))*"); + parser.Init(const_cast(reinterpret_cast(source.c_str())), source.size(), 0); + parser.Parse(); + bool parseResult = parser.IsError(); + ASSERT_FALSE(parseResult); + + RegExpExecutor executor(chunk_); + CString input("zaacbbbcac"); + bool ret = + executor.Execute(reinterpret_cast(input.c_str()), 0, input.size(), parser.GetOriginBuffer()); + ASSERT_TRUE(ret); + + MatchResult result = executor.GetResult(thread, ret); + ASSERT_EQ(result.captures_.size(), 6); + JSHandle str1 = factory->NewFromCanBeCompressString("zaacbbbcac"); + JSHandle str2 = factory->NewFromCanBeCompressString("z"); + JSHandle str3 = factory->NewFromCanBeCompressString("ac"); + JSHandle str4 = factory->NewFromCanBeCompressString("a"); + JSHandle str5 = factory->NewFromCanBeCompressString("c"); + ASSERT_TRUE(result.captures_[0].second->Compare(*str1) == 0); + ASSERT_TRUE(result.captures_[1].second->Compare(*str2) == 0); + ASSERT_TRUE(result.captures_[2].second->Compare(*str3) == 0); + ASSERT_TRUE(result.captures_[3].second->Compare(*str4) == 0); + ASSERT_TRUE(result.captures_[4].first); + ASSERT_TRUE(result.captures_[5].second->Compare(*str5) == 0); +} + +TEST_F(RegExpTest, ParseAndExec7) +{ + ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); + RegExpParser parser = RegExpParser(chunk_); + CString source("^abc"); + parser.Init(const_cast(reinterpret_cast(source.c_str())), source.size(), 4); + parser.Parse(); + bool parseResult = parser.IsError(); + ASSERT_FALSE(parseResult); + + RegExpExecutor executor(chunk_); + CString input("ab\nabc"); + bool ret = + executor.Execute(reinterpret_cast(input.c_str()), 0, input.size(), parser.GetOriginBuffer()); + ASSERT_TRUE(ret); + + MatchResult result = executor.GetResult(thread, ret); + ASSERT_EQ(result.captures_.size(), 1); + JSHandle str = factory->NewFromCanBeCompressString("abc"); + ASSERT_TRUE(result.captures_[0].second->Compare(*str) == 0); +} + +TEST_F(RegExpTest, ParseAndExec8) +{ + ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); + RegExpParser parser = RegExpParser(chunk_); + CString source("abc$"); + parser.Init(const_cast(reinterpret_cast(source.c_str())), source.size(), 4); + parser.Parse(); + bool parseResult = parser.IsError(); + ASSERT_FALSE(parseResult); + + RegExpExecutor executor(chunk_); + CString input("ab\nabc"); + bool ret = + executor.Execute(reinterpret_cast(input.c_str()), 0, input.size(), parser.GetOriginBuffer()); + ASSERT_TRUE(ret); + + MatchResult result = executor.GetResult(thread, ret); + ASSERT_EQ(result.captures_.size(), 1); + JSHandle str = factory->NewFromCanBeCompressString("abc"); + ASSERT_TRUE(result.captures_[0].second->Compare(*str) == 0); +} + +TEST_F(RegExpTest, ParseAndExec9) +{ + ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); + RegExpParser parser = RegExpParser(chunk_); + CString source("er\\B"); + parser.Init(const_cast(reinterpret_cast(source.c_str())), source.size(), 0); + parser.Parse(); + bool parseResult = parser.IsError(); + ASSERT_FALSE(parseResult); + + RegExpExecutor executor(chunk_); + CString input("erv"); + bool ret = + executor.Execute(reinterpret_cast(input.c_str()), 0, input.size(), parser.GetOriginBuffer()); + ASSERT_TRUE(ret); + + MatchResult result = executor.GetResult(thread, ret); + ASSERT_EQ(result.captures_.size(), 1); + JSHandle str = factory->NewFromCanBeCompressString("er"); + ASSERT_TRUE(result.captures_[0].second->Compare(*str) == 0); +} + +TEST_F(RegExpTest, ParseAndExec10) +{ + ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); + RegExpParser parser = RegExpParser(chunk_); + CString source("d\\b"); + parser.Init(const_cast(reinterpret_cast(source.c_str())), source.size(), 0); + parser.Parse(); + bool parseResult = parser.IsError(); + ASSERT_FALSE(parseResult); + + RegExpExecutor executor(chunk_); + CString input("bad good"); + bool ret = + executor.Execute(reinterpret_cast(input.c_str()), 0, input.size(), parser.GetOriginBuffer()); + ASSERT_TRUE(ret); + + MatchResult result = executor.GetResult(thread, ret); + ASSERT_EQ(result.captures_.size(), 1); + JSHandle str = factory->NewFromCanBeCompressString("d"); + ASSERT_TRUE(result.captures_[0].second->Compare(*str) == 0); +} + +TEST_F(RegExpTest, ParseAndExec11) +{ + ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); + RegExpParser parser = RegExpParser(chunk_); + CString source("."); + parser.Init(const_cast(reinterpret_cast(source.c_str())), source.size(), 0); + parser.Parse(); + bool parseResult = parser.IsError(); + ASSERT_FALSE(parseResult); + + RegExpExecutor executor(chunk_); + CString input("\na"); + bool ret = + executor.Execute(reinterpret_cast(input.c_str()), 0, input.size(), parser.GetOriginBuffer()); + ASSERT_TRUE(ret); + + MatchResult result = executor.GetResult(thread, ret); + ASSERT_EQ(result.captures_.size(), 1); + JSHandle str = factory->NewFromCanBeCompressString("a"); + ASSERT_TRUE(result.captures_[0].second->Compare(*str) == 0); +} + +TEST_F(RegExpTest, ParseAndExec12) +{ + ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); + RegExpParser parser = RegExpParser(chunk_); + CString source("."); + parser.Init(const_cast(reinterpret_cast(source.c_str())), source.size(), 8); + parser.Parse(); + bool parseResult = parser.IsError(); + ASSERT_FALSE(parseResult); + + RegExpExecutor executor(chunk_); + CString input("\n"); + bool ret = + executor.Execute(reinterpret_cast(input.c_str()), 0, input.size(), parser.GetOriginBuffer()); + ASSERT_TRUE(ret); + + MatchResult result = executor.GetResult(thread, ret); + ASSERT_EQ(result.captures_.size(), 1); + JSHandle str = factory->NewFromCanBeCompressString("\n"); + ASSERT_TRUE(result.captures_[0].second->Compare(*str) == 0); +} + +TEST_F(RegExpTest, ParseAndExec13) +{ + ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); + RegExpParser parser = RegExpParser(chunk_); + CString source("abc"); + parser.Init(const_cast(reinterpret_cast(source.c_str())), source.size(), 4); + parser.Parse(); + bool parseResult = parser.IsError(); + ASSERT_FALSE(parseResult); + + RegExpExecutor executor(chunk_); + CString input("\naabc"); + bool ret = + executor.Execute(reinterpret_cast(input.c_str()), 0, input.size(), parser.GetOriginBuffer()); + ASSERT_TRUE(ret); + + MatchResult result = executor.GetResult(thread, ret); + ASSERT_EQ(result.captures_.size(), 1); + JSHandle str = factory->NewFromCanBeCompressString("abc"); + ASSERT_TRUE(result.captures_[0].second->Compare(*str) == 0); +} + +TEST_F(RegExpTest, ParseAndExec14) +{ + ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); + RegExpParser parser = RegExpParser(chunk_); + CString source("abc"); + parser.Init(const_cast(reinterpret_cast(source.c_str())), source.size(), 4); + parser.Parse(); + bool parseResult = parser.IsError(); + ASSERT_FALSE(parseResult); + + RegExpExecutor executor(chunk_); + CString input("\nbbabc"); + bool ret = + executor.Execute(reinterpret_cast(input.c_str()), 0, input.size(), parser.GetOriginBuffer()); + ASSERT_TRUE(ret); + + MatchResult result = executor.GetResult(thread, ret); + ASSERT_EQ(result.captures_.size(), 1); + JSHandle str = factory->NewFromCanBeCompressString("abc"); + ASSERT_TRUE(result.captures_[0].second->Compare(*str) == 0); +} + +TEST_F(RegExpTest, ParseAndExec15) +{ + ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); + RegExpParser parser = RegExpParser(chunk_); + CString source("a(?=a)"); + parser.Init(const_cast(reinterpret_cast(source.c_str())), source.size(), 0); + parser.Parse(); + bool parseResult = parser.IsError(); + ASSERT_FALSE(parseResult); + + RegExpExecutor executor(chunk_); + CString input("aabc"); + bool ret = + executor.Execute(reinterpret_cast(input.c_str()), 0, input.size(), parser.GetOriginBuffer()); + ASSERT_TRUE(ret); + + MatchResult result = executor.GetResult(thread, ret); + ASSERT_EQ(result.captures_.size(), 1); + JSHandle str = factory->NewFromCanBeCompressString("a"); + ASSERT_TRUE(result.captures_[0].second->Compare(*str) == 0); +} + +TEST_F(RegExpTest, ParseAndExec16) +{ + ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); + RegExpParser parser = RegExpParser(chunk_); + CString source("abc"); + parser.Init(const_cast(reinterpret_cast(source.c_str())), source.size(), 2); + parser.Parse(); + bool parseResult = parser.IsError(); + ASSERT_FALSE(parseResult); + + RegExpExecutor executor(chunk_); + CString input("ABC"); + bool ret = + executor.Execute(reinterpret_cast(input.c_str()), 0, input.size(), parser.GetOriginBuffer()); + ASSERT_TRUE(ret); + + MatchResult result = executor.GetResult(thread, ret); + ASSERT_EQ(result.captures_.size(), 1); + JSHandle str = factory->NewFromCanBeCompressString("ABC"); + ASSERT_TRUE(result.captures_[0].second->Compare(*str) == 0); +} + +TEST_F(RegExpTest, ParseAndExec17) +{ + ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); + RegExpParser parser = RegExpParser(chunk_); + CString source("a\\n"); + parser.Init(const_cast(reinterpret_cast(source.c_str())), source.size(), 2); + parser.Parse(); + bool parseResult = parser.IsError(); + ASSERT_FALSE(parseResult); + + RegExpExecutor executor(chunk_); + CString input("a\n"); + bool ret = + executor.Execute(reinterpret_cast(input.c_str()), 0, input.size(), parser.GetOriginBuffer()); + ASSERT_TRUE(ret); + + MatchResult result = executor.GetResult(thread, ret); + ASSERT_EQ(result.captures_.size(), 1); + JSHandle str = factory->NewFromCanBeCompressString("a\n"); + ASSERT_TRUE(result.captures_[0].second->Compare(*str) == 0); +} + +TEST_F(RegExpTest, ParseAndExec18) +{ + RegExpParser parser = RegExpParser(chunk_); + CString source("a(?=a)"); + parser.Init(const_cast(reinterpret_cast(source.c_str())), source.size(), 0); + parser.Parse(); + bool parseResult = parser.IsError(); + ASSERT_FALSE(parseResult); + + RegExpExecutor executor(chunk_); + CString input("ababc"); + bool ret = + executor.Execute(reinterpret_cast(input.c_str()), 0, input.size(), parser.GetOriginBuffer()); + ASSERT_FALSE(ret); +} + +TEST_F(RegExpTest, ParseAndExec19) +{ + ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); + RegExpParser parser = RegExpParser(chunk_); + CString source("a(?!a)"); + parser.Init(const_cast(reinterpret_cast(source.c_str())), source.size(), 0); + parser.Parse(); + bool parseResult = parser.IsError(); + ASSERT_FALSE(parseResult); + + RegExpExecutor executor(chunk_); + CString input("ababc"); + bool ret = + executor.Execute(reinterpret_cast(input.c_str()), 0, input.size(), parser.GetOriginBuffer()); + ASSERT_TRUE(ret); + + MatchResult result = executor.GetResult(thread, ret); + ASSERT_EQ(result.captures_.size(), 1); + JSHandle str = factory->NewFromCanBeCompressString("a"); + ASSERT_TRUE(result.captures_[0].second->Compare(*str) == 0); +} + +TEST_F(RegExpTest, ParseAndExec20) +{ + ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); + RegExpParser parser = RegExpParser(chunk_); + CString source("(?=(a+))"); + parser.Init(const_cast(reinterpret_cast(source.c_str())), source.size(), 0); + parser.Parse(); + bool parseResult = parser.IsError(); + ASSERT_FALSE(parseResult); + + RegExpExecutor executor(chunk_); + CString input("baaabac"); + bool ret = + executor.Execute(reinterpret_cast(input.c_str()), 0, input.size(), parser.GetOriginBuffer()); + ASSERT_TRUE(ret); + + MatchResult result = executor.GetResult(thread, ret); + ASSERT_EQ(result.captures_.size(), 2); + JSHandle str1 = factory->NewFromCanBeCompressString(""); + JSHandle str2 = factory->NewFromCanBeCompressString("aaa"); + ASSERT_TRUE(result.captures_[0].second->Compare(*str1) == 0); + ASSERT_TRUE(result.captures_[1].second->Compare(*str2) == 0); +} + +TEST_F(RegExpTest, ParseAndExec21) +{ + ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); + RegExpParser parser = RegExpParser(chunk_); + CString source("a(?=a(?=b))"); + parser.Init(const_cast(reinterpret_cast(source.c_str())), source.size(), 0); + parser.Parse(); + bool parseResult = parser.IsError(); + ASSERT_FALSE(parseResult); + + RegExpExecutor executor(chunk_); + CString input("caab"); + bool ret = + executor.Execute(reinterpret_cast(input.c_str()), 0, input.size(), parser.GetOriginBuffer()); + ASSERT_TRUE(ret); + + MatchResult result = executor.GetResult(thread, ret); + ASSERT_EQ(result.captures_.size(), 1); + JSHandle str = factory->NewFromCanBeCompressString("a"); + ASSERT_TRUE(result.captures_[0].second->Compare(*str) == 0); +} + +TEST_F(RegExpTest, ParseAndExec22) +{ + ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); + RegExpParser parser = RegExpParser(chunk_); + CString source(".+:"); + parser.Init(const_cast(reinterpret_cast(source.c_str())), source.size(), 0); + parser.Parse(); + bool parseResult = parser.IsError(); + ASSERT_FALSE(parseResult); + + RegExpExecutor executor(chunk_); + CString input("aaaa:aa"); + bool ret = + executor.Execute(reinterpret_cast(input.c_str()), 0, input.size(), parser.GetOriginBuffer()); + ASSERT_TRUE(ret); + + MatchResult result = executor.GetResult(thread, ret); + ASSERT_EQ(result.captures_.size(), 1); + JSHandle str = factory->NewFromCanBeCompressString("aaaa:"); + ASSERT_TRUE(result.captures_[0].second->Compare(*str) == 0); +} + +TEST_F(RegExpTest, ParseAndExec23) +{ + ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); + RegExpParser parser = RegExpParser(chunk_); + CString source("a(?<=a(?(reinterpret_cast(source.c_str())), source.size(), 0); + parser.Parse(); + bool parseResult = parser.IsError(); + ASSERT_FALSE(parseResult); + + RegExpExecutor executor(chunk_); + CString input("caab"); + bool ret = + executor.Execute(reinterpret_cast(input.c_str()), 0, input.size(), parser.GetOriginBuffer()); + ASSERT_TRUE(ret); + + MatchResult result = executor.GetResult(thread, ret); + ASSERT_EQ(result.captures_.size(), 1); + JSHandle str = factory->NewFromCanBeCompressString("a"); + ASSERT_TRUE(result.captures_[0].second->Compare(*str) == 0); +} + +TEST_F(RegExpTest, ParseAndExec24) +{ + RegExpParser parser = RegExpParser(chunk_); + CString source("a(?<=ab(?(reinterpret_cast(source.c_str())), source.size(), 0); + parser.Parse(); + bool parseResult = parser.IsError(); + ASSERT_FALSE(parseResult); + + RegExpExecutor executor(chunk_); + CString input("caab"); + bool ret = + executor.Execute(reinterpret_cast(input.c_str()), 0, input.size(), parser.GetOriginBuffer()); + ASSERT_FALSE(ret); +} + +TEST_F(RegExpTest, ParseAndExec25) +{ + ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); + RegExpParser parser = RegExpParser(chunk_); + CString source("(?<=(ab))"); + parser.Init(const_cast(reinterpret_cast(source.c_str())), source.size(), 0); + parser.Parse(); + bool parseResult = parser.IsError(); + ASSERT_FALSE(parseResult); + + RegExpExecutor executor(chunk_); + CString input("cabab"); + bool ret = + executor.Execute(reinterpret_cast(input.c_str()), 0, input.size(), parser.GetOriginBuffer()); + ASSERT_TRUE(ret); + + MatchResult result = executor.GetResult(thread, ret); + ASSERT_EQ(result.captures_.size(), 2); + JSHandle str1 = factory->NewFromCanBeCompressString(""); + JSHandle str2 = factory->NewFromCanBeCompressString("ab"); + ASSERT_TRUE(result.captures_[0].second->Compare(*str1) == 0); + ASSERT_TRUE(result.captures_[1].second->Compare(*str2) == 0); +} + +TEST_F(RegExpTest, ParseAndExec26) +{ + ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); + RegExpParser parser = RegExpParser(chunk_); + CString source("[a-z]"); + parser.Init(const_cast(reinterpret_cast(source.c_str())), source.size(), 2); + parser.Parse(); + bool parseResult = parser.IsError(); + ASSERT_FALSE(parseResult); + + RegExpExecutor executor(chunk_); + CString input("A"); + bool ret = + executor.Execute(reinterpret_cast(input.c_str()), 0, input.size(), parser.GetOriginBuffer()); + ASSERT_TRUE(ret); + + MatchResult result = executor.GetResult(thread, ret); + ASSERT_EQ(result.captures_.size(), 1); + JSHandle str = factory->NewFromCanBeCompressString("A"); + ASSERT_TRUE(result.captures_[0].second->Compare(*str) == 0); +} + +TEST_F(RegExpTest, ParseAndExec27) +{ + ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); + RegExpParser parser = RegExpParser(chunk_); + CString source("[^a-b]"); + parser.Init(const_cast(reinterpret_cast(source.c_str())), source.size(), 2); + parser.Parse(); + bool parseResult = parser.IsError(); + ASSERT_FALSE(parseResult); + + RegExpExecutor executor(chunk_); + CString input("Z"); + bool ret = + executor.Execute(reinterpret_cast(input.c_str()), 0, input.size(), parser.GetOriginBuffer()); + ASSERT_TRUE(ret); + + MatchResult result = executor.GetResult(thread, ret); + ASSERT_EQ(result.captures_.size(), 1); + JSHandle str = factory->NewFromCanBeCompressString("Z"); + ASSERT_TRUE(result.captures_[0].second->Compare(*str) == 0); +} + +TEST_F(RegExpTest, ParseAndExec28) +{ + ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); + RegExpParser parser = RegExpParser(chunk_); + CString source("\\s"); + parser.Init(const_cast(reinterpret_cast(source.c_str())), source.size(), 0); + parser.Parse(); + bool parseResult = parser.IsError(); + ASSERT_FALSE(parseResult); + + RegExpExecutor executor(chunk_); + CString input("\n"); + bool ret = + executor.Execute(reinterpret_cast(input.c_str()), 0, input.size(), parser.GetOriginBuffer()); + ASSERT_TRUE(ret); + + MatchResult result = executor.GetResult(thread, ret); + ASSERT_EQ(result.captures_.size(), 1); + JSHandle str = factory->NewFromCanBeCompressString("\n"); + ASSERT_TRUE(result.captures_[0].second->Compare(*str) == 0); +} + +TEST_F(RegExpTest, ParseAndExec29) +{ + ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); + RegExpParser parser = RegExpParser(chunk_); + CString source("()|"); + parser.Init(const_cast(reinterpret_cast(source.c_str())), source.size(), 0); + parser.Parse(); + bool parseResult = parser.IsError(); + ASSERT_FALSE(parseResult); + + RegExpExecutor executor(chunk_); + CString input(""); + bool ret = + executor.Execute(reinterpret_cast(input.c_str()), 0, input.size(), parser.GetOriginBuffer()); + ASSERT_TRUE(ret); + + MatchResult result = executor.GetResult(thread, ret); + ASSERT_EQ(result.captures_.size(), 2); + JSHandle str = factory->NewFromCanBeCompressString(""); + ASSERT_TRUE(result.captures_[0].second->Compare(*str) == 0); + ASSERT_TRUE(result.captures_[1].second->Compare(*str) == 0); +} + +TEST_F(RegExpTest, ParseAndExec30) +{ + ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); + RegExpParser parser = RegExpParser(chunk_); + CString source("|()"); + parser.Init(const_cast(reinterpret_cast(source.c_str())), source.size(), 0); + parser.Parse(); + bool parseResult = parser.IsError(); + ASSERT_FALSE(parseResult); + + RegExpExecutor executor(chunk_); + CString input(""); + bool ret = + executor.Execute(reinterpret_cast(input.c_str()), 0, input.size(), parser.GetOriginBuffer()); + ASSERT_TRUE(ret); + + MatchResult result = executor.GetResult(thread, ret); + ASSERT_EQ(result.captures_.size(), 2); + JSHandle str = factory->NewFromCanBeCompressString(""); + ASSERT_TRUE(result.captures_[0].second->Compare(*str) == 0); + ASSERT_TRUE(result.captures_[1].first); +} + +TEST_F(RegExpTest, ParseAndExec31) +{ + ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); + RegExpParser parser = RegExpParser(chunk_); + CString source("a(a|b)\\1"); + parser.Init(const_cast(reinterpret_cast(source.c_str())), source.size(), 2); + parser.Parse(); + bool parseResult = parser.IsError(); + ASSERT_FALSE(parseResult); + RegExpExecutor executor(chunk_); + CString input("aabb"); + bool ret = + executor.Execute(reinterpret_cast(input.c_str()), 0, input.length(), parser.GetOriginBuffer()); + ASSERT_TRUE(ret); + MatchResult result = executor.GetResult(thread, ret); + ASSERT_EQ(result.captures_.size(), 2); + JSHandle str1 = factory->NewFromCanBeCompressString("abb"); + JSHandle str2 = factory->NewFromCanBeCompressString("b"); + ASSERT_TRUE(result.captures_[0].second->Compare(*str1) == 0); + ASSERT_TRUE(result.captures_[1].second->Compare(*str2) == 0); +} + +TEST_F(RegExpTest, ParseAndExec32) +{ + ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); + RegExpParser parser = RegExpParser(chunk_); + CString source("(a(a|b))\\2"); + parser.Init(const_cast(reinterpret_cast(source.c_str())), source.size(), 2); + parser.Parse(); + bool parseResult = parser.IsError(); + ASSERT_FALSE(parseResult); + RegExpExecutor executor(chunk_); + CString input("aabb"); + bool ret = + executor.Execute(reinterpret_cast(input.c_str()), 0, input.length(), parser.GetOriginBuffer()); + ASSERT_TRUE(ret); + MatchResult result = executor.GetResult(thread, ret); + ASSERT_EQ(result.captures_.size(), 3); + JSHandle str1 = factory->NewFromCanBeCompressString("abb"); + JSHandle str2 = factory->NewFromCanBeCompressString("ab"); + JSHandle str3 = factory->NewFromCanBeCompressString("b"); + ASSERT_TRUE(result.captures_[0].second->Compare(*str1) == 0); + ASSERT_TRUE(result.captures_[1].second->Compare(*str2) == 0); + ASSERT_TRUE(result.captures_[2].second->Compare(*str3) == 0); +} + +TEST_F(RegExpTest, ParseAndExec33) +{ + ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); + RegExpParser parser = RegExpParser(chunk_); + CString source("qya+"); + parser.Init(const_cast(reinterpret_cast(source.c_str())), source.size(), 2); + parser.Parse(); + bool parseResult = parser.IsError(); + ASSERT_FALSE(parseResult); + RegExpExecutor executor(chunk_); + CString input("qyqya"); + bool ret = + executor.Execute(reinterpret_cast(input.c_str()), 0, input.length(), parser.GetOriginBuffer()); + ASSERT_TRUE(ret); + MatchResult result = executor.GetResult(thread, ret); + ASSERT_EQ(result.captures_.size(), 1); + JSHandle str = factory->NewFromCanBeCompressString("qya"); + ASSERT_TRUE(result.captures_[0].second->Compare(*str) == 0); +} + +TEST_F(RegExpTest, ParseAndExec34) +{ + ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); + RegExpParser parser = RegExpParser(chunk_); + CString source("qy(?=\\s+)"); + parser.Init(const_cast(reinterpret_cast(source.c_str())), source.size(), 2); + parser.Parse(); + bool parseResult = parser.IsError(); + ASSERT_FALSE(parseResult); + RegExpExecutor executor(chunk_); + CString input("qyqy "); + bool ret = + executor.Execute(reinterpret_cast(input.c_str()), 0, input.length(), parser.GetOriginBuffer()); + ASSERT_TRUE(ret); + MatchResult result = executor.GetResult(thread, ret); + ASSERT_EQ(result.captures_.size(), 1); + JSHandle str = factory->NewFromCanBeCompressString("qy"); + ASSERT_TRUE(result.captures_[0].second->Compare(*str) == 0); +} + +TEST_F(RegExpTest, ParseAndExec35) +{ + ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); + RegExpParser parser = RegExpParser(chunk_); + CString source("(\\d{4})-(\\d{2})-(\\d{2})"); + parser.Init(const_cast(reinterpret_cast(source.c_str())), source.size(), 2); + parser.Parse(); + bool parseResult = parser.IsError(); + ASSERT_FALSE(parseResult); + RegExpExecutor executor(chunk_); + CString input("xx2021-01-09"); + bool ret = + executor.Execute(reinterpret_cast(input.c_str()), 0, input.length(), parser.GetOriginBuffer()); + ASSERT_TRUE(ret); + MatchResult result = executor.GetResult(thread, ret); + ASSERT_EQ(result.captures_.size(), 4); + JSHandle str1 = factory->NewFromCanBeCompressString("2021-01-09"); + JSHandle str2 = factory->NewFromCanBeCompressString("2021"); + JSHandle str3 = factory->NewFromCanBeCompressString("01"); + JSHandle str4 = factory->NewFromCanBeCompressString("09"); + ASSERT_TRUE(result.captures_[0].second->Compare(*str1) == 0); + ASSERT_TRUE(result.captures_[1].second->Compare(*str2) == 0); + ASSERT_TRUE(result.captures_[2].second->Compare(*str3) == 0); + ASSERT_TRUE(result.captures_[3].second->Compare(*str4) == 0); +} + +TEST_F(RegExpTest, ParseAndExec36) +{ + ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); + RegExpParser parser = RegExpParser(chunk_); + CString source("quick\\s(brown).+?(jumps)"); + parser.Init(const_cast(reinterpret_cast(source.c_str())), source.size(), 2); + parser.Parse(); + bool parseResult = parser.IsError(); + ASSERT_FALSE(parseResult); + RegExpExecutor executor(chunk_); + CString input("The Quick Brown Fox Jumps Over The Lazy Dog"); + bool ret = + executor.Execute(reinterpret_cast(input.c_str()), 0, input.length(), parser.GetOriginBuffer()); + ASSERT_TRUE(ret); + MatchResult result = executor.GetResult(thread, ret); + ASSERT_EQ(result.captures_.size(), 3); + JSHandle str1 = factory->NewFromCanBeCompressString("Quick Brown Fox Jumps"); + JSHandle str2 = factory->NewFromCanBeCompressString("Brown"); + JSHandle str3 = factory->NewFromCanBeCompressString("Jumps"); + ASSERT_TRUE(result.captures_[0].second->Compare(*str1) == 0); + ASSERT_TRUE(result.captures_[1].second->Compare(*str2) == 0); + ASSERT_TRUE(result.captures_[2].second->Compare(*str3) == 0); +} + +TEST_F(RegExpTest, ParseAndExec37) +{ + ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); + RegExpParser parser = RegExpParser(chunk_); + CString source("(ab){1,2}?c"); + parser.Init(const_cast(reinterpret_cast(source.c_str())), source.size(), 2); + parser.Parse(); + bool parseResult = parser.IsError(); + ASSERT_FALSE(parseResult); + RegExpExecutor executor(chunk_); + CString input("abABc"); + bool ret = + executor.Execute(reinterpret_cast(input.c_str()), 0, input.length(), parser.GetOriginBuffer()); + ASSERT_TRUE(ret); + MatchResult result = executor.GetResult(thread, ret); + ASSERT_EQ(result.captures_.size(), 2); + JSHandle str1 = factory->NewFromCanBeCompressString("abABc"); + JSHandle str2 = factory->NewFromCanBeCompressString("AB"); + ASSERT_TRUE(result.captures_[0].second->Compare(*str1) == 0); + ASSERT_TRUE(result.captures_[1].second->Compare(*str2) == 0); +} + +TEST_F(RegExpTest, ParseAndExec38) +{ + ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); + RegExpParser parser = RegExpParser(chunk_); + CString source("^(([a-z]+)*[a-z]\\.)+[a-z]{2,}$"); + parser.Init(const_cast(reinterpret_cast(source.c_str())), source.size(), 0); + parser.Parse(); + bool parseResult = parser.IsError(); + ASSERT_FALSE(parseResult); + RegExpExecutor executor(chunk_); + CString input("www.netscape.com"); + bool ret = + executor.Execute(reinterpret_cast(input.c_str()), 0, input.length(), parser.GetOriginBuffer()); + ASSERT_TRUE(ret); + MatchResult result = executor.GetResult(thread, ret); + ASSERT_EQ(result.captures_.size(), 3); + JSHandle str1 = factory->NewFromCanBeCompressString("www.netscape.com"); + JSHandle str2 = factory->NewFromCanBeCompressString("netscape."); + JSHandle str3 = factory->NewFromCanBeCompressString("netscap"); + ASSERT_TRUE(result.captures_[0].second->Compare(*str1) == 0); + ASSERT_TRUE(result.captures_[1].second->Compare(*str2) == 0); + ASSERT_TRUE(result.captures_[2].second->Compare(*str3) == 0); +} + +TEST_F(RegExpTest, ParseAndExec39) +{ + ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); + RegExpParser parser = RegExpParser(chunk_); + CString source("(a*)b\\1+"); + parser.Init(const_cast(reinterpret_cast(source.c_str())), source.size(), 0); + parser.Parse(); + bool parseResult = parser.IsError(); + ASSERT_FALSE(parseResult); + RegExpExecutor executor(chunk_); + CString input("baaaac"); + bool ret = + executor.Execute(reinterpret_cast(input.c_str()), 0, input.length(), parser.GetOriginBuffer()); + ASSERT_TRUE(ret); + MatchResult result = executor.GetResult(thread, ret); + ASSERT_EQ(result.captures_.size(), 2); + JSHandle str1 = factory->NewFromCanBeCompressString("b"); + JSHandle str2 = factory->NewFromCanBeCompressString(""); + ASSERT_TRUE(result.captures_[0].second->Compare(*str1) == 0); + ASSERT_TRUE(result.captures_[1].second->Compare(*str2) == 0); +} + +TEST_F(RegExpTest, ParseAndExec40) +{ + ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); + RegExpParser parser = RegExpParser(chunk_); + CString source("a*?"); + parser.Init(const_cast(reinterpret_cast(source.c_str())), source.size(), 0); + parser.Parse(); + bool parseResult = parser.IsError(); + ASSERT_FALSE(parseResult); + RegExpExecutor executor(chunk_); + CString input("ab"); + bool ret = + executor.Execute(reinterpret_cast(input.c_str()), 0, input.length(), parser.GetOriginBuffer()); + ASSERT_TRUE(ret); + MatchResult result = executor.GetResult(thread, ret); + ASSERT_EQ(result.captures_.size(), 1); + JSHandle str = factory->NewFromCanBeCompressString(""); + ASSERT_TRUE(result.captures_[0].second->Compare(*str) == 0); +} + +TEST_F(RegExpTest, ParseAndExec41) +{ + ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); + RegExpParser parser = RegExpParser(chunk_); + CString source("(.*?)a(?!(a+)b\\2c)\\2(.*)"); + parser.Init(const_cast(reinterpret_cast(source.c_str())), source.size(), 0); + parser.Parse(); + bool parseResult = parser.IsError(); + ASSERT_FALSE(parseResult); + RegExpExecutor executor(chunk_); + CString input("baaabaac"); + bool ret = + executor.Execute(reinterpret_cast(input.c_str()), 0, input.length(), parser.GetOriginBuffer()); + ASSERT_TRUE(ret); + MatchResult result = executor.GetResult(thread, ret); + ASSERT_EQ(result.captures_.size(), 4); + JSHandle str1 = factory->NewFromCanBeCompressString("baaabaac"); + JSHandle str2 = factory->NewFromCanBeCompressString("ba"); + JSHandle str3 = factory->NewFromCanBeCompressString("abaac"); + ASSERT_TRUE(result.captures_[0].second->Compare(*str1) == 0); + ASSERT_TRUE(result.captures_[1].second->Compare(*str2) == 0); + ASSERT_TRUE(result.captures_[2].first); + ASSERT_TRUE(result.captures_[3].second->Compare(*str3) == 0); +} + +TEST_F(RegExpTest, ParseAndExec42) +{ + ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); + RegExpParser parser = RegExpParser(chunk_); + CString source("[a-c\\d]+"); + parser.Init(const_cast(reinterpret_cast(source.c_str())), source.size(), 0); + parser.Parse(); + bool parseResult = parser.IsError(); + ASSERT_FALSE(parseResult); + RegExpExecutor executor(chunk_); + CString input("\n\n\\abc324234"); + bool ret = + executor.Execute(reinterpret_cast(input.c_str()), 0, input.length(), parser.GetOriginBuffer()); + ASSERT_TRUE(ret); + MatchResult result = executor.GetResult(thread, ret); + ASSERT_EQ(result.captures_.size(), 1); + JSHandle str = factory->NewFromCanBeCompressString("abc324234"); + ASSERT_TRUE(result.captures_[0].second->Compare(*str) == 0); +} + +TEST_F(RegExpTest, ParseAndExec43) +{ + ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); + RegExpParser parser = RegExpParser(chunk_); + CString source("[\\d][\n][^\\d]"); + parser.Init(const_cast(reinterpret_cast(source.c_str())), source.size(), 0); + parser.Parse(); + bool parseResult = parser.IsError(); + ASSERT_FALSE(parseResult); + RegExpExecutor executor(chunk_); + CString input("line1\nline2"); + bool ret = + executor.Execute(reinterpret_cast(input.c_str()), 0, input.length(), parser.GetOriginBuffer()); + ASSERT_TRUE(ret); + MatchResult result = executor.GetResult(thread, ret); + ASSERT_EQ(result.captures_.size(), 1); + JSHandle str = factory->NewFromCanBeCompressString("1\nl"); + ASSERT_TRUE(result.captures_[0].second->Compare(*str) == 0); +} + +TEST_F(RegExpTest, ParseAndExec44) +{ + ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); + RegExpParser parser = RegExpParser(chunk_); + CString source(".[\b]."); + parser.Init(const_cast(reinterpret_cast(source.c_str())), source.size(), 0); + parser.Parse(); + bool parseResult = parser.IsError(); + ASSERT_FALSE(parseResult); + RegExpExecutor executor(chunk_); + CString input("abc\bdef"); + bool ret = + executor.Execute(reinterpret_cast(input.c_str()), 0, input.length(), parser.GetOriginBuffer()); + ASSERT_TRUE(ret); + MatchResult result = executor.GetResult(thread, ret); + ASSERT_EQ(result.captures_.size(), 1); + JSHandle str = factory->NewFromCanBeCompressString("c\bd"); + ASSERT_TRUE(result.captures_[0].second->Compare(*str) == 0); +} + +TEST_F(RegExpTest, ParseAndExec45) +{ + ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); + RegExpParser parser = RegExpParser(chunk_); + CString source("[^\b]+"); + parser.Init(const_cast(reinterpret_cast(source.c_str())), source.size(), 0); + parser.Parse(); + bool parseResult = parser.IsError(); + ASSERT_FALSE(parseResult); + RegExpExecutor executor(chunk_); + CString input("easy\bto\u0008ride"); + bool ret = + executor.Execute(reinterpret_cast(input.c_str()), 0, input.length(), parser.GetOriginBuffer()); + ASSERT_TRUE(ret); + MatchResult result = executor.GetResult(thread, ret); + ASSERT_EQ(result.captures_.size(), 1); + JSHandle str = factory->NewFromCanBeCompressString("easy"); + ASSERT_TRUE(result.captures_[0].second->Compare(*str) == 0); +} + +TEST_F(RegExpTest, ParseAndExec46) +{ + ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); + RegExpParser parser = RegExpParser(chunk_); + CString source("([\\S]+([ \t]+[\\S]+)*)[ \t]*=[ \t]*[\\S]+"); + parser.Init(const_cast(reinterpret_cast(source.c_str())), source.size(), 0); + parser.Parse(); + bool parseResult = parser.IsError(); + ASSERT_FALSE(parseResult); + RegExpExecutor executor(chunk_); + CString input("Course_Creator = Test"); + bool ret = + executor.Execute(reinterpret_cast(input.c_str()), 0, input.length(), parser.GetOriginBuffer()); + ASSERT_TRUE(ret); + MatchResult result = executor.GetResult(thread, ret); + ASSERT_EQ(result.captures_.size(), 3); + JSHandle str1 = factory->NewFromCanBeCompressString("Course_Creator = Test"); + JSHandle str2 = factory->NewFromCanBeCompressString("Course_Creator"); + ASSERT_TRUE(result.captures_[0].second->Compare(*str1) == 0); + ASSERT_FALSE(result.captures_[1].first); + ASSERT_TRUE(result.captures_[1].second->Compare(*str2) == 0); + ASSERT_TRUE(result.captures_[2].first); +} + +TEST_F(RegExpTest, ParseAndExec47) +{ + ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); + RegExpParser parser = RegExpParser(chunk_); + CString source("[^o]t\\b"); + parser.Init(const_cast(reinterpret_cast(source.c_str())), source.size(), 2); + parser.Parse(); + bool parseResult = parser.IsError(); + ASSERT_FALSE(parseResult); + RegExpExecutor executor(chunk_); + CString input("pilOt\nsoviet robot\topenoffice"); + bool ret = + executor.Execute(reinterpret_cast(input.c_str()), 0, input.length(), parser.GetOriginBuffer()); + ASSERT_TRUE(ret); + MatchResult result = executor.GetResult(thread, ret); + ASSERT_EQ(result.captures_.size(), 1); + JSHandle str = factory->NewFromCanBeCompressString("et"); + ASSERT_TRUE(result.captures_[0].second->Compare(*str) == 0); +} + +TEST_F(RegExpTest, ParseAndExec49) +{ + ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); + RegExpParser parser = RegExpParser(chunk_); + CString source("(a(b)\\4(5)(5))"); + parser.Init(const_cast(reinterpret_cast(source.c_str())), source.size(), 2); + parser.Parse(); + bool parseResult = parser.IsError(); + ASSERT_FALSE(parseResult); + RegExpExecutor executor(chunk_); + CString input("ab55"); + bool ret = + executor.Execute(reinterpret_cast(input.c_str()), 0, input.length(), parser.GetOriginBuffer()); + ASSERT_TRUE(ret); + MatchResult result = executor.GetResult(thread, ret); + ASSERT_EQ(result.captures_.size(), 5); + JSHandle str1 = factory->NewFromCanBeCompressString("ab55"); + JSHandle str2 = factory->NewFromCanBeCompressString("ab55"); + JSHandle str3 = factory->NewFromCanBeCompressString("b"); + JSHandle str4 = factory->NewFromCanBeCompressString("5"); + JSHandle str5 = factory->NewFromCanBeCompressString("5"); + ASSERT_TRUE(result.captures_[0].second->Compare(*str1) == 0); + ASSERT_TRUE(result.captures_[1].second->Compare(*str2) == 0); + ASSERT_TRUE(result.captures_[2].second->Compare(*str3) == 0); + ASSERT_TRUE(result.captures_[3].second->Compare(*str4) == 0); + ASSERT_TRUE(result.captures_[4].second->Compare(*str5) == 0); +} + +TEST_F(RegExpTest, ParseAndExec50) +{ + ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); + RegExpParser parser = RegExpParser(chunk_); + CString source("(?\\d{4})-(?\\d{2}-(?\\d\\d))"); + parser.Init(const_cast(reinterpret_cast(source.c_str())), source.size(), 0); + parser.Parse(); + bool parseResult = parser.IsError(); + ASSERT_FALSE(parseResult); + RegExpExecutor executor(chunk_); + CString input("2020-12-31"); + bool ret = + executor.Execute(reinterpret_cast(input.c_str()), 0, input.length(), parser.GetOriginBuffer()); + ASSERT_TRUE(ret); + MatchResult result = executor.GetResult(thread, ret); + ASSERT_EQ(result.captures_.size(), 4); + JSHandle str1 = factory->NewFromCanBeCompressString("2020-12-31"); + JSHandle str2 = factory->NewFromCanBeCompressString("2020"); + JSHandle str3 = factory->NewFromCanBeCompressString("12-31"); + JSHandle str4 = factory->NewFromCanBeCompressString("31"); + ASSERT_TRUE(result.captures_[0].second->Compare(*str1) == 0); + ASSERT_TRUE(result.captures_[1].second->Compare(*str2) == 0); + ASSERT_TRUE(result.captures_[2].second->Compare(*str3) == 0); + ASSERT_TRUE(result.captures_[3].second->Compare(*str4) == 0); +} + +TEST_F(RegExpTest, ParseAndExec51) +{ + RegExpParser parser = RegExpParser(chunk_); + CString source("\\u0000"); + parser.Init(const_cast(reinterpret_cast(source.c_str())), source.size(), 0); + parser.Parse(); + bool parseResult = parser.IsError(); + ASSERT_FALSE(parseResult); + RegExpExecutor executor(chunk_); + std::u16string input(u"\u0000"); + bool ret = executor.Execute(reinterpret_cast(input.c_str()), 0, input.length() + 1, + parser.GetOriginBuffer(), true); + ASSERT_TRUE(ret); + MatchResult result = executor.GetResult(thread, ret); + ASSERT_EQ(result.captures_.size(), 1); +} + +TEST_F(RegExpTest, ParseAndExec52) +{ + ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); + RegExpParser parser = RegExpParser(chunk_); + CString source("(aa).+\\1"); + parser.Init(const_cast(reinterpret_cast(source.c_str())), source.size(), 0); + parser.Parse(); + bool parseResult = parser.IsError(); + ASSERT_FALSE(parseResult); + RegExpExecutor executor(chunk_); + CString input("aabcdaabcd"); + bool ret = + executor.Execute(reinterpret_cast(input.c_str()), 0, input.length(), parser.GetOriginBuffer()); + ASSERT_TRUE(ret); + MatchResult result = executor.GetResult(thread, ret); + ASSERT_EQ(result.captures_.size(), 2); + JSHandle str1 = factory->NewFromCanBeCompressString("aabcdaa"); + JSHandle str2 = factory->NewFromCanBeCompressString("aa"); + ASSERT_TRUE(result.captures_[0].second->Compare(*str1) == 0); + ASSERT_TRUE(result.captures_[1].second->Compare(*str2) == 0); +} + +TEST_F(RegExpTest, ParseAndExec53) +{ + ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); + RegExpParser parser = RegExpParser(chunk_); + CString source("\\x01"); + parser.Init(const_cast(reinterpret_cast(source.c_str())), source.size(), 0); + parser.Parse(); + bool parseResult = parser.IsError(); + ASSERT_FALSE(parseResult); + RegExpExecutor executor(chunk_); + std::u16string input(u"\u0001"); + bool ret = executor.Execute(reinterpret_cast(input.c_str()), 0, input.length(), + parser.GetOriginBuffer(), true); + ASSERT_TRUE(ret); + MatchResult result = executor.GetResult(thread, ret); + ASSERT_EQ(result.captures_.size(), 1); + JSHandle str = factory->NewFromCanBeCompressString("\u0001"); + ASSERT_TRUE(result.captures_[0].second->Compare(*str) == 0); +} + +TEST_F(RegExpTest, ParseAndExec54) +{ + RegExpParser parser = RegExpParser(chunk_); + CString source("\\bot"); + parser.Init(const_cast(reinterpret_cast(source.c_str())), source.size(), 0); + parser.Parse(); + bool parseResult = parser.IsError(); + ASSERT_FALSE(parseResult); + RegExpExecutor executor(chunk_); + CString input("pilot\nsoviet robot\topenoffice"); + bool ret = executor.Execute(reinterpret_cast(input.c_str()), 0, input.length(), + parser.GetOriginBuffer(), false); + ASSERT_FALSE(ret); +} + +TEST_F(RegExpTest, ParseAndExec55) +{ + ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); + RegExpParser parser = RegExpParser(chunk_); + CString source("e\\b"); + parser.Init(const_cast(reinterpret_cast(source.c_str())), source.size(), 0); + parser.Parse(); + bool parseResult = parser.IsError(); + ASSERT_FALSE(parseResult); + RegExpExecutor executor(chunk_); + CString input("c\u0065"); + bool ret = executor.Execute(reinterpret_cast(input.c_str()), 0, input.length(), + parser.GetOriginBuffer(), false); + ASSERT_TRUE(ret); + MatchResult result = executor.GetResult(thread, ret); + ASSERT_EQ(result.captures_.size(), 1); + JSHandle str = factory->NewFromCanBeCompressString("e"); + ASSERT_TRUE(result.captures_[0].second->Compare(*str) == 0); +} + +TEST_F(RegExpTest, ParseAndExec56) +{ + ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); + RegExpParser parser = RegExpParser(chunk_); + CString source("a啊"); + parser.Init(const_cast(reinterpret_cast(source.c_str())), source.size(), 0); + parser.Parse(); + bool parseResult = parser.IsError(); + ASSERT_FALSE(parseResult); + RegExpExecutor executor(chunk_); + std::u16string input(u"a啊"); + bool ret = executor.Execute(reinterpret_cast(input.c_str()), 0, input.length(), + parser.GetOriginBuffer(), true); + ASSERT_TRUE(ret); + MatchResult result = executor.GetResult(thread, ret); + ASSERT_EQ(result.captures_.size(), 1); + JSHandle str = factory->NewFromString("a啊"); + ASSERT_TRUE(result.captures_[0].second->Compare(*str) == 0); +} + +TEST_F(RegExpTest, ParseAndExec57) +{ + RegExpParser parser = RegExpParser(chunk_); + CString source("\\udf06"); + parser.Init(const_cast(reinterpret_cast(source.c_str())), source.size(), 16); + parser.Parse(); + bool parseResult = parser.IsError(); + ASSERT_FALSE(parseResult); + RegExpExecutor executor(chunk_); + char16_t data[] = {0xd834, 0xdf06}; + bool ret = executor.Execute(reinterpret_cast(data), 0, 2, parser.GetOriginBuffer(), true); + ASSERT_FALSE(ret); +} + +TEST_F(RegExpTest, ParseAndExec58) +{ + ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); + RegExpParser parser = RegExpParser(chunk_); + CString source("\\udf06"); + parser.Init(const_cast(reinterpret_cast(source.c_str())), source.size(), 0); + parser.Parse(); + bool parseResult = parser.IsError(); + ASSERT_FALSE(parseResult); + RegExpExecutor executor(chunk_); + char16_t data[] = {0xd834, 0xdf06}; + bool ret = executor.Execute(reinterpret_cast(data), 0, 2, parser.GetOriginBuffer(), true); + ASSERT_TRUE(ret); + MatchResult result = executor.GetResult(thread, ret); + ASSERT_EQ(result.captures_.size(), 1); + char16_t data1[] = {0xdf06}; + JSHandle str = factory->NewFromUtf16UnCheck(reinterpret_cast(data1), 1, true); + ASSERT_TRUE(result.captures_[0].second->Compare(*str) == 0); +} + +TEST_F(RegExpTest, ParseAndExec59) +{ + ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); + RegExpParser parser = RegExpParser(chunk_); + CString source("\\v"); + parser.Init(const_cast(reinterpret_cast(source.c_str())), source.size(), 0); + parser.Parse(); + bool parseResult = parser.IsError(); + ASSERT_FALSE(parseResult); + + RegExpExecutor executor(chunk_); + CString input("\u000B"); + bool ret = + executor.Execute(reinterpret_cast(input.c_str()), 0, input.size(), parser.GetOriginBuffer()); + ASSERT_TRUE(ret); + + MatchResult result = executor.GetResult(thread, ret); + ASSERT_EQ(result.captures_.size(), 1); + JSHandle str = factory->NewFromCanBeCompressString("\u000B"); + ASSERT_TRUE(result.captures_[0].second->Compare(*str) == 0); +} + +TEST_F(RegExpTest, RangeSet1) +{ + std::list> listInput = { + std::make_pair(1, 1), + std::make_pair(2, 2), + std::make_pair(3, 3), + }; + std::list> listExpected = { + std::make_pair(1, 5), + }; + RangeSet rangeResult(listInput); + RangeSet rangeExpected(listExpected); + rangeResult.Insert(4, 5); + rangeResult.Compress(); + EXPECT_EQ(rangeResult, rangeExpected); +} + +TEST_F(RegExpTest, RangeSet2) +{ + std::list> listExpected = { + std::make_pair(4, 5), + }; + RangeSet rangeResult; + RangeSet rangeExpected(listExpected); + rangeResult.Insert(4, 5); + rangeResult.Compress(); + EXPECT_EQ(rangeResult, rangeExpected); +} + +TEST_F(RegExpTest, RangeSet3) +{ + std::list> listInput = { + std::make_pair(2, 2), + }; + std::list> listExpected = { + std::make_pair(1, 5), + }; + RangeSet rangeResult(listInput); + RangeSet rangeExpected(listExpected); + rangeResult.Insert(1, 5); + rangeResult.Compress(); + EXPECT_EQ(rangeResult, rangeExpected); +} + +TEST_F(RegExpTest, RangeSet4) +{ + std::list> listInput = { + std::make_pair(1, 5), + }; + std::list> listExpected = { + std::make_pair(1, 5), + }; + RangeSet rangeResult(listInput); + RangeSet rangeExpected(listExpected); + rangeResult.Insert(2, 4); + rangeResult.Compress(); + EXPECT_EQ(rangeResult, rangeExpected); +} + +TEST_F(RegExpTest, RangeSet5) +{ + std::list> listInput = { + std::make_pair(1, 2), + std::make_pair(9, UINT16_MAX), + }; + std::list> listExpected = { + std::make_pair(1, 2), + std::make_pair(4, 7), + std::make_pair(9, UINT16_MAX), + }; + RangeSet rangeResult(listInput); + RangeSet rangeExpected(listExpected); + rangeResult.Insert(4, 7); + rangeResult.Compress(); + EXPECT_EQ(rangeResult, rangeExpected); +} + +TEST_F(RegExpTest, RangeSet6) +{ + std::list> listExpected = { + std::make_pair(0, UINT16_MAX), + }; + RangeSet rangeResult; + RangeSet rangeExpected(listExpected); + rangeResult.Invert(false); + EXPECT_EQ(rangeResult, rangeExpected); +} + +TEST_F(RegExpTest, RangeSet7) +{ + std::list> listInput = { + std::make_pair(1, 5), + }; + std::list> listExpected = { + std::make_pair(0, 0), + std::make_pair(6, UINT16_MAX), + }; + RangeSet rangeResult(listInput); + RangeSet rangeExpected(listExpected); + rangeResult.Invert(false); + EXPECT_EQ(rangeResult, rangeExpected); +} + +TEST_F(RegExpTest, RangeSet8) +{ + std::list> listInput = { + std::make_pair(1, 5), + std::make_pair(0xfffe, UINT16_MAX), + }; + std::list> listExpected = { + std::make_pair(0, 0), + std::make_pair(6, 0xfffd), + }; + RangeSet rangeResult(listInput); + RangeSet rangeExpected(listExpected); + rangeResult.Invert(false); + EXPECT_EQ(rangeResult, rangeExpected); +} + +TEST_F(RegExpTest, RangeSet9) +{ + std::list> listInput = { + std::make_pair(0, 5), + std::make_pair(0xfffe, 0xfffe), + }; + std::list> listExpected = { + std::make_pair(6, 0xfffd), + std::make_pair(UINT16_MAX, UINT16_MAX), + }; + RangeSet rangeResult(listInput); + RangeSet rangeExpected(listExpected); + rangeResult.Invert(false); + EXPECT_EQ(rangeResult, rangeExpected); +} + +TEST_F(RegExpTest, RangeSet10) +{ + std::list> listInput = { + std::make_pair(0, UINT16_MAX), + }; + RangeSet rangeResult(listInput); + RangeSet rangeExpected; + rangeResult.Invert(false); + EXPECT_EQ(rangeResult, rangeExpected); +} + +} // namespace panda::test diff --git a/tests/runtime/snapshot/snapshot_test.cpp b/tests/runtime/snapshot/snapshot_test.cpp new file mode 100644 index 000000000..483634add --- /dev/null +++ b/tests/runtime/snapshot/snapshot_test.cpp @@ -0,0 +1,52 @@ +/* + * Copyright (c) 2021-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 "gtest/gtest.h" +#include "test_helper.h" + +#include "plugins/ecmascript/runtime/ecma_vm.h" +#include "plugins/ecmascript/runtime/global_env.h" +#include "plugins/ecmascript/runtime/js_thread.h" +#include "include/runtime.h" +#include "plugins/ecmascript/runtime/js_handle.h" +#include "plugins/ecmascript/runtime/snapshot/mem/snapshot.h" +#include "plugins/ecmascript/runtime/js_hclass.h" +#include "plugins/ecmascript/runtime/object_factory.h" + +using namespace panda::coretypes; +using namespace panda::ecmascript; + +namespace panda::test { +class SnapShotTest : public testing::Test { +public: + void SetUp() override + { + TestHelper::CreateEcmaVMWithScope(instance, thread, scope); + } + + void TearDown() override + { + TestHelper::DestroyEcmaVMWithScope(instance, scope); + } + + PandaVM *instance {nullptr}; + EcmaHandleScope *scope {nullptr}; + JSThread *thread; +}; + +TEST_F(SnapShotTest, Serialize) +{ +} +} // namespace panda::test diff --git a/tests/runtime/tooling/CMakeLists.txt b/tests/runtime/tooling/CMakeLists.txt new file mode 100644 index 000000000..8617dbc0e --- /dev/null +++ b/tests/runtime/tooling/CMakeLists.txt @@ -0,0 +1,60 @@ +# Copyright (c) 2021-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. + +panda_add_gtest( + NO_CORES + NAME debug_api_tests_ecma + SOURCES + test_list.cpp + launcher.cpp + LIBRARIES + debugtest + INCLUDE_DIRS + ${PANDA_ROOT} + SANITIZERS + ${PANDA_SANITIZERS_LIST} +) + +add_dependencies(debug_api_tests_ecma arkstdlib) + +target_compile_definitions(debug_api_tests_ecma + PUBLIC + DEBUG_LIBRARY_PATH="$" + PANDA_STD_LIB="${PANDA_BINARY_ROOT}/pandastdlib/arkstdlib.abc") + +set(JS_SOURCES + Sample + GetVariable + SetVariable + GetFrame + FramePop + RestartFrame + SetNotification + ExceptionTest +) + +foreach(c ${JS_SOURCES}) + add_custom_target(es2panda_convert_${c} + COMMAND echo "Convert js: ${c} to js/${c}.abc" + COMMAND mkdir -p "${CMAKE_CURRENT_BINARY_DIR}/js/" + COMMAND ${PANDA_RUN_PREFIX} $ --opt-level=0 --debug-info ${CMAKE_CURRENT_SOURCE_DIR}/js/${c}.js --output ${CMAKE_CURRENT_BINARY_DIR}/js/${c}.abc + ) + add_dependencies(debug_api_tests_ecma_gtests es2panda_convert_${c}) +endforeach() + +add_gtests( + arkruntime_ecmascript_options_test + options_test/options_test.cpp +) + +add_check_style(".") diff --git a/tests/runtime/tooling/api_tests/api_tests.h b/tests/runtime/tooling/api_tests/api_tests.h new file mode 100644 index 000000000..a2b70b155 --- /dev/null +++ b/tests/runtime/tooling/api_tests/api_tests.h @@ -0,0 +1,26 @@ +/** + * Copyright (c) 2021-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 "js/js_method_event_test.h" +#include "js/js_breakpoint_test.h" +#include "js/js_get_current_frame_test.h" +#include "js/js_enumerate_frames_test.h" +#include "js/js_frame_pop_test.h" +#include "js/js_single_step_test.h" +#include "js/js_restart_frame_test.h" +#include "js/js_vm_event_test.h" +#include "js/js_set_notification_test.h" +#include "js/js_exception_events_test.h" +#include "js/js_set_variable_test.h" +#include "js/js_get_variable_test.h" diff --git a/tests/runtime/tooling/api_tests/js/js_breakpoint_test.h b/tests/runtime/tooling/api_tests/js/js_breakpoint_test.h new file mode 100644 index 000000000..03e6a5156 --- /dev/null +++ b/tests/runtime/tooling/api_tests/js/js_breakpoint_test.h @@ -0,0 +1,73 @@ +/** + * Copyright (c) 2021-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 PANDA_TOOLING_TEST_JS_BREAKPOINT_TEST_H +#define PANDA_TOOLING_TEST_JS_BREAKPOINT_TEST_H + +#include "test_util.h" + +namespace panda::tooling::test { +class JsBreakpointTest : public ApiTest { +public: + JsBreakpointTest() + { + vm_start = [this] { + location_ = TestUtil::GetLocation("Sample.js", 22, panda_file_.c_str()); + ASSERT_TRUE(location_.GetMethodId().IsValid()); + return true; + }; + + breakpoint = [this](PtThread, Method *, const PtLocation &location) { + ASSERT_TRUE(location.GetMethodId().IsValid()); + ASSERT_LOCATION_EQ(location, location_); + ++breakpoint_counter_; + return true; + }; + + load_module = [this](std::string_view moduleName) { + if (moduleName.find(panda_file_.c_str()) == std::string_view::npos) { + return true; + } + ASSERT_SUCCESS(debug_interface->SetBreakpoint(location_)); + auto error = debug_interface->SetBreakpoint(location_); + ASSERT_FALSE(!error); + return true; + }; + + vm_death = [this]() { + ASSERT_EQ(breakpoint_counter_, 2U); + return true; + }; + } + + std::pair GetEntryPoint() override + { + return {panda_file_.c_str(), entry_point_.c_str()}; + } + +private: + std::string panda_file_ = "js/Sample.abc"; + std::string entry_point_ = "_GLOBAL::func_main_0"; + PtLocation location_ {nullptr, PtLocation::EntityId(0), 0}; + size_t breakpoint_counter_ = 0; +}; + +std::unique_ptr GetJsBreakpointTest() +{ + return std::make_unique(); +} +} // namespace panda::tooling::test + +#endif // PANDA_TOOLING_TEST_JS_BREAKPOINT_TEST_H diff --git a/tests/runtime/tooling/api_tests/js/js_enumerate_frames_test.h b/tests/runtime/tooling/api_tests/js/js_enumerate_frames_test.h new file mode 100644 index 000000000..b2167f7cc --- /dev/null +++ b/tests/runtime/tooling/api_tests/js/js_enumerate_frames_test.h @@ -0,0 +1,73 @@ +/** + * Copyright (c) 2021-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 PANDA_TOOLING_TEST_JS_ENUMERATE_FRAMES_TEST_H +#define PANDA_TOOLING_TEST_JS_ENUMERATE_FRAMES_TEST_H + +#include "test_util.h" + +namespace panda::tooling::test { +class JsEnumerateFrameTest : public ApiTest { +public: + JsEnumerateFrameTest() + { + vm_death = [this]() { + ASSERT_EQ(count_frames_, 9U); + return true; + }; + + method_exit = [this](PtThread thread, Method *method, bool, VRegValue) { + if (method->GetFullName() == "_GLOBAL::func_method3_3") { + ASSERT_EQ(count_frames_, 0U); + debug_interface->EnumerateFrames(thread, callback); + ASSERT_EQ(count_frames_, 4U); + } else if (method->GetFullName() == "_GLOBAL::func_method2_2") { + ASSERT_EQ(count_frames_, 4U); + debug_interface->EnumerateFrames(thread, callback); + ASSERT_EQ(count_frames_, 7U); + } else if (method->GetFullName() == "_GLOBAL::func_method1_1") { + ASSERT_EQ(count_frames_, 7U); + debug_interface->EnumerateFrames(thread, callback); + ASSERT_EQ(count_frames_, 9U); + } + return true; + }; + } + + std::pair GetEntryPoint() override + { + return {panda_file_.c_str(), entry_point_.c_str()}; + } + +private: + std::string panda_file_ = "js/GetFrame.abc"; + std::string entry_point_ = "_GLOBAL::func_main_0"; + size_t count_frames_ = 0; + + std::function callback {[this](const PtFrame &debugFrame) { + this->count_frames_++; + if (debugFrame.GetArgumentNum() != 1) + return true; + return false; + }}; +}; + +std::unique_ptr JsEnumerateFramesTest() +{ + return std::make_unique(); +} +} // namespace panda::tooling::test + +#endif // PANDA_TOOLING_TEST_JS_ENUMERATE_FRAMES_TEST_H diff --git a/tests/runtime/tooling/api_tests/js/js_exception_events_test.h b/tests/runtime/tooling/api_tests/js/js_exception_events_test.h new file mode 100644 index 000000000..fdce4363d --- /dev/null +++ b/tests/runtime/tooling/api_tests/js/js_exception_events_test.h @@ -0,0 +1,90 @@ +/** + * Copyright (c) 2021-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 PANDA_TOOLING_TEST_JS_EXCEPTION_EVENTS_TEST_H +#define PANDA_TOOLING_TEST_JS_EXCEPTION_EVENTS_TEST_H + +#include "test_util.h" +#include "runtime/include/tooling/pt_location.h" + +namespace panda::tooling::test { +class JsExceptionEventTest : public ApiTest { +public: + JsExceptionEventTest() + { + vm_death = [this]() { + ASSERT_EQ(exception_counter_, 1U); + ASSERT_EQ(exception_catch_counter_, 1U); + return true; + }; + + method_entry = [this](PtThread thread, Method *method) { + if (method->GetFullName() == "_GLOBAL::func_main_0") { + ASSERT_TRUE(thread_id_ == PtThread::NONE.GetId()); + thread_id_ = thread.GetId(); + } + return true; + }; + + method_exit = [this](PtThread, Method *method, bool, VRegValue) { + if (method->GetFullName() == "_GLOBAL::func_main_0") { + thread_id_ = PtThread::NONE.GetId(); + } + return true; + }; + + exception = [this](PtThread thread, Method *, const PtLocation &, ObjectHeader *, Method *, + const PtLocation &catch_location) { + if (thread_id_ != thread.GetId()) { + return true; + } + + ++exception_counter_; + catch_location_ = catch_location; + return true; + }; + + exception_catch = [this](PtThread thread, Method *, const PtLocation &location, ObjectHeader *) { + if (thread_id_ != thread.GetId()) { + return true; + } + + ASSERT_LOCATION_EQ(location, catch_location_); + ++exception_catch_counter_; + return true; + }; + } + + std::pair GetEntryPoint() override + { + return {panda_file_.c_str(), entry_point_.c_str()}; + } + +private: + std::string panda_file_ = "js/ExceptionTest.abc"; + std::string entry_point_ = "_GLOBAL::func_main_0"; + size_t exception_counter_ = 0; + size_t exception_catch_counter_ = 0; + uint32_t thread_id_ = PtThread::NONE.GetId(); + PtLocation catch_location_ {nullptr, PtLocation::EntityId(0), 0}; +}; + +std::unique_ptr GetJsExceptionEventTest() +{ + return std::make_unique(); +} +} // namespace panda::tooling::test + +#endif // PANDA_TOOLING_TEST_JS_EXCEPTION_EVENTS_TEST_H diff --git a/tests/runtime/tooling/api_tests/js/js_frame_pop_test.h b/tests/runtime/tooling/api_tests/js/js_frame_pop_test.h new file mode 100644 index 000000000..9b1502fe7 --- /dev/null +++ b/tests/runtime/tooling/api_tests/js/js_frame_pop_test.h @@ -0,0 +1,113 @@ +/** + * Copyright (c) 2021-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 PANDA_TOOLING_TEST_JS_FRAME_POP_TEST_H +#define PANDA_TOOLING_TEST_JS_FRAME_POP_TEST_H + +#include "test_util.h" + +namespace panda::tooling::test { +class JsFramePopTest : public ApiTest { +public: + JsFramePopTest() + { + vm_start = [this] { + location_ = TestUtil::GetLocation("FramePop.js", 33, panda_file_.c_str()); + ASSERT_TRUE(location_.GetMethodId().IsValid()); + return true; + }; + + vm_death = [this] { + ASSERT_EQ(breakpoint_counter_, 2U); + ASSERT_EQ(frame_pop_counter_, 2U); + return true; + }; + + scenario = [this]() { + PtThread suspended = TestUtil::WaitForBreakpoint(location_); + ASSERT_NE(suspended.GetId(), PtThread::NONE.GetId()); + + TestUtil::Continue(); + + suspended = TestUtil::WaitForBreakpoint(location_); + ASSERT_NE(suspended.GetId(), PtThread::NONE.GetId()); + + // No more notifications + TestUtil::Continue(); + + ASSERT_EXITED(); + return true; + }; + + breakpoint = [this](PtThread thread, Method *, const PtLocation &location) { + ASSERT_TRUE(location.GetMethodId().IsValid()); + ASSERT_LOCATION_EQ(location, location_); + ++breakpoint_counter_; + TestUtil::SuspendUntilContinue(DebugEvent::BREAKPOINT, thread, location); + if (breakpoint_counter_ == 1) { + ASSERT_SUCCESS(debug_interface->NotifyFramePop(thread, 0)); + ASSERT_SUCCESS(debug_interface->NotifyFramePop(thread, 2)); + } + return true; + }; + + load_module = [this](std::string_view moduleName) { + if (moduleName.find(panda_file_.c_str()) == std::string_view::npos) { + return true; + } + ASSERT_SUCCESS(debug_interface->SetBreakpoint(location_)); + return true; + }; + + frame_pop = [this](PtThread thread_id, Method *method, bool was_popped_by_exception) { + auto method_name = method->GetFullName(); + if (method_name == "_GLOBAL::func_frame0_4") { + ASSERT_EQ(frame_pop_counter_, 0U); + } else if (method_name == "_GLOBAL::func_frame2_2") { + ASSERT_EQ(frame_pop_counter_, 1U); + } else { + // Not expected frame pop + return false; + } + + ASSERT_NE(thread_id.GetId(), PtThread::NONE.GetId()); + ASSERT_EQ(was_popped_by_exception, false); + ++frame_pop_counter_; + return true; + }; + } + + std::pair GetEntryPoint() override + { + return {panda_file_.c_str(), entry_point_.c_str()}; + } + +private: + std::string panda_file_ = "js/FramePop.abc"; + std::string entry_point_ = "_GLOBAL::func_main_0"; + PtLocation location_ {nullptr, PtLocation::EntityId(0), 0}; + size_t breakpoint_counter_ = 0; + size_t frame_pop_counter_ = 0; + PtMethod pop_method_ {nullptr}; +}; + +std::unique_ptr GetJsFramePopTest() +{ + return std::make_unique(); +} + +} // namespace panda::tooling::test + +#endif // PANDA_TOOLING_TEST_JS_FRAME_POP_TEST_H diff --git a/tests/runtime/tooling/api_tests/js/js_get_current_frame_test.h b/tests/runtime/tooling/api_tests/js/js_get_current_frame_test.h new file mode 100644 index 000000000..129c99670 --- /dev/null +++ b/tests/runtime/tooling/api_tests/js/js_get_current_frame_test.h @@ -0,0 +1,69 @@ +/** + * Copyright (c) 2021-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 PANDA_TOOLING_TEST_JS_GET_CURRENT_FRAME_TEST_H +#define PANDA_TOOLING_TEST_JS_GET_CURRENT_FRAME_TEST_H + +#include "test_util.h" + +namespace panda::tooling::test { +class JsCurrentFrameTest : public ApiTest { +public: + JsCurrentFrameTest() + { + vm_death = [this]() { + ASSERT_EQ(entry_, 111U); + return true; + }; + + method_exit = [this](PtThread thread, Method *method, bool, VRegValue) { + if (method->GetFullName() == "_GLOBAL::func_method3_3") { + ASSERT_EQ(entry_, 0U); + entry_++; + auto frme = debug_interface->GetCurrentFrame(thread); + ASSERT_EQ((*frme)->GetMethod(), method); + } else if (method->GetFullName() == "_GLOBAL::func_method2_2") { + ASSERT_EQ(entry_, 1U); + entry_ += 10; + auto frme = debug_interface->GetCurrentFrame(thread); + ASSERT_EQ((*frme)->GetMethod(), method); + } else if (method->GetFullName() == "_GLOBAL::func_method1_1") { + ASSERT_EQ(entry_, 11U); + entry_ += 100U; + auto frme = debug_interface->GetCurrentFrame(thread); + ASSERT_EQ((*frme)->GetMethod(), method); + } + return true; + }; + } + + std::pair GetEntryPoint() override + { + return {panda_file_.c_str(), entry_point_.c_str()}; + } + +private: + std::string panda_file_ = "js/GetFrame.abc"; + std::string entry_point_ = "_GLOBAL::func_main_0"; + size_t entry_ = 0; +}; + +std::unique_ptr GetJsCurrentFrameTest() +{ + return std::make_unique(); +} +} // namespace panda::tooling::test + +#endif // PANDA_TOOLING_TEST_JS_GET_CURRENT_FRAME_TEST_H diff --git a/tests/runtime/tooling/api_tests/js/js_get_variable_test.h b/tests/runtime/tooling/api_tests/js/js_get_variable_test.h new file mode 100644 index 000000000..96f285850 --- /dev/null +++ b/tests/runtime/tooling/api_tests/js/js_get_variable_test.h @@ -0,0 +1,120 @@ +/** + * Copyright (c) 2021-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 PANDA_TOOLING_TEST_JS_GET_VARIABLE_TEST_H +#define PANDA_TOOLING_TEST_JS_GET_VARIABLE_TEST_H + +#include "test_util.h" +#include "plugins/ecmascript/runtime/include/tooling/pt_ecmascript_extension.h" +#include "plugins/ecmascript/tests/runtime/tooling/js_test_api.h" + +#include +#include + +namespace panda::tooling::test { +using JSTaggedValue = panda::ecmascript::JSTaggedValue; + +class JsGetVariableTest : public ApiTest { +public: + JsGetVariableTest() + { + vm_death = [this]() { + const int expectedCount = 1; + ASSERT_EQ(count_checker_, expectedCount); + return true; + }; + + load_module = [this](std::string_view moduleName) { + if (moduleName.find(panda_file_.c_str()) == std::string_view::npos) { + return true; + } + auto location = TestUtil::GetLocation("GetVariable.js", 19, panda_file_.c_str()); // getVariable + ASSERT_TRUE(location.GetMethodId().IsValid()); + ASSERT_SUCCESS(debug_interface->SetBreakpoint(location)); + return true; + }; + + breakpoint = [this](PtThread thread, Method *method, const PtLocation &location) { + ASSERT_TRUE(CheckSetValues(thread, method, location.GetBytecodeOffset())); + return true; + }; + } + + std::pair GetEntryPoint() override + { + return {panda_file_.c_str(), entry_point_.c_str()}; + } + + bool CheckSetValues(PtThread thread, Method *method, uint32_t offset) + { + std::function checkersList[] = { + [&](JSTaggedValue value) { // boolean: false + ASSERT_EQ(value.GetRawData(), JSTaggedValue::VALUE_FALSE); + return true; + }, + [&](JSTaggedValue value) { // boolean: true + ASSERT_EQ(value.GetRawData(), JSTaggedValue::VALUE_TRUE); + return true; + }, + [&](JSTaggedValue value) { // int: -2147483648 (INT_MIN) + ASSERT_TRUE(value.IsInteger()); + ASSERT_EQ(value.GetDouble(), INT_MIN); + return true; + }, + [&](JSTaggedValue value) { // int: 2147483647 (INT_MAX) + ASSERT_TRUE(value.IsInteger()); + ASSERT_EQ(value.GetInt(), INT_MAX); + return true; + }, + [&](JSTaggedValue value) { // double: 1.5 + ASSERT_TRUE(value.IsDouble()); + ASSERT_EQ(value.GetDouble(), double(1.5)); // 1.5 + return true; + }, + [&](JSTaggedValue value) { // String: "new_string" + PandaString checkedValue; + ASSERT_TRUE(value.IsHeapObject()); + auto vregValue = ecmascript::PtEcmaScriptExtension::TaggedValueToVRegValue(value); + ASSERT_TRUE(ecmascript::JSTestApi::VRegValueToString(vregValue, &checkedValue)); + ASSERT_TRUE(checkedValue.compare("new_string") == 0); + return true; + }}; + + ++count_checker_; + uint32_t frameDepth = 0; + int32_t argNumber = TestUtil::GetValueRegister(method, "bl1", offset); + for (auto checker : checkersList) { + VRegValue vregValue; + ASSERT_SUCCESS(debug_interface->GetVariable(thread, frameDepth, argNumber++, &vregValue)); + ASSERT_TRUE(checker(ecmascript::PtEcmaScriptExtension::VRegValueToTaggedValue(vregValue))); + } + + return true; + } + +private: + std::string panda_file_ = "js/GetVariable.abc"; + std::string entry_point_ = "_GLOBAL::func_main_0"; + + int count_checker_ = 0; +}; + +std::unique_ptr GetJsGetVariableTest() +{ + return std::make_unique(); +} +} // namespace panda::tooling::test + +#endif // PANDA_TOOLING_TEST_JS_GET_VARIABLE_TEST_H diff --git a/tests/runtime/tooling/api_tests/js/js_method_event_test.h b/tests/runtime/tooling/api_tests/js/js_method_event_test.h new file mode 100644 index 000000000..7fa342d47 --- /dev/null +++ b/tests/runtime/tooling/api_tests/js/js_method_event_test.h @@ -0,0 +1,63 @@ +/** + * Copyright (c) 2021-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 PANDA_TOOLING_TEST_JS_METHOD_EVENT_TEST_H +#define PANDA_TOOLING_TEST_JS_METHOD_EVENT_TEST_H + +#include "test_util.h" + +namespace panda::tooling::test { +class JsMethodEventTest : public ApiTest { +public: + JsMethodEventTest() + { + vm_death = [this]() { + ASSERT_EQ(method_entry_exit_count_, 0); + ASSERT_NE(method_entry_count_, 0); + return true; + }; + + method_entry = [this](PtThread, Method *) { + method_entry_exit_count_++; + method_entry_count_++; + return true; + }; + + method_exit = [this](PtThread, Method *, bool, VRegValue) { + method_entry_exit_count_--; + return true; + }; + } + + std::pair GetEntryPoint() override + { + return {panda_file_.c_str(), entry_point_.c_str()}; + } + +private: + std::string panda_file_ = "js/Sample.abc"; + std::string entry_point_ = "_GLOBAL::func_main_0"; + + int method_entry_exit_count_ = 0; + int method_entry_count_ = 0; +}; + +std::unique_ptr GetJsMethodEventTest() +{ + return std::make_unique(); +} +} // namespace panda::tooling::test + +#endif // PANDA_TOOLING_TEST_JS_METHOD_EVENT_TEST_H diff --git a/tests/runtime/tooling/api_tests/js/js_restart_frame_test.h b/tests/runtime/tooling/api_tests/js/js_restart_frame_test.h new file mode 100644 index 000000000..27858b087 --- /dev/null +++ b/tests/runtime/tooling/api_tests/js/js_restart_frame_test.h @@ -0,0 +1,125 @@ +/** + * Copyright (c) 2021-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 PANDA_TOOLING_TEST_JS_SET_RESTART_FRAME_TEST_H +#define PANDA_TOOLING_TEST_JS_SET_RESTART_FRAME_TEST_H + +#include "test_util.h" +#include "plugins/ecmascript/runtime/include/tooling/pt_ecmascript_extension.h" + +namespace panda::tooling::test { +using JSTaggedValue = panda::ecmascript::JSTaggedValue; + +class JsRestartFrameTest : public ApiTest { +public: + JsRestartFrameTest() + { + vm_start = [this] { + location_ = TestUtil::GetLocation("RestartFrame.js", 35, panda_file_.c_str()); + ASSERT_TRUE(location_.GetMethodId().IsValid()); + return true; + }; + + breakpoint = [this](PtThread thread, Method *, const PtLocation &location) { + ASSERT_TRUE(location.GetMethodId().IsValid()); + ASSERT_LOCATION_EQ(location, location_); + ++breakpoint_counter_; + TestUtil::SuspendUntilContinue(DebugEvent::BREAKPOINT, thread, location); + if (breakpoint_counter_ == 1) { + ASSERT_SUCCESS(debug_interface->RestartFrame(thread, 2)); + } + if (breakpoint_counter_ == 2) { + ASSERT_SUCCESS(debug_interface->RestartFrame(thread, 0)); + } + return true; + }; + + load_module = [this](std::string_view moduleName) { + if (moduleName.find(panda_file_.c_str()) == std::string_view::npos) { + return true; + } + ASSERT_SUCCESS(debug_interface->SetBreakpoint(location_)); + return true; + }; + + method_entry = [this](PtThread, Method *) { + ++entry_exit_counter_; + return true; + }; + + method_exit = [this](PtThread, Method *method, bool, VRegValue val) { + auto moduleName = method->GetFullName(); + if (moduleName == "_GLOBAL::func_2") { + // Force exit always zero + auto taggedValue = ecmascript::PtEcmaScriptExtension::VRegValueToTaggedValue(val); + if (taggedValue.IsInt()) { + result_ = taggedValue.GetInt(); + } + } + + --entry_exit_counter_; + return true; + }; + + scenario = [this]() { + ASSERT_BREAKPOINT_SUCCESS(location_); + TestUtil::Continue(); + + ASSERT_BREAKPOINT_SUCCESS(location_); + TestUtil::Continue(); + + ASSERT_BREAKPOINT_SUCCESS(location_); + TestUtil::Continue(); + + ASSERT_EXITED(); + return true; + }; + + vm_death = [this] { + // result_ indicate count of calls + // frame0 +1 + // frame1 +10 + // frame2 +100 +100000 + // frame3 +1000 +100000 + // frame4 +10000 +100000 + ASSERT_EQ(result_, 322311); + ASSERT_EQ(entry_exit_counter_, 0U); + ASSERT_EQ(breakpoint_counter_, 3U); + + return true; + }; + } + + std::pair GetEntryPoint() override + { + return {panda_file_.c_str(), entry_point_.c_str()}; + } + +private: + std::string panda_file_ = "js/RestartFrame.abc"; + std::string entry_point_ = "_GLOBAL::func_main_0"; + PtLocation location_ {nullptr, PtLocation::EntityId(0), 0}; + size_t breakpoint_counter_ = 0; + size_t entry_exit_counter_ = 0; + int64_t result_ = 0; +}; + +std::unique_ptr GetJsRestartFrameTest() +{ + return std::make_unique(); +} +} // namespace panda::tooling::test + +#endif // PANDA_TOOLING_TEST_JS_SET_RESTART_FRAME_TEST_H diff --git a/tests/runtime/tooling/api_tests/js/js_set_notification_test.h b/tests/runtime/tooling/api_tests/js/js_set_notification_test.h new file mode 100644 index 000000000..c5c325dff --- /dev/null +++ b/tests/runtime/tooling/api_tests/js/js_set_notification_test.h @@ -0,0 +1,109 @@ +/** + * Copyright (c) 2021-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 PANDA_TOOLING_TEST_JS_SET_NOTIFICATION_TEST_H +#define PANDA_TOOLING_TEST_JS_SET_NOTIFICATION_TEST_H + +#include "test_util.h" +#include "runtime/include/tooling/debug_interface.h" + +namespace panda::tooling::test { +class JsSetNotificationTest : public ApiTest { +public: + JsSetNotificationTest() + { + vm_death = [this]() { + ASSERT_EQ(nn_, 5); + ASSERT_EQ(entry_, 3); + ASSERT_EQ(exit_, 5); + return true; + }; + + method_entry = [this](PtThread /* thread */, Method *method) { + if (method->GetFullName() == "_GLOBAL::func__2") { + ++entry_; + } + return true; + }; + + method_exit = [this](PtThread thread, Method *method, bool, VRegValue) { + if (method->GetFullName() == "_GLOBAL::func__2") { + ++exit_; + ++nn_; + + // NN | hook | action | counter value + // ---+-------+--------+--------------- + // 1 | entry | +=1 | 1 + // | exit | +=1 | 1 + // 2 | entry | skip | 1 + // | exit | +=1 | 2 + // 3 | entry | +=1 | 2 + // | exit | +=1 | 3 + // 4 | entry | skip | 2 + // | exit | +=1 | 4 + // 5 | entry | +=1 | 3 + // | exit | +=1 | 5 + + if (nn_ == 1) { + ASSERT_EQ(entry_, 1); + ASSERT_EQ(exit_, 1); + + // Disable the entry hook globaly + debug_interface->SetNotification(PtThread::NONE, false, PtHookType::PT_HOOK_TYPE_METHOD_ENTRY); + } else if (nn_ == 2) { + ASSERT_EQ(entry_, 1); + ASSERT_EQ(exit_, 2); + + // Enable the entry hook locally + debug_interface->SetNotification(thread, true, PtHookType::PT_HOOK_TYPE_METHOD_ENTRY); + } else if (nn_ == 3) { + ASSERT_EQ(entry_, 2); + ASSERT_EQ(exit_, 3); + + // Disable the entry hook locally + debug_interface->SetNotification(thread, false, PtHookType::PT_HOOK_TYPE_METHOD_ENTRY); + } else if (nn_ == 4) { + ASSERT_EQ(entry_, 2); + ASSERT_EQ(exit_, 4); + + // Enable the entry hook globaly + debug_interface->SetNotification(PtThread::NONE, true, PtHookType::PT_HOOK_TYPE_METHOD_ENTRY); + } else if (nn_ == 5) { + ASSERT_EQ(entry_, 3); + ASSERT_EQ(exit_, 5); + } + } + return true; + }; + } + + std::pair GetEntryPoint() override + { + return {"js/SetNotification.abc", "_GLOBAL::func_main_0"}; + } + +private: + int nn_ = 0; + int entry_ = 0; + int exit_ = 0; +}; + +std::unique_ptr GetJsSetNotificationTest() +{ + return std::make_unique(); +} +} // namespace panda::tooling::test + +#endif // PANDA_TOOLING_TEST_JS_SET_NOTIFICATION_TEST_H diff --git a/tests/runtime/tooling/api_tests/js/js_set_variable_test.h b/tests/runtime/tooling/api_tests/js/js_set_variable_test.h new file mode 100644 index 000000000..9e92062ad --- /dev/null +++ b/tests/runtime/tooling/api_tests/js/js_set_variable_test.h @@ -0,0 +1,164 @@ +/** + * Copyright (c) 2021-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 PANDA_TOOLING_TEST_JS_SET_VARIABLE_TEST_H +#define PANDA_TOOLING_TEST_JS_SET_VARIABLE_TEST_H + +#include "test_util.h" +#include "plugins/ecmascript/runtime/include/tooling/pt_ecmascript_extension.h" +#include "plugins/ecmascript/tests/runtime/tooling/js_test_api.h" +#include "runtime/include/mem/panda_string.h" +#include "plugins/ecmascript/runtime/js_thread.h" +#include "plugins/ecmascript/runtime/global_env.h" + +namespace panda::tooling::test { +using JSTaggedValue = panda::ecmascript::JSTaggedValue; +using JSThread = panda::ecmascript::JSThread; +class JsSetVariableTest : public ApiTest { +public: + JsSetVariableTest() + { + vm_death = [this]() { + ASSERT_TRUE(checked_); + ASSERT_EQ(breakpoint_count_, 4); + return true; + }; + + load_module = [this](std::string_view moduleName) { + if (moduleName.find(panda_file_.c_str()) == std::string_view::npos) { + return true; + } + SetBreakpoint(22); // setBoolean + SetBreakpoint(26); // setInt + SetBreakpoint(30); // setDouble + SetBreakpoint(34); // setString + return true; + }; + + breakpoint = [this](PtThread thread, Method *method, PtLocation location) { + breakpoint_count_ += 1; + auto methodName = method->GetFullName(); + int frameDepth = 0; + uint32_t currOffset = location.GetBytecodeOffset(); + if (methodName == "_GLOBAL::func_setBoolean_1") { + VRegValue value = ecmascript::PtEcmaScriptExtension::TaggedValueToVRegValue(JSTaggedValue(true)); + ASSERT_SUCCESS(debug_interface->SetVariable( + thread, frameDepth, TestUtil::GetValueRegister(method, "value", currOffset), value)); + } else if (methodName == "_GLOBAL::func_setInt_2") { + VRegValue value = + ecmascript::PtEcmaScriptExtension::TaggedValueToVRegValue(JSTaggedValue(123456789)); // 123456789 + ASSERT_SUCCESS(debug_interface->SetVariable( + thread, frameDepth, TestUtil::GetValueRegister(method, "value", currOffset), value)); + } else if (methodName == "_GLOBAL::func_setDouble_3") { + VRegValue value = + ecmascript::PtEcmaScriptExtension::TaggedValueToVRegValue(JSTaggedValue(12345.6789)); // 12345.6789 + ASSERT_SUCCESS(debug_interface->SetVariable( + thread, frameDepth, TestUtil::GetValueRegister(method, "value", currOffset), value)); + } else if (methodName == "_GLOBAL::func_setString_4") { + VRegValue value = ecmascript::JSTestApi::StringToVRegValue("x2348x"); + ASSERT_SUCCESS(debug_interface->SetVariable( + thread, frameDepth, TestUtil::GetValueRegister(method, "value", currOffset), value)); + } + return true; + }; + + method_entry = [this](PtThread thread, Method *method) { + auto methodName = method->GetFullName(); + if (methodName == "_GLOBAL::func_checkData_5") { + checked_ = CheckData(thread, method); + } + return true; + }; + } + + std::pair GetEntryPoint() override + { + return {panda_file_.c_str(), entry_point_.c_str()}; + } + +private: + static bool GetGlobalVariable(const PandaString &name, VRegValue *out) + { + JSThread *jsThread = JSThread::Cast(JSThread::GetCurrent()); + auto ecmaVm = jsThread->GetEcmaVM(); + panda::ecmascript::JSHandle globalEnv = ecmaVm->GetGlobalEnv(); + auto globalObject = globalEnv->GetGlobalObject(); + + panda::ecmascript::ObjectFactory *factory = ecmaVm->GetFactory(); + panda::ecmascript::JSHandle execHandle( + factory->NewFromStdString(name.c_str())); + panda::ecmascript::JSHandle objectHandle(jsThread, globalObject); + if (!panda::ecmascript::JSObject::HasProperty(jsThread, objectHandle, execHandle)) { + return false; + } + + auto property = panda::ecmascript::JSObject::GetProperty(jsThread, objectHandle, execHandle); + *out = ecmascript::PtEcmaScriptExtension::TaggedValueToVRegValue(property.GetValue().GetTaggedValue()); + return true; + } + + bool CheckData(PtThread, Method *) + { + { + VRegValue ptBool; + ASSERT_TRUE(GetGlobalVariable("boolData", &ptBool)); + ASSERT_EQ(ecmascript::PtEcmaScriptExtension::VRegValueToTaggedValue(ptBool).GetRawData(), + JSTaggedValue::VALUE_TRUE); + } + { + VRegValue ptInt; + const int expectedValue = 123456789; + ASSERT_TRUE(GetGlobalVariable("intData", &ptInt)); + int intValue = ecmascript::PtEcmaScriptExtension::VRegValueToTaggedValue(ptInt).GetInt(); + ASSERT_EQ(intValue, expectedValue); + } + { + VRegValue ptDouble; + const double expectedValue = 12345.6789; + ASSERT_TRUE(GetGlobalVariable("doubleData", &ptDouble)); + double doubleValue = ecmascript::PtEcmaScriptExtension::VRegValueToTaggedValue(ptDouble).GetDouble(); + ASSERT_EQ(doubleValue, expectedValue); + } + { + VRegValue ptString; + PandaString stringValue; + const PandaString expectedValue = "x2348x"; + ASSERT_TRUE(GetGlobalVariable("stringData", &ptString)); + ASSERT_TRUE(ecmascript::JSTestApi::VRegValueToString(ptString, &stringValue)); + ASSERT_TRUE(stringValue.compare(expectedValue) == 0); + } + return true; + } + + void SetBreakpoint(uint32_t line) + { + auto location = TestUtil::GetLocation("SetVariable.js", line, panda_file_.c_str()); + ASSERT_TRUE(location.GetMethodId().IsValid()); + ASSERT_SUCCESS(debug_interface->SetBreakpoint(location)); + } + + bool checked_ = false; + uint32_t breakpoint_count_ = 0; + std::string panda_file_ = "js/SetVariable.abc"; + std::string entry_point_ = "_GLOBAL::func_main_0"; +}; + +std::unique_ptr GetJsSetVariableTest() +{ + return std::make_unique(); +} +} // namespace panda::tooling::test + +#endif // PANDA_TOOLING_TEST_JS_SET_VARIABLE_TEST_H diff --git a/tests/runtime/tooling/api_tests/js/js_single_step_test.h b/tests/runtime/tooling/api_tests/js/js_single_step_test.h new file mode 100644 index 000000000..93b57466d --- /dev/null +++ b/tests/runtime/tooling/api_tests/js/js_single_step_test.h @@ -0,0 +1,98 @@ +/** + * Copyright (c) 2021-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 PANDA_TOOLING_TEST_JS_SINGLE_STEP_TEST_H +#define PANDA_TOOLING_TEST_JS_SINGLE_STEP_TEST_H + +#include "test_util.h" + +namespace panda::tooling::test { +class JsSingleStepTest : public ApiTest { +public: + JsSingleStepTest() + { + vm_start = [this] { + location_start_ = TestUtil::GetLocation("Sample.js", 19, panda_file_.c_str()); + location_end_ = TestUtil::GetLocation("Sample.js", 22, panda_file_.c_str()); + return true; + }; + + vm_death = [this]() { + ASSERT_NE(step_count_, 0); + ASSERT_EQ(breakpoint_count_, 2); + return true; + }; + + load_module = [this](std::string_view moduleName) { + if (moduleName.find(panda_file_.c_str()) == std::string_view::npos) { + return true; + } + ASSERT_SUCCESS(debug_interface->SetBreakpoint(location_end_)); + return true; + }; + + breakpoint = [this](PtThread, Method *, const PtLocation &location) { + ASSERT_TRUE(location.GetMethodId().IsValid()); + ASSERT_LOCATION_EQ(location, location_end_); + // Check's what step signalled before breakpoint + ASSERT_LOCATION_EQ(location, location_step_); + ASSERT_TRUE(collect_steps_); + breakpoint_count_++; + // Disable collect steps + collect_steps_ = false; + return true; + }; + + single_step = [this](PtThread, Method *, const PtLocation &location) { + ASSERT_TRUE(location.GetMethodId().IsValid()); + if (!collect_steps_) { + if (location_start_ == location) { + collect_steps_ = true; + } + return true; + } + + ASSERT_NE(bytecode_offset_, location.GetBytecodeOffset()); + location_step_ = location; + step_count_++; + bytecode_offset_ = location.GetBytecodeOffset(); + return true; + }; + } + + std::pair GetEntryPoint() override + { + return {panda_file_.c_str(), entry_point_.c_str()}; + } + +private: + std::string panda_file_ = "js/Sample.abc"; + std::string entry_point_ = "_GLOBAL::func_main_0"; + PtLocation location_start_ {nullptr, PtLocation::EntityId(0), 0}; + PtLocation location_end_ {nullptr, PtLocation::EntityId(0), 0}; + PtLocation location_step_ {nullptr, PtLocation::EntityId(0), 0}; + int step_count_ = 0; + int breakpoint_count_ = 0; + bool collect_steps_ = false; + uint32_t bytecode_offset_ = std::numeric_limits::max(); +}; + +std::unique_ptr GetJsSingleStepTest() +{ + return std::make_unique(); +} +} // namespace panda::tooling::test + +#endif // PANDA_TOOLING_TEST_JS_SINGLE_STEP_TEST_H diff --git a/tests/runtime/tooling/api_tests/js/js_vm_event_test.h b/tests/runtime/tooling/api_tests/js/js_vm_event_test.h new file mode 100644 index 000000000..5777a8cd2 --- /dev/null +++ b/tests/runtime/tooling/api_tests/js/js_vm_event_test.h @@ -0,0 +1,66 @@ +/** + * Copyright (c) 2021-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 PANDA_TOOLING_TEST_JS_VM_EVENT_TEST_H +#define PANDA_TOOLING_TEST_JS_VM_EVENT_TEST_H + +#include "test_util.h" + +namespace panda::tooling::test { +class JsVmEventTest : public ApiTest { +public: + JsVmEventTest() + { + vm_start = [this]() { + start_counter++; + return true; + }; + + vm_init = [this](PtThread thread) { + init_thread_ = thread.GetId(); + init_counter_++; + return true; + }; + + vm_death = [this]() { + death_counter_++; + ASSERT_NE(init_thread_, PtThread::NONE.GetId()); + ASSERT_EQ(start_counter, 1U); + ASSERT_EQ(init_counter_, 1U); + return true; + }; + } + + std::pair GetEntryPoint() override + { + return {panda_file_.c_str(), entry_point_.c_str()}; + } + +private: + std::string panda_file_ = "js/Sample.abc"; + std::string entry_point_ = "_GLOBAL::func_main_0"; + size_t start_counter = 0; + size_t init_counter_ = 0; + size_t death_counter_ = 0; + uint32_t init_thread_ = PtThread::NONE.GetId(); +}; + +std::unique_ptr GetJsVmEventTest() +{ + return std::make_unique(); +} +} // namespace panda::tooling::test + +#endif // PANDA_TOOLING_TEST_JS_VM_EVENT_TEST_H diff --git a/tests/runtime/tooling/js/ExceptionTest.js b/tests/runtime/tooling/js/ExceptionTest.js new file mode 100644 index 000000000..159abee55 --- /dev/null +++ b/tests/runtime/tooling/js/ExceptionTest.js @@ -0,0 +1,20 @@ +/* + * 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. + */ + +try { + throw new UserException("Exception string"); +} catch (e) { + print("Exception caught"); +} \ No newline at end of file diff --git a/tests/runtime/tooling/js/FramePop.js b/tests/runtime/tooling/js/FramePop.js new file mode 100644 index 000000000..c0e6a6f43 --- /dev/null +++ b/tests/runtime/tooling/js/FramePop.js @@ -0,0 +1,38 @@ +/* + * 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. + */ + +function frame3() { + frame2(); + return 3; +} + +function frame2() { + frame1(); + return 2; +} + +function frame1() { + frame0(); + return 1; +} + +function frame0() { + // Breakpoint here + var a = 0; + return a; +} + +frame3(); +frame3(); diff --git a/tests/runtime/tooling/js/GetFrame.js b/tests/runtime/tooling/js/GetFrame.js new file mode 100644 index 000000000..ce9a04757 --- /dev/null +++ b/tests/runtime/tooling/js/GetFrame.js @@ -0,0 +1,42 @@ +/* + * 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. + */ + + +var g_a = 0; +var g_str = ""; +var g_b = 0; + +function method1(a_int, s_str, b_int) { + g_a = a_int; + if (method2(s_str, b_int)) + return a_int + 1; + else + return 0; +} + +function method2(s_str, b_int) { + g_str = s_str; + method3(b_int); + return (g_b == b_int); +} + +function method3(b_int) { + g_b = b_int; +} + +var a = 1000; +var s = "Test String"; +var b = 12345; +a = method1(a, s, b); diff --git a/tests/runtime/tooling/js/GetVariable.js b/tests/runtime/tooling/js/GetVariable.js new file mode 100644 index 000000000..3b9d5c201 --- /dev/null +++ b/tests/runtime/tooling/js/GetVariable.js @@ -0,0 +1,27 @@ +/* + * 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. + */ + +function getVariable(bl1, bl2, i1, i2, d, obj) { + // check variables in native code + // ! use variables here for correct debug info + return (bl1 + bl2 + i1 + i2 + d + obj); +} + +getVariable( + false, true, + -2147483648, 2147483647, + 1.5, + "new_string" + ); diff --git a/tests/runtime/tooling/js/RestartFrame.js b/tests/runtime/tooling/js/RestartFrame.js new file mode 100644 index 000000000..6dcd09fd5 --- /dev/null +++ b/tests/runtime/tooling/js/RestartFrame.js @@ -0,0 +1,50 @@ +/* + * 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. + */ + +function MyClass () { // constructor function + this.FrameValue = 0; // Public variable + + this.frame4 = function () { + this.FrameValue += 10000; + this.frame3(); + this.FrameValue += 100000; + return this.FrameValue; + } + + this.frame3 = function () { + this.FrameValue += 1000; + this.frame2(); + this.FrameValue += 100000; + } + + this.frame2 = function () { + this.FrameValue += 100; + // Break point. And restart frame + this.frame1(); + this.FrameValue += 100000; + } + + this.frame1 = function () { + this.FrameValue += 10; + this.frame0(); + } + + this.frame0 = function () { + this.FrameValue += 1; + } +} + +var myInstance = new MyClass(); +var res = myInstance.frame4(); diff --git a/tests/runtime/tooling/js/Sample.js b/tests/runtime/tooling/js/Sample.js new file mode 100644 index 000000000..9a21b8b5c --- /dev/null +++ b/tests/runtime/tooling/js/Sample.js @@ -0,0 +1,30 @@ +/* + * 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. + */ + +function count_to_ten() { + var a = 1; + a = 2; + a = 3; + a = 4; + a = 5; + a = 6; + a = 7; + a = 8; + a = 9; + a = 10; +} + +count_to_ten(); +count_to_ten(); diff --git a/tests/runtime/tooling/js/SetNotification.js b/tests/runtime/tooling/js/SetNotification.js new file mode 100644 index 000000000..5d8ee4d02 --- /dev/null +++ b/tests/runtime/tooling/js/SetNotification.js @@ -0,0 +1,26 @@ +/* + * 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. + */ + +function SetNotification () { + this.notificationMethod = function () {} + +} + +var myInstance = new SetNotification(); +var i; + +for (i = 0; i < 5; i++) { + myInstance.notificationMethod(); +} \ No newline at end of file diff --git a/tests/runtime/tooling/js/SetVariable.js b/tests/runtime/tooling/js/SetVariable.js new file mode 100644 index 000000000..fbb034695 --- /dev/null +++ b/tests/runtime/tooling/js/SetVariable.js @@ -0,0 +1,46 @@ +/* + * 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. + */ + +var boolData = false; +var intData = 0; +var doubleData = 0.0; +var stringData = ""; + +function setBoolean(value) { + boolData = value; +}; + +function setInt(value) { + intData = value; +}; + +function setDouble(value) { + doubleData = value; +}; + +function setString(value) { + stringData = value; +}; + +function checkData() { + // check in native code +} + +setBoolean(false); +setInt(0); +setDouble(0.0); +setString(""); + +checkData(); diff --git a/tests/runtime/tooling/js_test_api.h b/tests/runtime/tooling/js_test_api.h new file mode 100644 index 000000000..6d8355ccc --- /dev/null +++ b/tests/runtime/tooling/js_test_api.h @@ -0,0 +1,50 @@ +/* + * Copyright (c) 2021-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 PANDA_PLUGINS_ECMASCRIPT_TESTS_RUNTIME_TOOLING_JS_TEST_API_H +#define PANDA_PLUGINS_ECMASCRIPT_TESTS_RUNTIME_TOOLING_JS_TEST_API_H + +#include "runtime/include/tooling/debug_interface.h" +#include "plugins/ecmascript/runtime/include/tooling/pt_ecmascript_extension.h" +#include "plugins/ecmascript/runtime/js_handle.h" +#include "plugins/ecmascript/runtime/global_env.h" + +namespace panda::tooling::ecmascript { +using JSThread = panda::ecmascript::JSThread; +class JSTestApi { +public: + static VRegValue StringToVRegValue(const PandaString &value) + { + JSThread *jsThread = JSThread::Cast(JSThread::GetCurrent()); + auto ecmaVm = jsThread->GetEcmaVM(); + + panda::ecmascript::ObjectFactory *factory = ecmaVm->GetFactory(); + panda::ecmascript::JSHandle strHandle( + factory->NewFromStdString(value.c_str())); + + return PtEcmaScriptExtension::TaggedValueToVRegValue(strHandle.GetTaggedValue()); + } + static bool VRegValueToString(VRegValue value, PandaString *out) + { + panda::ecmascript::EcmaString *string = + panda::ecmascript::EcmaString::Cast(PtEcmaScriptExtension::VRegValueToTaggedValue(value).GetHeapObject()); + + *out = panda::ecmascript::base::StringHelper::ToStdString(string).c_str(); + return true; + } +}; +} // namespace panda::tooling::ecmascript + +#endif // PANDA_PLUGINS_ECMASCRIPT_TESTS_RUNTIME_TOOLING_JS_TEST_API_H diff --git a/tests/runtime/tooling/launcher.cpp b/tests/runtime/tooling/launcher.cpp new file mode 100644 index 000000000..6c7b6d2e5 --- /dev/null +++ b/tests/runtime/tooling/launcher.cpp @@ -0,0 +1,75 @@ +/** + * Copyright (c) 2021-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 + +#include "runtime/include/runtime.h" +#include "test_list.h" +#include "runtime/tests/tooling/test_extractor.h" +#include "generated/base_options.h" + +namespace panda::tooling::test { +extern void SetExtractorFactoryForTest(TestExtractorFactory *test_extractor); + +class BaseDebugApiTest : public testing::TestWithParam { +public: + virtual ~BaseDebugApiTest() = default; + +protected: + void RunTest(RuntimeOptions &options, const char *testName) const + { + std::cout << "Running " << testName << std::endl; + SetCurrentTestName(testName); + auto *factory = new TestExtractorFactory(); + SetExtractorFactoryForTest(factory); + Logger::Initialize(base_options::Options("")); + auto [pandaFile, entryPoint] = GetTestEntryPoint(testName); + auto bootFiles = options.GetBootPandaFiles(); + bootFiles.push_back(pandaFile); + options.SetBootPandaFiles(bootFiles); + ASSERT_TRUE(Runtime::Create(options)) << testName; + auto res = Runtime::GetCurrent()->ExecutePandaFile(pandaFile, entryPoint, {}); + ASSERT_TRUE(res.HasValue()); + delete factory; + ASSERT_TRUE(Runtime::Destroy()); + } +}; + +class EcmaScriptDebugApiTest : public BaseDebugApiTest { +protected: + void RunEcmaScriptTest(const char *testName) const + { + RuntimeOptions options; + options.SetDebuggerLibraryPath(DEBUG_LIBRARY_PATH); + options.SetBootPandaFiles({PANDA_STD_LIB}); + options.SetLoadRuntimes({"ecmascript"}); + options.SetRunGcInPlace(true); + RunTest(options, testName); + } +}; + +TEST_P(EcmaScriptDebugApiTest, EcmaScriptSuite) +{ + const char *testName = GetParam(); + RunEcmaScriptTest(testName); +} + +INSTANTIATE_TEST_SUITE_P(DebugApiTests, EcmaScriptDebugApiTest, + ::testing::ValuesIn(GetTestList(panda_file::SourceLang::ECMASCRIPT)), + [](const testing::TestParamInfo &einfo) { + return einfo.param; + }); + +} // namespace panda::tooling::test diff --git a/tests/runtime/tooling/options_test/options_test.cpp b/tests/runtime/tooling/options_test/options_test.cpp new file mode 100644 index 000000000..8d3db2d73 --- /dev/null +++ b/tests/runtime/tooling/options_test/options_test.cpp @@ -0,0 +1,50 @@ +/** + * Copyright (c) 2022-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 +#include + +#include "runtime/include/runtime_options.h" +#include "libpandabase/utils/pandargs.h" +#include "runtime/tests/options_test_base.h" + +namespace panda::test { + +class EcmascriptRuntimeOptionsTest : public RuntimeOptionsTestBase { +public: + EcmascriptRuntimeOptionsTest() = default; + ~EcmascriptRuntimeOptionsTest() = default; + +private: + void LoadCorrectOptionsList() override; +}; + +void EcmascriptRuntimeOptionsTest::LoadCorrectOptionsList() +{ + AddTestingOption("ecmascript.run-gc-in-place", "true"); + AddTestingOption("ecmascript.gc-dump-heap", "true"); + AddTestingOption("ecmascript.pre-gc-heap-verify-enabled", "true"); + AddTestingOption("gc-trigger-type", "no-gc-for-start-up"); +} + +TEST_F(EcmascriptRuntimeOptionsTest, TestLangSpecificOptions) +{ + ASSERT_TRUE(GetParser()->Parse(GetCorrectOptionsList())); + ASSERT_EQ(GetRuntimeOptions()->GetGcTriggerType("core"), "no-gc-for-start-up"); + // Check that if we read JS specific option, it has the same value as a common one + ASSERT_EQ(GetRuntimeOptions()->GetGcTriggerType("core"), GetRuntimeOptions()->GetGcTriggerType("ecmascript")); +} + +} // namespace panda::test diff --git a/tests/runtime/tooling/test_list.cpp b/tests/runtime/tooling/test_list.cpp new file mode 100644 index 000000000..d62e25544 --- /dev/null +++ b/tests/runtime/tooling/test_list.cpp @@ -0,0 +1,72 @@ +/** + * Copyright (c) 2021-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 "test_list.h" + +#include "api_tests/api_tests.h" +#include "test_util.h" + +namespace panda::tooling::test { +static const char *g_currentTestName = nullptr; + +static void RegisterTests() +{ + TestUtil::RegisterTest(panda_file::SourceLang::ECMASCRIPT, "JsBreakpoint", GetJsBreakpointTest()); + TestUtil::RegisterTest(panda_file::SourceLang::ECMASCRIPT, "JsSingleStepTest", GetJsSingleStepTest()); + TestUtil::RegisterTest(panda_file::SourceLang::ECMASCRIPT, "JsVMEvents", GetJsVmEventTest()); + TestUtil::RegisterTest(panda_file::SourceLang::ECMASCRIPT, "JsMethodEvent", GetJsMethodEventTest()); + TestUtil::RegisterTest(panda_file::SourceLang::ECMASCRIPT, "JsGetVariable", GetJsGetVariableTest()); + TestUtil::RegisterTest(panda_file::SourceLang::ECMASCRIPT, "JsSetVariable", GetJsSetVariableTest()); + TestUtil::RegisterTest(panda_file::SourceLang::ECMASCRIPT, "JsGetCurrentFrame", GetJsCurrentFrameTest()); + TestUtil::RegisterTest(panda_file::SourceLang::ECMASCRIPT, "JsEnumerateFrames", JsEnumerateFramesTest()); + // TODO(maksenov): Enable tests with thread suspension for JS + // TestUtil::RegisterTest(panda_file::SourceLang::ECMASCRIPT, "JsFramePopNotification", GetJsFramePopTest()); + // TestUtil::RegisterTest(panda_file::SourceLang::ECMASCRIPT, "JsRestartFrame", GetJsRestartFrameTest()); + TestUtil::RegisterTest(panda_file::SourceLang::ECMASCRIPT, "JsSetNotification", GetJsSetNotificationTest()); + TestUtil::RegisterTest(panda_file::SourceLang::ECMASCRIPT, "JsExceptionCatchThrowEvents", + GetJsExceptionEventTest()); +} + +std::vector GetTestList(panda_file::SourceLang language) +{ + RegisterTests(); + std::vector res; + auto &tests = TestUtil::GetTests(); + auto languageIt = tests.find(language); + if (languageIt == tests.end()) { + return {}; + } + + for (const auto &entry : languageIt->second) { + res.push_back(entry.first); + } + return res; +} + +void SetCurrentTestName(const char *testName) +{ + g_currentTestName = testName; +} + +const char *GetCurrentTestName() +{ + return g_currentTestName; +} + +std::pair GetTestEntryPoint(const char *testName) +{ + return TestUtil::GetTest(testName)->GetEntryPoint(); +} +} // namespace panda::tooling::test diff --git a/tests/runtime/tooling/test_list.h b/tests/runtime/tooling/test_list.h new file mode 100644 index 000000000..3968c298b --- /dev/null +++ b/tests/runtime/tooling/test_list.h @@ -0,0 +1,31 @@ +/** + * Copyright (c) 2021-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 PANDA_RUNTIME_DEBUG_TEST_TEST_LIST_H_ +#define PANDA_RUNTIME_DEBUG_TEST_TEST_LIST_H_ + +#include +#include + +#include "libpandafile/file_items.h" + +namespace panda::tooling::test { +std::vector GetTestList(panda_file::SourceLang language); + +void SetCurrentTestName(const char *test_name); + +std::pair GetTestEntryPoint(const char *test_name); +} // namespace panda::tooling::test + +#endif // PANDA_RUNTIME_DEBUG_TEST_TEST_LIST_H_ -- Gitee